From 44bfd9090d67f26f58d7a324caa58e6563b96479 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 16 May 2012 19:24:35 -0700 Subject: [PATCH] 3.0-stable patches added patches: namespaces-pid_ns-fix-leakage-on-fork-failure.patch usbnet-fix-skb-traversing-races-during-unlink-v2.patch --- ...s-pid_ns-fix-leakage-on-fork-failure.patch | 47 ++++ queue-3.0/series | 2 + ...kb-traversing-races-during-unlink-v2.patch | 218 ++++++++++++++++++ 3 files changed, 267 insertions(+) create mode 100644 queue-3.0/namespaces-pid_ns-fix-leakage-on-fork-failure.patch create mode 100644 queue-3.0/usbnet-fix-skb-traversing-races-during-unlink-v2.patch diff --git a/queue-3.0/namespaces-pid_ns-fix-leakage-on-fork-failure.patch b/queue-3.0/namespaces-pid_ns-fix-leakage-on-fork-failure.patch new file mode 100644 index 00000000000..1c689abed60 --- /dev/null +++ b/queue-3.0/namespaces-pid_ns-fix-leakage-on-fork-failure.patch @@ -0,0 +1,47 @@ +From 5e2bf0142231194d36fdc9596b36a261ed2b9fe7 Mon Sep 17 00:00:00 2001 +From: Mike Galbraith +Date: Thu, 10 May 2012 13:01:45 -0700 +Subject: namespaces, pid_ns: fix leakage on fork() failure + +From: Mike Galbraith + +commit 5e2bf0142231194d36fdc9596b36a261ed2b9fe7 upstream. + +Fork() failure post namespace creation for a child cloned with +CLONE_NEWPID leaks pid_namespace/mnt_cache due to proc being mounted +during creation, but not unmounted during cleanup. Call +pid_ns_release_proc() during cleanup. + +Signed-off-by: Mike Galbraith +Acked-by: Oleg Nesterov +Reviewed-by: "Eric W. Biederman" +Cc: Pavel Emelyanov +Cc: Cyrill Gorcunov +Cc: Louis Rilling +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman + +--- + kernel/fork.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -48,6 +48,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1378,6 +1379,8 @@ bad_fork_cleanup_io: + if (p->io_context) + exit_io_context(p); + bad_fork_cleanup_namespaces: ++ if (unlikely(clone_flags & CLONE_NEWPID)) ++ pid_ns_release_proc(p->nsproxy->pid_ns); + exit_task_namespaces(p); + bad_fork_cleanup_mm: + if (p->mm) { diff --git a/queue-3.0/series b/queue-3.0/series index c7c4c26dfa9..43857b2ccf9 100644 --- a/queue-3.0/series +++ b/queue-3.0/series @@ -20,3 +20,5 @@ tcp-change-tcp_adv_win_scale-and-tcp_rmem.patch sony-laptop-enable-keyboard-backlight-by-default.patch alsa-echoaudio-remove-incorrect-part-of-assertion.patch alsa-hda-lessen-cpu-usage-when-waiting-for-chip-to-respond.patch +usbnet-fix-skb-traversing-races-during-unlink-v2.patch +namespaces-pid_ns-fix-leakage-on-fork-failure.patch diff --git a/queue-3.0/usbnet-fix-skb-traversing-races-during-unlink-v2.patch b/queue-3.0/usbnet-fix-skb-traversing-races-during-unlink-v2.patch new file mode 100644 index 00000000000..7c4bf7a07ef --- /dev/null +++ b/queue-3.0/usbnet-fix-skb-traversing-races-during-unlink-v2.patch @@ -0,0 +1,218 @@ +From 5b6e9bcdeb65634b4ad604eb4536404bbfc62cfa Mon Sep 17 00:00:00 2001 +From: Ming Lei +Date: Thu, 26 Apr 2012 11:33:46 +0800 +Subject: usbnet: fix skb traversing races during unlink(v2) + +From: Ming Lei + +commit 5b6e9bcdeb65634b4ad604eb4536404bbfc62cfa upstream. + +Commit 4231d47e6fe69f061f96c98c30eaf9fb4c14b96d(net/usbnet: avoid +recursive locking in usbnet_stop()) fixes the recursive locking +problem by releasing the skb queue lock before unlink, but may +cause skb traversing races: + - after URB is unlinked and the queue lock is released, + the refered skb and skb->next may be moved to done queue, + even be released + - in skb_queue_walk_safe, the next skb is still obtained + by next pointer of the last skb + - so maybe trigger oops or other problems + +This patch extends the usage of entry->state to describe 'start_unlink' +state, so always holding the queue(rx/tx) lock to change the state if +the referd skb is in rx or tx queue because we need to know if the +refered urb has been started unlinking in unlink_urbs. + +The other part of this patch is based on Huajun's patch: +always traverse from head of the tx/rx queue to get skb which is +to be unlinked but not been started unlinking. + +Signed-off-by: Huajun Li +Signed-off-by: Ming Lei +Cc: Oliver Neukum +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/net/usb/usbnet.c | 54 +++++++++++++++++++++++++++++++-------------- + include/linux/usb/usbnet.h | 3 +- + 2 files changed, 40 insertions(+), 17 deletions(-) + +--- a/drivers/net/usb/usbnet.c ++++ b/drivers/net/usb/usbnet.c +@@ -277,17 +277,32 @@ int usbnet_change_mtu (struct net_device + } + EXPORT_SYMBOL_GPL(usbnet_change_mtu); + ++/* The caller must hold list->lock */ ++static void __usbnet_queue_skb(struct sk_buff_head *list, ++ struct sk_buff *newsk, enum skb_state state) ++{ ++ struct skb_data *entry = (struct skb_data *) newsk->cb; ++ ++ __skb_queue_tail(list, newsk); ++ entry->state = state; ++} ++ + /*-------------------------------------------------------------------------*/ + + /* some LK 2.4 HCDs oopsed if we freed or resubmitted urbs from + * completion callbacks. 2.5 should have fixed those bugs... + */ + +-static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_head *list) ++static enum skb_state defer_bh(struct usbnet *dev, struct sk_buff *skb, ++ struct sk_buff_head *list, enum skb_state state) + { + unsigned long flags; ++ enum skb_state old_state; ++ struct skb_data *entry = (struct skb_data *) skb->cb; + + spin_lock_irqsave(&list->lock, flags); ++ old_state = entry->state; ++ entry->state = state; + __skb_unlink(skb, list); + spin_unlock(&list->lock); + spin_lock(&dev->done.lock); +@@ -295,6 +310,7 @@ static void defer_bh(struct usbnet *dev, + if (dev->done.qlen == 1) + tasklet_schedule(&dev->bh); + spin_unlock_irqrestore(&dev->done.lock, flags); ++ return old_state; + } + + /* some work can't be done in tasklets, so we use keventd +@@ -335,7 +351,6 @@ static int rx_submit (struct usbnet *dev + entry = (struct skb_data *) skb->cb; + entry->urb = urb; + entry->dev = dev; +- entry->state = rx_start; + entry->length = 0; + + usb_fill_bulk_urb (urb, dev->udev, dev->in, +@@ -367,7 +382,7 @@ static int rx_submit (struct usbnet *dev + tasklet_schedule (&dev->bh); + break; + case 0: +- __skb_queue_tail (&dev->rxq, skb); ++ __usbnet_queue_skb(&dev->rxq, skb, rx_start); + } + } else { + netif_dbg(dev, ifdown, dev->net, "rx: stopped\n"); +@@ -418,16 +433,17 @@ static void rx_complete (struct urb *urb + struct skb_data *entry = (struct skb_data *) skb->cb; + struct usbnet *dev = entry->dev; + int urb_status = urb->status; ++ enum skb_state state; + + skb_put (skb, urb->actual_length); +- entry->state = rx_done; ++ state = rx_done; + entry->urb = NULL; + + switch (urb_status) { + /* success */ + case 0: + if (skb->len < dev->net->hard_header_len) { +- entry->state = rx_cleanup; ++ state = rx_cleanup; + dev->net->stats.rx_errors++; + dev->net->stats.rx_length_errors++; + netif_dbg(dev, rx_err, dev->net, +@@ -466,7 +482,7 @@ static void rx_complete (struct urb *urb + "rx throttle %d\n", urb_status); + } + block: +- entry->state = rx_cleanup; ++ state = rx_cleanup; + entry->urb = urb; + urb = NULL; + break; +@@ -477,17 +493,18 @@ block: + // FALLTHROUGH + + default: +- entry->state = rx_cleanup; ++ state = rx_cleanup; + dev->net->stats.rx_errors++; + netif_dbg(dev, rx_err, dev->net, "rx status %d\n", urb_status); + break; + } + +- defer_bh(dev, skb, &dev->rxq); ++ state = defer_bh(dev, skb, &dev->rxq, state); + + if (urb) { + if (netif_running (dev->net) && +- !test_bit (EVENT_RX_HALT, &dev->flags)) { ++ !test_bit (EVENT_RX_HALT, &dev->flags) && ++ state != unlink_start) { + rx_submit (dev, urb, GFP_ATOMIC); + return; + } +@@ -573,16 +590,23 @@ EXPORT_SYMBOL_GPL(usbnet_purge_paused_rx + static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q) + { + unsigned long flags; +- struct sk_buff *skb, *skbnext; ++ struct sk_buff *skb; + int count = 0; + + spin_lock_irqsave (&q->lock, flags); +- skb_queue_walk_safe(q, skb, skbnext) { ++ while (!skb_queue_empty(q)) { + struct skb_data *entry; + struct urb *urb; + int retval; + +- entry = (struct skb_data *) skb->cb; ++ skb_queue_walk(q, skb) { ++ entry = (struct skb_data *) skb->cb; ++ if (entry->state != unlink_start) ++ goto found; ++ } ++ break; ++found: ++ entry->state = unlink_start; + urb = entry->urb; + + /* +@@ -1033,8 +1057,7 @@ static void tx_complete (struct urb *urb + } + + usb_autopm_put_interface_async(dev->intf); +- entry->state = tx_done; +- defer_bh(dev, skb, &dev->txq); ++ (void) defer_bh(dev, skb, &dev->txq, tx_done); + } + + /*-------------------------------------------------------------------------*/ +@@ -1087,7 +1110,6 @@ netdev_tx_t usbnet_start_xmit (struct sk + entry = (struct skb_data *) skb->cb; + entry->urb = urb; + entry->dev = dev; +- entry->state = tx_start; + entry->length = length; + + usb_fill_bulk_urb (urb, dev->udev, dev->out, +@@ -1146,7 +1168,7 @@ netdev_tx_t usbnet_start_xmit (struct sk + break; + case 0: + net->trans_start = jiffies; +- __skb_queue_tail (&dev->txq, skb); ++ __usbnet_queue_skb(&dev->txq, skb, tx_start); + if (dev->txq.qlen >= TX_QLEN (dev)) + netif_stop_queue (net); + } +--- a/include/linux/usb/usbnet.h ++++ b/include/linux/usb/usbnet.h +@@ -191,7 +191,8 @@ extern void usbnet_cdc_status(struct usb + enum skb_state { + illegal = 0, + tx_start, tx_done, +- rx_start, rx_done, rx_cleanup ++ rx_start, rx_done, rx_cleanup, ++ unlink_start + }; + + struct skb_data { /* skb->cb is one of these */ -- 2.47.3