vstakhov / libucl

Universal configuration library parser
BSD 2-Clause "Simplified" License
1.63k stars 139 forks source link

Heap-buffer-overflow in ucl_maybe_parse_number /src/libucl/src/ucl_parser.c #263

Open CCWANG19 opened 2 years ago

CCWANG19 commented 2 years ago
Build platform

ubuntu20.04

Build steps
CC = clang
CFLAGS = -O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link
LIB_FUZZING_ENGINE="-fsanitize=fuzzer"

$CC $CFLAGS $LIB_FUZZING_ENGINE tests/fuzzers/ucl_add_string_fuzzer.c \
    -DHAVE_CONFIG_H -I./src -I./include src/.libs/libucl.a -I./ \
    -o $OUT/ucl_add_string_fuzzer
Test case

poc.zip

Execution steps

./ucl_add_string_fuzzer poc

Output
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 227906990
INFO: Loaded 1 modules   (3863 inline 8-bit counters): 3863 [0x624b30, 0x625a47), 
INFO: Loaded 1 PC tables (3863 PCs): 3863 [0x5cde08,0x5dcf78), 
./ucl_add_string_fuzzer: Running 1 inputs 1 time(s) each.
Running: poc
=================================================================
==9526==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000000b3 at pc 0x00000055c824 bp 0x7ffeaeae0290 sp 0x7ffeaeae0288
READ of size 1 at 0x6020000000b3 thread T0
    #0 0x55c823 in ucl_maybe_parse_number /src/libucl/src/ucl_parser.c:846:45
    #1 0x56bd3f in ucl_lex_number /src/libucl/src/ucl_parser.c:1026:8
    #2 0x56bd3f in ucl_parse_value /src/libucl/src/ucl_parser.c:1899:10
    #3 0x56bd3f in ucl_state_machine /src/libucl/src/ucl_parser.c:2509:29
    #4 0x5618be in ucl_parser_add_chunk_full /src/libucl/src/ucl_parser.c:2995:12
    #5 0x571091 in ucl_parser_add_chunk_priority /src/libucl/src/ucl_parser.c:3030:9
    #6 0x571091 in ucl_parser_add_string_priority /src/libucl/src/ucl_parser.c:3093:9
    #7 0x571091 in ucl_parser_add_string /src/libucl/src/ucl_parser.c:3105:9
    #8 0x55b34e in LLVMFuzzerTestOneInput /src/libucl/tests/fuzzers/ucl_add_string_fuzzer.c:17:2
    #9 0x455402 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:611:15
    #10 0x440fb2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:324:6
    #11 0x44681c in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:860:9
    #12 0x46f1b2 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    #13 0x7fdb5594f0b2 in __libc_start_main /build/glibc-sMfBJT/glibc-2.31/csu/../csu/libc-start.c:308:16
    #14 0x41f6fd in _start (/home/wcc/workspace/libucl/build-out/ucl_add_string_fuzzer+0x41f6fd)

0x6020000000b3 is located 0 bytes to the right of 3-byte region [0x6020000000b0,0x6020000000b3)
allocated by thread T0 here:
    #0 0x52482d in malloc /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:129:3
    #1 0x436f27 in operator new(unsigned long) cxa_noexception.cpp
    #2 0x440fb2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:324:6
    #3 0x44681c in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:860:9
    #4 0x46f1b2 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    #5 0x7fdb5594f0b2 in __libc_start_main /build/glibc-sMfBJT/glibc-2.31/csu/../csu/libc-start.c:308:16

SUMMARY: AddressSanitizer: heap-buffer-overflow /src/libucl/src/ucl_parser.c:846:45 in ucl_maybe_parse_number
Shadow bytes around the buggy address:
  0x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff8000: fa fa 00 00 fa fa 00 fa fa fa 00 fa fa fa 00 fa
=>0x0c047fff8010: fa fa 03 fa fa fa[03]fa fa fa 00 fa fa fa 00 04
  0x0c047fff8020: fa fa 00 01 fa fa 00 01 fa fa 05 fa fa fa 00 fa
  0x0c047fff8030: fa fa 00 01 fa fa 06 fa fa fa 07 fa fa fa 02 fa
  0x0c047fff8040: fa fa 04 fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8060: 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
==9526==ABORTING
vstakhov commented 1 year ago

In fact, all that code needs to be reworked to use zero-terminated buffer. It involves some copy but it should be safe and fine.