Skip to content

heap-buffer-overflow #73

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.

Activity

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

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