NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

lib/60052: libedit: history_load() off by one memory access bug



>Number:         60052
>Category:       lib
>Synopsis:       libedit: history_load() off by one memory access bug
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Mar 03 15:05:00 +0000 2026
>Originator:     Kristofer Peterson
>Release:        NetBSD-current
>Organization:
Tranception Limited
>Environment:
>Description:
history_load() reads in encoded historical command lines from the history file line by line, allocating a buffer to hold the contents as decoded by strunvis().

strunvis() requires a decoding buffer at least equal in size to the encoded source string, and history_load() grows this buffer if necessary by checking the number of characters read in by getline().

As getline() returns the number of characters read but does not include the terminate null character. As such history_load() can fail to allocate a buffer large enough to accomodate the terminating null character.

In particular, when the encoded input line consists of 1,024 unescaped characters followed by a newline, a 1,025 byte buffer is required however history_load will not increase the size of the initial 1,024 decoded buffer.
>How-To-Repeat:
Append a line of 1,024 'X' and a newline to ~/.sh_history and run interactive shell.

$ sh -c 'X=ABCDEFGH;X=$X$X;X=$X$X$X$X;X=$X$X$X$X;X=$X$X$X$X;echo $X >>~/.sh_history'
$ valgrind --leak-check=full --show-leak-kinds=all /bin/sh -i /dev/null

# valgrind output
...
==95054== Invalid write of size 1
==95054==    at 0x498F226: strnunvisx (in /home/dev/box/obj3/home/dev/box/src3/amd64.amd64/tmp/lib/libc.so.7)
==95054==    by 0x48ACB23: history (in /home/dev/box/obj3/home/dev/box/src3/amd64.amd64/tmp/lib/libedit.so.8)
==95054==    by 0x40152E2: histload (src3/bin/sh/histedit.c:137)
==95054==    by 0x401AE19: main (src3/bin/sh/main.c:158)
==95054==  Address 0x5822d10 is 0 bytes after a block of size 1,024 alloc'd
==95054==    at 0x487C224: malloc (vg_replace_malloc.c:451)
==95054==    by 0x48ACA79: history (in /home/dev/box/obj3/home/dev/box/src3/amd64.amd64/tmp/lib/libedit.so.8)
==95054==    by 0x40152E2: histload (src3/bin/sh/histedit.c:137)
==95054==    by 0x401AE19: main (src3/bin/sh/main.c:158)
==95054== 
==95054== Invalid read of size 1
==95054==    at 0x4883339: strlen (vg_replace_strmem.c:520)
==95054==    by 0x4AAC66E: strdup (in /home/dev/box/obj3/home/dev/box/src3/amd64.amd64/tmp/lib/libc.so.7)
==95054==    by 0x48ABC04: ??? (in /home/dev/box/obj3/home/dev/box/src3/amd64.amd64/tmp/lib/libedit.so.8)
==95054==    by 0x48ACB31: history (in /home/dev/box/obj3/home/dev/box/src3/amd64.amd64/tmp/lib/libedit.so.8)
==95054==    by 0x40152E2: histload (src3/bin/sh/histedit.c:137)
==95054==    by 0x401AE19: main (src3/bin/sh/main.c:158)
==95054==  Address 0x5822d10 is 0 bytes after a block of size 1,024 alloc'd
==95054==    at 0x487C224: malloc (vg_replace_malloc.c:451)
==95054==    by 0x48ACA79: history (in /home/dev/box/obj3/home/dev/box/src3/amd64.amd64/tmp/lib/libedit.so.8)
==95054==    by 0x40152E2: histload (src3/bin/sh/histedit.c:137)
==95054==    by 0x401AE19: main (src3/bin/sh/main.c:158)
==95054== 
==95054== Invalid read of size 1
==95054==    at 0x4884A00: memcpy (vg_replace_strmem.c:1168)
==95054==    by 0x48ABC04: ??? (in /home/dev/box/obj3/home/dev/box/src3/amd64.amd64/tmp/lib/libedit.so.8)
==95054==    by 0x48ACB31: history (in /home/dev/box/obj3/home/dev/box/src3/amd64.amd64/tmp/lib/libedit.so.8)
==95054==    by 0x40152E2: histload (src3/bin/sh/histedit.c:137)
==95054==    by 0x401AE19: main (src3/bin/sh/main.c:158)
==95054==  Address 0x5822d10 is 0 bytes after a block of size 1,024 alloc'd
==95054==    at 0x487C224: malloc (vg_replace_malloc.c:451)
==95054==    by 0x48ACA79: history (in /home/dev/box/obj3/home/dev/box/src3/amd64.amd64/tmp/lib/libedit.so.8)
==95054==    by 0x40152E2: histload (src3/bin/sh/histedit.c:137)
==95054==    by 0x401AE19: main (src3/bin/sh/main.c:158)
...
>Fix:
diff --git a/contrib/libedit/history.c b/contrib/libedit/history.c
index 8395b329784d..b49c59bf523c 100644
--- a/contrib/libedit/history.c
+++ b/contrib/libedit/history.c
@@ -800,7 +800,7 @@ history_load(TYPE(History) *h, const char *fname)
        for (i = 0; (sz = getline(&line, &llen, fp)) != -1; i++) {
                if (sz > 0 && line[sz - 1] == '\n')
                        line[--sz] = '\0';
-               if (max_size < (size_t)sz) {
+               if (max_size <= (size_t)sz) {
                        char *nptr;
                        max_size = ((size_t)sz + 1024) & (size_t)~1023;
                        nptr = h_realloc(ptr, max_size * sizeof(*ptr));



Home | Main Index | Thread Index | Old Index