ikatyang / tree-sitter-yaml

YAML grammar for tree-sitter
https://ikatyang.github.io/tree-sitter-yaml/
MIT License
94 stars 38 forks source link

heap-buffer overflow #16

Closed dimbleby closed 3 years ago

dimbleby commented 3 years ago

I thought I'd see how this parser handled being fuzzed. I didn't get very far: it looks as though every file provokes invalid memory access! The pointer being dereferenced here is not valid.

Certainly the empty file is sufficient to provoke this error.

==21234==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000000ee at pc 0x00000057f42f bp 0x7ffe873e9090 sp 0x7ffe873e9088
READ of size 2 at 0x6020000000ee thread T0
    #0 0x57f42e in (anonymous namespace)::Scanner::scan(TSLexer*, bool const*) /data/developer/tree-sitter/test/fixtures/grammars/yaml/src/scanner.cc:660:23
    #1 0x5c42bd in ts_parser__lex (/data/developer/tree-sitter/out/yaml_fuzzer+0x5c42bd)
    #2 0x5ae757 in ts_parser__advance (/data/developer/tree-sitter/out/yaml_fuzzer+0x5ae757)
    #3 0x5a838c in ts_parser_parse (/data/developer/tree-sitter/out/yaml_fuzzer+0x5a838c)
    #4 0x5b9d52 in ts_parser_parse_string_encoding (/data/developer/tree-sitter/out/yaml_fuzzer+0x5b9d52)
    #5 0x5b9a13 in ts_parser_parse_string (/data/developer/tree-sitter/out/yaml_fuzzer+0x5b9a13)
    #6 0x5751da in LLVMFuzzerTestOneInput (/data/developer/tree-sitter/out/yaml_fuzzer+0x5751da)
    #7 0x42fe1f in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/data/developer/tree-sitter/out/yaml_fuzzer+0x42fe1f)
    #8 0x43424f in fuzzer::Fuzzer::ReadAndExecuteSeedCorpora(std::vector<std::string, fuzzer::fuzzer_allocator<std::string> > const&) (/data/developer/tree-sitter/out/yaml_fuzzer+0x43424f)
    #9 0x434748 in fuzzer::Fuzzer::Loop(std::vector<std::string, fuzzer::fuzzer_allocator<std::string> > const&) (/data/developer/tree-sitter/out/yaml_fuzzer+0x434748)
    #10 0x429535 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/data/developer/tree-sitter/out/yaml_fuzzer+0x429535)
    #11 0x41ebb2 in main (/data/developer/tree-sitter/out/yaml_fuzzer+0x41ebb2)
    #12 0x7f88680be554 in __libc_start_main (/lib64/libc.so.6+0x22554)
    #13 0x41ebdd in _start (/data/developer/tree-sitter/out/yaml_fuzzer+0x41ebdd)

0x6020000000ee is located 2 bytes to the left of 2-byte region [0x6020000000f0,0x6020000000f2)
allocated by thread T0 here:
    #0 0x570818 in operator new(unsigned long) (/data/developer/tree-sitter/out/yaml_fuzzer+0x570818)
    #1 0x594910 in void std::vector<short, std::allocator<short> >::_M_realloc_insert<short>(__gnu_cxx::__normal_iterator<short*, std::vector<short, std::allocator<short> > >, short&&) /opt/rh/devtoolset-8/root/usr/lib/gcc/x86_64-redhat-li
nux/8/../../../../include/c++/8/bits/vector.tcc:427:33
    #2 0x579d89 in (anonymous namespace)::Scanner::deserialize(char const*, unsigned int) /data/developer/tree-sitter/test/fixtures/grammars/yaml/src/scanner.cc:175:17
    #3 0x578d04 in (anonymous namespace)::Scanner::Scanner() /data/developer/tree-sitter/test/fixtures/grammars/yaml/src/scanner.cc:145:5
    #4 0x578c39 in tree_sitter_yaml_external_scanner_create /data/developer/tree-sitter/test/fixtures/grammars/yaml/src/scanner.cc:946:14
    #5 0x59eb7b in ts_parser_set_language (/data/developer/tree-sitter/out/yaml_fuzzer+0x59eb7b)
    #6 0x5750f1 in LLVMFuzzerTestOneInput (/data/developer/tree-sitter/out/yaml_fuzzer+0x5750f1)
    #7 0x42fe1f in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/data/developer/tree-sitter/out/yaml_fuzzer+0x42fe1f)
    #8 0x43424f in fuzzer::Fuzzer::ReadAndExecuteSeedCorpora(std::vector<std::string, fuzzer::fuzzer_allocator<std::string> > const&) (/data/developer/tree-sitter/out/yaml_fuzzer+0x43424f)
    #9 0x434748 in fuzzer::Fuzzer::Loop(std::vector<std::string, fuzzer::fuzzer_allocator<std::string> > const&) (/data/developer/tree-sitter/out/yaml_fuzzer+0x434748)
    #10 0x429535 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/data/developer/tree-sitter/out/yaml_fuzzer+0x429535)
    #11 0x41ebb2 in main (/data/developer/tree-sitter/out/yaml_fuzzer+0x41ebb2)
    #12 0x7f88680be554 in __libc_start_main (/lib64/libc.so.6+0x22554)

SUMMARY: AddressSanitizer: heap-buffer-overflow /data/developer/tree-sitter/test/fixtures/grammars/yaml/src/scanner.cc:660:23 in (anonymous namespace)::Scanner::scan(TSLexer*, bool const*)
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 fd fa
=>0x0c047fff8010: fa fa 00 00 fa fa 01 fa fa fa 02 fa fa[fa]02 fa
  0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8040: fa fa fa 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
  Shadow gap:              cc
==21234==ABORTING
dimbleby commented 3 years ago

I don't understand the code to know whether this is what's wanted, but the following fix certainly sidesteps the issue, and doesn't immediately fall over...

-    int16_t prt_ind = *ind_ptr;
+    int16_t prt_ind = ind_len_stk.size() > 1 ? *ind_ptr : 0;