Skip to content

Commit c07ff85

Browse files
shemmingerkuba-moo
authored andcommitted
netem: fix return value if duplicate enqueue fails
There is a bug in netem_enqueue() introduced by commit 5845f70 ("net: netem: fix skb length BUG_ON in __skb_to_sgvec") that can lead to a use-after-free. This commit made netem_enqueue() always return NET_XMIT_SUCCESS when a packet is duplicated, which can cause the parent qdisc's q.qlen to be mistakenly incremented. When this happens qlen_notify() may be skipped on the parent during destruction, leaving a dangling pointer for some classful qdiscs like DRR. There are two ways for the bug happen: - If the duplicated packet is dropped by rootq->enqueue() and then the original packet is also dropped. - If rootq->enqueue() sends the duplicated packet to a different qdisc and the original packet is dropped. In both cases NET_XMIT_SUCCESS is returned even though no packets are enqueued at the netem qdisc. The fix is to defer the enqueue of the duplicate packet until after the original packet has been guaranteed to return NET_XMIT_SUCCESS. Fixes: 5845f70 ("net: netem: fix skb length BUG_ON in __skb_to_sgvec") Reported-by: Budimir Markovic <[email protected]> Signed-off-by: Stephen Hemminger <[email protected]> Reviewed-by: Simon Horman <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 528876d commit c07ff85

File tree

1 file changed

+29
-18
lines changed

1 file changed

+29
-18
lines changed

net/sched/sch_netem.c

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -446,12 +446,10 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
446446
struct netem_sched_data *q = qdisc_priv(sch);
447447
/* We don't fill cb now as skb_unshare() may invalidate it */
448448
struct netem_skb_cb *cb;
449-
struct sk_buff *skb2;
449+
struct sk_buff *skb2 = NULL;
450450
struct sk_buff *segs = NULL;
451451
unsigned int prev_len = qdisc_pkt_len(skb);
452452
int count = 1;
453-
int rc = NET_XMIT_SUCCESS;
454-
int rc_drop = NET_XMIT_DROP;
455453

456454
/* Do not fool qdisc_drop_all() */
457455
skb->prev = NULL;
@@ -480,19 +478,11 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
480478
skb_orphan_partial(skb);
481479

482480
/*
483-
* If we need to duplicate packet, then re-insert at top of the
484-
* qdisc tree, since parent queuer expects that only one
485-
* skb will be queued.
481+
* If we need to duplicate packet, then clone it before
482+
* original is modified.
486483
*/
487-
if (count > 1 && (skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
488-
struct Qdisc *rootq = qdisc_root_bh(sch);
489-
u32 dupsave = q->duplicate; /* prevent duplicating a dup... */
490-
491-
q->duplicate = 0;
492-
rootq->enqueue(skb2, rootq, to_free);
493-
q->duplicate = dupsave;
494-
rc_drop = NET_XMIT_SUCCESS;
495-
}
484+
if (count > 1)
485+
skb2 = skb_clone(skb, GFP_ATOMIC);
496486

497487
/*
498488
* Randomized packet corruption.
@@ -504,7 +494,8 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
504494
if (skb_is_gso(skb)) {
505495
skb = netem_segment(skb, sch, to_free);
506496
if (!skb)
507-
return rc_drop;
497+
goto finish_segs;
498+
508499
segs = skb->next;
509500
skb_mark_not_on_list(skb);
510501
qdisc_skb_cb(skb)->pkt_len = skb->len;
@@ -530,7 +521,24 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
530521
/* re-link segs, so that qdisc_drop_all() frees them all */
531522
skb->next = segs;
532523
qdisc_drop_all(skb, sch, to_free);
533-
return rc_drop;
524+
if (skb2)
525+
__qdisc_drop(skb2, to_free);
526+
return NET_XMIT_DROP;
527+
}
528+
529+
/*
530+
* If doing duplication then re-insert at top of the
531+
* qdisc tree, since parent queuer expects that only one
532+
* skb will be queued.
533+
*/
534+
if (skb2) {
535+
struct Qdisc *rootq = qdisc_root_bh(sch);
536+
u32 dupsave = q->duplicate; /* prevent duplicating a dup... */
537+
538+
q->duplicate = 0;
539+
rootq->enqueue(skb2, rootq, to_free);
540+
q->duplicate = dupsave;
541+
skb2 = NULL;
534542
}
535543

536544
qdisc_qstats_backlog_inc(sch, skb);
@@ -601,9 +609,12 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
601609
}
602610

603611
finish_segs:
612+
if (skb2)
613+
__qdisc_drop(skb2, to_free);
614+
604615
if (segs) {
605616
unsigned int len, last_len;
606-
int nb;
617+
int rc, nb;
607618

608619
len = skb ? skb->len : 0;
609620
nb = skb ? 1 : 0;

0 commit comments

Comments
 (0)