Skip to content

Conversation

@xmoezzz
Copy link

@xmoezzz xmoezzz commented Dec 23, 2025

Hi, this PR addresses a UAF bug here.

Summary

This PR fixes a UAF triggered when NASM parses options from a response file (-@ <file>) and later resolves the debug format (e.g., via dfmt_find() / strcasecmp()).

The issue is reproducible in the newest commit 22a9118.

Output

==1015601==ERROR: AddressSanitizer: heap-use-after-free on address 0x7c4402be0102 at pc 0x000000419be7 bp 0x7fff758ba8c0 sp 0x7fff758ba070
READ of size 1 at 0x7c4402be0102 thread T0
    #0 0x000000419be6 in strcasecmp (/data/nasm/build-sanitize/nasm+0x419be6) (BuildId: 0d1cdac1ac1d130f5d6c4f82c13144ff22a7fee3)
    #1 0x00000053ee53 in dfmt_find /data/nasm/build-sanitize/../output/outform.c:51:14
    #2 0x0000004dd8c2 in main /data/nasm/build-sanitize/../asm/nasm.c:517:16
    #3 0x7f8403846574 in __libc_start_call_main (/lib64/libc.so.6+0x3574) (BuildId: 48c4b9b1efb1df15da8e787f489128bf31893317)
    #4 0x7f8403846627 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x3627) (BuildId: 48c4b9b1efb1df15da8e787f489128bf31893317)
    #5 0x000000400824 in _start (/data/nasm/build-sanitize/nasm+0x400824) (BuildId: 0d1cdac1ac1d130f5d6c4f82c13144ff22a7fee3)

0x7c4402be0102 is located 2 bytes inside of 128-byte region [0x7c4402be0100,0x7c4402be0180)
freed by thread T0 here:
    #0 0x0000004a220a in free (/data/nasm/build-sanitize/nasm+0x4a220a) (BuildId: 0d1cdac1ac1d130f5d6c4f82c13144ff22a7fee3)
    #1 0x000000590ba5 in nasm_free /data/nasm/build-sanitize/../nasmlib/alloc.c:78:9
    #2 0x0000004e25ba in process_respfile /data/nasm/build-sanitize/../asm/nasm.c:1370:13
    #3 0x0000004e25ba in open_and_process_respfile /data/nasm/build-sanitize/../asm/nasm.c:1444:9
    #4 0x0000004e1305 in process_arg /data/nasm/build-sanitize/../asm/nasm.c:956:13
    #5 0x0000004dfd19 in parse_cmdline /data/nasm/build-sanitize/../asm/nasm.c:1490:19
    #6 0x0000004dd873 in main /data/nasm/build-sanitize/../asm/nasm.c:505:5
    #7 0x7f8403846574 in __libc_start_call_main (/lib64/libc.so.6+0x3574) (BuildId: 48c4b9b1efb1df15da8e787f489128bf31893317)
    #8 0x7f8403846627 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x3627) (BuildId: 48c4b9b1efb1df15da8e787f489128bf31893317)
    #9 0x000000400824 in _start (/data/nasm/build-sanitize/nasm+0x400824) (BuildId: 0d1cdac1ac1d130f5d6c4f82c13144ff22a7fee3)

previously allocated by thread T0 here:
    #0 0x0000004a24a8 in malloc (/data/nasm/build-sanitize/nasm+0x4a24a8) (BuildId: 0d1cdac1ac1d130f5d6c4f82c13144ff22a7fee3)
    #1 0x000000590a68 in nasm_malloc /data/nasm/build-sanitize/../nasmlib/alloc.c:25:9
    #2 0x0000004e228b in process_respfile /data/nasm/build-sanitize/../asm/nasm.c:1345:15
    #3 0x0000004e228b in open_and_process_respfile /data/nasm/build-sanitize/../asm/nasm.c:1444:9
    #4 0x0000004e1305 in process_arg /data/nasm/build-sanitize/../asm/nasm.c:956:13
    #5 0x0000004dfd19 in parse_cmdline /data/nasm/build-sanitize/../asm/nasm.c:1490:19
    #6 0x0000004dd873 in main /data/nasm/build-sanitize/../asm/nasm.c:505:5
    #7 0x7f8403846574 in __libc_start_call_main (/lib64/libc.so.6+0x3574) (BuildId: 48c4b9b1efb1df15da8e787f489128bf31893317)
    #8 0x7f8403846627 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x3627) (BuildId: 48c4b9b1efb1df15da8e787f489128bf31893317)
    #9 0x000000400824 in _start (/data/nasm/build-sanitize/nasm+0x400824) (BuildId: 0d1cdac1ac1d130f5d6c4f82c13144ff22a7fee3)

SUMMARY: AddressSanitizer: heap-use-after-free (/data/nasm/build-sanitize/nasm+0x419be6) (BuildId: 0d1cdac1ac1d130f5d6c4f82c13144ff22a7fee3) in strcasecmp
Shadow bytes around the buggy address:
  0x7c4402bdfe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7c4402bdff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7c4402bdff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7c4402be0000: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x7c4402be0080: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
=>0x7c4402be0100:[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x7c4402be0180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7c4402be0200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7c4402be0280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7c4402be0300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x7c4402be0380: 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
==1015601==ABORTING

Root Cause

When options are parsed from a response file, tokens are typically stored in a temporary variable that owns a heap buffer in the parser, which is also assigned to a static pointer static const char *debug_format, without copying.

After the parsing routine returns, it frees the temporary buffer. The stored pointer then becomes dangling and is used later during debug & output format lookup, triggering a UAF.

Reproduce

rm -rf build-sanitize2 \
&& mkdir build-sanitize2 \
&& cd build-sanitize2 \
&& (test -f ../configure || (cd .. && sh autogen.sh && cd build-sanitize2)) \
&& CC=clang \
   CFLAGS='-O1 -g3 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address,undefined -fno-sanitize-recover=all' \
   LDFLAGS='-fsanitize=address,undefined' \
   ../configure --prefix="$PWD/_install" \
&& make -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)"

Create a response file that specifies a debug format, then run:

nasm -@ A

The input file A is attach here (inside the zip file):
A.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant