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;
> +}



Reply via email to