diff options
author | Koki Sanagi <sanagi.koki@jp.fujitsu.com> | 2011-05-31 01:48:34 +0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-06-03 01:06:31 +0400 |
commit | ec764bf083a6ff396234351b51fd236f53c903bf (patch) | |
tree | 30c0f8232fe10c76651114dea999b93fa2c8ade5 | |
parent | 2e4ceec4edaef6e903422792de4f7f37de98cec6 (diff) | |
download | linux-ec764bf083a6ff396234351b51fd236f53c903bf.tar.xz |
net: tracepoint of net_dev_xmit sees freed skb and causes panic
Because there is a possibility that skb is kfree_skb()ed and zero cleared
after ndo_start_xmit, we should not see the contents of skb like skb->len and
skb->dev->name after ndo_start_xmit. But trace_net_dev_xmit does that
and causes panic by NULL pointer dereference.
This patch fixes trace_net_dev_xmit not to see the contents of skb directly.
If you want to reproduce this panic,
1. Get tracepoint of net_dev_xmit on
2. Create 2 guests on KVM
2. Make 2 guests use virtio_net
4. Execute netperf from one to another for a long time as a network burden
5. host will panic(It takes about 30 minutes)
Signed-off-by: Koki Sanagi <sanagi.koki@jp.fujitsu.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/trace/events/net.h | 12 | ||||
-rw-r--r-- | net/core/dev.c | 7 |
2 files changed, 12 insertions, 7 deletions
diff --git a/include/trace/events/net.h b/include/trace/events/net.h index 5f247f5ffc56..f99645d05a8f 100644 --- a/include/trace/events/net.h +++ b/include/trace/events/net.h @@ -12,22 +12,24 @@ TRACE_EVENT(net_dev_xmit, TP_PROTO(struct sk_buff *skb, - int rc), + int rc, + struct net_device *dev, + unsigned int skb_len), - TP_ARGS(skb, rc), + TP_ARGS(skb, rc, dev, skb_len), TP_STRUCT__entry( __field( void *, skbaddr ) __field( unsigned int, len ) __field( int, rc ) - __string( name, skb->dev->name ) + __string( name, dev->name ) ), TP_fast_assign( __entry->skbaddr = skb; - __entry->len = skb->len; + __entry->len = skb_len; __entry->rc = rc; - __assign_str(name, skb->dev->name); + __assign_str(name, dev->name); ), TP_printk("dev=%s skbaddr=%p len=%u rc=%d", diff --git a/net/core/dev.c b/net/core/dev.c index c7e305d13b71..939307891e71 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2096,6 +2096,7 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, { const struct net_device_ops *ops = dev->netdev_ops; int rc = NETDEV_TX_OK; + unsigned int skb_len; if (likely(!skb->next)) { u32 features; @@ -2146,8 +2147,9 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, } } + skb_len = skb->len; rc = ops->ndo_start_xmit(skb, dev); - trace_net_dev_xmit(skb, rc); + trace_net_dev_xmit(skb, rc, dev, skb_len); if (rc == NETDEV_TX_OK) txq_trans_update(txq); return rc; @@ -2167,8 +2169,9 @@ gso: if (dev->priv_flags & IFF_XMIT_DST_RELEASE) skb_dst_drop(nskb); + skb_len = nskb->len; rc = ops->ndo_start_xmit(nskb, dev); - trace_net_dev_xmit(nskb, rc); + trace_net_dev_xmit(nskb, rc, dev, skb_len); if (unlikely(rc != NETDEV_TX_OK)) { if (rc & ~NETDEV_TX_MASK) goto out_kfree_gso_skb; |