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

Commit 2745b5b7 authored by Michael S. Tsirkin's avatar Michael S. Tsirkin Committed by Roland Dreier
Browse files

IPoIB: Fix skb leak when freeing neighbour



ipoib_neigh_free() is sometimes called while neighbour is still alive,
so it might still have queued skbs.  Fix skb leak in this case.

Signed-off-by: default avatarMichael S. Tsirkin <mst@mellanox.co.il>
Signed-off-by: default avatarRoland Dreier <rolandd@cisco.com>
parent d2fcea7d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -233,7 +233,7 @@ static inline struct ipoib_neigh **to_ipoib_neigh(struct neighbour *neigh)
}

struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neigh);
void ipoib_neigh_free(struct ipoib_neigh *neigh);
void ipoib_neigh_free(struct net_device *dev, struct ipoib_neigh *neigh);

extern struct workqueue_struct *ipoib_workqueue;

+13 −6
Original line number Diff line number Diff line
@@ -264,7 +264,7 @@ static void path_free(struct net_device *dev, struct ipoib_path *path)
		if (neigh->ah)
			ipoib_put_ah(neigh->ah);

		ipoib_neigh_free(neigh);
		ipoib_neigh_free(dev, neigh);
	}

	spin_unlock_irqrestore(&priv->lock, flags);
@@ -525,10 +525,11 @@ static void neigh_add_path(struct sk_buff *skb, struct net_device *dev)
		ipoib_send(dev, skb, path->ah, IPOIB_QPN(skb->dst->neighbour->ha));
	} else {
		neigh->ah  = NULL;
		__skb_queue_tail(&neigh->queue, skb);

		if (!path->query && path_rec_start(dev, path))
			goto err_list;

		__skb_queue_tail(&neigh->queue, skb);
	}

	spin_unlock(&priv->lock);
@@ -538,7 +539,7 @@ static void neigh_add_path(struct sk_buff *skb, struct net_device *dev)
	list_del(&neigh->list);

err_path:
	ipoib_neigh_free(neigh);
	ipoib_neigh_free(dev, neigh);
	++priv->stats.tx_dropped;
	dev_kfree_skb_any(skb);

@@ -655,7 +656,7 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
				 */
				ipoib_put_ah(neigh->ah);
				list_del(&neigh->list);
				ipoib_neigh_free(neigh);
				ipoib_neigh_free(dev, neigh);
				spin_unlock(&priv->lock);
				ipoib_path_lookup(skb, dev);
				goto out;
@@ -786,7 +787,7 @@ static void ipoib_neigh_destructor(struct neighbour *n)
		if (neigh->ah)
			ah = neigh->ah;
		list_del(&neigh->list);
		ipoib_neigh_free(neigh);
		ipoib_neigh_free(n->dev, neigh);
	}

	spin_unlock_irqrestore(&priv->lock, flags);
@@ -809,9 +810,15 @@ struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neighbour)
	return neigh;
}

void ipoib_neigh_free(struct ipoib_neigh *neigh)
void ipoib_neigh_free(struct net_device *dev, struct ipoib_neigh *neigh)
{
	struct ipoib_dev_priv *priv = netdev_priv(dev);
	struct sk_buff *skb;
	*to_ipoib_neigh(neigh->neighbour) = NULL;
	while ((skb = __skb_dequeue(&neigh->queue))) {
		++priv->stats.tx_dropped;
		dev_kfree_skb_any(skb);
	}
	kfree(neigh);
}

+1 −1
Original line number Diff line number Diff line
@@ -114,7 +114,7 @@ static void ipoib_mcast_free(struct ipoib_mcast *mcast)
		 */
		if (neigh->ah)
			ipoib_put_ah(neigh->ah);
		ipoib_neigh_free(neigh);
		ipoib_neigh_free(dev, neigh);
	}

	spin_unlock_irqrestore(&priv->lock, flags);