GNU bug report logs -
#78880
od Heap-buffer overflow
Previous Next
To reply to this bug, email your comments to 78880 AT debbugs.gnu.org.
There is no need to reopen the bug first.
Toggle the display of automated, internal messages from the tracker.
Report forwarded
to
bug-coreutils <at> gnu.org
:
bug#78880
; Package
coreutils
.
(Mon, 23 Jun 2025 19:13:05 GMT)
Full text and
rfc822 format available.
Acknowledgement sent
to
Jaehoon Jang <jaehoon.jang <at> prosys.kaist.ac.kr>
:
New bug report received and forwarded. Copy sent to
bug-coreutils <at> gnu.org
.
(Mon, 23 Jun 2025 19:13:05 GMT)
Full text and
rfc822 format available.
Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):
[Message part 1 (text/plain, inline)]
*###### Build options*```
git clone https://github.com/coreutils/coreutils
export GNULIB_SRCDIR=./gnulib
export FORCE_UNSAFE_CONFIGURE=1
./bootstrap
CC="clang -g -fsanitize=address" CXX="clang -g -fsanitize=address"
./configure $CONFIG_OPTIONS
make -j
```
*###### od version*```
$ src/od --version
od (GNU coreutils) 9.7.52-b7db77
Copyright (C) 2025 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <
https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Jim Meyering.
```
*###### reproduce script*```c
// gcc -o replay_od replay_od.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_CMDLINE_LEN 100000
#define MAX_CMDLINE_PAR 1000
int main() {
static char in_buf[MAX_CMDLINE_LEN];
static char *argv[MAX_CMDLINE_PAR];
char *ptr = in_buf;
int rc = 0;
int n = read(0, in_buf, MAX_CMDLINE_LEN - 2);
if (n <= 0) {
perror("read");
exit(1);
}
while (*ptr) {
argv[rc] = ptr;
if (argv[rc][0] == 0x02 && !argv[rc][1]) {
argv[rc]++;
}
rc++;
while (*ptr) ptr++;
ptr++;
}
argv[0] = "od";
// exec od with parsed argv
execvp("src/od", argv);
perror("execvp failed");
return 1;
}
```
This is a C wrapper program that automatically sets argv to reproduce the
vulnerability. It is a simple wrapper around the od program and does not
modify the original od binary.
###### ASAN log
```
$ ./replay_od < /root/250623/od-poc
od: 'u'$'\353''d'$'\001''4
'\'''$'\350\003\346\346\346\346\027\027\027\027\027\027\027\027''!'$'\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027''{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{'$'\024''{{{{'$'\200\016\377\377''{{{{{{{{{{{s{{'$'\220''{{{{'$'\213\207\213\213\213\213\213\213\213\213\213\213\213\213''}'$'\213\213\213\213\213\213\213\213\213\213''E':
No such file or directory
=================================================================
==1151699==ERROR: AddressSanitizer: heap-buffer-overflow on address
0x6150000004f9 at pc 0x0000004d153f bp 0x7fff937f0410 sp 0x7fff937f0408
WRITE of size 1 at 0x6150000004f9 thread T0
#0 0x4d153e in dump_strings coreutils/src/od.c:1570:14
#1 0x4d153e in main coreutils/src/od.c:2037:30
#2 0x7fc132ca6d8f in __libc_start_call_main
csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#3 0x7fc132ca6e3f in __libc_start_main csu/../csu/libc-start.c:392:3
#4 0x41f664 in _start (coreutils/src/od+0x41f664)
0x6150000004f9 is located 0 bytes to the right of 505-byte region
[0x615000000300,0x6150000004f9)
allocated by thread T0 here:
#0 0x49c843 in __interceptor_realloc (coreutils/src/od+0x49c843)
#1 0x4ddcea in rpl_realloc coreutils/./lib/stdlib.h:2095:10
#2 0x4ddcea in xrealloc coreutils/lib/xmalloc.c:66:13
#3 0x4ddcea in xpalloc coreutils/lib/xmalloc.c:271:8
#4 0x7fc132ca6d8f in __libc_start_call_main
csu/../sysdeps/nptl/libc_start_call_main.h:58:16
SUMMARY: AddressSanitizer: heap-buffer-overflow coreutils/src/od.c:1570:14
in dump_strings
Shadow bytes around the buggy address:
0x0c2a7fff8040: fd fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa
0x0c2a7fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c2a7fff8060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2a7fff8070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2a7fff8080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c2a7fff8090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00[01]
0x0c2a7fff80a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c2a7fff80b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c2a7fff80c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c2a7fff80d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c2a7fff80e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==1151699==ABORTING
```
*###### Description*
```
1513 static bool
1514 dump_strings (void)
1515 {
1516 idx_t bufsize = MAX (100, string_min);
1517 char *buf = xmalloc (bufsize);
...
1550 while (!limit_bytes_to_format || address < end_offset)
1551 {
1552 if (i == bufsize)
1553 buf = xpalloc (buf, &bufsize, 1, -1, sizeof *buf);
1554 ok &= read_char (&c);
1555 address++;
1556 if (c < 0)
1557 {
1558 free (buf);
1559 return ok;
1560 }
1561 if (c == '\0')
1562 break; /* It is; print this string. */
1563 if (! isprint (c))
1564 goto tryline; /* It isn't; give up on this string.
*/
1565 buf[i++] = c; /* String continues; store it all. */
1566 }
...
1570 buf[i] = 0;
1571 format_address (address - i - 1, ' ');
```
The vulnerability occurs in the dump_strings function. The variable buf is
allocated with a size of bufsize, which is limited to a maximum of 100
bytes.
However, inside the while loop, the variable i is incremented by 1 on each
iteration. When the attached PoC file is passed as arguments to the od
program, the value of i can exceed 100. This leads to a heap buffer
overflow vulnerability.
An attacker could potentially exploit this by manipulating both the i and c
values, resulting in dangerous modifications to the buf chunk on the heap.
Therefore, we recommend adding proper bounds checking for the i variable to
prevent this issue.
[Message part 2 (text/html, inline)]
[od-poc (application/octet-stream, attachment)]
[replay_od.c (application/octet-stream, attachment)]
Information forwarded
to
bug-coreutils <at> gnu.org
:
bug#78880
; Package
coreutils
.
(Tue, 24 Jun 2025 00:17:02 GMT)
Full text and
rfc822 format available.
Message #8 received at 78880 <at> debbugs.gnu.org (full text, mbox):
On 23/06/2025 09:24, Jaehoon Jang wrote:
> =================================================================
> ==1151699==ERROR: AddressSanitizer: heap-buffer-overflow on address
> 0x6150000004f9 at pc 0x0000004d153f bp 0x7fff937f0410 sp 0x7fff937f0408
> WRITE of size 1 at 0x6150000004f9 thread T0
> #0 0x4d153e in dump_strings coreutils/src/od.c:1570:14
Nice fuzzing.
There looks to be all sorts of off by one errors in the dump_strings() function.
The issue is most easily demonstrated with:
printf '%100s' | tr ' ' . | valgrind od -N100 -S99
The following should fix this I think.
I've only analyzed it for a few minutes, so I'll look more tomorrow.
The following should also fix the printed offset,
and also support the -N100 -S100 combination.
thanks!
Pádraig.
diff --git a/src/od.c b/src/od.c
index 88d467c73..7f0cd5ab7 100644
--- a/src/od.c
+++ b/src/od.c
@@ -1513,7 +1513,7 @@ dump (void)
static bool
dump_strings (void)
{
- idx_t bufsize = MAX (100, string_min);
+ idx_t bufsize = MAX (100, string_min + 1);
char *buf = xmalloc (bufsize);
uintmax_t address = n_bytes_to_skip;
bool ok = true;
@@ -1527,7 +1527,7 @@ dump_strings (void)
tryline:
if (limit_bytes_to_format
- && (end_offset < string_min || end_offset - string_min <= address))
+ && (end_offset < string_min || end_offset - string_min < address))
break;
for (i = 0; i < string_min; i++)
@@ -1549,7 +1549,7 @@ dump_strings (void)
Now see if it is terminated with a null byte. */
while (!limit_bytes_to_format || address < end_offset)
{
- if (i == bufsize)
+ if (i == bufsize - 1)
buf = xpalloc (buf, &bufsize, 1, -1, sizeof *buf);
ok &= read_char (&c);
address++;
@@ -1568,7 +1568,7 @@ dump_strings (void)
/* If we get here, the string is all printable and null-terminated,
so print it. It is all in 'buf' and 'i' is its length. */
buf[i] = 0;
- format_address (address - i - 1, ' ');
+ format_address (address - i, ' ');
for (i = 0; (c = buf[i]); i++)
{
Reply sent
to
Pádraig Brady <P <at> draigBrady.com>
:
You have taken responsibility.
(Tue, 24 Jun 2025 14:04:02 GMT)
Full text and
rfc822 format available.
Notification sent
to
Jaehoon Jang <jaehoon.jang <at> prosys.kaist.ac.kr>
:
bug acknowledged by developer.
(Tue, 24 Jun 2025 14:04:03 GMT)
Full text and
rfc822 format available.
Message #13 received at 78880-done <at> debbugs.gnu.org (full text, mbox):
[Message part 1 (text/plain, inline)]
On 24/06/2025 01:16, Pádraig Brady wrote:
> On 23/06/2025 09:24, Jaehoon Jang wrote:
>> =================================================================
>> ==1151699==ERROR: AddressSanitizer: heap-buffer-overflow on address
>> 0x6150000004f9 at pc 0x0000004d153f bp 0x7fff937f0410 sp 0x7fff937f0408
>> WRITE of size 1 at 0x6150000004f9 thread T0
>> #0 0x4d153e in dump_strings coreutils/src/od.c:1570:14
>
> Nice fuzzing.
>
> There looks to be all sorts of off by one errors in the dump_strings() function.
> The issue is most easily demonstrated with:
>
> printf '%100s' | tr ' ' . | valgrind od -N100 -S99
>
> The following should fix this I think.
> I've only analyzed it for a few minutes, so I'll look more tomorrow.
> The following should also fix the printed offset,
> and also support the -N100 -S100 combination.
The previous patch didn't handle the invalid address output in all cases.
Also I didn't see a need for both read() loops in this function,
so I simplified the function in the attached more complete patch.
Marking this as done.
I'll apply this later.
thanks again,
Padraig.
[0001-od-fix-various-off-by-one-issues-with-strings-with-N.patch (text/x-patch, attachment)]
Information forwarded
to
bug-coreutils <at> gnu.org
:
bug#78880
; Package
coreutils
.
(Tue, 24 Jun 2025 14:29:01 GMT)
Full text and
rfc822 format available.
Message #16 received at 78880 <at> debbugs.gnu.org (full text, mbox):
On Tue, Jun 24, 2025, 10:04 Pádraig Brady <P <at> draigbrady.com> wrote:
>
> On 24/06/2025 01:16, Pádraig Brady wrote:
> > On 23/06/2025 09:24, Jaehoon Jang wrote:
> >> =================================================================
> >> ==1151699==ERROR: AddressSanitizer: heap-buffer-overflow on address
> >> 0x6150000004f9 at pc 0x0000004d153f bp 0x7fff937f0410 sp 0x7fff937f0408
> >> WRITE of size 1 at 0x6150000004f9 thread T0
> >> #0 0x4d153e in dump_strings coreutils/src/od.c:1570:14
> >
> > Nice fuzzing.
> >
> > There looks to be all sorts of off by one errors in the dump_strings() function.
> > The issue is most easily demonstrated with:
> >
> > printf '%100s' | tr ' ' . | valgrind od -N100 -S99
> >
> > The following should fix this I think.
> > I've only analyzed it for a few minutes, so I'll look more tomorrow.
> > The following should also fix the printed offset,
> > and also support the -N100 -S100 combination.
>
> The previous patch didn't handle the invalid address output in all cases.
> Also I didn't see a need for both read() loops in this function,
> so I simplified the function in the attached more complete patch.
This part:
> tryline:
> + idx_t i = 0;
> + int c = 1; /* Init to 1 so can distinguish if NUL read. */
triggers
warning: label followed by a declaration is a C23 extension
[-Wc23-extensions]
Information forwarded
to
bug-coreutils <at> gnu.org
:
bug#78880
; Package
coreutils
.
(Tue, 24 Jun 2025 14:29:02 GMT)
Full text and
rfc822 format available.
Information forwarded
to
bug-coreutils <at> gnu.org
:
bug#78880
; Package
coreutils
.
(Tue, 24 Jun 2025 15:29:03 GMT)
Full text and
rfc822 format available.
Message #22 received at 78880 <at> debbugs.gnu.org (full text, mbox):
[Message part 1 (text/plain, inline)]
On 24/06/2025 15:27, Grisha Levit wrote:
> This part:
>
>> tryline:
>> + idx_t i = 0;
>> + int c = 1; /* Init to 1 so can distinguish if NUL read. */
>
> triggers
>
> warning: label followed by a declaration is a C23 extension
> [-Wc23-extensions]
OK version 3 attached which simplifies
by removing the label and gotos entirely.
thanks!
Padraig
[0001-od-fix-various-off-by-one-issues-with-strings-with-N.patch (text/x-patch, attachment)]
Information forwarded
to
bug-coreutils <at> gnu.org
:
bug#78880
; Package
coreutils
.
(Tue, 24 Jun 2025 16:35:02 GMT)
Full text and
rfc822 format available.
Message #25 received at 78880 <at> debbugs.gnu.org (full text, mbox):
Pádraig Brady <P <at> draigBrady.com> writes:
> On 24/06/2025 15:27, Grisha Levit wrote:
>
>> This part:
>>
>>> tryline:
>>> + idx_t i = 0;
>>> + int c = 1; /* Init to 1 so can distinguish if NUL read. */
>> triggers
>> warning: label followed by a declaration is a C23 extension
>> [-Wc23-extensions]
>
> OK version 3 attached which simplifies
> by removing the label and gotos entirely.
Grisha, what did you pass to ./configure?
I'm suprised we don't disable this in Gnulib. Since there we do:
static_assert (1 < sizeof (example))
Without a second argument message string, I would expect Clang to
complain about it, unless we disable it.
Collin
Information forwarded
to
bug-coreutils <at> gnu.org
:
bug#78880
; Package
coreutils
.
(Wed, 25 Jun 2025 01:32:01 GMT)
Full text and
rfc822 format available.
Message #28 received at 78880 <at> debbugs.gnu.org (full text, mbox):
On Tue, Jun 24, 2025 at 8:29 AM Pádraig Brady <P <at> draigbrady.com> wrote:
> OK version 3 attached which simplifies
> by removing the label and gotos entirely.
Thanks for fixing my very old bugs! That goes way back. I think od.c
was the second stand-alone program I contributed to coreutils (first
was tr). The earliest email I still have that mentions it is from
1997-01 prior to textutils-1.22, but that was just a ChangeLog entry
about adapting to a changed strtod API.
This bug report was last modified today.
Previous Next
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.