Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix out of buffer read in StreamCursor while parsing HTTP request #1192

Merged
merged 1 commit into from
Feb 12, 2024

Conversation

tyler92
Copy link
Contributor

@tyler92 tyler92 commented Feb 12, 2024

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.

Copy link

codecov bot commented Feb 12, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (1c733a1) 78.09% compared to head (097e004) 78.11%.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1192      +/-   ##
==========================================
+ Coverage   78.09%   78.11%   +0.01%     
==========================================
  Files          53       53              
  Lines        7077     7077              
==========================================
+ Hits         5527     5528       +1     
+ Misses       1550     1549       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@kiplingw
Copy link
Member

Excellent work @tyler92!

@kiplingw kiplingw merged commit 1964b0c into pistacheio:master Feb 12, 2024
26 of 38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants