bgpd: add STREAM_READABLE pre-check in bgp_open_receive#22277
bgpd: add STREAM_READABLE pre-check in bgp_open_receive#22277guoguojia2021 wants to merge 1 commit into
Conversation
Greptile SummaryThis PR adds a defensive stream-length guard at the top of
Confidence Score: 4/5Safe to merge; the guard is correct and unreachable under normal operation, making it a low-risk addition. The byte count of 10 is arithmetically correct for RFC 4271's fixed OPEN fields, the %zu format specifier matches the size_t return type of STREAM_READABLE, and the early-return path mirrors the existing guards later in the same function. The only note is that BGP_NOTIFY_OPEN_MALFORMED_ATTR (subcode 0) isn't an RFC-defined OPEN error subcode for a short-message condition — a header-error subcode would be more precise — though this matches FRR's existing convention in bgp_open_receive. No files require special attention; the single-file change is straightforward and self-contained. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[bgp_open_receive called] --> B{STREAM_READABLE < 10?}
B -- Yes --> C[flog_err EC_BGP_PKT_OPEN]
C --> D[bgp_notify_send\nOPEN_ERR / MALFORMED_ATTR]
D --> E[return BGP_Stop]
B -- No --> F[stream_getc: version]
F --> G[stream_getw: remote_as]
G --> H[stream_getw: holdtime]
H --> I[stream_get_ipv4: remote_id]
I --> J[stream_getc: optlen]
J --> K{Extended opt params?}
K -- Yes --> L{STREAM_READABLE < 1?}
L -- Yes --> M[bgp_notify_send\nOPEN_ERR / MALFORMED_ATTR\nreturn BGP_Stop]
L -- No --> N[Continue parsing capabilities...]
K -- No --> N
Prompt To Fix All With AIFix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
bgpd/bgp_packet.c:1802-1803
**NOTIFICATION subcode doesn't match the error condition**
`BGP_NOTIFY_OPEN_MALFORMED_ATTR` has value 0, which is not a defined subcode for OPEN Message Errors (error code 2) in RFC 4271 §6.2. A stream that is too short is closer to a bad message length, for which `BGP_NOTIFY_HEADER_ERR` / `BGP_NOTIFY_HEADER_BAD_MESLEN` (error code 1, subcode 2) would be a more precise choice. That said, this subcode value is already used by the other existing guards in this same function (lines ~1846, ~1858, ~1886), so the new check is at least internally consistent within FRR's current convention.
Reviews (1): Last reviewed commit: "bgpd: add STREAM_READABLE pre-check in b..." | Re-trigger Greptile |
| bgp_notify_send(connection, BGP_NOTIFY_OPEN_ERR, | ||
| BGP_NOTIFY_OPEN_MALFORMED_ATTR); |
There was a problem hiding this comment.
NOTIFICATION subcode doesn't match the error condition
BGP_NOTIFY_OPEN_MALFORMED_ATTR has value 0, which is not a defined subcode for OPEN Message Errors (error code 2) in RFC 4271 §6.2. A stream that is too short is closer to a bad message length, for which BGP_NOTIFY_HEADER_ERR / BGP_NOTIFY_HEADER_BAD_MESLEN (error code 1, subcode 2) would be a more precise choice. That said, this subcode value is already used by the other existing guards in this same function (lines ~1846, ~1858, ~1886), so the new check is at least internally consistent within FRR's current convention.
Prompt To Fix With AI
This is a comment left during a code review.
Path: bgpd/bgp_packet.c
Line: 1802-1803
Comment:
**NOTIFICATION subcode doesn't match the error condition**
`BGP_NOTIFY_OPEN_MALFORMED_ATTR` has value 0, which is not a defined subcode for OPEN Message Errors (error code 2) in RFC 4271 §6.2. A stream that is too short is closer to a bad message length, for which `BGP_NOTIFY_HEADER_ERR` / `BGP_NOTIFY_HEADER_BAD_MESLEN` (error code 1, subcode 2) would be a more precise choice. That said, this subcode value is already used by the other existing guards in this same function (lines ~1846, ~1858, ~1886), so the new check is at least internally consistent within FRR's current convention.
How can I resolve this? If you propose a fix, please make it concise.| /* Parse open packet. */ | ||
| /* Parse open packet. | ||
| * Minimum OPEN body: version(1) + AS(2) + holdtime(2) + BGP-ID(4) + optlen(1) = 10 */ | ||
| if (STREAM_READABLE(connection->curr) < 10) { |
There was a problem hiding this comment.
Good suggestion, done. Defined BGP_OPEN_BODY_MIN_SIZE (10) in bgpd.h with a comment documenting the field breakdown, and reused it in both the new guard and the existing BGP_MSG_OPEN_MIN_SIZE macro to avoid duplication.
eb00b9f to
4ac99a9
Compare
| "%s: OPEN message too short (%zu bytes, need %u)", | ||
| peer->host, STREAM_READABLE(connection->curr), | ||
| BGP_OPEN_BODY_MIN_SIZE); | ||
| bgp_notify_send(connection, BGP_NOTIFY_OPEN_ERR, |
There was a problem hiding this comment.
Thanks for the review, Changed to BGP_NOTIFY_HEADER_ERR / BGP_NOTIFY_HEADER_BAD_MESLEN.
| peer->host, STREAM_READABLE(connection->curr), | ||
| BGP_OPEN_BODY_MIN_SIZE); | ||
| bgp_notify_send(connection, BGP_NOTIFY_OPEN_ERR, | ||
| BGP_NOTIFY_OPEN_MALFORMED_ATTR); |
There was a problem hiding this comment.
This must be changed to BGP_NOTIFY_HEADER_BAD_MESLEN.
There was a problem hiding this comment.
Thanks for the review, Changed to BGP_NOTIFY_HEADER_ERR / BGP_NOTIFY_HEADER_BAD_MESLEN.
bgp_open_receive() immediately calls stream_getc(), stream_getw(), and stream_get_ipv4() to parse the OPEN message fixed fields (version, AS number, hold time, BGP Identifier, opt parm len) without first verifying that the stream contains at least 10 bytes of readable data. While bgp_io.c validates BGP_MSG_OPEN_MIN_SIZE at the I/O layer, that check uses the total packet size including the 19-byte header. If a bug or future refactor changes how the stream getp is positioned before calling bgp_open_receive(), the stream_get*() calls could read past the end of valid data. The stream functions have internal assertions that abort the entire daemon rather than sending a proper NOTIFICATION. Add a STREAM_READABLE(connection->curr) < BGP_OPEN_BODY_MIN_SIZE guard at the top of bgp_open_receive(). If the stream has fewer than BGP_OPEN_BODY_MIN_SIZE bytes remaining, log an error, send a NOTIFICATION with Header Error / Bad Message Length, and return BGP_Stop. Define BGP_OPEN_BODY_MIN_SIZE (10) as a constant in bgpd.h to avoid hardcoding the magic number. This value matches RFC 4271 Section 4.2 which specifies the OPEN message format with exactly 10 bytes of fixed fields after the header: - Version (1 byte) - My Autonomous System (2 bytes) - Hold Time (2 bytes) - BGP Identifier (4 bytes) - Optional Parameters Length (1 byte) Use BGP_NOTIFY_HEADER_ERR / BGP_NOTIFY_HEADER_BAD_MESLEN instead of BGP_NOTIFY_OPEN_ERR / BGP_NOTIFY_OPEN_MALFORMED_ATTR, since a message that is too short to contain all required fields is a header-level error (bad message length), not an OPEN-specific malformed attribute error. This is defense-in-depth: it ensures the function is self-contained in its safety guarantees rather than relying on the caller having validated the size correctly. Signed-off-by: guozhongfeng.gzf <guozhongfeng.gzf@alibaba-inc.com>
4ac99a9 to
5402972
Compare
bgp_open_receive() immediately calls stream_getc(), stream_getw(), and stream_get_ipv4() to parse the OPEN message fixed fields (version, AS number, hold time, BGP Identifier, opt parm len) without first verifying that the stream contains at least 10 bytes of readable data.
While bgp_io.c validates BGP_MSG_OPEN_MIN_SIZE at the I/O layer, that check uses the total packet size including the 19-byte header. If a bug or future refactor changes how the stream getp is positioned before calling bgp_open_receive(), the stream_get*() calls could read past the end of valid data. The stream functions have internal assertions that abort the entire daemon rather than sending a proper NOTIFICATION.
Add a STREAM_READABLE(connection->curr) < 10 guard at the top of bgp_open_receive(). If the stream has fewer than 10 bytes remaining, log an error, send a NOTIFICATION with OPEN Message Error / Malformed Attribute, and return BGP_Stop. This is defense-in-depth: it ensures the function is self-contained in its safety guarantees rather than relying on the caller having validated the size correctly.
RFC 4271 Section 4.2 specifies the OPEN message format with exactly 10 bytes of fixed fields after the header: