Skip to content

heap-buffer-overflow #73

@vvarg229

Description

@vvarg229

Hi, I decided to run a fuzz test and immediately noticed a crash.

==1309300==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50200002e236 at pc 0x607ae66664c5 bp 0x7ffc3e4badb0 sp 0x7ffc3e4bada8
WRITE of size 1 at 0x50200002e236 thread T0
    #0 0x607ae66664c4 in parse_string(cJSON*, char const*, char const**) /home/fagus/isp/CJsonObject/cJSON.c:261:21
    #1 0x607ae6660d80 in cJSON_Parse /home/fagus/isp/CJsonObject/cJSON.c:438:10
    #2 0x607ae663e5bd in neb::CJsonObject::Parse(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) /home/fagus/isp/CJsonObject/CJsonObject.cpp:507:19
    #3 0x607ae663d9d4 in LLVMFuzzerTestOneInput /home/fagus/isp/CJsonObject/fuzz/cjson_fuzz.cpp:9:7
    #4 0x607ae6548cb4 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/fagus/isp/CJsonObject/cjson_fuzz+0x5ccb4) (BuildId: 97cab75bade6e43708a54d786b0a65e04127801e)
    #5 0x607ae65483a9 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (/home/fagus/isp/CJsonObject/cjson_fuzz+0x5c3a9) (BuildId: 97cab75bade6e43708a54d786b0a65e04127801e)
    #6 0x607ae6549b95 in fuzzer::Fuzzer::MutateAndTestOne() (/home/fagus/isp/CJsonObject/cjson_fuzz+0x5db95) (BuildId: 97cab75bade6e43708a54d786b0a65e04127801e)
    #7 0x607ae654a6f5 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile>>&) (/home/fagus/isp/CJsonObject/cjson_fuzz+0x5e6f5) (BuildId: 97cab75bade6e43708a54d786b0a65e04127801e)
    #8 0x607ae65379cf in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/fagus/isp/CJsonObject/cjson_fuzz+0x4b9cf) (BuildId: 97cab75bade6e43708a54d786b0a65e04127801e)
    #9 0x607ae6562056 in main (/home/fagus/isp/CJsonObject/cjson_fuzz+0x76056) (BuildId: 97cab75bade6e43708a54d786b0a65e04127801e)
    #10 0x73ae0aa2a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #11 0x73ae0aa2a28a in __libc_start_main csu/../csu/libc-start.c:360:3
    #12 0x607ae652c9b4 in _start (/home/fagus/isp/CJsonObject/cjson_fuzz+0x409b4) (BuildId: 97cab75bade6e43708a54d786b0a65e04127801e)

0x50200002e236 is located 0 bytes after 6-byte region [0x50200002e230,0x50200002e236)
allocated by thread T0 here:
    #0 0x607ae65fcde3 in malloc (/home/fagus/isp/CJsonObject/cjson_fuzz+0x110de3) (BuildId: 97cab75bade6e43708a54d786b0a65e04127801e)
    #1 0x607ae6665c3e in parse_string(cJSON*, char const*, char const**) /home/fagus/isp/CJsonObject/cJSON.c:252:19
    #2 0x607ae6660d80 in cJSON_Parse /home/fagus/isp/CJsonObject/cJSON.c:438:10
    #3 0x607ae663e5bd in neb::CJsonObject::Parse(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) /home/fagus/isp/CJsonObject/CJsonObject.cpp:507:19
    #4 0x607ae663d9d4 in LLVMFuzzerTestOneInput /home/fagus/isp/CJsonObject/fuzz/cjson_fuzz.cpp:9:7
    #5 0x607ae6548cb4 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/fagus/isp/CJsonObject/cjson_fuzz+0x5ccb4) (BuildId: 97cab75bade6e43708a54d786b0a65e04127801e)
    #6 0x607ae65483a9 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (/home/fagus/isp/CJsonObject/cjson_fuzz+0x5c3a9) (BuildId: 97cab75bade6e43708a54d786b0a65e04127801e)
    #7 0x607ae6549b95 in fuzzer::Fuzzer::MutateAndTestOne() (/home/fagus/isp/CJsonObject/cjson_fuzz+0x5db95) (BuildId: 97cab75bade6e43708a54d786b0a65e04127801e)
    #8 0x607ae654a6f5 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile>>&) (/home/fagus/isp/CJsonObject/cjson_fuzz+0x5e6f5) (BuildId: 97cab75bade6e43708a54d786b0a65e04127801e)
    #9 0x607ae65379cf in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/fagus/isp/CJsonObject/cjson_fuzz+0x4b9cf) (BuildId: 97cab75bade6e43708a54d786b0a65e04127801e)
    #10 0x607ae6562056 in main (/home/fagus/isp/CJsonObject/cjson_fuzz+0x76056) (BuildId: 97cab75bade6e43708a54d786b0a65e04127801e)
    #11 0x73ae0aa2a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #12 0x73ae0aa2a28a in __libc_start_main csu/../csu/libc-start.c:360:3
    #13 0x607ae652c9b4 in _start (/home/fagus/isp/CJsonObject/cjson_fuzz+0x409b4) (BuildId: 97cab75bade6e43708a54d786b0a65e04127801e)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/fagus/isp/CJsonObject/cJSON.c:261:21 in parse_string(cJSON*, char const*, char const**)
Shadow bytes around the buggy address:
  0x50200002df80: fa fa fd fa fa fa fd fd fa fa fd fa fa fa fd fd
  0x50200002e000: fa fa fd fa fa fa fd fa fa fa 00 00 fa fa fd fa
  0x50200002e080: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fd
  0x50200002e100: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
  0x50200002e180: fa fa fd fa fa fa fd fa fa fa fd fd fa fa fd fd
=>0x50200002e200: fa fa 00 00 fa fa[06]fa fa fa fa fa fa fa fa fa
  0x50200002e280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50200002e300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50200002e380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50200002e400: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x50200002e480: 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
==1309300==ABORTING
MS: 2 CopyPart-CMP- DE: "\000\014"-; base unit: 9b41b23eda7b652e05849707df184aa0506d173f
0x22,0x5c,0x22,0x5c,0x22,0x5c,0x22,0x5c,0x75,0x65,0x0,0xc,0x5b,0x75,0x65,0x5b,
\"\\\"\\\"\\\"\\ue\000\014[ue[
artifact_prefix='./'; Test unit written to ./crash-555d7775e697d9f9f0e169a6ce015a7ad1f24730
Base64: IlwiXCJcIlx1ZQAMW3VlWw==

The crash is reproducible even without sanitizer.

Steps to reproduce:

Create a Makefile for fuzzing in the root of the project (this is the easiest way to build, but not necessary)

CXX := clang++
CXXFLAGS := -std=c++11 -g -O1 -fsanitize=address,fuzzer
SRCS := fuzz/cjson_fuzz.cpp CJsonObject.cpp cJSON.c
TARGET := cjson_fuzz

all:
	$(CXX) $(CXXFLAGS) -I. -o $(TARGET) $(SRCS)

clean:
	rm -f $(TARGET)

Create a directory for the input data corpus to begin testing

mkdir corpus
echo '{}' > corpus/empty
echo '[]' > corpus/array
echo '{"a":1}' > corpus/obj

And let's create a simple fuzzing target using https://llvm.org/docs/LibFuzzer.html

cjson_fuzz.cpp

#include <cstddef>
#include <cstdint>
#include <string>
#include "CJsonObject.hpp"

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
    std::string s(Data, Data + Size);
    neb::CJsonObject j;
    j.Parse(s);
    return 0;
}

Make:

make

And let's just run fuzzing

./cjson_fuzz ./corpus

We'll very quickly get a heap-buffer-overflow crash:

INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 4160759713
INFO: Loaded 1 modules   (2594 inline 8-bit counters): 2594 [0x62d030b4a028, 0x62d030b4aa4a), 
INFO: Loaded 1 PC tables (2594 PCs): 2594 [0x62d030b4aa50,0x62d030b54c70), 
INFO:      322 files found in ./corpus
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: seed corpus: files: 322 min: 1b max: 129b total: 5144b rss: 32Mb
#323	INITED cov: 168 ft: 483 corp: 166/3175b exec/s: 0 rss: 38Mb
#324	REDUCE cov: 168 ft: 483 corp: 166/3166b lim: 129 exec/s: 0 rss: 39Mb L: 81/129 MS: 1 EraseBytes-
#350	REDUCE cov: 168 ft: 483 corp: 166/3151b lim: 129 exec/s: 0 rss: 39Mb L: 44/129 MS: 1 EraseBytes-
#389	REDUCE cov: 168 ft: 483 corp: 166/3134b lim: 129 exec/s: 0 rss: 39Mb L: 55/129 MS: 4 CrossOver-ChangeBit-CrossOver-EraseBytes-
#415	NEW    cov: 168 ft: 485 corp: 167/3165b lim: 129 exec/s: 0 rss: 39Mb L: 31/129 MS: 1 EraseBytes-
#606	REDUCE cov: 168 ft: 485 corp: 167/3148b lim: 129 exec/s: 0 rss: 40Mb L: 30/129 MS: 1 CrossOver-
#727	REDUCE cov: 168 ft: 485 corp: 167/3146b lim: 129 exec/s: 0 rss: 40Mb L: 11/129 MS: 1 EraseBytes-
#820	REDUCE cov: 168 ft: 485 corp: 167/3145b lim: 129 exec/s: 0 rss: 40Mb L: 7/129 MS: 3 ShuffleBytes-EraseBytes-ChangeByte-
#961	REDUCE cov: 168 ft: 485 corp: 167/3144b lim: 129 exec/s: 0 rss: 40Mb L: 11/129 MS: 1 CrossOver-
#1112	REDUCE cov: 168 ft: 485 corp: 167/3104b lim: 129 exec/s: 0 rss: 41Mb L: 50/129 MS: 1 EraseBytes-
=================================================================
==1314564==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x5080000032f7 at pc 0x62d030aff4b8 bp 0x7ffdac561e70 sp 0x7ffdac561e68
READ of size 1 at 0x5080000032f7 thread T0
    #0 0x62d030aff4b7 in parse_string(cJSON*, char const*, char const**) /home/fagus/isp/CJsonObject/cJSON.c:258:12
    #1 0x62d030af9d80 in cJSON_Parse /home/fagus/isp/CJsonObject/cJSON.c:438:10
    #2 0x62d030ad75bd in neb::CJsonObject::Parse(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) /home/fagus/isp/CJsonObject/CJsonObject.cpp:507:19
    #3 0x62d030ad69d4 in LLVMFuzzerTestOneInput /home/fagus/isp/CJsonObject/fuzz/cjson_fuzz.cpp:7:7
    #4 0x62d0309e1cb4 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/fagus/isp/CJsonObject/cjson_fuzz+0x5ccb4) (BuildId: 8811f46008271d7e5d23c0a7daf79a90f812b0ab)
    #5 0x62d0309e13a9 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (/home/fagus/isp/CJsonObject/cjson_fuzz+0x5c3a9) (BuildId: 8811f46008271d7e5d23c0a7daf79a90f812b0ab)
    #6 0x62d0309e2b95 in fuzzer::Fuzzer::MutateAndTestOne() (/home/fagus/isp/CJsonObject/cjson_fuzz+0x5db95) (BuildId: 8811f46008271d7e5d23c0a7daf79a90f812b0ab)
    #7 0x62d0309e36f5 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile>>&) (/home/fagus/isp/CJsonObject/cjson_fuzz+0x5e6f5) (BuildId: 8811f46008271d7e5d23c0a7daf79a90f812b0ab)
    #8 0x62d0309d09cf in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/fagus/isp/CJsonObject/cjson_fuzz+0x4b9cf) (BuildId: 8811f46008271d7e5d23c0a7daf79a90f812b0ab)
    #9 0x62d0309fb056 in main (/home/fagus/isp/CJsonObject/cjson_fuzz+0x76056) (BuildId: 8811f46008271d7e5d23c0a7daf79a90f812b0ab)
    #10 0x758f1a22a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #11 0x758f1a22a28a in __libc_start_main csu/../csu/libc-start.c:360:3
    #12 0x62d0309c59b4 in _start (/home/fagus/isp/CJsonObject/cjson_fuzz+0x409b4) (BuildId: 8811f46008271d7e5d23c0a7daf79a90f812b0ab)

0x5080000032f7 is located 2 bytes after 85-byte region [0x5080000032a0,0x5080000032f5)
allocated by thread T0 here:
    #0 0x62d030ad43e1 in operator new(unsigned long) (/home/fagus/isp/CJsonObject/cjson_fuzz+0x14f3e1) (BuildId: 8811f46008271d7e5d23c0a7daf79a90f812b0ab)
    #1 0x62d030ad68c8 in void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_construct<unsigned char const*>(unsigned char const*, unsigned char const*, std::forward_iterator_tag) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/basic_string.tcc:229:14
    #2 0x62d030ad68c8 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::basic_string<unsigned char const*, void>(unsigned char const*, unsigned char const*, std::allocator<char> const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/basic_string.h:765:4
    #3 0x62d030ad68c8 in LLVMFuzzerTestOneInput /home/fagus/isp/CJsonObject/fuzz/cjson_fuzz.cpp:5:17
    #4 0x62d0309e1cb4 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/fagus/isp/CJsonObject/cjson_fuzz+0x5ccb4) (BuildId: 8811f46008271d7e5d23c0a7daf79a90f812b0ab)
    #5 0x62d0309e13a9 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (/home/fagus/isp/CJsonObject/cjson_fuzz+0x5c3a9) (BuildId: 8811f46008271d7e5d23c0a7daf79a90f812b0ab)
    #6 0x62d0309e2b95 in fuzzer::Fuzzer::MutateAndTestOne() (/home/fagus/isp/CJsonObject/cjson_fuzz+0x5db95) (BuildId: 8811f46008271d7e5d23c0a7daf79a90f812b0ab)
    #7 0x62d0309e36f5 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile>>&) (/home/fagus/isp/CJsonObject/cjson_fuzz+0x5e6f5) (BuildId: 8811f46008271d7e5d23c0a7daf79a90f812b0ab)
    #8 0x62d0309d09cf in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/fagus/isp/CJsonObject/cjson_fuzz+0x4b9cf) (BuildId: 8811f46008271d7e5d23c0a7daf79a90f812b0ab)
    #9 0x62d0309fb056 in main (/home/fagus/isp/CJsonObject/cjson_fuzz+0x76056) (BuildId: 8811f46008271d7e5d23c0a7daf79a90f812b0ab)
    #10 0x758f1a22a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #11 0x758f1a22a28a in __libc_start_main csu/../csu/libc-start.c:360:3
    #12 0x62d0309c59b4 in _start (/home/fagus/isp/CJsonObject/cjson_fuzz+0x409b4) (BuildId: 8811f46008271d7e5d23c0a7daf79a90f812b0ab)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/fagus/isp/CJsonObject/cJSON.c:258:12 in parse_string(cJSON*, char const*, char const**)
Shadow bytes around the buggy address:
  0x508000003000: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
  0x508000003080: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
  0x508000003100: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
  0x508000003180: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
  0x508000003200: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 04 fa
=>0x508000003280: fa fa fa fa 00 00 00 00 00 00 00 00 00 00[05]fa
  0x508000003300: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 03 fa
  0x508000003380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x508000003400: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x508000003480: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x508000003500: 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
==1314564==ABORTING
MS: 1 InsertRepeatedBytes-; base unit: 469d265084ed5682f6171c7cb0bb0fe20bdc2a88
0x22,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x45,0x5c,0x75,0x30,
\"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\\u0
artifact_prefix='./'; Test unit written to ./crash-9d3143562b538b4c0311117276bcc334eefc4cf2
Base64: IkVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFXHUw

The crash is reproducible consistently, if you need any help with debugging, I can help.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions