quick-lint / quick-lint-js

quick-lint-js finds bugs in JavaScript programs
https://quick-lint-js.com
GNU General Public License v3.0
1.52k stars 191 forks source link

Lexer heap buffer overflow #1191

Closed rol1510 closed 5 months ago

rol1510 commented 7 months ago

Found this heap-buffer-overflow while working on the fuzzer ci.

Steps to reproduce:

  1. Build the quick-lint-js fuzzers with CC=clang CXX=clang++ CFLAGS="-fsanitize=address,undefined -stdlib=libstdc++" CXXFLAGS=-fsanitize=address,undefined cmake -G Ninja -DCMAKE_BUILD_TYPE=Release ..
  2. create the empty directory "corpus"
  3. Download this file crash.txt and place it in the corpus directory.
  4. run ./build-fuzz/fuzz/quick-lint-js-fuzz-lex corpus/
  5. You should see the following ASAN message:
==1644==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x621000002507 at pc 0x55be9eea0c1f bp 0x7fffbc794530 sp 0x7fffbc794528
READ of size 16 at 0x621000002507 thread T0
    #0 0x55be9eea0c1e in quick_lint_js::Keyword_Lexer::key_strings_equal(char8_t const*, char8_t const*, unsigned long) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x1b3c1e) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #1 0x55be9eea03ee in quick_lint_js::Lexer::identifier_token_type(std::basic_string_view<char8_t, std::char_traits<char8_t> >) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x1b33ee) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #2 0x55be9ee5aad7 in quick_lint_js::Lexer::try_parse_current_token() (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x16dad7) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #3 0x55be9ee57fde in quick_lint_js::Lexer::parse_current_token() (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x16afde) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #4 0x55be9ee26d47 in LLVMFuzzerTestOneInput (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x139d47) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #5 0x55be9ed59b53 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x6cb53) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #6 0x55be9ed59279 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x6c279) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #7 0x55be9ed5ac08 in fuzzer::Fuzzer::ReadAndExecuteSeedCorpora(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile> >&) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x6dc08) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #8 0x55be9ed5b0f2 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile> >&) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x6e0f2) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #9 0x55be9ed49cf2 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x5ccf2) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #10 0x55be9ed729f2 in main (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x859f2) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #11 0x7ff3dd9781c9  (/lib/x86_64-linux-gnu/libc.so.6+0x271c9) (BuildId: 51657f818beb1ae70372216a99b7412b8a100a20)
    #12 0x7ff3dd978284 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x27284) (BuildId: 51657f818beb1ae70372216a99b7412b8a100a20)
    #13 0x55be9ed3e900 in _start (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x51900) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)

0x621000002507 is located 7 bytes to the right of 4096-byte region [0x621000001500,0x621000002500)
allocated by thread T0 here:
    #0 0x55be9ee2450d in operator new(unsigned long) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x13750d) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #1 0x55be9eea3f6d in quick_lint_js::Flexible_Array<char, quick_lint_js::Linked_Bump_Allocator::Chunk_Header>* quick_lint_js::Flexible_Array<char, quick_lint_js::Linked_Bump_Allocator::Chunk_Header>::allocate_and_construct_header<quick_lint_js::Flexible_Array<char, quick_lint_js::Linked_Bump_Allocator::Chunk_Header>*&>(quick_lint_js::Memory_Resource*, unsigned long, quick_lint_js::Flexible_Array<char, quick_lint_js::Linked_Bump_Allocator::Chunk_Header>*&) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x1b6f6d) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #2 0x55be9eea3d5b in quick_lint_js::Linked_Bump_Allocator::append_chunk(unsigned long, unsigned long) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x1b6d5b) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #3 0x55be9eea3a27 in quick_lint_js::Linked_Bump_Allocator::allocate_bytes(unsigned long, unsigned long) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x1b6a27) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #4 0x55be9ee6e8b9 in quick_lint_js::Lexer::parse_identifier_slow(char8_t const*, char8_t const*, quick_lint_js::Lexer::Identifier_Kind) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x1818b9) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #5 0x55be9ee622fc in quick_lint_js::Lexer::parse_identifier(char8_t const*, quick_lint_js::Lexer::Identifier_Kind) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x1752fc) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #6 0x55be9ee66126 in quick_lint_js::Lexer::reparse_as_regexp() (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x179126) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #7 0x55be9ee629b8 in quick_lint_js::Lexer::test_for_regexp(char8_t const*) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x1759b8) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #8 0x55be9ee5c76c in quick_lint_js::Lexer::try_parse_current_token() (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x16f76c) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #9 0x55be9ee57fca in quick_lint_js::Lexer::parse_current_token() (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x16afca) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #10 0x55be9ee26d47 in LLVMFuzzerTestOneInput (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x139d47) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #11 0x55be9ed59b53 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x6cb53) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #12 0x55be9ed59279 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x6c279) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #13 0x55be9ed5ac08 in fuzzer::Fuzzer::ReadAndExecuteSeedCorpora(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile> >&) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x6dc08) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #14 0x55be9ed5b0f2 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile> >&) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x6e0f2) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #15 0x55be9ed49cf2 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x5ccf2) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #16 0x55be9ed729f2 in main (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x859f2) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4)
    #17 0x7ff3dd9781c9  (/lib/x86_64-linux-gnu/libc.so.6+0x271c9) (BuildId: 51657f818beb1ae70372216a99b7412b8a100a20)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/mnt/g/repos/quick-lint-js/build-fuzz/fuzz/quick-lint-js-fuzz-lex+0x1b3c1e) (BuildId: 2608e54ff4ea687b215dde8163c80509c663dcf4) in quick_lint_js::Keyword_Lexer::key_strings_equal(char8_t const*, char8_t const*, unsigned long)
Shadow bytes around the buggy address:
  0x0c427fff8450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fff8460: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fff8470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fff8480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fff8490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c427fff84a0:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff84b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff84c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff84d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff84e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff84f0: 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
==1644==ABORTING
MS: 0 ; base unit: 0000000000000000000000000000000000000000
0x11,0x47,0x2a,0x2a,0x2f,0x5c,0x75,0x5c,0x2a,0x47,0x2a,0x2a,0x2f,0x5c,0x75,0x5c,0x2a,0x2f,0x5c,0x75,0x5c,0x2a,0x2f,0x5c,0x75,0x5c,0x2a,0x47,0x2a,0x2a,0x2f,0x5c,0x75,0x5c,0x2a,0x2f,0x5c,0x75,0x5c,0x75,0x2a,0x2a,0x2f,0x38,0x3c,0x0,0x0,0x0,0x75,0x2a,0x2f,0x5c,0x75,0x0,0x0,0x2f,0x5c,0x75,0x5c,0x75,0x0,0x0,0x0,0x0,0x0,0x0,0x2a,0x2a,0x2f,0x36,0x2f,0x5c,0x75,0x5c,0x75,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x2a,0x2f,0x5c,0x75,0x5c,0x2a,0x47,0x2a,0x2a,0x2f,0x5c,0x75,0x5c,0x2a,0x2f,0x5c,0x75,0x5c,0x75,0x2a,0x2a,0x2f,0x38,0x3c,0x0,0x0,0x0,0x75,0x2a,0x2f,0x5c,0x75,0x0,0x0,0x75,0x2a,0x2f,0x5c,0x75,0x0,0x0,0x2f,0x5c,0x75,0x5c,0x75,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x75,0x2a,0x2a,0x2f,0x38,0x71,0x2a,0x2f,0x34,0x71,
\021G**/\\u\\*G**/\\u\\*/\\u\\*/\\u\\*G**/\\u\\*/\\u\\u**/8<\000\000\000u*/\\u\000\000/\\u\\u\000\000\000\000\000\000**/6/\\u\\u\377\377\377\377\377\377\377\377\377\377\377*/\\u\\*G**/\\u\\*/\\u\\u**/8<\000\000\000u*/\\u\000\000u*/\\u\000\000/\\u\\uUUUUUUUUUUUu**/8q*/4q
artifact_prefix='./'; Test unit written to ./crash-fd5dfa97fcd291bce68ca883e5c43217ec3a0508
Base64: EUcqKi9cdVwqRyoqL1x1XCovXHVcKi9cdVwqRyoqL1x1XCovXHVcdSoqLzg8AAAAdSovXHUAAC9cdVx1AAAAAAAAKiovNi9cdVx1//////////////8qL1x1XCpHKiovXHVcKi9cdVx1KiovODwAAAB1Ki9cdQAAdSovXHUAAC9cdVx1VVVVVVVVVVVVVVV1KiovOHEqLzRx
rol1510 commented 7 months ago

./quick-lint-js crash.txt works. Only the fuzzer is affected.

strager commented 5 months ago

Thanks for reporting! There is a real bug which affects the main program as well.

strager commented 5 months ago

Fixed in Git commit 02a1c8949b4fc1a897afa90dc871eda74d6f9764.

strager commented 5 months ago

Fix released in version 3.2.0.