Subject: od Heap-buffer overflow
From: Jaehoon Jang <[email-masked]>
To: [email-masked]
Date: Mon, 23 Jun 2025 17:24:53 +0900
List-Id: <debbugs-submit.debbugs.gnu.org>

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