mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-10 15:10:38 +00:00
[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>
This commit is contained in:
parent
db8b22550d
commit
c01003c205
@ -96,17 +96,24 @@ static void ri_tasklet(unsigned long dev)
|
|||||||
skb->tc_verd = SET_TC_NCLS(skb->tc_verd);
|
skb->tc_verd = SET_TC_NCLS(skb->tc_verd);
|
||||||
stats->tx_packets++;
|
stats->tx_packets++;
|
||||||
stats->tx_bytes +=skb->len;
|
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) {
|
if (from & AT_EGRESS) {
|
||||||
dp->st_rx_frm_egr++;
|
dp->st_rx_frm_egr++;
|
||||||
dev_queue_xmit(skb);
|
dev_queue_xmit(skb);
|
||||||
} else if (from & AT_INGRESS) {
|
} else if (from & AT_INGRESS) {
|
||||||
|
|
||||||
dp->st_rx_frm_ing++;
|
dp->st_rx_frm_ing++;
|
||||||
|
skb_pull(skb, skb->dev->hard_header_len);
|
||||||
netif_rx(skb);
|
netif_rx(skb);
|
||||||
} else {
|
} else
|
||||||
dev_kfree_skb(skb);
|
BUG();
|
||||||
stats->tx_dropped++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (netif_tx_trylock(_dev)) {
|
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_packets++;
|
||||||
stats->rx_bytes+=skb->len;
|
stats->rx_bytes+=skb->len;
|
||||||
|
|
||||||
if (!from || !skb->input_dev) {
|
if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->iif) {
|
||||||
dropped:
|
|
||||||
dev_kfree_skb(skb);
|
dev_kfree_skb(skb);
|
||||||
stats->rx_dropped++;
|
stats->rx_dropped++;
|
||||||
return ret;
|
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) {
|
if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) {
|
||||||
|
@ -188,7 +188,7 @@ enum {
|
|||||||
* @sk: Socket we are owned by
|
* @sk: Socket we are owned by
|
||||||
* @tstamp: Time we arrived
|
* @tstamp: Time we arrived
|
||||||
* @dev: Device we arrived on/are leaving by
|
* @dev: Device we arrived on/are leaving by
|
||||||
* @input_dev: Device we arrived on
|
* @iif: ifindex of device we arrived on
|
||||||
* @h: Transport layer header
|
* @h: Transport layer header
|
||||||
* @nh: Network layer header
|
* @nh: Network layer header
|
||||||
* @mac: Link layer header
|
* @mac: Link layer header
|
||||||
@ -235,7 +235,8 @@ struct sk_buff {
|
|||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
struct skb_timeval tstamp;
|
struct skb_timeval tstamp;
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
struct net_device *input_dev;
|
int iif;
|
||||||
|
/* 4 byte hole on 64 bit*/
|
||||||
|
|
||||||
union {
|
union {
|
||||||
struct tcphdr *th;
|
struct tcphdr *th;
|
||||||
|
@ -352,10 +352,13 @@ tcf_change_indev(struct tcf_proto *tp, char *indev, struct rtattr *indev_tlv)
|
|||||||
static inline int
|
static inline int
|
||||||
tcf_match_indev(struct sk_buff *skb, char *indev)
|
tcf_match_indev(struct sk_buff *skb, char *indev)
|
||||||
{
|
{
|
||||||
|
struct net_device *dev;
|
||||||
|
|
||||||
if (indev[0]) {
|
if (indev[0]) {
|
||||||
if (!skb->input_dev)
|
if (!skb->iif)
|
||||||
return 0;
|
return 0;
|
||||||
if (strcmp(indev, skb->input_dev->name))
|
dev = __dev_get_by_index(skb->iif);
|
||||||
|
if (!dev || strcmp(indev, dev->name))
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1741,8 +1741,8 @@ static int ing_filter(struct sk_buff *skb)
|
|||||||
if (dev->qdisc_ingress) {
|
if (dev->qdisc_ingress) {
|
||||||
__u32 ttl = (__u32) G_TC_RTTL(skb->tc_verd);
|
__u32 ttl = (__u32) G_TC_RTTL(skb->tc_verd);
|
||||||
if (MAX_RED_LOOP < ttl++) {
|
if (MAX_RED_LOOP < ttl++) {
|
||||||
printk(KERN_WARNING "Redir loop detected Dropping packet (%s->%s)\n",
|
printk(KERN_WARNING "Redir loop detected Dropping packet (%d->%d)\n",
|
||||||
skb->input_dev->name, skb->dev->name);
|
skb->iif, skb->dev->ifindex);
|
||||||
return TC_ACT_SHOT;
|
return TC_ACT_SHOT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1775,8 +1775,8 @@ int netif_receive_skb(struct sk_buff *skb)
|
|||||||
if (!skb->tstamp.off_sec)
|
if (!skb->tstamp.off_sec)
|
||||||
net_timestamp(skb);
|
net_timestamp(skb);
|
||||||
|
|
||||||
if (!skb->input_dev)
|
if (!skb->iif)
|
||||||
skb->input_dev = skb->dev;
|
skb->iif = skb->dev->ifindex;
|
||||||
|
|
||||||
orig_dev = skb_bond(skb);
|
orig_dev = skb_bond(skb);
|
||||||
|
|
||||||
|
@ -496,7 +496,7 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
|
|||||||
n->tc_verd = SET_TC_VERD(skb->tc_verd,0);
|
n->tc_verd = SET_TC_VERD(skb->tc_verd,0);
|
||||||
n->tc_verd = CLR_TC_OK2MUNGE(n->tc_verd);
|
n->tc_verd = CLR_TC_OK2MUNGE(n->tc_verd);
|
||||||
n->tc_verd = CLR_TC_MUNGED(n->tc_verd);
|
n->tc_verd = CLR_TC_MUNGED(n->tc_verd);
|
||||||
C(input_dev);
|
C(iif);
|
||||||
#endif
|
#endif
|
||||||
skb_copy_secmark(n, skb);
|
skb_copy_secmark(n, skb);
|
||||||
#endif
|
#endif
|
||||||
|
@ -198,7 +198,7 @@ bad_mirred:
|
|||||||
skb2->tc_verd = SET_TC_FROM(skb2->tc_verd, at);
|
skb2->tc_verd = SET_TC_FROM(skb2->tc_verd, at);
|
||||||
|
|
||||||
skb2->dev = dev;
|
skb2->dev = dev;
|
||||||
skb2->input_dev = skb->dev;
|
skb2->iif = skb->dev->ifindex;
|
||||||
dev_queue_xmit(skb2);
|
dev_queue_xmit(skb2);
|
||||||
spin_unlock(&m->tcf_lock);
|
spin_unlock(&m->tcf_lock);
|
||||||
return m->tcf_action;
|
return m->tcf_action;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user