Fix out of buffer read in StreamCursor while parsing HTTP request #1192
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This issue has been found as a result of fuzzing #1191
AddressSanitizer report
==2032182==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x506000aa5fe0 at pc 0x55739b8e848d bp 0x7ffcc86b6550 sp 0x7ffcc86b6548
READ of size 1 at 0x506000aa5fe0 thread T0
#0 0x55739b8e848c in Pistache::StreamBuf::snext() const pistache/src/../include/pistache/stream.h:62:20
#1 0x55739b8e823f in Pistache::StreamCursor::next() const pistache/src/common/stream.cc:186:21
#2 0x55739b8e80f7 in Pistache::StreamCursor::eol() const pistache/src/common/stream.cc:177:67
#3 0x55739b756857 in Pistache::Http::Private::RequestLineStep::apply(Pistache::StreamCursor&) pistache/src/common/http.cc:235:28
#4 0x55739b75d81a in Pistache::Http::Private::ParserBase::parse() pistache/src/common/http.cc:545:36
#5 0x55739b724983 in fuzz_request_parser(std::__cxx11::basic_string<char, std::char_traits, std::allocator> const&)::$_0::operator()() const pistache/tests/fuzzers/fuzz_parser.cpp:82:48
#6 0x55739b7126c4 in void ignoreExceptions<fuzz_request_parser(std::__cxx11::basic_string<char, std::char_traits, std::allocator> const&)::$_0>(fuzz_request_parser(std::__cxx11::basic_string<char, std::char_traits, std::allocator> const&)::$_0 const&) pistache/tests/fuzzers/fuzz_parser.cpp:17:9
#7 0x55739b71238c in fuzz_request_parser(std::__cxx11::basic_string<char, std::char_traits, std::allocator> const&) pistache/tests/fuzzers/fuzz_parser.cpp:82:9
#8 0x55739b7141c3 in LLVMFuzzerTestOneInput pistache/tests/fuzzers/fuzz_parser.cpp:153:9
#9 0x55739b6198f0 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (pistache/build/tests/fuzzers/fuzz_parser+0x13a8f0) (BuildId: 77a710c1e69da7f096aec08fd6fb4b1fc2e8e9e4)
#10 0x55739b619065 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (pistache/build/tests/fuzzers/fuzz_parser+0x13a065) (BuildId: 77a710c1e69da7f096aec08fd6fb4b1fc2e8e9e4)
#11 0x55739b61a845 in fuzzer::Fuzzer::MutateAndTestOne() (pistache/build/tests/fuzzers/fuzz_parser+0x13b845) (BuildId: 77a710c1e69da7f096aec08fd6fb4b1fc2e8e9e4)
#12 0x55739b61b455 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocatorfuzzer::SizedFile>&) (pistache/build/tests/fuzzers/fuzz_parser+0x13c455) (BuildId: 77a710c1e69da7f096aec08fd6fb4b1fc2e8e9e4)
#13 0x55739b6095bb in fuzzer::FuzzerDriver(int*, char***, int ()(unsigned char const, unsigned long)) (pistache/build/tests/fuzzers/fuzz_parser+0x12a5bb) (BuildId: 77a710c1e69da7f096aec08fd6fb4b1fc2e8e9e4)
#14 0x55739b6325f2 in main (pistache/build/tests/fuzzers/fuzz_parser+0x1535f2) (BuildId: 77a710c1e69da7f096aec08fd6fb4b1fc2e8e9e4)
#15 0x7fea27127d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#16 0x7fea27127e3f in __libc_start_main csu/../csu/libc-start.c:392:3
#17 0x55739b5fea24 in _start (pistache/build/tests/fuzzers/fuzz_parser+0x11fa24) (BuildId: 77a710c1e69da7f096aec08fd6fb4b1fc2e8e9e4)
0x506000aa5fe0 is located 0 bytes after 64-byte region [0x506000aa5fa0,0x506000aa5fe0)
allocated by thread T0 here:
#0 0x55739b70b32d in operator new(unsigned long) (pistache/build/tests/fuzzers/fuzz_parser+0x22c32d) (BuildId: 77a710c1e69da7f096aec08fd6fb4b1fc2e8e9e4)
#1 0x55739b795b48 in std::__new_allocator::allocate(unsigned long, void const*) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/new_allocator.h:137:27
#2 0x55739b7959fa in std::allocator_traits<std::allocator>::allocate(std::allocator&, unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/alloc_traits.h:464:20
#3 0x55739b79515b in std::_Vector_base<char, std::allocator>::_M_allocate(unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/stl_vector.h:378:20
#4 0x55739b79449c in void std::vector<char, std::allocator>::_M_realloc_insert<char const&>(__gnu_cxx::__normal_iterator<char*, std::vector<char, std::allocator>>, char const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/vector.tcc:453:33
#5 0x55739b7940e7 in std::vector<char, std::allocator>::push_back(char const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/stl_vector.h:1287:4
#6 0x55739b793ddb in std::back_insert_iterator<std::vector<char, std::allocator>>::operator=(char const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/stl_iterator.h:735:13
#7 0x55739b793bfe in std::back_insert_iterator<std::vector<char, std::allocator>> std::__copy_move<false, false, std::random_access_iterator_tag>::__copy_m<char const*, std::back_insert_iterator<std::vector<char, std::allocator>>>(char const*, char const*, std::back_insert_iterator<std::vector<char, std::allocator>>) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/stl_algobase.h:385:18
#8 0x55739b793965 in std::back_insert_iterator<std::vector<char, std::allocator>> std::__copy_move_a2<false, char const*, std::back_insert_iterator<std::vector<char, std::allocator>>>(char const*, char const*, std::back_insert_iterator<std::vector<char, std::allocator>>) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/stl_algobase.h:494:14
#9 0x55739b7934b5 in std::back_insert_iterator<std::vector<char, std::allocator>> std::__copy_move_a1<false, char const*, std::back_insert_iterator<std::vector<char, std::allocator>>>(char const*, char const*, std::back_insert_iterator<std::vector<char, std::allocator>>) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/stl_algobase.h:522:14
#10 0x55739b792f6a in std::back_insert_iterator<std::vector<char, std::allocator>> std::__copy_move_a<false, char const*, std::back_insert_iterator<std::vector<char, std::allocator>>>(char const*, char const*, std::back_insert_iterator<std::vector<char, std::allocator>>) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/stl_algobase.h:530:3
#11 0x55739b792a63 in std::back_insert_iterator<std::vector<char, std::allocator>> std::copy<char const*, std::back_insert_iterator<std::vector<char, std::allocator>>>(char const*, char const*, std::back_insert_iterator<std::vector<char, std::allocator>>) /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/stl_algobase.h:619:14
#12 0x55739b75dd72 in Pistache::ArrayStreamBuf::feed(char const*, unsigned long) pistache/src/../include/pistache/stream.h:111:13
#13 0x55739b75da6a in Pistache::Http::Private::ParserBase::feed(char const*, unsigned long) pistache/src/common/http.cc:558:27
#14 0x55739b7122ad in fuzz_request_parser(std::__cxx11::basic_string<char, std::char_traits, std::allocator> const&) pistache/tests/fuzzers/fuzz_parser.cpp:79:17
#15 0x55739b7141c3 in LLVMFuzzerTestOneInput pistache/tests/fuzzers/fuzz_parser.cpp:153:9
#16 0x55739b6198f0 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (pistache/build/tests/fuzzers/fuzz_parser+0x13a8f0) (BuildId: 77a710c1e69da7f096aec08fd6fb4b1fc2e8e9e4)
#17 0x55739b619065 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (pistache/build/tests/fuzzers/fuzz_parser+0x13a065) (BuildId: 77a710c1e69da7f096aec08fd6fb4b1fc2e8e9e4)
#18 0x55739b61a845 in fuzzer::Fuzzer::MutateAndTestOne() (pistache/build/tests/fuzzers/fuzz_parser+0x13b845) (BuildId: 77a710c1e69da7f096aec08fd6fb4b1fc2e8e9e4)
#19 0x55739b61b455 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocatorfuzzer::SizedFile>&) (pistache/build/tests/fuzzers/fuzz_parser+0x13c455) (BuildId: 77a710c1e69da7f096aec08fd6fb4b1fc2e8e9e4)
#20 0x55739b6095bb in fuzzer::FuzzerDriver(int*, char***, int ()(unsigned char const, unsigned long)) (pistache/build/tests/fuzzers/fuzz_parser+0x12a5bb) (BuildId: 77a710c1e69da7f096aec08fd6fb4b1fc2e8e9e4)
#21 0x55739b6325f2 in main (pistache/build/tests/fuzzers/fuzz_parser+0x1535f2) (BuildId: 77a710c1e69da7f096aec08fd6fb4b1fc2e8e9e4)
#22 0x7fea27127d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
SUMMARY: AddressSanitizer: heap-buffer-overflow pistache/src/../include/pistache/stream.h:62:20 in Pistache::StreamBuf::snext() const
Shadow bytes around the buggy address:
0x506000aa5d00: fd fd fd fd fd fd fd fd fa fa fa fa fd fd fd fd
0x506000aa5d80: fd fd fd fd fa fa fa fa fd fd fd fd fd fd fd fd
0x506000aa5e00: fa fa fa fa fd fd fd fd fd fd fd fd fa fa fa fa
0x506000aa5e80: fd fd fd fd fd fd fd fd fa fa fa fa 00 00 00 00
0x506000aa5f00: 00 00 00 fa fa fa fa fa fd fd fd fd fd fd fd fd
=>0x506000aa5f80: fa fa fa fa 00 00 00 00 00 00 00 00[fa]fa fa fa
0x506000aa6000: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
0x506000aa6080: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
0x506000aa6100: fa fa fa fa fd fd fd fd fd fd fd fd fa fa fa fa
0x506000aa6180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x506000aa6200: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
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
==2032182==ABORTING
Explanation:
buf->in_avail() == 1
means that we can read the current character, but the next one might be out of bounds because there is no guarantee that StreamCursor works with zero-terminated string.