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

Generated code segfaults when parsing bytes with &until #1918

Closed
evantypanski opened this issue Nov 7, 2024 · 6 comments · Fixed by #1923
Closed

Generated code segfaults when parsing bytes with &until #1918

evantypanski opened this issue Nov 7, 2024 · 6 comments · Fixed by #1923
Assignees
Labels
Bug Something isn't working Priority High

Comments

@evantypanski
Copy link
Contributor

evantypanski commented Nov 7, 2024

I can't actually provide a reproducer - the one I have is with production data and I haven't been able to narrow it down. But I do have some info.

For context, I'm getting this when running the Redis analyzer on all TCP ports 1-10000 for some big pcaps. So not an every day use case, but I wouldn't expect a segfault :)

The segfault occurs here:

if ( *i != first )

where that function is called when parsing RedisBytes, namely in the __parse_RESP__RedisBytes_6_stage2 function.

The segfault is here when dereferencing i. Note that it does check ! byte but the value is not nullptr. In fact, when I run i->_chunk->_data, it also is invalid memory in the debugger ("error: Cannot access memory at address 0xXX")

first in this case is \r - which is the first byte of the &until clause. I removed synchronization and still get the segfault.

This was from a find call in parser code (when searching for the __until_bytes)

That's basically all I have for here. Maybe it's a use-after-free of some sort?

I'll continue to try to narrow this down a bit, but for now it may be useful to have input. I've had weird issues with spicy segfaults on rebuilds in the past that get magically fixed when I rebuild, but this seems to be different (I've deleted & reinstalled many times and rebuilt Zeek in debug).

@evantypanski evantypanski added the Bug Something isn't working label Nov 7, 2024
evantypanski added a commit to evantypanski/spicy-redis that referenced this issue Nov 7, 2024
This seems to cause spicy segfaults with some big pcaps:

zeek/spicy#1918
@evantypanski
Copy link
Contributor Author

Okay, I have this narrowed down to an 8K pcap and less than 50 lines of code, but I am probably further away from understanding the issue than I thought.

I still can't share the PCAP. :(

Okay, first, here's the reproducing code:

segfault.spicy:

module SegfaultRESP;

public type Messages = unit {
	: (Data &synchronize)[];
};

public type Data = unit {
	%synchronize-after=b"\x0d\x0a";
	ty: uint8;
	inline: bytes &until=b"\x0d\x0a" &max-size=1024;
};

segfault.evt:

protocol analyzer spicy::SegfaultRESP over TCP:
	parse with SegfaultRESP::Messages;

import SegfaultRESP;

on SegfaultRESP::Data -> event segfault::test($conn, $is_orig);

enable.zeek:

event zeek_init() {
	local i = 0;
	while ( i < 10000 ) {
		++i;
		Analyzer::register_for_port(Analyzer::ANALYZER_SPICY_SEGFAULTRESP, count_to_port(i, tcp));
	}
	print "HI";
}

Command line:

$ spicyz -o testsegfault.hlto segfault.evt segfault.spicy
$ zeek -bCr mypcap.pcap testsegfault.hlto enable.zeek 
Segmentation fault (core dumped)

The segfault goes away for a lot of things, some more surprising than others.

Decreasing the &max-size to 100 makes the segfault go away. So does removing synchronization. But, removing any possibility for synchronization to trigger (ie with a string that would/does never appear in the input) will still segfault.

Note, the print "HI"; is sometimes necessary (depending on the input data) - it will not segfault without it in this particular case. Even though it's spicy segfaulting. :)

Overall I'm not really sure what's going on here at all. My guess is still a use-after-free, all things considered.

I can always go in and test with various changes to the file, but I don't really know if that'd get more info than "memory stuff" honestly. It seems related to the &until clause - that check is where the segfault happens, and changing that is the most reliable way to stop the segfault from happening. Anything other than CRLF in that &until clause never causes the issue from what I can tell, I think it's triggering from very particular HTTP traffic with that.

My next goal will be to find a pcap somewhere that will segfault with those minimal files that I can share.

@evantypanski
Copy link
Contributor Author

Given those files, I found some pcaps that produce the segfault. The pcaps on this site (not the first one though) produce it for me. Just run the commands I gave before with those files, but swap the pcap file out for the reproducer.

I grabbed 4SICS-GeekLounge-151021.pcap and narrowed it down a bit, this should be a little more manageable. Splitting it will often get multiple pcaps, where none of them have the segfault. This is all I could get for now:

https://github.com/evantypanski/spicy-redis/raw/refs/heads/topic/etyp/all-ports/testing/Traces/segfault.pcap

it actually doesn't segfault from the Redis analyzer on all ports, but debugging it's the same spot, so my guess is it's the same issue.

evantypanski added a commit to evantypanski/spicy-redis that referenced this issue Nov 11, 2024
This seems to cause spicy segfaults with some big pcaps:

zeek/spicy#1918
evantypanski added a commit to evantypanski/spicy-redis that referenced this issue Nov 11, 2024
This seems to cause spicy segfaults with some big pcaps:

zeek/spicy#1918
@awelzel
Copy link
Contributor

awelzel commented Nov 12, 2024

Here's an ASAN dump - seem trim() removes something that's later accessed.

Here's just the flow extracted from segfault.pcap that triggers it (looking at Zeek's debug.log which was the last packet to be processed , then extracting just that flow - port 47013):
segfault-47013.pcap.gz

So that's now 15 packets, hopefully a bit better :-)

$ zeek -r segfault.pcap ./segfault.hlto ./enable.zeek 2>&1 | sed -e 's,/home/awelzel/corelight-oss,,g'
=================================================================
==2656387==ERROR: AddressSanitizer: heap-use-after-free on address 0x504001cb8790 at pc 0x7fae3c85243d bp 0x7fae295c8190 sp 0x7fae295c8180
READ of size 8 at 0x504001cb8790 thread T0
    #0 0x7fae3c85243c in hilti::rt::stream::detail::Chunk::offset() const /zeek/auxil/spicy/hilti/runtime/include/hilti/rt/types/stream.h:185
    #1 0x7fae3c85243c in hilti::rt::stream::detail::Chain::findChunk(SafeInt<unsigned long, hilti::rt::integer::detail::SafeIntException> const&, hilti::rt::stream::detail::Chunk const*) const /zeek/auxil/spicy/hilti/runtime/include/hilti/rt/types/stream.h:1091
    #2 0x7fae3c85d265 in hilti::rt::stream::SafeConstIterator::operator+(SafeInt<unsigned long, hilti::rt::integer::detail::SafeIntException> const&) const (/zeek/issues/resp-segfault/segfault.hlto+0x50265) (BuildId: d20e94064a3e561099549a8515196ced39a39fb8)
    #3 0x7fae3c863751 in hilti::rt::stream::View::advance(SafeInt<unsigned long, hilti::rt::integer::detail::SafeIntException> const&) const (/zeek/issues/resp-segfault/segfault.hlto+0x56751) (BuildId: d20e94064a3e561099549a8515196ced39a39fb8)
    #4 0x7fae3c826575 in __hlt::SegfaultRESP::Data::__parse_SegfaultRESP__Data_2_stage2(hilti::rt::ValueReference<hilti::rt::Stream>&, hilti::rt::stream::SafeConstIterator const&, hilti::rt::stream::View, hilti::rt::Bool, SafeInt<long, hilti::rt::integer::detail::SafeIntException>, hilti::rt::stream::SafeConstIterator, std::optional<hilti::rt::RecoverableFailure>) /tmp/SegfaultRESP_7eb7ac80dad0bd92-a72a92026f399ec7.cc:199
    #5 0x7fae3c829138 in __hlt::SegfaultRESP::Data::__parse_stage1(hilti::rt::ValueReference<hilti::rt::Stream>&, hilti::rt::stream::SafeConstIterator const&, hilti::rt::stream::View, hilti::rt::Bool, SafeInt<long, hilti::rt::integer::detail::SafeIntException>, hilti::rt::stream::SafeConstIterator, std::optional<hilti::rt::RecoverableFailure>) /tmp/SegfaultRESP_7eb7ac80dad0bd92-a72a92026f399ec7.cc:222
    #6 0x7fae3c835e64 in __hlt::SegfaultRESP::Messages::__parse__anon_2_stage1(hilti::rt::ValueReference<hilti::rt::Stream>&, hilti::rt::stream::SafeConstIterator const&, hilti::rt::stream::View, hilti::rt::Bool, SafeInt<long, hilti::rt::integer::detail::SafeIntException>, hilti::rt::stream::SafeConstIterator, std::optional<hilti::rt::RecoverableFailure>, hilti::rt::Vector<__hlt::SegfaultRESP::Data, std::allocator<__hlt::SegfaultRESP::Data> >&) /tmp/SegfaultRESP_7eb7ac80dad0bd92-a72a92026f399ec7.cc:361
    #7 0x7fae3c83a96d in __hlt::SegfaultRESP::Messages::__parse_SegfaultRESP__Messages_stage2(hilti::rt::ValueReference<hilti::rt::Stream>&, hilti::rt::stream::SafeConstIterator const&, hilti::rt::stream::View, hilti::rt::Bool, SafeInt<long, hilti::rt::integer::detail::SafeIntException>, hilti::rt::stream::SafeConstIterator, std::optional<hilti::rt::RecoverableFailure>) /tmp/SegfaultRESP_7eb7ac80dad0bd92-a72a92026f399ec7.cc:322
    #8 0x7fae3c83cff6 in __hlt::SegfaultRESP::Messages::__parse_stage1(hilti::rt::ValueReference<hilti::rt::Stream>&, hilti::rt::stream::SafeConstIterator const&, hilti::rt::stream::View, hilti::rt::Bool, SafeInt<long, hilti::rt::integer::detail::SafeIntException>, hilti::rt::stream::SafeConstIterator, std::optional<hilti::rt::RecoverableFailure>) /tmp/SegfaultRESP_7eb7ac80dad0bd92-a72a92026f399ec7.cc:488
    #9 0x7fae3c83f6ce in __hlt::SegfaultRESP::Messages::parse1(hilti::rt::ValueReference<hilti::rt::Stream>&, std::optional<hilti::rt::stream::View> const&, std::optional<spicy::rt::UnitContext> const&) /tmp/SegfaultRESP_7eb7ac80dad0bd92-a72a92026f399ec7.cc:506
    #10 0x7fae3c8404e4 in operator() /tmp/SegfaultRESP_7eb7ac80dad0bd92-a72a92026f399ec7.cc:621
    #11 0x7fae3c8404e4 in operator() /zeek/auxil/spicy/hilti/runtime/include/hilti/rt/fiber.h:132
    #12 0x7fae3c8404e4 in _FUN /zeek/auxil/spicy/hilti/runtime/include/hilti/rt/fiber.h:131
    #13 0x7fae3fcc539b in hilti::rt::detail::Callback::operator()(hilti::rt::detail::Fiber*) const /zeek/auxil/spicy/hilti/runtime/include/hilti/rt/fiber.h:141
    #14 0x7fae3fcc539b in __fiber_run_trampoline /zeek/auxil/spicy/hilti/runtime/src/fiber.cc:95
    #15 0x7fae3fe8b40f in fiber_asm_invoke /zeek/auxil/spicy/3rdparty/fiber/src/fiber_asm_amd64_sysv.S:52

0x504001cb8790 is located 0 bytes inside of 48-byte region [0x504001cb8790,0x504001cb87c0)
freed by thread T0 here:
    #0 0x7fae43aff5e8 in operator delete(void*, unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:164
    #1 0x7fae3fdfd25c in std::default_delete<hilti::rt::stream::detail::Chunk>::operator()(hilti::rt::stream::detail::Chunk*) const /usr/include/c++/13/bits/unique_ptr.h:99
    #2 0x7fae3fdfd25c in std::__uniq_ptr_impl<hilti::rt::stream::detail::Chunk, std::default_delete<hilti::rt::stream::detail::Chunk> >::reset(hilti::rt::stream::detail::Chunk*) /usr/include/c++/13/bits/unique_ptr.h:211
    #3 0x7fae3fdfd25c in std::__uniq_ptr_impl<hilti::rt::stream::detail::Chunk, std::default_delete<hilti::rt::stream::detail::Chunk> >::operator=(std::__uniq_ptr_impl<hilti::rt::stream::detail::Chunk, std::default_delete<hilti::rt::stream::detail::Chunk> >&&) /usr/include/c++/13/bits/unique_ptr.h:191
    #4 0x7fae3fdfd25c in std::__uniq_ptr_data<hilti::rt::stream::detail::Chunk, std::default_delete<hilti::rt::stream::detail::Chunk>, true, true>::operator=(std::__uniq_ptr_data<hilti::rt::stream::detail::Chunk, std::default_delete<hilti::rt::stream::detail::Chunk>, true, true>&&) /usr/include/c++/13/bits/unique_ptr.h:243
    #5 0x7fae3fdfd25c in std::unique_ptr<hilti::rt::stream::detail::Chunk, std::default_delete<hilti::rt::stream::detail::Chunk> >::operator=(std::unique_ptr<hilti::rt::stream::detail::Chunk, std::default_delete<hilti::rt::stream::detail::Chunk> >&&) /usr/include/c++/13/bits/unique_ptr.h:414
    #6 0x7fae3fdfd25c in hilti::rt::stream::detail::Chain::trim(SafeInt<unsigned long, hilti::rt::integer::detail::SafeIntException> const&) /zeek/auxil/spicy/hilti/runtime/src/types/stream.cc:184
    #7 0x7fae3c851d9f in hilti::rt::stream::detail::Chain::trim(hilti::rt::stream::SafeConstIterator const&) /zeek/auxil/spicy/hilti/runtime/include/hilti/rt/types/stream.h:1073
    #8 0x7fae3c825d70 in hilti::rt::Stream::trim(hilti::rt::stream::SafeConstIterator const&) /zeek/auxil/spicy/hilti/runtime/include/hilti/rt/types/stream.h:1787
    #9 0x7fae3c825d70 in __hlt::SegfaultRESP::Data::__parse_SegfaultRESP__Data_2_stage2(hilti::rt::ValueReference<hilti::rt::Stream>&, hilti::rt::stream::SafeConstIterator const&, hilti::rt::stream::View, hilti::rt::Bool, SafeInt<long, hilti::rt::integer::detail::SafeIntException>, hilti::rt::stream::SafeConstIterator, std::optional<hilti::rt::RecoverableFailure>) /tmp/SegfaultRESP_7eb7ac80dad0bd92-a72a92026f399ec7.cc:174
    #10 0x7fae3c829138 in __hlt::SegfaultRESP::Data::__parse_stage1(hilti::rt::ValueReference<hilti::rt::Stream>&, hilti::rt::stream::SafeConstIterator const&, hilti::rt::stream::View, hilti::rt::Bool, SafeInt<long, hilti::rt::integer::detail::SafeIntException>, hilti::rt::stream::SafeConstIterator, std::optional<hilti::rt::RecoverableFailure>) /tmp/SegfaultRESP_7eb7ac80dad0bd92-a72a92026f399ec7.cc:222
    #11 0x7fae3c835e64 in __hlt::SegfaultRESP::Messages::__parse__anon_2_stage1(hilti::rt::ValueReference<hilti::rt::Stream>&, hilti::rt::stream::SafeConstIterator const&, hilti::rt::stream::View, hilti::rt::Bool, SafeInt<long, hilti::rt::integer::detail::SafeIntException>, hilti::rt::stream::SafeConstIterator, std::optional<hilti::rt::RecoverableFailure>, hilti::rt::Vector<__hlt::SegfaultRESP::Data, std::allocator<__hlt::SegfaultRESP::Data> >&) /tmp/SegfaultRESP_7eb7ac80dad0bd92-a72a92026f399ec7.cc:361
    #12 0x7fae3c83a96d in __hlt::SegfaultRESP::Messages::__parse_SegfaultRESP__Messages_stage2(hilti::rt::ValueReference<hilti::rt::Stream>&, hilti::rt::stream::SafeConstIterator const&, hilti::rt::stream::View, hilti::rt::Bool, SafeInt<long, hilti::rt::integer::detail::SafeIntException>, hilti::rt::stream::SafeConstIterator, std::optional<hilti::rt::RecoverableFailure>) /tmp/SegfaultRESP_7eb7ac80dad0bd92-a72a92026f399ec7.cc:322
    #13 0x7fae3c83cff6 in __hlt::SegfaultRESP::Messages::__parse_stage1(hilti::rt::ValueReference<hilti::rt::Stream>&, hilti::rt::stream::SafeConstIterator const&, hilti::rt::stream::View, hilti::rt::Bool, SafeInt<long, hilti::rt::integer::detail::SafeIntException>, hilti::rt::stream::SafeConstIterator, std::optional<hilti::rt::RecoverableFailure>) /tmp/SegfaultRESP_7eb7ac80dad0bd92-a72a92026f399ec7.cc:488
    #14 0x7fae3c83f6ce in __hlt::SegfaultRESP::Messages::parse1(hilti::rt::ValueReference<hilti::rt::Stream>&, std::optional<hilti::rt::stream::View> const&, std::optional<spicy::rt::UnitContext> const&) /tmp/SegfaultRESP_7eb7ac80dad0bd92-a72a92026f399ec7.cc:506
    #15 0x7fae3c8404e4 in operator() /tmp/SegfaultRESP_7eb7ac80dad0bd92-a72a92026f399ec7.cc:621
    #16 0x7fae3c8404e4 in operator() /zeek/auxil/spicy/hilti/runtime/include/hilti/rt/fiber.h:132
    #17 0x7fae3c8404e4 in _FUN /zeek/auxil/spicy/hilti/runtime/include/hilti/rt/fiber.h:131
    #18 0x7fae3fcc539b in hilti::rt::detail::Callback::operator()(hilti::rt::detail::Fiber*) const /zeek/auxil/spicy/hilti/runtime/include/hilti/rt/fiber.h:141
    #19 0x7fae3fcc539b in __fiber_run_trampoline /zeek/auxil/spicy/hilti/runtime/src/fiber.cc:95
    #20 0x7fae3fe8b40f in fiber_asm_invoke /zeek/auxil/spicy/3rdparty/fiber/src/fiber_asm_amd64_sysv.S:52

previously allocated by thread T0 here:
    #0 0x7fae43afe548 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:95
    #1 0x7fae3fdfae9b in std::__detail::_MakeUniq<hilti::rt::stream::detail::Chunk>::__single_object std::make_unique<hilti::rt::stream::detail::Chunk, int, unsigned char const*&, unsigned long&, hilti::rt::stream::NonOwning>(int&&, unsigned char const*&, unsigned long&, hilti::rt::stream::NonOwning&&) /usr/include/c++/13/bits/unique_ptr.h:1070
    #2 0x7fae3fdfae9b in hilti::rt::stream::detail::Chain::append(unsigned char const*, unsigned long, hilti::rt::stream::NonOwning) /zeek/auxil/spicy/hilti/runtime/src/types/stream.cc:91
    #3 0x7fae3fdfebe9 in hilti::rt::Stream::append(char const*, unsigned long, hilti::rt::stream::NonOwning) /zeek/auxil/spicy/hilti/runtime/src/types/stream.cc:516
    #4 0x7fae414651e1 in spicy::rt::driver::ParsingState::_process(unsigned long, char const*, bool) /zeek/auxil/spicy/spicy/runtime/src/driver.cc:344
    #5 0x56126c3996ab in spicy::rt::driver::ParsingState::process(unsigned long, char const*) /zeek/auxil/spicy/spicy/runtime/include/spicy/rt/driver.h:101
    #6 0x56126c3996ab in zeek::spicy::rt::ProtocolAnalyzer::Process(bool, int, unsigned char const*) /zeek/src/spicy/protocol-analyzer.cc:68
    #7 0x56126c39a1c3 in zeek::spicy::rt::TCP_Analyzer::DeliverStream(int, unsigned char const*, bool) /zeek/src/spicy/protocol-analyzer.cc:141
    #8 0x56126ade44f3 in zeek::analyzer::Analyzer::NextStream(int, unsigned char const*, bool) /zeek/src/analyzer/Analyzer.cc:203
    #9 0x56126adeb2ff in zeek::analyzer::Analyzer::ForwardStream(int, unsigned char const*, bool) /zeek/src/analyzer/Analyzer.cc:271
    #10 0x56126bd27636 in zeek::analyzer::tcp::TCP_Reassembler::Deliver(unsigned long, int, unsigned char const*) /zeek/src/analyzer/protocol/tcp/TCP_Reassembler.cc:414
    #11 0x56126bd2ada3 in zeek::analyzer::tcp::TCP_Reassembler::DeliverBlock(unsigned long, int, unsigned char const*) /zeek/src/analyzer/protocol/tcp/TCP_Reassembler.cc:551
    #12 0x56126bd2c71a in zeek::analyzer::tcp::TCP_Reassembler::BlockInserted(std::_Rb_tree_const_iterator<std::pair<unsigned long const, zeek::DataBlock> >) /zeek/src/analyzer/protocol/tcp/TCP_Reassembler.cc:365
    #13 0x56126cc13bb7 in zeek::Reassembler::NewBlock(double, unsigned long, unsigned long, unsigned char const*) /zeek/src/Reassem.cc:332
    #14 0x56126bd2791c in zeek::analyzer::tcp::TCP_Reassembler::DataSent(double, unsigned long, int, unsigned char const*, zeek::analyzer::tcp::TCP_Flags, bool) /zeek/src/analyzer/protocol/tcp/TCP_Reassembler.cc:447
    #15 0x56126bd262ac in zeek::analyzer::tcp::TCP_Endpoint::DataSent(double, unsigned long, int, int, unsigned char const*, zeek::IP_Hdr const*, tcphdr const*) /zeek/src/analyzer/protocol/tcp/TCP_Endpoint.cc:173
    #16 0x56126be40881 in zeek::packet_analysis::TCP::TCPSessionAdapter::Process(bool, tcphdr const*, int, std::shared_ptr<zeek::IP_Hdr> const&, unsigned char const*, int) /zeek/src/packet_analysis/protocol/tcp/TCPSessionAdapter.cc:623
    #17 0x56126be2ddde in zeek::packet_analysis::TCP::TCPAnalyzer::DeliverPacket(zeek::Connection*, double, bool, int, zeek::Packet*) /zeek/src/packet_analysis/protocol/tcp/TCP.cc:111
    #18 0x56126be1b73f in zeek::packet_analysis::IP::IPBasedAnalyzer::AnalyzePacket(unsigned long, unsigned char const*, zeek::Packet*) /zeek/src/packet_analysis/protocol/ip/IPBasedAnalyzer.cc:88
    #19 0x56126bdbde40 in zeek::packet_analysis::Analyzer::ForwardPacket(unsigned long, unsigned char const*, zeek::Packet*, unsigned int) const /zeek/src/packet_analysis/Analyzer.cc:118
    #20 0x56126be1360e in zeek::packet_analysis::IP::IPAnalyzer::AnalyzePacket(unsigned long, unsigned char const*, zeek::Packet*) /zeek/src/packet_analysis/protocol/ip/IP.cc:265
    #21 0x56126bdbde40 in zeek::packet_analysis::Analyzer::ForwardPacket(unsigned long, unsigned char const*, zeek::Packet*, unsigned int) const /zeek/src/packet_analysis/Analyzer.cc:118
    #22 0x56126bde95b1 in zeek::packet_analysis::Ethernet::EthernetAnalyzer::AnalyzePacket(unsigned long, unsigned char const*, zeek::Packet*) /zeek/src/packet_analysis/protocol/ethernet/Ethernet.cc:45
    #23 0x56126bdbde40 in zeek::packet_analysis::Analyzer::ForwardPacket(unsigned long, unsigned char const*, zeek::Packet*, unsigned int) const /zeek/src/packet_analysis/Analyzer.cc:118
    #24 0x56126bdce167 in zeek::packet_analysis::Manager::ProcessPacket(zeek::Packet*) /zeek/src/packet_analysis/Manager.cc:117
    #25 0x56126cc41532 in zeek::run_state::detail::dispatch_packet(zeek::Packet*, zeek::iosource::PktSrc*) /zeek/src/RunState.cc:251
    #26 0x56126c21da36 in zeek::iosource::PktSrc::Process() /zeek/src/iosource/PktSrc.cc:116
    #27 0x56126cc43396 in zeek::run_state::detail::run_loop() /zeek/src/RunState.cc:293
    #28 0x56126d6d87c0 in main /zeek/src/main.cc:93
    #29 0x7fae3d22a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #30 0x7fae3d22a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #31 0x56126ade01b4 in _start (/zeek/asan-build/src/zeek+0xd691b4) (BuildId: 92dd40e1d4de4c37)

@evantypanski evantypanski self-assigned this Nov 12, 2024
evantypanski added a commit that referenced this issue Nov 12, 2024
Fixes #1918

I am not 100% sure what causes this. The issue seems to be with
decrementing `_begin` by `_begin.offset()` in combination with trimmed
input (from c6177f9). This fixes the minimal example I got
evantypanski added a commit that referenced this issue Nov 13, 2024
This was somewhat from #1918, but it isn't the cause of the segfault.
You could get in a scenario where decrementing an iterator technically
went into memory freed from `trim` but was never actually used, so only
do that calculation if we must.
@evantypanski
Copy link
Contributor Author

evantypanski commented Nov 14, 2024

I've gotten a little closer to solving this, but a lot of time was spent just understanding how Views and Chunks work in the generated code, honestly. I don't think I have all of the necessary background in Spicy to make it "quick" :)

For the record, this is "caused" by 306d141 - removing the advance makes the use-after-free disappear. But, obviously, that breaks other things, including Spicy tests.

I went back with the sanitizer and found that this happens on one of the Redis analyzer's tests, with perfectly valid Redis that is getting parsed correctly, so that might be a good place to start. It's not as minimal as the other example above. The good thing is, for this one I have more detailed logs, which I have attached:

redis-repro.tar.gz

The uaf.pcap file will have a use-after-free if using the redis analyzer on that with the address sanitizer enabled.

Whenever I've seen the use-after-free, it always has something along these lines from those logs right before:

- parsing production: Variable: data_6 -> bytes
suspending to wait for more input for stream 0x603001d93818, currently have 1
resuming after insufficient input, now have 209 for stream 0x603001d93818
- trimming input
- trimming input
=================================================================
==59262==ERROR: AddressSanitizer: heap-use-after-free on address 0x604001d12410 at pc 0x00010a0de5e8 bp 0x00010ab5ba30 sp 0x00010ab5ba28

Namely going to parse the bytes, suspending to wait for more input, resuming, trimming after finding the &until, then finally use-after-free. I just don't really know how to act on this right now and ran out of time to figure it out :)

anyways I'm going away until Wednesday so if anyone wants to take that and run with it feel free. Otherwise I'll grab it when I'm back

@evantypanski evantypanski removed their assignment Nov 14, 2024
@rsmmr
Copy link
Member

rsmmr commented Nov 18, 2024

I'll take a look.

@rsmmr rsmmr self-assigned this Nov 18, 2024
rsmmr added a commit that referenced this issue Nov 19, 2024
When trimming off the beginning of a stream, an existing iterator
could end up dereferencing its internal chunk pointer even if the
chunk now no longer existed. The issue was inside the
increment/decrement operations, which didn't check if the *current*
iterator offset was still valid (because only then the current chunk
is guaranteed to be valid too).

TODO:
    - Test for original problem missing, need to see if I can build one

Closes #1918.
@rsmmr
Copy link
Member

rsmmr commented Nov 19, 2024

I have a potential fix in #1923.

Thanks for uaf.pcap, that helped. For the record, I was able to reproduce it with pure Spicy by turning that trace into a batch for spicy-driver, adding %port = 6379/tcp; to Redis' ServerMessages, and then compiling spicy-redis/analyzer/*.spicy into an HLTO.

rsmmr added a commit that referenced this issue Nov 22, 2024
rsmmr added a commit that referenced this issue Nov 22, 2024
When trimming off the beginning of a stream, an existing iterator
could end up dereferencing its internal chunk pointer even if the
chunk now no longer existed. The issue was inside the
increment/decrement operations, which didn't check if the *current*
iterator offset was still valid (because only then the current chunk
is guaranteed to be valid too).

Closes #1918.
rsmmr added a commit that referenced this issue Nov 25, 2024
@rsmmr rsmmr closed this as completed in 153eca3 Nov 25, 2024
rsmmr added a commit that referenced this issue Nov 25, 2024
* origin/topic/robin/gh-1918-trim-fix:
  Fix potential segfault with stream iterators.
  Add regression tests triggering #1918.
rsmmr added a commit that referenced this issue Nov 25, 2024
(cherry picked from commit ff34c28)
rsmmr added a commit that referenced this issue Nov 25, 2024
When trimming off the beginning of a stream, an existing iterator
could end up dereferencing its internal chunk pointer even if the
chunk now no longer existed. The issue was inside the
increment/decrement operations, which didn't check if the *current*
iterator offset was still valid (because only then the current chunk
is guaranteed to be valid too).

Closes #1918.

(cherry picked from commit 153eca3)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Something isn't working Priority High
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants