mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-03 18:20:25 +02:00 
			
		
		
		
	bpf: allow programs to write to certain skb fields
allow programs read/write skb->mark, tc_index fields and ((struct qdisc_skb_cb *)cb)->data. mark and tc_index are generically useful in TC. cb[0]-cb[4] are primarily used to pass arguments from one program to another called via bpf_tail_call() which can be seen in sockex3_kern.c example. All fields of 'struct __sk_buff' are readable to socket and tc_cls_act progs. mark, tc_index are writeable from tc_cls_act only. cb[0]-cb[4] are writeable by both sockets and tc_cls_act. Add verifier tests and improve sample code. Signed-off-by: Alexei Starovoitov <ast@plumgrid.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									3431205e03
								
							
						
					
					
						commit
						d691f9e8d4
					
				
					 6 changed files with 207 additions and 48 deletions
				
			
		| 
						 | 
				
			
			@ -105,7 +105,8 @@ struct bpf_verifier_ops {
 | 
			
		|||
	 */
 | 
			
		||||
	bool (*is_valid_access)(int off, int size, enum bpf_access_type type);
 | 
			
		||||
 | 
			
		||||
	u32 (*convert_ctx_access)(int dst_reg, int src_reg, int ctx_off,
 | 
			
		||||
	u32 (*convert_ctx_access)(enum bpf_access_type type, int dst_reg,
 | 
			
		||||
				  int src_reg, int ctx_off,
 | 
			
		||||
				  struct bpf_insn *insn);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -248,6 +248,8 @@ struct __sk_buff {
 | 
			
		|||
	__u32 priority;
 | 
			
		||||
	__u32 ingress_ifindex;
 | 
			
		||||
	__u32 ifindex;
 | 
			
		||||
	__u32 tc_index;
 | 
			
		||||
	__u32 cb[5];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* _UAPI__LINUX_BPF_H__ */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1692,6 +1692,8 @@ static int do_check(struct verifier_env *env)
 | 
			
		|||
			}
 | 
			
		||||
 | 
			
		||||
		} else if (class == BPF_STX) {
 | 
			
		||||
			enum bpf_reg_type dst_reg_type;
 | 
			
		||||
 | 
			
		||||
			if (BPF_MODE(insn->code) == BPF_XADD) {
 | 
			
		||||
				err = check_xadd(env, insn);
 | 
			
		||||
				if (err)
 | 
			
		||||
| 
						 | 
				
			
			@ -1700,11 +1702,6 @@ static int do_check(struct verifier_env *env)
 | 
			
		|||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (BPF_MODE(insn->code) != BPF_MEM ||
 | 
			
		||||
			    insn->imm != 0) {
 | 
			
		||||
				verbose("BPF_STX uses reserved fields\n");
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			}
 | 
			
		||||
			/* check src1 operand */
 | 
			
		||||
			err = check_reg_arg(regs, insn->src_reg, SRC_OP);
 | 
			
		||||
			if (err)
 | 
			
		||||
| 
						 | 
				
			
			@ -1714,6 +1711,8 @@ static int do_check(struct verifier_env *env)
 | 
			
		|||
			if (err)
 | 
			
		||||
				return err;
 | 
			
		||||
 | 
			
		||||
			dst_reg_type = regs[insn->dst_reg].type;
 | 
			
		||||
 | 
			
		||||
			/* check that memory (dst_reg + off) is writeable */
 | 
			
		||||
			err = check_mem_access(env, insn->dst_reg, insn->off,
 | 
			
		||||
					       BPF_SIZE(insn->code), BPF_WRITE,
 | 
			
		||||
| 
						 | 
				
			
			@ -1721,6 +1720,15 @@ static int do_check(struct verifier_env *env)
 | 
			
		|||
			if (err)
 | 
			
		||||
				return err;
 | 
			
		||||
 | 
			
		||||
			if (insn->imm == 0) {
 | 
			
		||||
				insn->imm = dst_reg_type;
 | 
			
		||||
			} else if (dst_reg_type != insn->imm &&
 | 
			
		||||
				   (dst_reg_type == PTR_TO_CTX ||
 | 
			
		||||
				    insn->imm == PTR_TO_CTX)) {
 | 
			
		||||
				verbose("same insn cannot be used with different pointers\n");
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		} else if (class == BPF_ST) {
 | 
			
		||||
			if (BPF_MODE(insn->code) != BPF_MEM ||
 | 
			
		||||
			    insn->src_reg != BPF_REG_0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1839,12 +1847,18 @@ static int replace_map_fd_with_map_ptr(struct verifier_env *env)
 | 
			
		|||
 | 
			
		||||
	for (i = 0; i < insn_cnt; i++, insn++) {
 | 
			
		||||
		if (BPF_CLASS(insn->code) == BPF_LDX &&
 | 
			
		||||
		    (BPF_MODE(insn->code) != BPF_MEM ||
 | 
			
		||||
		     insn->imm != 0)) {
 | 
			
		||||
		    (BPF_MODE(insn->code) != BPF_MEM || insn->imm != 0)) {
 | 
			
		||||
			verbose("BPF_LDX uses reserved fields\n");
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (BPF_CLASS(insn->code) == BPF_STX &&
 | 
			
		||||
		    ((BPF_MODE(insn->code) != BPF_MEM &&
 | 
			
		||||
		      BPF_MODE(insn->code) != BPF_XADD) || insn->imm != 0)) {
 | 
			
		||||
			verbose("BPF_STX uses reserved fields\n");
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (insn[0].code == (BPF_LD | BPF_IMM | BPF_DW)) {
 | 
			
		||||
			struct bpf_map *map;
 | 
			
		||||
			struct fd f;
 | 
			
		||||
| 
						 | 
				
			
			@ -1967,12 +1981,17 @@ static int convert_ctx_accesses(struct verifier_env *env)
 | 
			
		|||
	struct bpf_prog *new_prog;
 | 
			
		||||
	u32 cnt;
 | 
			
		||||
	int i;
 | 
			
		||||
	enum bpf_access_type type;
 | 
			
		||||
 | 
			
		||||
	if (!env->prog->aux->ops->convert_ctx_access)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < insn_cnt; i++, insn++) {
 | 
			
		||||
		if (insn->code != (BPF_LDX | BPF_MEM | BPF_W))
 | 
			
		||||
		if (insn->code == (BPF_LDX | BPF_MEM | BPF_W))
 | 
			
		||||
			type = BPF_READ;
 | 
			
		||||
		else if (insn->code == (BPF_STX | BPF_MEM | BPF_W))
 | 
			
		||||
			type = BPF_WRITE;
 | 
			
		||||
		else
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (insn->imm != PTR_TO_CTX) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1982,7 +2001,7 @@ static int convert_ctx_accesses(struct verifier_env *env)
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		cnt = env->prog->aux->ops->
 | 
			
		||||
			convert_ctx_access(insn->dst_reg, insn->src_reg,
 | 
			
		||||
			convert_ctx_access(type, insn->dst_reg, insn->src_reg,
 | 
			
		||||
					   insn->off, insn_buf);
 | 
			
		||||
		if (cnt == 0 || cnt >= ARRAY_SIZE(insn_buf)) {
 | 
			
		||||
			verbose("bpf verifier is misconfigured\n");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,6 +46,7 @@
 | 
			
		|||
#include <linux/seccomp.h>
 | 
			
		||||
#include <linux/if_vlan.h>
 | 
			
		||||
#include <linux/bpf.h>
 | 
			
		||||
#include <net/sch_generic.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *	sk_filter - run a packet through a socket filter
 | 
			
		||||
| 
						 | 
				
			
			@ -1463,13 +1464,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id)
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool sk_filter_is_valid_access(int off, int size,
 | 
			
		||||
				      enum bpf_access_type type)
 | 
			
		||||
static bool __is_valid_access(int off, int size, enum bpf_access_type type)
 | 
			
		||||
{
 | 
			
		||||
	/* only read is allowed */
 | 
			
		||||
	if (type != BPF_READ)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	/* check bounds */
 | 
			
		||||
	if (off < 0 || off >= sizeof(struct __sk_buff))
 | 
			
		||||
		return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -1485,8 +1481,42 @@ static bool sk_filter_is_valid_access(int off, int size,
 | 
			
		|||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static u32 sk_filter_convert_ctx_access(int dst_reg, int src_reg, int ctx_off,
 | 
			
		||||
					struct bpf_insn *insn_buf)
 | 
			
		||||
static bool sk_filter_is_valid_access(int off, int size,
 | 
			
		||||
				      enum bpf_access_type type)
 | 
			
		||||
{
 | 
			
		||||
	if (type == BPF_WRITE) {
 | 
			
		||||
		switch (off) {
 | 
			
		||||
		case offsetof(struct __sk_buff, cb[0]) ...
 | 
			
		||||
			offsetof(struct __sk_buff, cb[4]):
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return __is_valid_access(off, size, type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool tc_cls_act_is_valid_access(int off, int size,
 | 
			
		||||
				       enum bpf_access_type type)
 | 
			
		||||
{
 | 
			
		||||
	if (type == BPF_WRITE) {
 | 
			
		||||
		switch (off) {
 | 
			
		||||
		case offsetof(struct __sk_buff, mark):
 | 
			
		||||
		case offsetof(struct __sk_buff, tc_index):
 | 
			
		||||
		case offsetof(struct __sk_buff, cb[0]) ...
 | 
			
		||||
			offsetof(struct __sk_buff, cb[4]):
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return __is_valid_access(off, size, type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static u32 bpf_net_convert_ctx_access(enum bpf_access_type type, int dst_reg,
 | 
			
		||||
				      int src_reg, int ctx_off,
 | 
			
		||||
				      struct bpf_insn *insn_buf)
 | 
			
		||||
{
 | 
			
		||||
	struct bpf_insn *insn = insn_buf;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1538,7 +1568,15 @@ static u32 sk_filter_convert_ctx_access(int dst_reg, int src_reg, int ctx_off,
 | 
			
		|||
		break;
 | 
			
		||||
 | 
			
		||||
	case offsetof(struct __sk_buff, mark):
 | 
			
		||||
		return convert_skb_access(SKF_AD_MARK, dst_reg, src_reg, insn);
 | 
			
		||||
		BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4);
 | 
			
		||||
 | 
			
		||||
		if (type == BPF_WRITE)
 | 
			
		||||
			*insn++ = BPF_STX_MEM(BPF_W, dst_reg, src_reg,
 | 
			
		||||
					      offsetof(struct sk_buff, mark));
 | 
			
		||||
		else
 | 
			
		||||
			*insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg,
 | 
			
		||||
					      offsetof(struct sk_buff, mark));
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case offsetof(struct __sk_buff, pkt_type):
 | 
			
		||||
		return convert_skb_access(SKF_AD_PKTTYPE, dst_reg, src_reg, insn);
 | 
			
		||||
| 
						 | 
				
			
			@ -1553,6 +1591,38 @@ static u32 sk_filter_convert_ctx_access(int dst_reg, int src_reg, int ctx_off,
 | 
			
		|||
	case offsetof(struct __sk_buff, vlan_tci):
 | 
			
		||||
		return convert_skb_access(SKF_AD_VLAN_TAG,
 | 
			
		||||
					  dst_reg, src_reg, insn);
 | 
			
		||||
 | 
			
		||||
	case offsetof(struct __sk_buff, cb[0]) ...
 | 
			
		||||
		offsetof(struct __sk_buff, cb[4]):
 | 
			
		||||
		BUILD_BUG_ON(FIELD_SIZEOF(struct qdisc_skb_cb, data) < 20);
 | 
			
		||||
 | 
			
		||||
		ctx_off -= offsetof(struct __sk_buff, cb[0]);
 | 
			
		||||
		ctx_off += offsetof(struct sk_buff, cb);
 | 
			
		||||
		ctx_off += offsetof(struct qdisc_skb_cb, data);
 | 
			
		||||
		if (type == BPF_WRITE)
 | 
			
		||||
			*insn++ = BPF_STX_MEM(BPF_W, dst_reg, src_reg, ctx_off);
 | 
			
		||||
		else
 | 
			
		||||
			*insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg, ctx_off);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case offsetof(struct __sk_buff, tc_index):
 | 
			
		||||
#ifdef CONFIG_NET_SCHED
 | 
			
		||||
		BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, tc_index) != 2);
 | 
			
		||||
 | 
			
		||||
		if (type == BPF_WRITE)
 | 
			
		||||
			*insn++ = BPF_STX_MEM(BPF_H, dst_reg, src_reg,
 | 
			
		||||
					      offsetof(struct sk_buff, tc_index));
 | 
			
		||||
		else
 | 
			
		||||
			*insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg,
 | 
			
		||||
					      offsetof(struct sk_buff, tc_index));
 | 
			
		||||
		break;
 | 
			
		||||
#else
 | 
			
		||||
		if (type == BPF_WRITE)
 | 
			
		||||
			*insn++ = BPF_MOV64_REG(dst_reg, dst_reg);
 | 
			
		||||
		else
 | 
			
		||||
			*insn++ = BPF_MOV64_IMM(dst_reg, 0);
 | 
			
		||||
		break;
 | 
			
		||||
#endif
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return insn - insn_buf;
 | 
			
		||||
| 
						 | 
				
			
			@ -1561,13 +1631,13 @@ static u32 sk_filter_convert_ctx_access(int dst_reg, int src_reg, int ctx_off,
 | 
			
		|||
static const struct bpf_verifier_ops sk_filter_ops = {
 | 
			
		||||
	.get_func_proto = sk_filter_func_proto,
 | 
			
		||||
	.is_valid_access = sk_filter_is_valid_access,
 | 
			
		||||
	.convert_ctx_access = sk_filter_convert_ctx_access,
 | 
			
		||||
	.convert_ctx_access = bpf_net_convert_ctx_access,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct bpf_verifier_ops tc_cls_act_ops = {
 | 
			
		||||
	.get_func_proto = tc_cls_act_func_proto,
 | 
			
		||||
	.is_valid_access = sk_filter_is_valid_access,
 | 
			
		||||
	.convert_ctx_access = sk_filter_convert_ctx_access,
 | 
			
		||||
	.is_valid_access = tc_cls_act_is_valid_access,
 | 
			
		||||
	.convert_ctx_access = bpf_net_convert_ctx_access,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct bpf_prog_type_list sk_filter_type __read_mostly = {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -89,7 +89,6 @@ static inline __u32 ipv6_addr_hash(struct __sk_buff *ctx, __u64 off)
 | 
			
		|||
 | 
			
		||||
struct globals {
 | 
			
		||||
	struct flow_keys flow;
 | 
			
		||||
	__u32 nhoff;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct bpf_map_def SEC("maps") percpu_map = {
 | 
			
		||||
| 
						 | 
				
			
			@ -139,7 +138,7 @@ static void update_stats(struct __sk_buff *skb, struct globals *g)
 | 
			
		|||
static __always_inline void parse_ip_proto(struct __sk_buff *skb,
 | 
			
		||||
					   struct globals *g, __u32 ip_proto)
 | 
			
		||||
{
 | 
			
		||||
	__u32 nhoff = g->nhoff;
 | 
			
		||||
	__u32 nhoff = skb->cb[0];
 | 
			
		||||
	int poff;
 | 
			
		||||
 | 
			
		||||
	switch (ip_proto) {
 | 
			
		||||
| 
						 | 
				
			
			@ -165,7 +164,7 @@ static __always_inline void parse_ip_proto(struct __sk_buff *skb,
 | 
			
		|||
		if (gre_flags & GRE_SEQ)
 | 
			
		||||
			nhoff += 4;
 | 
			
		||||
 | 
			
		||||
		g->nhoff = nhoff;
 | 
			
		||||
		skb->cb[0] = nhoff;
 | 
			
		||||
		parse_eth_proto(skb, gre_proto);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -195,7 +194,7 @@ PROG(PARSE_IP)(struct __sk_buff *skb)
 | 
			
		|||
	if (!g)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	nhoff = g->nhoff;
 | 
			
		||||
	nhoff = skb->cb[0];
 | 
			
		||||
 | 
			
		||||
	if (unlikely(ip_is_fragment(skb, nhoff)))
 | 
			
		||||
		return 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -210,7 +209,7 @@ PROG(PARSE_IP)(struct __sk_buff *skb)
 | 
			
		|||
	verlen = load_byte(skb, nhoff + 0/*offsetof(struct iphdr, ihl)*/);
 | 
			
		||||
	nhoff += (verlen & 0xF) << 2;
 | 
			
		||||
 | 
			
		||||
	g->nhoff = nhoff;
 | 
			
		||||
	skb->cb[0] = nhoff;
 | 
			
		||||
	parse_ip_proto(skb, g, ip_proto);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -223,7 +222,7 @@ PROG(PARSE_IPV6)(struct __sk_buff *skb)
 | 
			
		|||
	if (!g)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	nhoff = g->nhoff;
 | 
			
		||||
	nhoff = skb->cb[0];
 | 
			
		||||
 | 
			
		||||
	ip_proto = load_byte(skb,
 | 
			
		||||
			     nhoff + offsetof(struct ipv6hdr, nexthdr));
 | 
			
		||||
| 
						 | 
				
			
			@ -233,25 +232,21 @@ PROG(PARSE_IPV6)(struct __sk_buff *skb)
 | 
			
		|||
				     nhoff + offsetof(struct ipv6hdr, daddr));
 | 
			
		||||
	nhoff += sizeof(struct ipv6hdr);
 | 
			
		||||
 | 
			
		||||
	g->nhoff = nhoff;
 | 
			
		||||
	skb->cb[0] = nhoff;
 | 
			
		||||
	parse_ip_proto(skb, g, ip_proto);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PROG(PARSE_VLAN)(struct __sk_buff *skb)
 | 
			
		||||
{
 | 
			
		||||
	struct globals *g = this_cpu_globals();
 | 
			
		||||
	__u32 nhoff, proto;
 | 
			
		||||
 | 
			
		||||
	if (!g)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	nhoff = g->nhoff;
 | 
			
		||||
	nhoff = skb->cb[0];
 | 
			
		||||
 | 
			
		||||
	proto = load_half(skb, nhoff + offsetof(struct vlan_hdr,
 | 
			
		||||
						h_vlan_encapsulated_proto));
 | 
			
		||||
	nhoff += sizeof(struct vlan_hdr);
 | 
			
		||||
	g->nhoff = nhoff;
 | 
			
		||||
	skb->cb[0] = nhoff;
 | 
			
		||||
 | 
			
		||||
	parse_eth_proto(skb, proto);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -260,17 +255,13 @@ PROG(PARSE_VLAN)(struct __sk_buff *skb)
 | 
			
		|||
 | 
			
		||||
PROG(PARSE_MPLS)(struct __sk_buff *skb)
 | 
			
		||||
{
 | 
			
		||||
	struct globals *g = this_cpu_globals();
 | 
			
		||||
	__u32 nhoff, label;
 | 
			
		||||
 | 
			
		||||
	if (!g)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	nhoff = g->nhoff;
 | 
			
		||||
	nhoff = skb->cb[0];
 | 
			
		||||
 | 
			
		||||
	label = load_word(skb, nhoff);
 | 
			
		||||
	nhoff += sizeof(struct mpls_label);
 | 
			
		||||
	g->nhoff = nhoff;
 | 
			
		||||
	skb->cb[0] = nhoff;
 | 
			
		||||
 | 
			
		||||
	if (label & MPLS_LS_S_MASK) {
 | 
			
		||||
		__u8 verlen = load_byte(skb, nhoff);
 | 
			
		||||
| 
						 | 
				
			
			@ -288,14 +279,10 @@ PROG(PARSE_MPLS)(struct __sk_buff *skb)
 | 
			
		|||
SEC("socket/0")
 | 
			
		||||
int main_prog(struct __sk_buff *skb)
 | 
			
		||||
{
 | 
			
		||||
	struct globals *g = this_cpu_globals();
 | 
			
		||||
	__u32 nhoff = ETH_HLEN;
 | 
			
		||||
	__u32 proto = load_half(skb, 12);
 | 
			
		||||
 | 
			
		||||
	if (!g)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	g->nhoff = nhoff;
 | 
			
		||||
	skb->cb[0] = nhoff;
 | 
			
		||||
	parse_eth_proto(skb, proto);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,6 +29,7 @@ struct bpf_test {
 | 
			
		|||
		ACCEPT,
 | 
			
		||||
		REJECT
 | 
			
		||||
	} result;
 | 
			
		||||
	enum bpf_prog_type prog_type;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct bpf_test tests[] = {
 | 
			
		||||
| 
						 | 
				
			
			@ -743,6 +744,84 @@ static struct bpf_test tests[] = {
 | 
			
		|||
		.errstr = "different pointers",
 | 
			
		||||
		.result = REJECT,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"check skb->mark is not writeable by sockets",
 | 
			
		||||
		.insns = {
 | 
			
		||||
			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
 | 
			
		||||
				    offsetof(struct __sk_buff, mark)),
 | 
			
		||||
			BPF_EXIT_INSN(),
 | 
			
		||||
		},
 | 
			
		||||
		.errstr = "invalid bpf_context access",
 | 
			
		||||
		.result = REJECT,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"check skb->tc_index is not writeable by sockets",
 | 
			
		||||
		.insns = {
 | 
			
		||||
			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
 | 
			
		||||
				    offsetof(struct __sk_buff, tc_index)),
 | 
			
		||||
			BPF_EXIT_INSN(),
 | 
			
		||||
		},
 | 
			
		||||
		.errstr = "invalid bpf_context access",
 | 
			
		||||
		.result = REJECT,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"check non-u32 access to cb",
 | 
			
		||||
		.insns = {
 | 
			
		||||
			BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_1,
 | 
			
		||||
				    offsetof(struct __sk_buff, cb[0])),
 | 
			
		||||
			BPF_EXIT_INSN(),
 | 
			
		||||
		},
 | 
			
		||||
		.errstr = "invalid bpf_context access",
 | 
			
		||||
		.result = REJECT,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"check out of range skb->cb access",
 | 
			
		||||
		.insns = {
 | 
			
		||||
			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
 | 
			
		||||
				    offsetof(struct __sk_buff, cb[60])),
 | 
			
		||||
			BPF_EXIT_INSN(),
 | 
			
		||||
		},
 | 
			
		||||
		.errstr = "invalid bpf_context access",
 | 
			
		||||
		.result = REJECT,
 | 
			
		||||
		.prog_type = BPF_PROG_TYPE_SCHED_ACT,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"write skb fields from socket prog",
 | 
			
		||||
		.insns = {
 | 
			
		||||
			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
 | 
			
		||||
				    offsetof(struct __sk_buff, cb[4])),
 | 
			
		||||
			BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1),
 | 
			
		||||
			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
 | 
			
		||||
				    offsetof(struct __sk_buff, mark)),
 | 
			
		||||
			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
 | 
			
		||||
				    offsetof(struct __sk_buff, tc_index)),
 | 
			
		||||
			BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1),
 | 
			
		||||
			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
 | 
			
		||||
				    offsetof(struct __sk_buff, cb[0])),
 | 
			
		||||
			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
 | 
			
		||||
				    offsetof(struct __sk_buff, cb[2])),
 | 
			
		||||
			BPF_EXIT_INSN(),
 | 
			
		||||
		},
 | 
			
		||||
		.result = ACCEPT,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"write skb fields from tc_cls_act prog",
 | 
			
		||||
		.insns = {
 | 
			
		||||
			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
 | 
			
		||||
				    offsetof(struct __sk_buff, cb[0])),
 | 
			
		||||
			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
 | 
			
		||||
				    offsetof(struct __sk_buff, mark)),
 | 
			
		||||
			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
 | 
			
		||||
				    offsetof(struct __sk_buff, tc_index)),
 | 
			
		||||
			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
 | 
			
		||||
				    offsetof(struct __sk_buff, tc_index)),
 | 
			
		||||
			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
 | 
			
		||||
				    offsetof(struct __sk_buff, cb[3])),
 | 
			
		||||
			BPF_EXIT_INSN(),
 | 
			
		||||
		},
 | 
			
		||||
		.result = ACCEPT,
 | 
			
		||||
		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int probe_filter_length(struct bpf_insn *fp)
 | 
			
		||||
| 
						 | 
				
			
			@ -775,6 +854,7 @@ static int test(void)
 | 
			
		|||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(tests); i++) {
 | 
			
		||||
		struct bpf_insn *prog = tests[i].insns;
 | 
			
		||||
		int prog_type = tests[i].prog_type;
 | 
			
		||||
		int prog_len = probe_filter_length(prog);
 | 
			
		||||
		int *fixup = tests[i].fixup;
 | 
			
		||||
		int map_fd = -1;
 | 
			
		||||
| 
						 | 
				
			
			@ -789,8 +869,8 @@ static int test(void)
 | 
			
		|||
		}
 | 
			
		||||
		printf("#%d %s ", i, tests[i].descr);
 | 
			
		||||
 | 
			
		||||
		prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, prog,
 | 
			
		||||
					prog_len * sizeof(struct bpf_insn),
 | 
			
		||||
		prog_fd = bpf_prog_load(prog_type ?: BPF_PROG_TYPE_SOCKET_FILTER,
 | 
			
		||||
					prog, prog_len * sizeof(struct bpf_insn),
 | 
			
		||||
					"GPL", 0);
 | 
			
		||||
 | 
			
		||||
		if (tests[i].result == ACCEPT) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue