Repository navigation
UCT/TCP: Validate AM receive length #12081
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1280,6 +1280,16 @@ static inline void uct_tcp_ep_handle_recv_err(uct_tcp_ep_t *ep, | |
| } | ||
| } | ||
|
|
||
| static UCS_F_NOINLINE void | ||
| uct_tcp_ep_handle_invalid_am_length(uct_tcp_ep_t *ep, uint32_t length, | ||
| size_t max_length) | ||
| { | ||
| ucs_debug("tcp_ep %p (state %s): received AM length %u exceeds maximum %zu", | ||
| ep, uct_tcp_ep_cm_state[ep->conn_state].name, length, max_length); | ||
| uct_tcp_ep_ctx_reset(&ep->rx); | ||
| uct_tcp_ep_handle_disconnected(ep, UCS_ERR_CONNECTION_RESET); | ||
| } | ||
|
|
||
| static inline unsigned uct_tcp_ep_recv(uct_tcp_ep_t *ep, size_t recv_length) | ||
| { | ||
| uct_tcp_iface_t UCS_V_UNUSED *iface = ucs_derived_of(ep->super.super.iface, | ||
|
|
@@ -1485,10 +1495,13 @@ static unsigned uct_tcp_ep_progress_am_rx(uct_tcp_ep_t *ep) | |
| } | ||
|
|
||
| hdr = UCS_PTR_BYTE_OFFSET(ep->rx.buf, ep->rx.offset); | ||
| ucs_assertv(hdr->length <= (iface->config.rx_seg_size - sizeof(*hdr)), | ||
| "tcp_ep %p (conn state - %s): %u vs %zu", | ||
| ep, uct_tcp_ep_cm_state[ep->conn_state].name, hdr->length, | ||
| (iface->config.rx_seg_size - sizeof(*hdr))); | ||
| if (ucs_unlikely(hdr->length > | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is intentionally out of scope for this change. This PR prevents an oversized AM length from being retained and used to size a later receive beyond the RX buffer. Exact semantic validation of internal PUT and CM messages is a separate parser-hardening issue that needs event-specific checks and focused tests. |
||
| (iface->config.rx_seg_size - sizeof(*hdr)))) { | ||
| uct_tcp_ep_handle_invalid_am_length( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. minor: |
||
| ep, hdr->length, | ||
| iface->config.rx_seg_size - sizeof(*hdr)); | ||
| goto out; | ||
| } | ||
|
|
||
| if (remaining < (sizeof(*hdr) + hdr->length)) { | ||
| handled++; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -259,6 +259,30 @@ UCS_TEST_P(test_uct_tcp, listener_flood_connect_and_close) { | |
| test_listener_flood(*m_ent, max_conn, 0); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Residual coverage gap: the new test only covers the single-header case in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test intentionally sends only the oversized header. Without the fix it enters the partial-message path and subsequent progress calls attempt the second receive, but no payload is sent, so it fails safely by leaving the endpoint active instead of corrupting memory. The exact second-receive overwrite was reproduced separately under ASan. With the fix, every complete header is rejected before it can be retained, independent of TCP segmentation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Residual coverage gap: no coverage for a TX-capable EP hitting this path (error-handler flow via
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The new check only selects the existing uct_tcp_ep_handle_disconnected() path. The TX-capable branch and uct_iface_handle_ep_err() are existing peer-failure plumbing covered by the generic peer-failure tests, while this focused regression covers the new oversized-header decision and the RX-only destruction path. I do not think a duplicate TX-capable fixture is needed here. |
||
| } | ||
|
|
||
| UCS_TEST_P(test_uct_tcp, reject_oversized_am) | ||
| { | ||
| const uint64_t magic_number = UCT_TCP_MAGIC_NUMBER; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| const uct_tcp_am_hdr_t hdr = { | ||
| 0, std::numeric_limits<uint32_t>::max() | ||
| }; | ||
| std::vector<char> buf(sizeof(magic_number) + sizeof(hdr)); | ||
| std::vector<int> fds; | ||
|
|
||
| memcpy(&buf[0], &magic_number, sizeof(magic_number)); | ||
| memcpy(&buf[sizeof(magic_number)], &hdr, sizeof(hdr)); | ||
|
|
||
| setup_conns_to_entity(*m_ent, 1, fds); | ||
| post_send(fds.front(), buf); | ||
|
|
||
| wait_for_cond([this]() { | ||
| return ucs_list_is_empty(&m_tcp_iface->ep_list); | ||
| }, | ||
| [this]() { progress(); }, 1.0); | ||
| EXPECT_TRUE(ucs_list_is_empty(&m_tcp_iface->ep_list)); | ||
|
|
||
| close(fds.front()); | ||
| } | ||
|
|
||
| UCS_TEST_P(test_uct_tcp, check_addr_len) | ||
| { | ||
| uct_iface_attr_t iface_attr; | ||
|
|
||
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.
ucs_debughides this entirely in release runs. Peers do not exchange segment sizes, so a peer configured with a largerRX_SEG_SIZEwill legitimately hit this and the user only sees a connection reset with no hint. Can we useucs_diaghere? The connection is reset right after, so there is at most one message per connection.