-
Notifications
You must be signed in to change notification settings - Fork 1.4k
isisd: add stream bounds checks in unpack_tlv_router_cap #20358
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
base: master
Are you sure you want to change the base?
Conversation
|
typoe: do you mean the issue #20329 ? Update the commit log. |
|
The added code to handle duplicate FAD entries has a memory leak. The cleanup in isis_tlvs.c shows the correct approach: The four STREAM_READABLE() checks look correct and address the crash scenarios described in #20329. The FAD length validation (length < 4) is also appropriate. Please address the two items above and this should be better. Consider running with ASan/valgrind/FRR memory leakage to verify no memory leaks occur when processing duplicate FAD sub-TLVs. Do you have a mean to add a test for it ? |
Thank you for the review! I've addressed both issues:
|
|
|
||
| sbuf_push(log, indent, "Unpacking Router Capability TLV...\n"); | ||
| if (tlv_len < ISIS_ROUTER_CAP_SIZE) { | ||
| if (tlv_len < ISIS_ROUTER_CAP_SIZE || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
several of these changes seem to be oriented around "we can't trust the tlv_len value" that was passed in. it would be clearer to fix that: to ensure that tlv_len was valid before calling this function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I completely agree that validating tlv_len against the stream bounds before dispatching to the specific unpack function would be a better design for the entire TLV parsing.
However, unpack_tlv and the dispatch logic currently trust the tlv_len from the packet header. Changing that behavior would require refactoring how tlv_len is handled for all TLV types to ensure no regressions.
Given that this PR targets a specific crash/vulnerability, I'd prefer to keep the fix localized to unpack_tlv_router_cap to ensure it's safe and minimal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, but ... that's just wrong isn't it? "trust the value from the packet header" I mean - if that's true, that's something that has caused trouble in this specific path, and will continue to cause trouble until it's fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've refactored the code as suggested to ensure tlv_len validity before calling the handler.
Changes:
- Added
size_t min_lentostruct tlv_ops. - Updated
unpack_tlv()to checkif (tlv_len < ops->min_len)before dispatching. - Defined
TLV_OPS_MIN_LENmacro and used it for Router Capability (Type 242) with.min_len = 5. - Removed the manual length check from
unpack_tlv_router_capas it is now redundant.
This provides a central mechanism for enforcing minimum TLV lengths.
riw777
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good ... just waiting on an answer to @mjstapp 's comments
Add validation to ensure sufficient stream data is available before calling stream_getc() in unpack_tlv_router_cap(). This prevents assertion failures when processing malformed Router Capability TLVs. The fix adds four boundary checks: 1. Verify STREAM_READABLE before reading the 5-byte Router Capability header (router ID + flags). 2. Check stream availability before reading sub-TLV type and length in the main sub-TLV processing loop. 3. Validate FAD sub-TLV length is at least 4 bytes before reading mandatory fields (algorithm, metric_type, calc_type, priority). 4. Verify stream has 2 bytes available before reading sub-sub-TLV headers in the FAD processing loop. Edit2: Additionally, add a check to prevent memory leak when duplicate FAD entries are received for the same algorithm. Edit3: Set `min_len` to 5 for Router Capability TLV (Type 242) to prevent processing malformed TLVs that are shorter than the header size, which previously caused assertion failures. Fixes: FRRouting#20329 Signed-off-by: ravikumark815 <[email protected]>
mjstapp
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this has sort of wandered a bit.
the issue says that it's possible to get a stream with a readable size less than five through unpack_tlv_router_cap() - but I don't see how that's possible: the ROUTER_CAP_SIZE check appears to prevent that.
the issue also says that in general the tlv lengths aren't checked against the stream, but I do see checks in the incoming callers of isis_unpack_tlvs(), and in unpack_tlvs() and unpack_tlv(). can you show where it's possible to get past those existing checks?
the issue with the subtlvs is a different, and does appear to be real - so it would be better just to focus on that?
I've reverted the
Yes, the crash occurs when a TLV advertises a Scenario:
That is why the check |
|
sorry: the point was that there are existing checks that appear to prevent several of the faults that the open issue complains about. I think that issue is largely bogus. you've now gone back and forth several times, without ever showing exactly what problem you were trying to solve - you were just pointing at the bogus open issue. you had removed the content check that was already present, and was preventing the read of 5 octets from a too-small stream. I see you've now put that check back in place. so again, I'll ask this: please describe whether any of the complaints in the issue are actually valid, and then explain how to fix them. I think the complaint about sub-tlvs may be valid, for example.
|
| sbuf_push(log, indent, "Unpacking Router Capability TLV...\n"); | ||
| if (tlv_len < ISIS_ROUTER_CAP_SIZE) { | ||
| if (tlv_len < ISIS_ROUTER_CAP_SIZE || | ||
| STREAM_READABLE(s) < ISIS_ROUTER_CAP_SIZE) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this can be meaningful - please show how an invalid length value could be passed here.
| #endif /* ifndef FABRICD */ | ||
| uint8_t msd_type; | ||
|
|
||
| if (STREAM_READABLE(s) < 2) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again, please show how this could be needed, how the check above could be incorrect.
| if (STREAM_READABLE(s) < 2) { | ||
| sbuf_push(log, indent, | ||
| "WARNING: Unexpected end of stream\n"); | ||
| return 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's not the correct error return handling, is it?
won't matter if this block is removed, of course.
| uint32_t v; | ||
| int n_ag, i; | ||
|
|
||
| if (STREAM_READABLE(s) < 2) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and again: if the subtlv length was valid, then the subsubtlvs_len check that's already present should be valid. how could this occur?
|
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
Summary
This PR fixes a critical crash in isisd when processing malformed Router Capability TLVs (Issue #20329).
Problem
The
unpack_tlv_router_capfunction inisisd/isis_tlvs.ccalledstream_getc()without validating that sufficient bytes were available in the stream. This triggered an assertion failure inlib/stream.c:353, causing the daemon to abort and creating a Remote Denial of Service (DoS) vulnerability.Solution
Added four stream boundary checks using
STREAM_READABLE()before allstream_getc()calls in the Router Capability TLV parsing logic:Also added a check to prevent memory leaks when duplicate FAD entries are received.
Testing
Checklist
Fixes #20329