summary refs log tree commit diff
path: root/drivers
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2007-03-29 11:46:52 -0700
committerDavid S. Miller <davem@davemloft.net>2007-03-29 11:46:52 -0700
commitc01003c20563d1e75ec9828d21743919d2b43977 (patch)
tree21cae8933e8a4908d8e8c24244a627bf0c997e77 /drivers
parentdb8b22550d4b83f0910d27a34d05aa16f7f7159f (diff)
downloadlinux-c01003c20563d1e75ec9828d21743919d2b43977.tar.gz
[IFB]: Fix crash on input device removal
The input_device pointer is not refcounted, which means the device may
disappear while packets are queued, causing a crash when ifb passes packets
with a stale skb->dev pointer to netif_rx().

Fix by storing the interface index instead and do a lookup where neccessary.

Signed-off-by: Patrick McHardy <kaber@trash.net>
Acked-by: Jamal Hadi Salim <hadi@cyberus.ca>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/ifb.c35
1 files changed, 13 insertions, 22 deletions
diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c
index ca2b21f9d444..07b4c0d7a75c 100644
--- a/drivers/net/ifb.c
+++ b/drivers/net/ifb.c
@@ -96,17 +96,24 @@ static void ri_tasklet(unsigned long dev)
 		skb->tc_verd = SET_TC_NCLS(skb->tc_verd);
 		stats->tx_packets++;
 		stats->tx_bytes +=skb->len;
+
+		skb->dev = __dev_get_by_index(skb->iif);
+		if (!skb->dev) {
+			dev_kfree_skb(skb);
+			stats->tx_dropped++;
+			break;
+		}
+		skb->iif = _dev->ifindex;
+
 		if (from & AT_EGRESS) {
 			dp->st_rx_frm_egr++;
 			dev_queue_xmit(skb);
 		} else if (from & AT_INGRESS) {
-
 			dp->st_rx_frm_ing++;
+			skb_pull(skb, skb->dev->hard_header_len);
 			netif_rx(skb);
-		} else {
-			dev_kfree_skb(skb);
-			stats->tx_dropped++;
-		}
+		} else
+			BUG();
 	}
 
 	if (netif_tx_trylock(_dev)) {
@@ -157,26 +164,10 @@ static int ifb_xmit(struct sk_buff *skb, struct net_device *dev)
 	stats->rx_packets++;
 	stats->rx_bytes+=skb->len;
 
-	if (!from || !skb->input_dev) {
-dropped:
+	if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->iif) {
 		dev_kfree_skb(skb);
 		stats->rx_dropped++;
 		return ret;
-	} else {
-		/*
-		 * note we could be going
-		 * ingress -> egress or
-		 * egress -> ingress
-		*/
-		skb->dev = skb->input_dev;
-		skb->input_dev = dev;
-		if (from & AT_INGRESS) {
-			skb_pull(skb, skb->dev->hard_header_len);
-		} else {
-			if (!(from & AT_EGRESS)) {
-				goto dropped;
-			}
-		}
 	}
 
 	if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) {