bpf: sockmap, strparser, and tls are reusing qdisc_skb_cb and colliding
Strparser is reusing the qdisc_skb_cb struct to stash the skb message handling
progress, e.g. offset and length of the skb. First this is poorly named and
inherits a struct from qdisc that doesn't reflect the actual usage of cb[] at
this layer.
But, more importantly strparser is using the following to access its metadata.
  (struct _strp_msg *)((void *)skb->cb + offsetof(struct qdisc_skb_cb, data))
Where _strp_msg is defined as:
  struct _strp_msg {
        struct strp_msg            strp;                 /*     0     8 */
        int                        accum_len;            /*     8     4 */
        /* size: 12, cachelines: 1, members: 2 */
        /* last cacheline: 12 bytes */
  };
So we use 12 bytes of ->data[] in struct. However in BPF code running parser
and verdict the user has read capabilities into the data[] array as well. Its
not too problematic, but we should not be exposing internal state to BPF
program. If its really needed then we can use the probe_read() APIs which allow
reading kernel memory. And I don't believe cb[] layer poses any API breakage by
moving this around because programs can't depend on cb[] across layers.
In order to fix another issue with a ctx rewrite we need to stash a temp
variable somewhere. To make this work cleanly this patch builds a cb struct
for sk_skb types called sk_skb_cb struct. Then we can use this consistently
in the strparser, sockmap space. Additionally we can start allowing ->cb[]
write access after this.
Fixes: 604326b41a ("bpf, sockmap: convert to generic sk_msg interface")
Signed-off-by: John Fastabend <john.fastabend@gmail.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Tested-by: Jussi Maki <joamaki@gmail.com>
Reviewed-by: Jakub Sitnicki <jakub@cloudflare.com>
Link: https://lore.kernel.org/bpf/20211103204736.248403-5-john.fastabend@gmail.com
			
			
This commit is contained in:
		
							parent
							
								
									c5d2177a72
								
							
						
					
					
						commit
						e0dc3b93bd
					
				| @ -54,10 +54,24 @@ struct strp_msg { | ||||
| 	int offset; | ||||
| }; | ||||
| 
 | ||||
| struct _strp_msg { | ||||
| 	/* Internal cb structure. struct strp_msg must be first for passing
 | ||||
| 	 * to upper layer. | ||||
| 	 */ | ||||
| 	struct strp_msg strp; | ||||
| 	int accum_len; | ||||
| }; | ||||
| 
 | ||||
| struct sk_skb_cb { | ||||
| #define SK_SKB_CB_PRIV_LEN 20 | ||||
| 	unsigned char data[SK_SKB_CB_PRIV_LEN]; | ||||
| 	struct _strp_msg strp; | ||||
| }; | ||||
| 
 | ||||
| static inline struct strp_msg *strp_msg(struct sk_buff *skb) | ||||
| { | ||||
| 	return (struct strp_msg *)((void *)skb->cb + | ||||
| 		offsetof(struct qdisc_skb_cb, data)); | ||||
| 		offsetof(struct sk_skb_cb, strp)); | ||||
| } | ||||
| 
 | ||||
| /* Structure for an attached lower socket */ | ||||
|  | ||||
| @ -9782,11 +9782,33 @@ static u32 sk_skb_convert_ctx_access(enum bpf_access_type type, | ||||
| 				     struct bpf_prog *prog, u32 *target_size) | ||||
| { | ||||
| 	struct bpf_insn *insn = insn_buf; | ||||
| 	int off; | ||||
| 
 | ||||
| 	switch (si->off) { | ||||
| 	case offsetof(struct __sk_buff, data_end): | ||||
| 		insn = bpf_convert_data_end_access(si, insn); | ||||
| 		break; | ||||
| 	case offsetof(struct __sk_buff, cb[0]) ... | ||||
| 	     offsetofend(struct __sk_buff, cb[4]) - 1: | ||||
| 		BUILD_BUG_ON(sizeof_field(struct sk_skb_cb, data) < 20); | ||||
| 		BUILD_BUG_ON((offsetof(struct sk_buff, cb) + | ||||
| 			      offsetof(struct sk_skb_cb, data)) % | ||||
| 			     sizeof(__u64)); | ||||
| 
 | ||||
| 		prog->cb_access = 1; | ||||
| 		off  = si->off; | ||||
| 		off -= offsetof(struct __sk_buff, cb[0]); | ||||
| 		off += offsetof(struct sk_buff, cb); | ||||
| 		off += offsetof(struct sk_skb_cb, data); | ||||
| 		if (type == BPF_WRITE) | ||||
| 			*insn++ = BPF_STX_MEM(BPF_SIZE(si->code), si->dst_reg, | ||||
| 					      si->src_reg, off); | ||||
| 		else | ||||
| 			*insn++ = BPF_LDX_MEM(BPF_SIZE(si->code), si->dst_reg, | ||||
| 					      si->src_reg, off); | ||||
| 		break; | ||||
| 
 | ||||
| 
 | ||||
| 	default: | ||||
| 		return bpf_convert_ctx_access(type, si, insn_buf, prog, | ||||
| 					      target_size); | ||||
|  | ||||
| @ -27,18 +27,10 @@ | ||||
| 
 | ||||
| static struct workqueue_struct *strp_wq; | ||||
| 
 | ||||
| struct _strp_msg { | ||||
| 	/* Internal cb structure. struct strp_msg must be first for passing
 | ||||
| 	 * to upper layer. | ||||
| 	 */ | ||||
| 	struct strp_msg strp; | ||||
| 	int accum_len; | ||||
| }; | ||||
| 
 | ||||
| static inline struct _strp_msg *_strp_msg(struct sk_buff *skb) | ||||
| { | ||||
| 	return (struct _strp_msg *)((void *)skb->cb + | ||||
| 		offsetof(struct qdisc_skb_cb, data)); | ||||
| 		offsetof(struct sk_skb_cb, strp)); | ||||
| } | ||||
| 
 | ||||
| /* Lower lock held */ | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user