diff options
author | Koki Sanagi <sanagi.koki@jp.fujitsu.com> | 2011-05-30 17:48:34 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-06-02 17:06:31 -0400 |
commit | ec764bf083a6ff396234351b51fd236f53c903bf (patch) | |
tree | 30c0f8232fe10c76651114dea999b93fa2c8ade5 | |
parent | 2e4ceec4edaef6e903422792de4f7f37de98cec6 (diff) |
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 @@ | |||
12 | TRACE_EVENT(net_dev_xmit, | 12 | TRACE_EVENT(net_dev_xmit, |
13 | 13 | ||
14 | TP_PROTO(struct sk_buff *skb, | 14 | TP_PROTO(struct sk_buff *skb, |
15 | int rc), | 15 | int rc, |
16 | struct net_device *dev, | ||
17 | unsigned int skb_len), | ||
16 | 18 | ||
17 | TP_ARGS(skb, rc), | 19 | TP_ARGS(skb, rc, dev, skb_len), |
18 | 20 | ||
19 | TP_STRUCT__entry( | 21 | TP_STRUCT__entry( |
20 | __field( void *, skbaddr ) | 22 | __field( void *, skbaddr ) |
21 | __field( unsigned int, len ) | 23 | __field( unsigned int, len ) |
22 | __field( int, rc ) | 24 | __field( int, rc ) |
23 | __string( name, skb->dev->name ) | 25 | __string( name, dev->name ) |
24 | ), | 26 | ), |
25 | 27 | ||
26 | TP_fast_assign( | 28 | TP_fast_assign( |
27 | __entry->skbaddr = skb; | 29 | __entry->skbaddr = skb; |
28 | __entry->len = skb->len; | 30 | __entry->len = skb_len; |
29 | __entry->rc = rc; | 31 | __entry->rc = rc; |
30 | __assign_str(name, skb->dev->name); | 32 | __assign_str(name, dev->name); |
31 | ), | 33 | ), |
32 | 34 | ||
33 | TP_printk("dev=%s skbaddr=%p len=%u rc=%d", | 35 | 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, | |||
2096 | { | 2096 | { |
2097 | const struct net_device_ops *ops = dev->netdev_ops; | 2097 | const struct net_device_ops *ops = dev->netdev_ops; |
2098 | int rc = NETDEV_TX_OK; | 2098 | int rc = NETDEV_TX_OK; |
2099 | unsigned int skb_len; | ||
2099 | 2100 | ||
2100 | if (likely(!skb->next)) { | 2101 | if (likely(!skb->next)) { |
2101 | u32 features; | 2102 | u32 features; |
@@ -2146,8 +2147,9 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, | |||
2146 | } | 2147 | } |
2147 | } | 2148 | } |
2148 | 2149 | ||
2150 | skb_len = skb->len; | ||
2149 | rc = ops->ndo_start_xmit(skb, dev); | 2151 | rc = ops->ndo_start_xmit(skb, dev); |
2150 | trace_net_dev_xmit(skb, rc); | 2152 | trace_net_dev_xmit(skb, rc, dev, skb_len); |
2151 | if (rc == NETDEV_TX_OK) | 2153 | if (rc == NETDEV_TX_OK) |
2152 | txq_trans_update(txq); | 2154 | txq_trans_update(txq); |
2153 | return rc; | 2155 | return rc; |
@@ -2167,8 +2169,9 @@ gso: | |||
2167 | if (dev->priv_flags & IFF_XMIT_DST_RELEASE) | 2169 | if (dev->priv_flags & IFF_XMIT_DST_RELEASE) |
2168 | skb_dst_drop(nskb); | 2170 | skb_dst_drop(nskb); |
2169 | 2171 | ||
2172 | skb_len = nskb->len; | ||
2170 | rc = ops->ndo_start_xmit(nskb, dev); | 2173 | rc = ops->ndo_start_xmit(nskb, dev); |
2171 | trace_net_dev_xmit(nskb, rc); | 2174 | trace_net_dev_xmit(nskb, rc, dev, skb_len); |
2172 | if (unlikely(rc != NETDEV_TX_OK)) { | 2175 | if (unlikely(rc != NETDEV_TX_OK)) { |
2173 | if (rc & ~NETDEV_TX_MASK) | 2176 | if (rc & ~NETDEV_TX_MASK) |
2174 | goto out_kfree_gso_skb; | 2177 | goto out_kfree_gso_skb; |