Re: [PATCH] PPPOE can kfree SKB twice (was Re: kernel panic problem. (smp, iptables?))

Michal Ostrowski (mostrows@us.ibm.com)
19 Jul 2001 14:57:21 -0400


Alexey replied to my last post with some valuable comments and in
response I have a new patch (that goes on top of David Miller's patch
from yesterday).

The approach here is that in case we don't have room in the skb for
PPPoE headers, we create a new one (skb2) and copy the entire thing.
If we do have space, we clone it. We always give dev_queue_xmit the
copy/clone pointer and let it free it. We then kfree_skb the original
skb depending on the return value of dev_queue_xmit (to conform to the
expectations of ppp_generic).

Michal Ostrowski
mostrows@speakeasy.net

--- drivers/net/pppoe.c~ Wed Jul 18 10:24:25 2001
+++ drivers/net/pppoe.c Thu Jul 19 14:49:36 2001
@@ -5,7 +5,7 @@
* PPPoE --- PPP over Ethernet (RFC 2516)
*
*
- * Version: 0.6.7
+ * Version: 0.6.8
*
* 030700 : Fixed connect logic to allow for disconnect.
* 270700 : Fixed potential SMP problems; we must protect against
@@ -25,8 +25,12 @@
* locked. (DaveM)
* Ignore return value of dev_queue_xmit in __pppoe_xmit
* or else we may kfree an SKB twice. (DaveM)
+ * 190701 : When doing copies of skb's in __pppoe_xmit, always delete
+ * the original skb that was passed in on success, never on
+ * failure. Delete the copy of the skb on failure to avoid
+ * a memory leak.
*
- * Author: Michal Ostrowski <mostrows@styx.uwaterloo.ca>
+ * Author: Michal Ostrowski <mostrows@speakeasy.net>
* Contributors:
* Arnaldo Carvalho de Melo <acme@xconectiva.com.br>
* David S. Miller (davem@redhat.com)
@@ -837,6 +841,7 @@
return error;
}

+
/************************************************************************
*
* xmit function for internal use.
@@ -849,6 +854,7 @@
struct pppoe_hdr *ph;
int headroom = skb_headroom(skb);
int data_len = skb->len;
+ struct sk_buff *skb2;

if (sk->dead || !(sk->state & PPPOX_CONNECTED))
goto abort;
@@ -864,7 +870,6 @@

/* Copy the skb if there is no space for the header. */
if (headroom < (sizeof(struct pppoe_hdr) + dev->hard_header_len)) {
- struct sk_buff *skb2;

skb2 = dev_alloc_skb(32+skb->len +
sizeof(struct pppoe_hdr) +
@@ -876,30 +881,36 @@
skb_reserve(skb2, dev->hard_header_len + sizeof(struct pppoe_hdr));
memcpy(skb_put(skb2, skb->len), skb->data, skb->len);

- skb_unlink(skb);
- kfree_skb(skb);
- skb = skb2;
+ } else {
+ /* Make a clone so as to not disturb the original skb,
+ * give dev_queue_xmit something it can free.
+ */
+ skb2 = skb_clone(skb, GFP_ATOMIC);
}

- /* We may not return error beyond this point. Potentially this
- * is a new SKB and in such a case we've already freed the
- * original SKB. So if we were to return error, our caller would
- * double free that original SKB. -DaveM
- */
-
- ph = (struct pppoe_hdr *) skb_push(skb, sizeof(struct pppoe_hdr));
+ ph = (struct pppoe_hdr *) skb_push(skb2, sizeof(struct pppoe_hdr));
memcpy(ph, &hdr, sizeof(struct pppoe_hdr));
- skb->protocol = __constant_htons(ETH_P_PPP_SES);
+ skb2->protocol = __constant_htons(ETH_P_PPP_SES);

- skb->nh.raw = skb->data;
+ skb2->nh.raw = skb2->data;

- skb->dev = dev;
+ skb2->dev = dev;

- dev->hard_header(skb, dev, ETH_P_PPP_SES,
+ dev->hard_header(skb2, dev, ETH_P_PPP_SES,
sk->protinfo.pppox->pppoe_pa.remote,
NULL, data_len);

- dev_queue_xmit(skb);
+ /* We're transmitting skb2, and assuming that dev_queue_xmit
+ * will free it. The generic ppp layer however, is expecting
+ * that we give back the skb in case of failure,
+ * but free it in case of success.
+ */
+
+ if (dev_queue_xmit(skb2)<0) {
+ goto abort;
+ }
+
+ kfree_skb(skb);
return 1;

abort:
@@ -1049,7 +1060,6 @@
int err = register_pppox_proto(PX_PROTO_OE, &pppoe_proto);

if (err == 0) {
- printk(KERN_INFO "Registered PPPoE v0.6.5\n");

dev_add_pack(&pppoes_ptype);
dev_add_pack(&pppoed_ptype);
--- drivers/net/pppox.c~ Tue Feb 13 16:15:05 2001
+++ drivers/net/pppox.c Wed Jul 18 10:27:25 2001
@@ -148,10 +148,6 @@

err = sock_register(&pppox_proto_family);

- if (err == 0) {
- printk(KERN_INFO "Registered PPPoX v0.5\n");
- }
-
return err;
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/