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:
committed by
David S. Miller
parent
3431205e03
commit
d691f9e8d4
+28
-9
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user