diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index b02d2472b003..c1fb419a9cf0 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -79,6 +79,7 @@ struct frag_v6_compare_key { * @tstamp_type: stamp has a mono delivery time (EDT) * @flags: fragment queue flags * @max_size: maximum received fragment size + * @truesize: sum of the truesize of queued fragments * @fqdir: pointer to struct fqdir * @rcu: rcu head for freeing deferall */ @@ -100,6 +101,7 @@ struct inet_frag_queue { u8 tstamp_type; __u8 flags; u16 max_size; + unsigned int truesize; struct fqdir *fqdir; struct rcu_head rcu; }; @@ -172,6 +174,13 @@ extern const u8 ip_frag_ecn_table[16]; #define IPFRAG_DUP 1 #define IPFRAG_OVERLAP 2 #define IPFRAG_MIXED_SECPATH 3 +#define IPFRAG_TOOBIG 4 + +/* Max sum of the truesize of fragments in a queue. This keeps the sums + * done in inet_frag_reasm_prepare() and inet_frag_reasm_finish() well + * below INT_MAX. Legitimate traffic stays far below this limit. + */ +#define INET_FRAG_MAX_TRUESIZE (INT_MAX / 2) int inet_frag_queue_insert(struct inet_frag_queue *q, struct sk_buff *skb, int offset, int end); void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb, diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index c18da91c1f03..0b8d22ffafca 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -336,6 +336,7 @@ void inet_frag_queue_flush(struct inet_frag_queue *q, q->rb_fragments = RB_ROOT; q->fragments_tail = NULL; q->last_run_head = NULL; + q->truesize = 0; } EXPORT_SYMBOL(inet_frag_queue_flush); @@ -446,6 +447,9 @@ int inet_frag_queue_insert(struct inet_frag_queue *q, struct sk_buff *skb, if (last && !secpath_equal(last, skb)) return IPFRAG_MIXED_SECPATH; + if (skb->truesize > INET_FRAG_MAX_TRUESIZE - q->truesize) + return IPFRAG_TOOBIG; + /* An IP fragment is never a GSO packet, but an untrusted source * (virtio_net_hdr) may have attached GSO metadata to it. Do not let * that reach the reassembled skb, whose head keeps the first @@ -510,6 +514,7 @@ int inet_frag_queue_insert(struct inet_frag_queue *q, struct sk_buff *skb, if (offset) nf_reset_ct(skb); + q->truesize += skb->truesize; return IPFRAG_OK; } EXPORT_SYMBOL(inet_frag_queue_insert);