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