6.18-stable review patch. If anyone has any objections, please let me know. ------------------ From: Bernard Pidoux nr_sock.fraglen is an unsigned short, but nr_sendmsg() accepts messages of up to 65536 bytes, which nr_output() then splits into NR_INFO frames with the MORE flag. Reassembling such a message on the receiving side makes fraglen wrap to 0, so alloc_skb() returns a buffer far smaller than the data copied into it just after. The same happens with any stream of MORE fragments that adds up to more than 65535 bytes. Mashiro Chen proposed rejecting fragments once the total would exceed USHRT_MAX by returning 1 to the caller. Review pointed out that this return value makes nr_state3_machine() put the frame back on the resequencing queue after nr_queue_rx_frame() has already pulled its headers, and that the limit would reject a legitimate 65536-byte message. Instead: - make fraglen an unsigned int, so a full 64 KiB message fits; - when a message would grow beyond 65536 bytes, drop what has been gathered so far, consume the frame and return 0, so the caller does not retry it; - discard the remaining fragments of that message up to its last fragment, using a new NR_COND_FRAG_DISCARD condition bit, rather than delivering its tail as a separate message. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Mashiro Chen Closes: https://patchwork.kernel.org/project/netdevbpf/patch/20260407171600.102988-2-mashiro.chen@mailbox.org/ Signed-off-by: Bernard Pidoux (cherry picked from commit 6ad8be739b22495d6edc3f6d7b916098ceb3fdf0) Signed-off-by: Sasha Levin --- include/net/netrom.h | 3 ++- net/netrom/nr_in.c | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/include/net/netrom.h b/include/net/netrom.h index f0565a5987d1b..30ecf812b4ab4 100644 --- a/include/net/netrom.h +++ b/include/net/netrom.h @@ -46,6 +46,7 @@ enum { #define NR_COND_REJECT 0x02 #define NR_COND_PEER_RX_BUSY 0x04 #define NR_COND_OWN_RX_BUSY 0x08 +#define NR_COND_FRAG_DISCARD 0x10 #define NR_DEFAULT_T1 120000 /* Outstanding frames - 120 seconds */ #define NR_DEFAULT_T2 5000 /* Response delay - 5 seconds */ @@ -74,7 +75,7 @@ struct nr_sock { unsigned short vs, vr, va, vl; unsigned char n2, n2count; unsigned long t1, t2, t4, idle; - unsigned short fraglen; + unsigned int fraglen; struct timer_list t1timer; struct timer_list t2timer; struct timer_list t4timer; diff --git a/net/netrom/nr_in.c b/net/netrom/nr_in.c index 97944db6b5ac6..b90a47dcf165e 100644 --- a/net/netrom/nr_in.c +++ b/net/netrom/nr_in.c @@ -26,6 +26,9 @@ #include #include +/* nr_sendmsg() never sends more than this in one message */ +#define NR_MAX_REASM_LEN 65536 + static int nr_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) { struct sk_buff *skbo, *skbn = skb; @@ -35,6 +38,29 @@ static int nr_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) nr_start_idletimer(sk); + /* Rest of a message already found to be too long: drop it */ + if (nr->condition & NR_COND_FRAG_DISCARD) { + if (!more) + nr->condition &= ~NR_COND_FRAG_DISCARD; + kfree_skb(skb); + return 0; + } + + /* + * Give up on a message that would grow beyond what a peer may + * send. The frame is consumed rather than handed back to the + * caller, which would queue it again after skb_pull(). + */ + if ((more || nr->fraglen > 0) && + nr->fraglen + skb->len > NR_MAX_REASM_LEN) { + skb_queue_purge(&nr->frag_queue); + nr->fraglen = 0; + if (more) + nr->condition |= NR_COND_FRAG_DISCARD; + kfree_skb(skb); + return 0; + } + if (more) { nr->fraglen += skb->len; skb_queue_tail(&nr->frag_queue, skb); -- 2.53.0