On Mon, 2016-05-09 at 00:55 +0200, Pablo Neira Ayuso wrote: > +static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, > + bool xnet) > +{ > + unsigned int hdrlen = sizeof(struct udphdr) + > + sizeof(struct gtp1_header); > + struct gtp1_header *gtp1; > + struct pdp_ctx *pctx; > + int ret = 0; > + > + if (!pskb_may_pull(skb, hdrlen)) > + return -1; > + > + gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr)); > + > + if ((gtp1->flags >> 5) != GTP_V1) > + return 1; > + > + if (gtp1->type != GTP_TPDU) > + return 1; > + > + /* From 29.060: "This field shall be present if and only if any one or > + * more of the S, PN and E flags are set.". > + * > + * If any of the bit is set, then the remaining ones also have to be > + * set. > + */ > + if (gtp1->flags & GTP1_F_MASK) > + hdrlen += 4; > + > + /* Make sure the header is larger enough, including extensions. */ > + if (!pskb_may_pull(skb, hdrlen)) > + return -1;
You need to reload gtp1 here, as the previous pskb_may_pull() might have reallocated skb->head > + > + rcu_read_lock(); > + pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid)); Or risk a use after free here. > + if (!pctx) { > + netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb); > + ret = -1; > + goto out_rcu; > + } > + > + if (!gtp_check_src_ms(skb, pctx, hdrlen)) { > + netdev_dbg(gtp->dev, "No PDP ctx for this MS\n"); > + ret = -1; > + goto out_rcu; > + } > + rcu_read_unlock(); > + > + /* Get rid of the GTP + UDP headers. */ > + return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); > +out_rcu: > + rcu_read_unlock(); > + return ret; > +}