* [PATCH net 1/1] tipc: validate Gap ACK blocks in STATE message [not found] <cover.1775269941.git.caoruide123@gmail.com> @ 2026-04-05 4:54 ` Ren Wei 2026-04-06 8:29 ` Tung Quang Nguyen 0 siblings, 1 reply; 6+ messages in thread From: Ren Wei @ 2026-04-05 4:54 UTC (permalink / raw) To: netdev Cc: jmaloy, davem, edumazet, kuba, pabeni, horms, ying.xue, tuong.t.lien, yifanwucs, tomapufckgml, yuantan098, bird, enjou1224z, caoruide123, n05ec From: Ruide Cao <caoruide123@gmail.com> 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 callers do not treat malformed Gap ACK data as monitor payload offset, while preserving the declared size for valid but unused Gap ACK records. This keeps malformed Gap ACK data ignored without misaligning monitor payload parsing in unicast STATE messages. Fixes: d7626b5acff9 ("tipc: introduce Gap ACK blocks for broadcast link") Reported-by: Yifan Wu <yifanwucs@gmail.com> Reported-by: Juefei Pu <tomapufckgml@gmail.com> Co-developed-by: Yuan Tan <yuantan098@gmail.com> Signed-off-by: Yuan Tan <yuantan098@gmail.com> Suggested-by: Xin Liu <bird@lzu.edu.cn> Tested-by: Ren Wei <enjou1224z@gmail.com> Signed-off-by: Ruide Cao <caoruide123@gmail.com> Signed-off-by: Ren Wei <n05ec@lzu.edu.cn> --- net/tipc/link.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/net/tipc/link.c b/net/tipc/link.c index 49dfc098d89b..a364822c1cd8 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -1415,12 +1415,20 @@ 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) { + if (dlen < sizeof(*p)) + goto ignore; + p = (struct tipc_gap_ack_blks *)msg_data(hdr); sz = ntohs(p->len); + if (sz < sizeof(*p) || 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 +1442,8 @@ u16 tipc_get_gap_ack_blks(struct tipc_gap_ack_blks **ga, struct tipc_link *l, } } } + +ignore: /* Other cases: ignore! */ p = NULL; -- 2.34.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* RE: [PATCH net 1/1] tipc: validate Gap ACK blocks in STATE message 2026-04-05 4:54 ` [PATCH net 1/1] tipc: validate Gap ACK blocks in STATE message Ren Wei @ 2026-04-06 8:29 ` Tung Quang Nguyen 0 siblings, 0 replies; 6+ messages in thread From: Tung Quang Nguyen @ 2026-04-06 8:29 UTC (permalink / raw) To: Ren Wei Cc: jmaloy@redhat.com, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, horms@kernel.org, ying.xue@windriver.com, yifanwucs@gmail.com, tomapufckgml@gmail.com, yuantan098@gmail.com, bird@lzu.edu.cn, enjou1224z@gmail.com, caoruide123@gmail.com, netdev@vger.kernel.org >Subject: [PATCH net 1/1] tipc: validate Gap ACK blocks in STATE message > >From: Ruide Cao <caoruide123@gmail.com> > >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 callers do not treat malformed Gap ACK data as >monitor payload offset, while preserving the declared size for valid but unused >Gap ACK records. This keeps malformed Gap ACK data ignored without >misaligning monitor payload parsing in unicast STATE messages. > >Fixes: d7626b5acff9 ("tipc: introduce Gap ACK blocks for broadcast link") >Reported-by: Yifan Wu <yifanwucs@gmail.com> >Reported-by: Juefei Pu <tomapufckgml@gmail.com> >Co-developed-by: Yuan Tan <yuantan098@gmail.com> >Signed-off-by: Yuan Tan <yuantan098@gmail.com> >Suggested-by: Xin Liu <bird@lzu.edu.cn> >Tested-by: Ren Wei <enjou1224z@gmail.com> >Signed-off-by: Ruide Cao <caoruide123@gmail.com> >Signed-off-by: Ren Wei <n05ec@lzu.edu.cn> >--- > net/tipc/link.c | 12 +++++++++++- > 1 file changed, 11 insertions(+), 1 deletion(-) > >diff --git a/net/tipc/link.c b/net/tipc/link.c index 49dfc098d89b..a364822c1cd8 >100644 >--- a/net/tipc/link.c >+++ b/net/tipc/link.c >@@ -1415,12 +1415,20 @@ 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) { >+ if (dlen < sizeof(*p)) Your patch is wrong. sizeof(*p) is equivalent to sizeof(NULL) which is 4 bytes. I guess you did not really test this code to see if it is ever hit. >+ goto ignore; >+ > p = (struct tipc_gap_ack_blks *)msg_data(hdr); > sz = ntohs(p->len); >+ if (sz < sizeof(*p) || sz > dlen) { sizeof(*p) cannot be used because tipc_gap_ack_blks contains flexible array. struct_size() must be used in stead. The above 2 comparisons are redundant because they are already done in below "Sanity check" and in tipc_link_proto_rcv(): tipc_link_proto_rcv() { ... /* Validate Gap ACK blocks, drop if invalid */ glen = tipc_get_gap_ack_blks(&ga, l, hdr, true); if (glen > dlen) break; ... } >+ 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 +1442,8 @@ u16 tipc_get_gap_ack_blks(struct tipc_gap_ack_blks **ga, >struct tipc_link *l, > } > } > } >+ >+ignore: > /* Other cases: ignore! */ > p = NULL; > >-- >2.34.1 > ^ permalink raw reply [flat|nested] 6+ messages in thread
[parent not found: <cover.1775809726.git.caoruide123@gmail.com>]
* [PATCH net 1/1] tipc: validate Gap ACK blocks in STATE message [not found] <cover.1775809726.git.caoruide123@gmail.com> @ 2026-04-10 15:53 ` Ren Wei 2026-04-13 3:06 ` Tung Quang Nguyen 0 siblings, 1 reply; 6+ messages in thread From: Ren Wei @ 2026-04-10 15:53 UTC (permalink / raw) To: netdev Cc: jmaloy, davem, edumazet, kuba, pabeni, horms, tuong.t.lien, ying.xue, yifanwucs, tomapufckgml, yuantan098, bird, enjou1224z, caoruide123, n05ec From: Ruide Cao <caoruide123@gmail.com> 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 <yifanwucs@gmail.com> Reported-by: Juefei Pu <tomapufckgml@gmail.com> Co-developed-by: Yuan Tan <yuantan098@gmail.com> Signed-off-by: Yuan Tan <yuantan098@gmail.com> Suggested-by: Xin Liu <bird@lzu.edu.cn> Tested-by: Ren Wei <enjou1224z@gmail.com> Signed-off-by: Ruide Cao <caoruide123@gmail.com> Signed-off-by: Ren Wei <n05ec@lzu.edu.cn> --- 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 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* RE: [PATCH net 1/1] tipc: validate Gap ACK blocks in STATE message 2026-04-10 15:53 ` Ren Wei @ 2026-04-13 3:06 ` Tung Quang Nguyen 2026-04-13 6:01 ` Ruide Cao 0 siblings, 1 reply; 6+ messages in thread From: Tung Quang Nguyen @ 2026-04-13 3:06 UTC (permalink / raw) To: Ren Wei Cc: jmaloy@redhat.com, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, horms@kernel.org, yifanwucs@gmail.com, tomapufckgml@gmail.com, yuantan098@gmail.com, bird@lzu.edu.cn, enjou1224z@gmail.com, caoruide123@gmail.com, netdev@vger.kernel.org >Subject: [PATCH net 1/1] tipc: validate Gap ACK blocks in STATE message > >From: Ruide Cao <caoruide123@gmail.com> > >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. Can you explain how that peer can alter the STATE message ? If it can, what concrete values are used and on what fields of the STATE messages ? > >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 <yifanwucs@gmail.com> >Reported-by: Juefei Pu <tomapufckgml@gmail.com> >Co-developed-by: Yuan Tan <yuantan098@gmail.com> >Signed-off-by: Yuan Tan <yuantan098@gmail.com> >Suggested-by: Xin Liu <bird@lzu.edu.cn> >Tested-by: Ren Wei <enjou1224z@gmail.com> >Signed-off-by: Ruide Cao <caoruide123@gmail.com> >Signed-off-by: Ren Wei <n05ec@lzu.edu.cn> >--- > 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; This checking is redundant because with existing sanity checking, the invalid gap ACK blocks will not be used to release acked messages in transmit queue. >+ > p = (struct tipc_gap_ack_blks *)msg_data(hdr); > sz = ntohs(p->len); >+ if (sz < min_sz || sz > dlen) { >+ sz = 0; >+ goto ignore; >+ } This checking is redundant. Existing sanity checking is good enough. >+ > /* 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)) This checking is redundant. Existing sanity checking is good enough. > break; > > l->rcv_nxt_state = msg_seqno(hdr) + 1; >-- >2.34.1 > ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net 1/1] tipc: validate Gap ACK blocks in STATE message 2026-04-13 3:06 ` Tung Quang Nguyen @ 2026-04-13 6:01 ` Ruide Cao 2026-04-13 10:01 ` Tung Quang Nguyen 0 siblings, 1 reply; 6+ messages in thread From: Ruide Cao @ 2026-04-13 6:01 UTC (permalink / raw) To: Tung Quang Nguyen, Ren Wei Cc: jmaloy@redhat.com, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, horms@kernel.org, yifanwucs@gmail.com, tomapufckgml@gmail.com, yuantan098@gmail.com, bird@lzu.edu.cn, enjou1224z@gmail.com, netdev@vger.kernel.org On 4/12/2026 8:06 PM, Tung Quang Nguyen wrote: >> Subject: [PATCH net 1/1] tipc: validate Gap ACK blocks in STATE message >> >> From: Ruide Cao <caoruide123@gmail.com> >> >> 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. > Can you explain how that peer can alter the STATE message ? If it can, what concrete values are used and on what fields of the STATE messages ? Thanks for the review. To clarify, the peer is not "altering" an already received STATE message; it is actively sending a malformed LINK_PROTOCOL/STATE_MSG after the link has already negotiated the TIPC_GAP_ACK_BLOCK capability. Concretely, the crafted STATE message is sent with a modified msg_size so that msg_data_sz(hdr) is 0, but the actual UDP payload still carries extra physical bytes after the 40-byte TIPC header. Those bytes are then interpreted as the fixed Gap ACK header. For example: len = 0x07fc ugack_cnt = 0xff bgack_cnt = 0xff These values are specifically chosen so that the existing sanity check remains internally consistent: struct_size(p, gacks, 0xff + 0xff) == 0x07fc Therefore, the existing sanity check does not reject this case. It only checks the self-consistency of the attacker-controlled Gap ACK fields; it completely fails to check if the declared Gap ACK record actually fits inside the enclosing STATE message's logical payload length. >> 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 <yifanwucs@gmail.com> >> Reported-by: Juefei Pu <tomapufckgml@gmail.com> >> Co-developed-by: Yuan Tan <yuantan098@gmail.com> >> Signed-off-by: Yuan Tan <yuantan098@gmail.com> >> Suggested-by: Xin Liu <bird@lzu.edu.cn> >> Tested-by: Ren Wei <enjou1224z@gmail.com> >> Signed-off-by: Ruide Cao <caoruide123@gmail.com> >> Signed-off-by: Ren Wei <n05ec@lzu.edu.cn> >> --- >> 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; > This checking is redundant because with existing sanity checking, the invalid gap ACK blocks will not be used to release acked messages in transmit queue. The `dlen < min_sz` check is required because the existing sanity check already dereferences `p->len`, `p->ugack_cnt`, and `p->bgack_cnt`. Without this new check, an Out-of-Bounds (OOB) read occurs before the old sanity check even has a chance to run. >> + >> p = (struct tipc_gap_ack_blks *)msg_data(hdr); >> sz = ntohs(p->len); >> + if (sz < min_sz || sz > dlen) { >> + sz = 0; >> + goto ignore; >> + } > This checking is redundant. Existing sanity checking is good enough. The `sz < min_sz || sz > dlen` check is not redundant because the old sanity check completely fails to verify if the declared Gap ACK length (`sz`) actually fits inside the enclosing STATE message's logical payload length (`dlen`). Without checking against `dlen`, an internally consistent spoofed packet will pass the old check and cause OOB reads during the subsequent block parsing. >> + >> /* 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)) > This checking is redundant. Existing sanity checking is good enough. The unicast caller-side drop `((l->peer_caps & TIPC_GAP_ACK_BLOCK) && !glen)` is also necessary. Once the capability is negotiated, a valid Gap ACK record MUST have at least the fixed 4-byte header. If `glen == 0` from such a peer, it indicates a malformed payload. The STATE message must be dropped here so it is not passed on to `tipc_mon_rcv()` as if monitor data started at `data + 0`, which would misalign the monitor payload parsing. >> break; >> >> l->rcv_nxt_state = msg_seqno(hdr) + 1; >> -- >> 2.34.1 >> ^ permalink raw reply [flat|nested] 6+ messages in thread
* RE: [PATCH net 1/1] tipc: validate Gap ACK blocks in STATE message 2026-04-13 6:01 ` Ruide Cao @ 2026-04-13 10:01 ` Tung Quang Nguyen 0 siblings, 0 replies; 6+ messages in thread From: Tung Quang Nguyen @ 2026-04-13 10:01 UTC (permalink / raw) To: Ruide Cao, Ren Wei Cc: jmaloy@redhat.com, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com, horms@kernel.org, yifanwucs@gmail.com, tomapufckgml@gmail.com, yuantan098@gmail.com, bird@lzu.edu.cn, enjou1224z@gmail.com, netdev@vger.kernel.org >Subject: Re: [PATCH net 1/1] tipc: validate Gap ACK blocks in STATE message > > >On 4/12/2026 8:06 PM, Tung Quang Nguyen wrote: >>> Subject: [PATCH net 1/1] tipc: validate Gap ACK blocks in STATE >>> message >>> >>> From: Ruide Cao <caoruide123@gmail.com> >>> >>> 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. >> Can you explain how that peer can alter the STATE message ? If it can, what >concrete values are used and on what fields of the STATE messages ? > >Thanks for the review. > >To clarify, the peer is not "altering" an already received STATE message; it is >actively sending a malformed LINK_PROTOCOL/STATE_MSG after the link has >already negotiated the TIPC_GAP_ACK_BLOCK capability. > >Concretely, the crafted STATE message is sent with a modified msg_size so that >msg_data_sz(hdr) is 0, but the actual UDP payload still carries extra physical >bytes after the 40-byte TIPC header. Those bytes are then interpreted as the >fixed Gap ACK header. For example: > len = 0x07fc > ugack_cnt = 0xff > bgack_cnt = 0xff > It is surprising that you can modify any field you want in the TIPC message. I do not think that current TIPC code can handle this corrupt message . Can you send me the stack trace at receiving peer when real crash happens after you send the "crafted" state message ? >These values are specifically chosen so that the existing sanity check remains >internally consistent: > struct_size(p, gacks, 0xff + 0xff) == 0x07fc > >Therefore, the existing sanity check does not reject this case. It only checks the >self-consistency of the attacker-controlled Gap ACK fields; it completely fails to >check if the declared Gap ACK record actually fits inside the enclosing STATE >message's logical payload length. > >>> 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 <yifanwucs@gmail.com> >>> Reported-by: Juefei Pu <tomapufckgml@gmail.com> >>> Co-developed-by: Yuan Tan <yuantan098@gmail.com> >>> Signed-off-by: Yuan Tan <yuantan098@gmail.com> >>> Suggested-by: Xin Liu <bird@lzu.edu.cn> >>> Tested-by: Ren Wei <enjou1224z@gmail.com> >>> Signed-off-by: Ruide Cao <caoruide123@gmail.com> >>> Signed-off-by: Ren Wei <n05ec@lzu.edu.cn> >>> --- >>> 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; >> This checking is redundant because with existing sanity checking, the invalid >gap ACK blocks will not be used to release acked messages in transmit queue. > >The `dlen < min_sz` check is required because the existing sanity check already >dereferences `p->len`, `p->ugack_cnt`, and `p->bgack_cnt`. In the case of dlen > p->len and p->len = 0x07fc, p->ugack_cnt = 0xff, p->bgack_cnt = 0xff (Sender modified or kept dlen and modified the remaining 3 fields), how could your above and subsequent sanity checks validate p->len, p->bgack_cnt and p->ugack_cnt ? >Without this new check, an Out-of-Bounds (OOB) read occurs before the old >sanity check even has a chance to run. > >>> + >>> p = (struct tipc_gap_ack_blks *)msg_data(hdr); >>> sz = ntohs(p->len); >>> + if (sz < min_sz || sz > dlen) { >>> + sz = 0; >>> + goto ignore; >>> + } >> This checking is redundant. Existing sanity checking is good enough. > >The `sz < min_sz || sz > dlen` check is not redundant because the old sanity >check completely fails to verify if the declared Gap ACK length >(`sz`) actually fits inside the enclosing STATE message's logical payload length >(`dlen`). > >Without checking against `dlen`, an internally consistent spoofed packet will >pass the old check and cause OOB reads during the subsequent block parsing. > >>> + >>> /* 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)) >> This checking is redundant. Existing sanity checking is good enough. > >The unicast caller-side drop `((l->peer_caps & TIPC_GAP_ACK_BLOCK) && >!glen)` is also necessary. Once the capability is negotiated, a valid Gap ACK >record MUST have at least the fixed 4-byte header. If `glen == 0` from such a >peer, it indicates a malformed payload. > >The STATE message must be dropped here so it is not passed on to >`tipc_mon_rcv()` as if monitor data started at `data + 0`, which would misalign >the monitor payload parsing. > >>> break; >>> >>> l->rcv_nxt_state = msg_seqno(hdr) + 1; >>> -- >>> 2.34.1 >>> ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-04-13 10:01 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <cover.1775269941.git.caoruide123@gmail.com>
2026-04-05 4:54 ` [PATCH net 1/1] tipc: validate Gap ACK blocks in STATE message Ren Wei
2026-04-06 8:29 ` Tung Quang Nguyen
[not found] <cover.1775809726.git.caoruide123@gmail.com>
2026-04-10 15:53 ` Ren Wei
2026-04-13 3:06 ` Tung Quang Nguyen
2026-04-13 6:01 ` Ruide Cao
2026-04-13 10:01 ` Tung Quang Nguyen
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox