From: Ruide Cao tipc_get_gap_ack_blks() reads len, ugack_cnt and bgack_cnt directly from msg_data(hdr) before verifying that a STATE message actually contains the fixed Gap ACK block header in its logical data area. A peer that negotiates TIPC_GAP_ACK_BLOCK can send a short STATE message with a declared TIPC payload shorter than struct tipc_gap_ack_blks and still append a few physical bytes after the header. The helper then trusts those bytes as Gap ACK metadata, and the forged bgack_cnt/len values can drive the broadcast receive path into kmemdup() beyond the skb boundary. Fix this by rejecting Gap ACK parsing unless the logical STATE payload is large enough to cover the fixed header, and by rejecting declared Gap ACK lengths that are smaller than the fixed header or larger than the logical payload. Return 0 for invalid lengths so malformed Gap ACK data is not treated as a valid payload offset, and drop unicast STATE messages that advertise Gap ACK support but still yield an invalid Gap ACK length. This keeps malformed Gap ACK data ignored without misaligning monitor payload parsing. Fixes: d7626b5acff9 ("tipc: introduce Gap ACK blocks for broadcast link") Cc: stable@kernel.org Reported-by: Yifan Wu Reported-by: Juefei Pu Co-developed-by: Yuan Tan Signed-off-by: Yuan Tan Suggested-by: Xin Liu Tested-by: Ren Wei Signed-off-by: Ruide Cao Signed-off-by: Ren Wei --- net/tipc/link.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/net/tipc/link.c b/net/tipc/link.c index 49dfc098d89b..44678d98939a 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -1415,12 +1415,22 @@ u16 tipc_get_gap_ack_blks(struct tipc_gap_ack_blks **ga, struct tipc_link *l, struct tipc_msg *hdr, bool uc) { struct tipc_gap_ack_blks *p; - u16 sz = 0; + u16 sz = 0, dlen = msg_data_sz(hdr); /* Does peer support the Gap ACK blocks feature? */ if (l->peer_caps & TIPC_GAP_ACK_BLOCK) { + u16 min_sz = struct_size(p, gacks, 0); + + if (dlen < min_sz) + goto ignore; + p = (struct tipc_gap_ack_blks *)msg_data(hdr); sz = ntohs(p->len); + if (sz < min_sz || sz > dlen) { + sz = 0; + goto ignore; + } + /* Sanity check */ if (sz == struct_size(p, gacks, size_add(p->ugack_cnt, p->bgack_cnt))) { /* Good, check if the desired type exists */ @@ -1434,6 +1444,8 @@ u16 tipc_get_gap_ack_blks(struct tipc_gap_ack_blks **ga, struct tipc_link *l, } } } + +ignore: /* Other cases: ignore! */ p = NULL; @@ -2270,7 +2282,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb, case STATE_MSG: /* Validate Gap ACK blocks, drop if invalid */ glen = tipc_get_gap_ack_blks(&ga, l, hdr, true); - if (glen > dlen) + if (glen > dlen || ((l->peer_caps & TIPC_GAP_ACK_BLOCK) && !glen)) break; l->rcv_nxt_state = msg_seqno(hdr) + 1; -- 2.34.1