]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - net/ipv4/udp.c
udp: fix secpath leak
[karo-tx-linux.git] / net / ipv4 / udp.c
index b057653ceca9208d1e1765525e5492876c0b651e..62344804baaef96daf405dbdd5418db541b95864 100644 (file)
@@ -802,7 +802,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4)
        if (is_udplite)                                  /*     UDP-Lite      */
                csum = udplite_csum(skb);
 
        if (is_udplite)                                  /*     UDP-Lite      */
                csum = udplite_csum(skb);
 
-       else if (sk->sk_no_check_tx) {   /* UDP csum disabled */
+       else if (sk->sk_no_check_tx && !skb_is_gso(skb)) {   /* UDP csum off */
 
                skb->ip_summed = CHECKSUM_NONE;
                goto send;
 
                skb->ip_summed = CHECKSUM_NONE;
                goto send;
@@ -1163,34 +1163,32 @@ out:
        return ret;
 }
 
        return ret;
 }
 
-#if BITS_PER_LONG == 64
+#define UDP_SKB_IS_STATELESS 0x80000000
+
 static void udp_set_dev_scratch(struct sk_buff *skb)
 {
 static void udp_set_dev_scratch(struct sk_buff *skb)
 {
-       struct udp_dev_scratch *scratch;
+       struct udp_dev_scratch *scratch = udp_skb_scratch(skb);
 
        BUILD_BUG_ON(sizeof(struct udp_dev_scratch) > sizeof(long));
 
        BUILD_BUG_ON(sizeof(struct udp_dev_scratch) > sizeof(long));
-       scratch = (struct udp_dev_scratch *)&skb->dev_scratch;
-       scratch->truesize = skb->truesize;
+       scratch->_tsize_state = skb->truesize;
+#if BITS_PER_LONG == 64
        scratch->len = skb->len;
        scratch->csum_unnecessary = !!skb_csum_unnecessary(skb);
        scratch->is_linear = !skb_is_nonlinear(skb);
        scratch->len = skb->len;
        scratch->csum_unnecessary = !!skb_csum_unnecessary(skb);
        scratch->is_linear = !skb_is_nonlinear(skb);
+#endif
+       if (likely(!skb->_skb_refdst && !skb_sec_path(skb)))
+               scratch->_tsize_state |= UDP_SKB_IS_STATELESS;
 }
 
 static int udp_skb_truesize(struct sk_buff *skb)
 {
 }
 
 static int udp_skb_truesize(struct sk_buff *skb)
 {
-       return ((struct udp_dev_scratch *)&skb->dev_scratch)->truesize;
-}
-#else
-static void udp_set_dev_scratch(struct sk_buff *skb)
-{
-       skb->dev_scratch = skb->truesize;
+       return udp_skb_scratch(skb)->_tsize_state & ~UDP_SKB_IS_STATELESS;
 }
 
 }
 
-static int udp_skb_truesize(struct sk_buff *skb)
+static bool udp_skb_has_head_state(struct sk_buff *skb)
 {
 {
-       return skb->dev_scratch;
+       return !(udp_skb_scratch(skb)->_tsize_state & UDP_SKB_IS_STATELESS);
 }
 }
-#endif
 
 /* fully reclaim rmem/fwd memory allocated for skb */
 static void udp_rmem_release(struct sock *sk, int size, int partial,
 
 /* fully reclaim rmem/fwd memory allocated for skb */
 static void udp_rmem_release(struct sock *sk, int size, int partial,
@@ -1388,10 +1386,10 @@ void skb_consume_udp(struct sock *sk, struct sk_buff *skb, int len)
                unlock_sock_fast(sk, slow);
        }
 
                unlock_sock_fast(sk, slow);
        }
 
-       /* we cleared the head states previously only if the skb lacks any IP
-        * options, see __udp_queue_rcv_skb().
+       /* In the more common cases we cleared the head states previously,
+        * see __udp_queue_rcv_skb().
         */
         */
-       if (unlikely(IPCB(skb)->opt.optlen > 0))
+       if (unlikely(udp_skb_has_head_state(skb)))
                skb_release_head_state(skb);
        consume_stateless_skb(skb);
 }
                skb_release_head_state(skb);
        consume_stateless_skb(skb);
 }
@@ -1576,7 +1574,8 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock,
                return ip_recv_error(sk, msg, len, addr_len);
 
 try_again:
                return ip_recv_error(sk, msg, len, addr_len);
 
 try_again:
-       peeking = off = sk_peek_offset(sk, flags);
+       peeking = flags & MSG_PEEK;
+       off = sk_peek_offset(sk, flags);
        skb = __skb_recv_udp(sk, flags, noblock, &peeked, &off, &err);
        if (!skb)
                return err;
        skb = __skb_recv_udp(sk, flags, noblock, &peeked, &off, &err);
        if (!skb)
                return err;
@@ -1784,11 +1783,11 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
                sk_mark_napi_id_once(sk, skb);
        }
 
                sk_mark_napi_id_once(sk, skb);
        }
 
-       /* At recvmsg() time we need skb->dst to process IP options-related
-        * cmsg, elsewhere can we clear all pending head states while they are
-        * hot in the cache
+       /* At recvmsg() time we may access skb->dst or skb->sp depending on
+        * the IP options and the cmsg flags, elsewhere can we clear all
+        * pending head states while they are hot in the cache
         */
         */
-       if (likely(IPCB(skb)->opt.optlen == 0))
+       if (likely(IPCB(skb)->opt.optlen == 0 && !skb_sec_path(skb)))
                skb_release_head_state(skb);
 
        rc = __udp_enqueue_schedule_skb(sk, skb);
                skb_release_head_state(skb);
 
        rc = __udp_enqueue_schedule_skb(sk, skb);
@@ -1930,15 +1929,18 @@ drop:
 /* For TCP sockets, sk_rx_dst is protected by socket lock
  * For UDP, we use xchg() to guard against concurrent changes.
  */
 /* For TCP sockets, sk_rx_dst is protected by socket lock
  * For UDP, we use xchg() to guard against concurrent changes.
  */
-static void udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst)
+bool udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst)
 {
        struct dst_entry *old;
 
        if (dst_hold_safe(dst)) {
                old = xchg(&sk->sk_rx_dst, dst);
                dst_release(old);
 {
        struct dst_entry *old;
 
        if (dst_hold_safe(dst)) {
                old = xchg(&sk->sk_rx_dst, dst);
                dst_release(old);
+               return old != dst;
        }
        }
+       return false;
 }
 }
+EXPORT_SYMBOL(udp_sk_rx_dst_set);
 
 /*
  *     Multicasts and broadcasts go to each listener.
 
 /*
  *     Multicasts and broadcasts go to each listener.