mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	udp: preserve head state for IP_CMSG_PASSSEC
Paul Moore reported a SELinux/IP_PASSSEC regression caused by missing skb->sp at recvmsg() time. We need to preserve the skb head state to process the IP_CMSG_PASSSEC cmsg. With this commit we avoid releasing the skb head state in the BH even if a secpath is attached to the current skb, and stores the skb status (with/without head states) in the scratch area, so that we can access it at skb deallocation time, without incurring in cache-miss penalties. This also avoids misusing the skb CB for ipv6 packets, as introduced by the commit0ddf3fb2c4("udp: preserve skb->dst if required for IP options processing"). Clean a bit the scratch area helpers implementation, to reduce the code differences between 32 and 64 bits build. Reported-by: Paul Moore <paul@paul-moore.com> Fixes:0a463c78d2("udp: avoid a cache miss on dequeue") Fixes:0ddf3fb2c4("udp: preserve skb->dst if required for IP options processing") Signed-off-by: Paolo Abeni <pabeni@redhat.com> Tested-by: Paul Moore <paul@paul-moore.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									9f9e772da2
								
							
						
					
					
						commit
						dce4551cb2
					
				
					 2 changed files with 47 additions and 38 deletions
				
			
		| 
						 | 
					@ -305,33 +305,44 @@ struct sock *udp6_lib_lookup_skb(struct sk_buff *skb,
 | 
				
			||||||
/* UDP uses skb->dev_scratch to cache as much information as possible and avoid
 | 
					/* UDP uses skb->dev_scratch to cache as much information as possible and avoid
 | 
				
			||||||
 * possibly multiple cache miss on dequeue()
 | 
					 * possibly multiple cache miss on dequeue()
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
#if BITS_PER_LONG == 64
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* truesize, len and the bit needed to compute skb_csum_unnecessary will be on
 | 
					 | 
				
			||||||
 * cold cache lines at recvmsg time.
 | 
					 | 
				
			||||||
 * skb->len can be stored on 16 bits since the udp header has been already
 | 
					 | 
				
			||||||
 * validated and pulled.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
struct udp_dev_scratch {
 | 
					struct udp_dev_scratch {
 | 
				
			||||||
	u32 truesize;
 | 
						/* skb->truesize and the stateless bit are embedded in a single field;
 | 
				
			||||||
 | 
						 * do not use a bitfield since the compiler emits better/smaller code
 | 
				
			||||||
 | 
						 * this way
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						u32 _tsize_state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if BITS_PER_LONG == 64
 | 
				
			||||||
 | 
						/* len and the bit needed to compute skb_csum_unnecessary
 | 
				
			||||||
 | 
						 * will be on cold cache lines at recvmsg time.
 | 
				
			||||||
 | 
						 * skb->len can be stored on 16 bits since the udp header has been
 | 
				
			||||||
 | 
						 * already validated and pulled.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	u16 len;
 | 
						u16 len;
 | 
				
			||||||
	bool is_linear;
 | 
						bool is_linear;
 | 
				
			||||||
	bool csum_unnecessary;
 | 
						bool csum_unnecessary;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct udp_dev_scratch *udp_skb_scratch(struct sk_buff *skb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return (struct udp_dev_scratch *)&skb->dev_scratch;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if BITS_PER_LONG == 64
 | 
				
			||||||
static inline unsigned int udp_skb_len(struct sk_buff *skb)
 | 
					static inline unsigned int udp_skb_len(struct sk_buff *skb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return ((struct udp_dev_scratch *)&skb->dev_scratch)->len;
 | 
						return udp_skb_scratch(skb)->len;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline bool udp_skb_csum_unnecessary(struct sk_buff *skb)
 | 
					static inline bool udp_skb_csum_unnecessary(struct sk_buff *skb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return ((struct udp_dev_scratch *)&skb->dev_scratch)->csum_unnecessary;
 | 
						return udp_skb_scratch(skb)->csum_unnecessary;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline bool udp_skb_is_linear(struct sk_buff *skb)
 | 
					static inline bool udp_skb_is_linear(struct sk_buff *skb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return ((struct udp_dev_scratch *)&skb->dev_scratch)->is_linear;
 | 
						return udp_skb_scratch(skb)->is_linear;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1163,34 +1163,32 @@ int udp_sendpage(struct sock *sk, struct page *page, int offset,
 | 
				
			||||||
	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->_tsize_state = skb->truesize;
 | 
				
			||||||
	scratch->truesize = skb->truesize;
 | 
					#if BITS_PER_LONG == 64
 | 
				
			||||||
	scratch->len = skb->len;
 | 
						scratch->len = skb->len;
 | 
				
			||||||
	scratch->csum_unnecessary = !!skb_csum_unnecessary(skb);
 | 
						scratch->csum_unnecessary = !!skb_csum_unnecessary(skb);
 | 
				
			||||||
	scratch->is_linear = !skb_is_nonlinear(skb);
 | 
						scratch->is_linear = !skb_is_nonlinear(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;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int udp_skb_truesize(struct sk_buff *skb)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return skb->dev_scratch;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
						if (likely(!skb->_skb_refdst))
 | 
				
			||||||
 | 
							scratch->_tsize_state |= UDP_SKB_IS_STATELESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int udp_skb_truesize(struct sk_buff *skb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return udp_skb_scratch(skb)->_tsize_state & ~UDP_SKB_IS_STATELESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool udp_skb_has_head_state(struct sk_buff *skb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return !(udp_skb_scratch(skb)->_tsize_state & UDP_SKB_IS_STATELESS);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* fully reclaim rmem/fwd memory allocated for skb */
 | 
					/* fully reclaim rmem/fwd memory allocated for skb */
 | 
				
			||||||
static void udp_rmem_release(struct sock *sk, int size, int partial,
 | 
					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
 | 
						/* In the more common cases we cleared the head states previously,
 | 
				
			||||||
	 * options, see __udp_queue_rcv_skb().
 | 
						 * 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);
 | 
							skb_release_head_state(skb);
 | 
				
			||||||
	consume_stateless_skb(skb);
 | 
						consume_stateless_skb(skb);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1784,11 +1782,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
 | 
						/* At recvmsg() time we may access skb->dst or skb->sp depending on
 | 
				
			||||||
	 * cmsg, elsewhere can we clear all pending head states while they are
 | 
						 * the IP options and the cmsg flags, elsewhere can we clear all
 | 
				
			||||||
	 * hot in the cache
 | 
						 * 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->sp))
 | 
				
			||||||
		skb_release_head_state(skb);
 | 
							skb_release_head_state(skb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = __udp_enqueue_schedule_skb(sk, skb);
 | 
						rc = __udp_enqueue_schedule_skb(sk, skb);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue