Skip to content

Commit da4b692

Browse files
jiriwiesnergregkh
authored andcommitted
ipv4: ipv6: netfilter: Adjust the frag mem limit when truesize changes
[ Upstream commit ebaf39e ] The *_frag_reasm() functions are susceptible to miscalculating the byte count of packet fragments in case the truesize of a head buffer changes. The truesize member may be changed by the call to skb_unclone(), leaving the fragment memory limit counter unbalanced even if all fragments are processed. This miscalculation goes unnoticed as long as the network namespace which holds the counter is not destroyed. Should an attempt be made to destroy a network namespace that holds an unbalanced fragment memory limit counter the cleanup of the namespace never finishes. The thread handling the cleanup gets stuck in inet_frags_exit_net() waiting for the percpu counter to reach zero. The thread is usually in running state with a stacktrace similar to: PID: 1073 TASK: ffff880626711440 CPU: 1 COMMAND: "kworker/u48:4" #5 [ffff880621563d48] _raw_spin_lock at ffffffff815f5480 #6 [ffff880621563d48] inet_evict_bucket at ffffffff8158020b #7 [ffff880621563d80] inet_frags_exit_net at ffffffff8158051c #8 [ffff880621563db0] ops_exit_list at ffffffff814f5856 #9 [ffff880621563dd8] cleanup_net at ffffffff814f67c0 #10 [ffff880621563e38] process_one_work at ffffffff81096f14 It is not possible to create new network namespaces, and processes that call unshare() end up being stuck in uninterruptible sleep state waiting to acquire the net_mutex. The bug was observed in the IPv6 netfilter code by Per Sundstrom. I thank him for his analysis of the problem. The parts of this patch that apply to IPv4 and IPv6 fragment reassembly are preemptive measures. Signed-off-by: Jiri Wiesner <[email protected]> Reported-by: Per Sundstrom <[email protected]> Acked-by: Peter Oskolkov <[email protected]> Signed-off-by: David S. Miller <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 1bb538a commit da4b692

File tree

3 files changed

+21
-2
lines changed

3 files changed

+21
-2
lines changed

net/ipv4/ip_fragment.c

+7
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,
513513
struct rb_node *rbn;
514514
int len;
515515
int ihlen;
516+
int delta;
516517
int err;
517518
u8 ecn;
518519

@@ -554,10 +555,16 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,
554555
if (len > 65535)
555556
goto out_oversize;
556557

558+
delta = - head->truesize;
559+
557560
/* Head of list must not be cloned. */
558561
if (skb_unclone(head, GFP_ATOMIC))
559562
goto out_nomem;
560563

564+
delta += head->truesize;
565+
if (delta)
566+
add_frag_mem_limit(qp->q.net, delta);
567+
561568
/* If the first fragment is fragmented itself, we split
562569
* it to two chunks: the first with data and paged part
563570
* and the second, holding only fragments. */

net/ipv6/netfilter/nf_conntrack_reasm.c

+7-1
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ static bool
349349
nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *prev, struct net_device *dev)
350350
{
351351
struct sk_buff *fp, *head = fq->q.fragments;
352-
int payload_len;
352+
int payload_len, delta;
353353
u8 ecn;
354354

355355
inet_frag_kill(&fq->q);
@@ -371,10 +371,16 @@ nf_ct_frag6_reasm(struct frag_queue *fq, struct sk_buff *prev, struct net_devic
371371
return false;
372372
}
373373

374+
delta = - head->truesize;
375+
374376
/* Head of list must not be cloned. */
375377
if (skb_unclone(head, GFP_ATOMIC))
376378
return false;
377379

380+
delta += head->truesize;
381+
if (delta)
382+
add_frag_mem_limit(fq->q.net, delta);
383+
378384
/* If the first fragment is fragmented itself, we split
379385
* it to two chunks: the first with data and paged part
380386
* and the second, holding only fragments. */

net/ipv6/reassembly.c

+7-1
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
348348
{
349349
struct net *net = container_of(fq->q.net, struct net, ipv6.frags);
350350
struct sk_buff *fp, *head = fq->q.fragments;
351-
int payload_len;
351+
int payload_len, delta;
352352
unsigned int nhoff;
353353
int sum_truesize;
354354
u8 ecn;
@@ -389,10 +389,16 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
389389
if (payload_len > IPV6_MAXPLEN)
390390
goto out_oversize;
391391

392+
delta = - head->truesize;
393+
392394
/* Head of list must not be cloned. */
393395
if (skb_unclone(head, GFP_ATOMIC))
394396
goto out_oom;
395397

398+
delta += head->truesize;
399+
if (delta)
400+
add_frag_mem_limit(fq->q.net, delta);
401+
396402
/* If the first fragment is fragmented itself, we split
397403
* it to two chunks: the first with data and paged part
398404
* and the second, holding only fragments. */

0 commit comments

Comments
 (0)