On 01/31/17 13:36, Soner Tari wrote:
>> Synopsis: With large line numbers, tail(1) makes the system unusable.
>> Category: system
>> Environment:
>       System      : OpenBSD 6.0
>       Details     : OpenBSD 6.0 (GENERIC) #2148: Tue Jul 26 12:55:20
> MDT 2016
>                        [email protected]:/usr/src/sys/arch/am
> d64/compile/GENERIC
> 
>       Architecture: OpenBSD.amd64
>       Machine     : amd64
>> Description:
> When provided with a large line number, tail gives "Cannot allocate
> memory" to a regular user, but when run as root tail gets stuck making
> the system unusable.
> 
> The same issue exists on 5.9 too. Note that I see a couple of changes
> to tail on 5.9 Changelog.
> 
>> How-To-Repeat:
> Simply run the following command line as root:
> 
> echo blah | /usr/bin/tail -99999999
> 
> Normally, it should print blah and exit, or give "Cannot allocate
> memory" as it does to a regular user. (Note that tail on Linux prints
> blah or gives "Numerical result out of range" for much larger numbers,
> to both regular users and root.)
> 
>> Fix:

Here's a patch to allocate memory in chunks of 1024.
This value arbitrarily chosen.

It still won't save you if your input is 99999999 lines long,
but it will safe you in this use case.

martijn@

Index: read.c
===================================================================
RCS file: /cvs/src/usr.bin/tail/read.c,v
retrieving revision 1.17
diff -u -p -r1.17 read.c
--- read.c      19 Nov 2015 17:50:04 -0000      1.17
+++ read.c      31 Jan 2017 15:18:49 -0000
@@ -141,20 +141,17 @@ lines(struct tailfile *tf, off_t off)
                size_t blen;
                size_t len;
                char *l;
-       } *lines;
+       } *lines = NULL;
        int ch, rc = 0;
        char *p = NULL;
        int wrap;
-       size_t cnt, recno, blen, newsize;
+       size_t cnt, lineno, nlineno, recno, blen, newsize;
        char *sp = NULL, *newp = NULL;
 
        if (off > SIZE_MAX)
                errx(1, "offset too large");
 
-       if ((lines = calloc(off, sizeof(*lines))) == NULL)
-               err(1, NULL);
-
-       blen = cnt = recno = wrap = 0;
+       lineno = blen = cnt = recno = wrap = 0;
 
        while ((ch = getc(tf->fp)) != EOF) {
                if (++cnt > blen) {
@@ -166,6 +163,15 @@ lines(struct tailfile *tf, off_t off)
                        p = sp + cnt - 1;
                }
                *p++ = ch;
+               if (recno >= lineno) {
+                       nlineno = (lineno + 1024) > off ?
+                           (size_t) off : lineno + 1024;
+                       lines = reallocarray(lines, nlineno, sizeof(*lines));
+                       if (lines == NULL)
+                               err(1, NULL);
+                       bzero(lines+recno, nlineno - lineno);
+                       lineno = nlineno;
+               }
                if (ch == '\n') {
                        if (lines[recno].blen < cnt) {
                                newsize = cnt + 256;
@@ -213,7 +219,7 @@ lines(struct tailfile *tf, off_t off)
                        WR(lines[cnt].l, lines[cnt].len);
        }
 done:
-       for (cnt = 0; cnt < off; cnt++)
+       for (cnt = 0; cnt < lineno; cnt++)
                free(lines[cnt].l);
        free(sp);
        free(lines);

Reply via email to