On Fri, Dec 31, 2021 at 6:22 AM Tobias Stoeckmann <tob...@stoeckmann.org>
wrote:

> Hi,
>
> it is possible to trigger a use after free bug in less with huge
> files or tight memory constraints. PoC with 100 MB file:
>
> dd if=/dev/zero bs=1024 count=102400 | tr '\0' 'a' > less-poc.txt
> ulimit -d 157286
> less less-poc.txt
>
> The linebuf and attr buffers in line.c are supposed to never be freed,
> since they are used for keeping (meta) data of current line in RAM.
>
> The linebuf and attr buffers are expanded in expand_linebuf. If linebuf
> could be expanded but attr could not, then returned pointers are freed,
> but linebuf variable still points to previous location. Subsequent
> accesses will therefore trigger a use after free bug.
>
> The easiest fix is to keep reallocated linebuf. The size of both buffers
> differ, but the code still assumes that the previous (smaller) size is
> the correct one.
>
> Thoughts on this approach?
>

Analysis makes sense.

To bikeshed slightly I would be inclined to do the work progressively,
perhaps like the diff below...but your diff works too.


Philip Guenther


Index: line.c
===================================================================
RCS file: /data/src/openbsd/src/usr.bin/less/line.c,v
retrieving revision 1.34
diff -u -p -r1.34 line.c
--- line.c      25 Oct 2021 19:54:29 -0000      1.34
+++ line.c      1 Jan 2022 06:24:31 -0000
@@ -96,16 +96,16 @@ expand_linebuf(void)

        /* Just realloc to expand the buffer, if we can. */
        char *new_buf = recallocarray(linebuf, size_linebuf, new_size, 1);
-       char *new_attr = recallocarray(attr, size_linebuf, new_size, 1);
-       if (new_buf == NULL || new_attr == NULL) {
-               free(new_attr);
-               free(new_buf);
-               return (1);
+       if (new_buf != NULL) {
+               char *new_attr = recallocarray(attr, size_linebuf,
new_size, 1);
+               linebuf = new_buf;
+               if (new_attr != NULL) {
+                       attr = new_attr;
+                       size_linebuf = new_size;
+                       return (0);
+               }
        }
-       linebuf = new_buf;
-       attr = new_attr;
-       size_linebuf = new_size;
-       return (0);
+       return (1);
 }

 /*

Reply via email to