From: the.true.nathan.mi...@gmail.com
To: bug-bash@gnu.org
Subject: Getopts crashes when passing several zeroes.

Configuration Information [Automatically generated, do not change]:
Machine: x86_64
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS: -g -O2
uname output: Linux nixos 6.6.87.2-microsoft-standard-WSL2 #1 SMP
PREEMPT_DYNAMIC Thu Jun  5 18:30:46 UTC 2025 x86_64 GNU/Linux
Machine Type: x86_64-pc-linux-gnu

Bash Version: 5.3-devel
Patch Level: 0
Release Status: devel-a23c863e

Clang:
```
clang version 19.1.7
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /nix/store/x6rsdc4s0f1j9bn1cx2h1l5fj8765ykw-clang-19.1.7/bin
```

NixOS:
```
nixos-version
> 25.05.806304.dfcd5b901dba (Warbler)
```

Description:
Running the following code causes AddressSanitizer to crash with a
heap buffer overflow. It seems that nextchar points one past the NUL
terminator on the 2nd getopts call. Probably because the 3rd argument
of the first call was one character longer. It seems that sh_charindex
does not get reset to zero between getopts invocations.

Found with AFL++ in Docker but I could not get crashwalk to work in
docker. This is crash id #1898 which includes NUL bytes and tab
characters, but I minimized it to the following command:

./bash -c "getopts 0 0 -00;getopts 0 0 0"

The laptop I'm running this on is a MSI GS63 Stealth 8RE (6-cores, 12
hyperthreads) with 32GB RAM and Windows 10 (Bash is being run in a
WSL2 VM).

System bash on NixOS does not crash given the same command.

    [Detailed description of the problem, suggestion, or complaint.]

Repeat-By:
1. Compile Bash from source with commit a23c863e as follows:
> nix --enable-experimental-features 'nix-command flakes' develop nixpkgs#bash.
> export CC=clang CXX=clang CFLAGS='-O0 -g3 -fno-omit-frame-pointer 
> -fsanitize=address,undefined' LDFLAGS='-fsanitize=address,undefined';time 
> (./configure --without-bash-malloc && make -j6)
2. Run the development Bash with -c "getopts 0 0 -00;getopts 0 0 0"
(that's the number zero, not a NUL byte or the letter O)
3. Address Sanitizer complains of a heap buffer overflow.

Fix:
I don't know how to fix this. I have tried resetting sh_charindex in a
few places, but the testsuite hangs in an infinite loop of printing
"opt: ?" when running the run-getopts test. That test runs fine with
unmodified Bash from source.

    [Description of how to fix the problem.  If you don't know a
    fix for the problem, don't include this section.]

Output:

../1898-heap-buffer-overflow-getopts.sh: line 2: getopts: `0': not a
valid identifier
=================================================================
==29221==ERROR: AddressSanitizer: heap-buffer-overflow on address
0x5020000050f2 at pc 0x555555be2761 bp 0x7fffffff7770 sp
0x7fffffff7768
READ of size 1 at 0x5020000050f2 thread T0
    #0 0x555555be2760  (/home/nixos/src/bash/nonAFL/bash+0x68e760)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #1 0x555555bc5fc7  (/home/nixos/src/bash/nonAFL/bash+0x671fc7)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #2 0x555555bc5a91  (/home/nixos/src/bash/nonAFL/bash+0x671a91)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #3 0x55555598ea39  (/home/nixos/src/bash/nonAFL/bash+0x43aa39)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #4 0x55555598d65b  (/home/nixos/src/bash/nonAFL/bash+0x43965b)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #5 0x555555969513  (/home/nixos/src/bash/nonAFL/bash+0x415513)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #6 0x55555595dcc1  (/home/nixos/src/bash/nonAFL/bash+0x409cc1)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #7 0x55555595a7f1  (/home/nixos/src/bash/nonAFL/bash+0x4067f1)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #8 0x5555558f2a90  (/home/nixos/src/bash/nonAFL/bash+0x39ea90)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #9 0x5555558e9fb2  (/home/nixos/src/bash/nonAFL/bash+0x395fb2)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #10 0x7ffff7a2a47d
(/nix/store/zdpby3l6azi78sl83cpad2qjpfj25aqx-glibc-2.40-66/lib/libc.so.6+0x2a47d)
(BuildId: 184c6644327611cadef0a544327ebb842fceaa2c)
    #11 0x7ffff7a2a538
(/nix/store/zdpby3l6azi78sl83cpad2qjpfj25aqx-glibc-2.40-66/lib/libc.so.6+0x2a538)
(BuildId: 184c6644327611cadef0a544327ebb842fceaa2c)
    #12 0x5555557a8a84  (/home/nixos/src/bash/nonAFL/bash+0x254a84)
(BuildId: dab15212a73e587148877896feaabf620911fe79)

0x5020000050f2 is located 0 bytes after 2-byte region
[0x5020000050f0,0x5020000050f2)
allocated by thread T0 here:
    #0 0x55555589b787  (/home/nixos/src/bash/nonAFL/bash+0x347787)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #1 0x555555b52801  (/home/nixos/src/bash/nonAFL/bash+0x5fe801)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #2 0x555555a433dd  (/home/nixos/src/bash/nonAFL/bash+0x4ef3dd)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #3 0x555555a89082  (/home/nixos/src/bash/nonAFL/bash+0x535082)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #4 0x555555a4ecd6  (/home/nixos/src/bash/nonAFL/bash+0x4facd6)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #5 0x555555a4eae1  (/home/nixos/src/bash/nonAFL/bash+0x4faae1)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #6 0x55555596726e  (/home/nixos/src/bash/nonAFL/bash+0x41326e)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #7 0x55555595dcc1  (/home/nixos/src/bash/nonAFL/bash+0x409cc1)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #8 0x55555595a7f1  (/home/nixos/src/bash/nonAFL/bash+0x4067f1)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #9 0x5555558f2a90  (/home/nixos/src/bash/nonAFL/bash+0x39ea90)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #10 0x5555558e9fb2  (/home/nixos/src/bash/nonAFL/bash+0x395fb2)
(BuildId: dab15212a73e587148877896feaabf620911fe79)
    #11 0x7ffff7a2a47d
(/nix/store/zdpby3l6azi78sl83cpad2qjpfj25aqx-glibc-2.40-66/lib/libc.so.6+0x2a47d)
(BuildId: 184c6644327611cadef0a544327ebb842fceaa2c)

SUMMARY: AddressSanitizer: heap-buffer-overflow
(/home/nixos/src/bash/nonAFL/bash+0x68e760) (BuildId:
dab15212a73e587148877896feaabf620911fe79)
Shadow bytes around the buggy address:
  0x502000004e00: fa fa fd fa fa fa fd fd fa fa fd fa fa fa fd fa
  0x502000004e80: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
  0x502000004f00: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
  0x502000004f80: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
  0x502000005000: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
=>0x502000005080: fa fa 00 fa fa fa 02 fa fa fa 00 01 fa fa[02]fa
  0x502000005100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000005180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000005200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000005280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000005300: 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
==29221==ABORTING

Program received signal SIGKILL, Killed.
0x00005555558b9810 in __sanitizer::internal__exit(int) ()
(rr) bt
#0  0x00005555558b9810 in __sanitizer::internal__exit(int) ()
#1  0x00005555558c6853 in __sanitizer::Die() ()
#2  0x00005555558a56c3 in __asan::ScopedInErrorReport::~ScopedInErrorReport() ()
#3  0x00005555558a4cba in __asan::ReportGenericError(unsigned long,
unsigned long, unsigned long, unsigned long, bool, unsigned long,
unsigned int, bool) [clone .part.0] ()
#4  0x00005555558a6006 in __asan_report_load1 ()
#5  0x0000555555be2761 in sh_getopt (argc=2, argv=0x504000011620,
optstring=0x5020000050b0 "0") at getopt.c:137
#6  0x0000555555bc5fc8 in dogetopts (argc=2, argv=0x504000011620) at
./getopts.def:194
#7  0x0000555555bc5a92 in getopts_builtin (list=0x5020000048d0) at
./getopts.def:326
#8  0x000055555598ea3a in execute_builtin (builtin=0x555555bc5918
<getopts_builtin>, words=0x502000004930, flags=0,
    subshell=0) at execute_cmd.c:5097
#9  0x000055555598d65c in execute_builtin_or_function
(words=0x502000004930, builtin=0x555555bc5918 <getopts_builtin>,
    var=0x0, redirects=0x0, fds_to_close=0x502000004dd0, flags=0) at
execute_cmd.c:5643
#10 0x0000555555969514 in execute_simple_command
(simple_command=0x503000010ea0, pipe_in=-1, pipe_out=-1, async=0,
    fds_to_close=0x502000004dd0) at execute_cmd.c:4856
#11 0x000055555595dcc2 in execute_command_internal
(command=0x503000010e70, asynchronous=0, pipe_in=-1, pipe_out=-1,
    fds_to_close=0x502000004dd0) at execute_cmd.c:938
#12 0x000055555595a7f2 in execute_command (command=0x503000010e70) at
execute_cmd.c:456
#13 0x00005555558f2a91 in reader_loop () at eval.c:183
#14 0x00005555558e9fb3 in main (argc=2, argv=0x7fffffff8e78,
env=0x7fffffff8e90) at shell.c:834

Reply via email to