protocol/routing_info_entry: bounds-check the address/port memcpys#1040
Open
SoundMatt wants to merge 1 commit into
Open
protocol/routing_info_entry: bounds-check the address/port memcpys#1040SoundMatt wants to merge 1 commit into
SoundMatt wants to merge 1 commit into
Conversation
routing_info_entry::deserialize had two missing bounds checks that let a peer routing client read up to ~22 bytes of adjacent heap memory off the receiving process by sending a malformed routing_info_command: 1. The initial _buffer.size() check covered sizeof(type_) + sizeof(its_size) + sizeof(client_) = 7 bytes, but the function actually consumes 1 + 4 + 4 + 2 = 11 bytes from _buffer at the top (it was missing sizeof(its_client_size) from the check). Send a routing_info_command whose payload is exactly 7 bytes and the memcpy()s for its_client_size and client_ read 4 + 2 bytes past the end of the std::vector. 2. Inside 'if (its_client_size > sizeof(client_))', the v4/v6 address memcpy and the trailing port memcpy ran with no _buffer.size() check at all. its_client_size is wire-controlled, so a peer setting it to 8 (v4) or 20 (v6) drives memcpy() to read 4 or 16 bytes plus 2 more for the port from past the buffer end. Fix: - Extend the initial size check to include sizeof(its_client_size). - Reject its_client_size values that would underflow the its_address_size subtraction below. - Validate its_address_size is a recognised IP family (v4=4, v6=16) before doing the bounds check, so the bounds check below can rely on a sane value. - Bounds-check 'its_address_size + sizeof(port_)' against the remaining buffer before any memcpy. - Restructure the v4/v6/else-MALFORMED dispatch into v4/else-v6 since the validation above has already rejected anything that isn't one of the two. Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
This was referenced May 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Problem
routing_info_entry::deserialize(implementation/protocol/src/routing_info_entry.cpp) had two missing bounds checks that let a peer routing client read up to ~22 bytes of adjacent heap memory off the receiving process by sending a malformedrouting_info_command.The function ends up being called from
routing_manager_client::on_routing_info(and the routing-stub side), so the trust boundary here is the local IPC routing socket — which under the default security policy is bounded but is the same surface that vsomeip's security layer is supposed to protect.Bug 1 — initial size check missed
sizeof(its_client_size)That check covers
1 + 4 + 2 = 7bytes, but the function actually consumes1 + 4 + 4 + 2 = 11bytes from_bufferat the top (theits_client_sizeread at line 102 isn't in the check). A peer that writes a routing_info_command whose payload is exactly 7 bytes drives thememcpy(&its_client_size, ...)to read 2 bytes past the end of the buffer and thememcpy(&client_, ...)to read another 2 bytes past — 4 bytes of OOB read, into adjacent heap memory of the routing manager'sstd::vector<byte_t>.Bug 2 — address/port
memcpys have no_buffer.size()checkits_client_sizeis wire-controlled. A peer that sets it to8selects the v4 branch (4-byte address read) and a peer that sets it to20selects the v6 branch (16-byte address read), in both cases plus a 2-byte port read — all unguarded. Total OOB read primitive: 6, 12, or 18 bytes depending on which combination is reached.The subtraction
its_client_size - (sizeof(client_t) + sizeof(port_))also underflows whenits_client_size < 4, producing a near-UINT32_MAXits_address_size. The existingelse { ERROR_MALFORMED }arm catches the underflow case incidentally, but the underflow itself shouldn't have been reachable in the first place.Fix
sizeof(its_client_size)so it actually covers the 11 bytes read at the top.its_client_sizevalues that would underflow theits_address_sizesubtraction.its_address_sizeagainst the two recognised IP families (v4=4, v6=16) before the bounds check, so the bounds check below can rely on a sane value.its_address_size + sizeof(port_)against the remaining buffer before anymemcpy.Diff: +41 / −7 in a single file.
Notes
implementation/protocol/,implementation/service_discovery/, andimplementation/endpoints/. There are a small number of further findings I'd like to bring up in separate PRs (e.g.message_impl::deserializeunderflowsheader_.length_ - VSOMEIP_SOMEIP_HEADER_SIZEand passes the result todata_.reserve()— a ~4 GB allocation per malformed message). Happy to file those one at a time after this lands.