Currently there are a number of error paths in the lowpan_rcv function that free the skb before returning, this patch simplifies the receive path by ensuring that the skb is only freed from this function. Signed-off-by: Martin Townsend <martin.townsend@xxxxxxxxxx> --- include/net/6lowpan.h | 2 +- net/6lowpan/iphc.c | 27 +++++++++++++++------------ net/bluetooth/6lowpan.c | 12 ++++++++---- net/ieee802154/6lowpan_rtnl.c | 12 ++++++------ 4 files changed, 30 insertions(+), 23 deletions(-) diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h index d184df1..883e7a2 100644 --- a/include/net/6lowpan.h +++ b/include/net/6lowpan.h @@ -374,7 +374,7 @@ lowpan_uncompress_size(const struct sk_buff *skb, u16 *dgram_offset) typedef int (*skb_delivery_cb)(struct sk_buff *skb, struct net_device *dev); -int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, +int lowpan_process_data(struct sk_buff **skb_inout, struct net_device *dev, const u8 *saddr, const u8 saddr_type, const u8 saddr_len, const u8 *daddr, const u8 daddr_type, const u8 daddr_len, u8 iphc0, u8 iphc1, skb_delivery_cb skb_deliver); diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c index 142eef5..eca24bf 100644 --- a/net/6lowpan/iphc.c +++ b/net/6lowpan/iphc.c @@ -179,11 +179,11 @@ static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr, new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb), GFP_ATOMIC); - kfree_skb(skb); - if (!new) return -ENOMEM; + kfree_skb(skb); + skb_push(new, sizeof(struct ipv6hdr)); skb_reset_network_header(new); skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr)); @@ -196,6 +196,8 @@ static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr, new->data, new->len); stat = deliver_skb(new, dev); + if (stat < 0) + stat = NET_RX_DROP; kfree_skb(new); @@ -332,7 +334,7 @@ err: /* TTL uncompression values */ static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 }; -int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, +int lowpan_process_data(struct sk_buff **skb_inout, struct net_device *dev, const u8 *saddr, const u8 saddr_type, const u8 saddr_len, const u8 *daddr, const u8 daddr_type, const u8 daddr_len, u8 iphc0, u8 iphc1, skb_delivery_cb deliver_skb) @@ -340,6 +342,8 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, struct ipv6hdr hdr = {}; u8 tmp, num_context = 0; int err; + int err_ret = -EINVAL; + struct sk_buff *skb = *skb_inout; raw_dump_table(__func__, "raw skb data dump uncompressed", skb->data, skb->len); @@ -460,7 +464,6 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, /* UDP data uncompression */ if (iphc0 & LOWPAN_IPHC_NH_C) { struct udphdr uh; - struct sk_buff *new; if (uncompress_udp_header(skb, &uh)) goto drop; @@ -468,14 +471,15 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, /* replace the compressed UDP head by the uncompressed UDP * header */ - new = skb_copy_expand(skb, sizeof(struct udphdr), + skb = skb_copy_expand(skb, sizeof(struct udphdr), skb_tailroom(skb), GFP_ATOMIC); - kfree_skb(skb); - - if (!new) - return -ENOMEM; + if (!skb) { + err_ret = -ENOMEM; + goto drop; + } - skb = new; + kfree_skb(*skb_inout); + *skb_inout = skb; skb_push(skb, sizeof(struct udphdr)); skb_reset_transport_header(skb); @@ -502,8 +506,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, return skb_deliver(skb, &hdr, dev, deliver_skb); drop: - kfree_skb(skb); - return -EINVAL; + return err_ret; } EXPORT_SYMBOL_GPL(lowpan_process_data); diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 206b65c..f5f7ee0 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -215,7 +215,7 @@ static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev) return ret; } -static int process_data(struct sk_buff *skb, struct net_device *netdev, +static int process_data(struct sk_buff **skb_inout, struct net_device *netdev, struct l2cap_chan *chan) { const u8 *saddr, *daddr; @@ -223,6 +223,7 @@ static int process_data(struct sk_buff *skb, struct net_device *netdev, struct lowpan_dev *dev; struct lowpan_peer *peer; unsigned long flags; + struct sk_buff *skb = *skb_inout; dev = lowpan_dev(netdev); @@ -245,13 +246,12 @@ static int process_data(struct sk_buff *skb, struct net_device *netdev, if (lowpan_fetch_skb_u8(skb, &iphc1)) goto drop; - return lowpan_process_data(skb, netdev, + return lowpan_process_data(skb_inout, netdev, saddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN, daddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN, iphc0, iphc1, give_skb_to_upper); drop: - kfree_skb(skb); return -EINVAL; } @@ -300,7 +300,11 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev, if (!local_skb) goto drop; - ret = process_data(local_skb, dev, chan); + ret = process_data(&local_skb, dev, chan); + if (ret < 0) { + kfree_skb(local_skb); + goto drop; + } if (ret != NET_RX_SUCCESS) goto drop; diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c index 6591d27..fdb6c10 100644 --- a/net/ieee802154/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan_rtnl.c @@ -167,11 +167,12 @@ static int lowpan_give_skb_to_devices(struct sk_buff *skb, return stat; } -static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr) +static int process_data(struct sk_buff **skb_inout, const struct ieee802154_hdr *hdr) { u8 iphc0, iphc1; struct ieee802154_addr_sa sa, da; void *sap, *dap; + struct sk_buff *skb = *skb_inout; raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len); /* at least two bytes will be used for the encoding */ @@ -197,13 +198,12 @@ static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr) else dap = &da.hwaddr; - return lowpan_process_data(skb, skb->dev, sap, sa.addr_type, + return lowpan_process_data(skb_inout, skb->dev, sap, sa.addr_type, IEEE802154_ADDR_LEN, dap, da.addr_type, IEEE802154_ADDR_LEN, iphc0, iphc1, lowpan_give_skb_to_devices); drop: - kfree_skb(skb); return -EINVAL; } @@ -490,14 +490,14 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, } else { switch (skb->data[0] & 0xe0) { case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ - ret = process_data(skb, &hdr); + ret = process_data(&skb, &hdr); if (ret == NET_RX_DROP) goto drop; break; case LOWPAN_DISPATCH_FRAG1: /* first fragment header */ ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1); if (ret == 1) { - ret = process_data(skb, &hdr); + ret = process_data(&skb, &hdr); if (ret == NET_RX_DROP) goto drop; } @@ -505,7 +505,7 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */ ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN); if (ret == 1) { - ret = process_data(skb, &hdr); + ret = process_data(&skb, &hdr); if (ret == NET_RX_DROP) goto drop; } -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html