Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ec764bf0 authored by Koki Sanagi's avatar Koki Sanagi Committed by David S. Miller
Browse files

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: default avatarKoki Sanagi <sanagi.koki@jp.fujitsu.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 2e4ceec4
Loading
Loading
Loading
Loading
+7 −5
Original line number Original line Diff line number Diff line
@@ -12,22 +12,24 @@
TRACE_EVENT(net_dev_xmit,
TRACE_EVENT(net_dev_xmit,


	TP_PROTO(struct sk_buff *skb,
	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(
	TP_STRUCT__entry(
		__field(	void *,		skbaddr		)
		__field(	void *,		skbaddr		)
		__field(	unsigned int,	len		)
		__field(	unsigned int,	len		)
		__field(	int,		rc		)
		__field(	int,		rc		)
		__string(	name,		skb->dev->name	)
		__string(	name,		dev->name	)
	),
	),


	TP_fast_assign(
	TP_fast_assign(
		__entry->skbaddr = skb;
		__entry->skbaddr = skb;
		__entry->len = skb->len;
		__entry->len = skb_len;
		__entry->rc = rc;
		__entry->rc = rc;
		__assign_str(name, skb->dev->name);
		__assign_str(name, dev->name);
	),
	),


	TP_printk("dev=%s skbaddr=%p len=%u rc=%d",
	TP_printk("dev=%s skbaddr=%p len=%u rc=%d",
+5 −2
Original line number Original line Diff line number Diff line
@@ -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;
	const struct net_device_ops *ops = dev->netdev_ops;
	int rc = NETDEV_TX_OK;
	int rc = NETDEV_TX_OK;
	unsigned int skb_len;


	if (likely(!skb->next)) {
	if (likely(!skb->next)) {
		u32 features;
		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);
		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)
		if (rc == NETDEV_TX_OK)
			txq_trans_update(txq);
			txq_trans_update(txq);
		return rc;
		return rc;
@@ -2167,8 +2169,9 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
		if (dev->priv_flags & IFF_XMIT_DST_RELEASE)
		if (dev->priv_flags & IFF_XMIT_DST_RELEASE)
			skb_dst_drop(nskb);
			skb_dst_drop(nskb);


		skb_len = nskb->len;
		rc = ops->ndo_start_xmit(nskb, dev);
		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 (unlikely(rc != NETDEV_TX_OK)) {
			if (rc & ~NETDEV_TX_MASK)
			if (rc & ~NETDEV_TX_MASK)
				goto out_kfree_gso_skb;
				goto out_kfree_gso_skb;