Commit 62c7989b authored by Daniel Borkmann's avatar Daniel Borkmann Committed by David S. Miller

bpf: allow b/h/w/dw access for bpf's cb in ctx

When structs are used to store temporary state in cb[] buffer that is
used with programs and among tail calls, then the generated code will
not always access the buffer in bpf_w chunks. We can ease programming
of it and let this act more natural by allowing for aligned b/h/w/dw
sized access for cb[] ctx member. Various test cases are attached as
well for the selftest suite. Potentially, this can also be reused for
other program types to pass data around.
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Acked-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6b8cc1d1
...@@ -3165,10 +3165,14 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) ...@@ -3165,10 +3165,14 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
insn = env->prog->insnsi + delta; insn = env->prog->insnsi + delta;
for (i = 0; i < insn_cnt; i++, insn++) { 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_B) ||
insn->code == (BPF_LDX | BPF_MEM | BPF_H) ||
insn->code == (BPF_LDX | BPF_MEM | BPF_W) ||
insn->code == (BPF_LDX | BPF_MEM | BPF_DW)) insn->code == (BPF_LDX | BPF_MEM | BPF_DW))
type = BPF_READ; type = BPF_READ;
else if (insn->code == (BPF_STX | BPF_MEM | BPF_W) || else if (insn->code == (BPF_STX | BPF_MEM | BPF_B) ||
insn->code == (BPF_STX | BPF_MEM | BPF_H) ||
insn->code == (BPF_STX | BPF_MEM | BPF_W) ||
insn->code == (BPF_STX | BPF_MEM | BPF_DW)) insn->code == (BPF_STX | BPF_MEM | BPF_DW))
type = BPF_WRITE; type = BPF_WRITE;
else else
......
...@@ -2776,11 +2776,33 @@ static bool __is_valid_access(int off, int size) ...@@ -2776,11 +2776,33 @@ static bool __is_valid_access(int off, int size)
{ {
if (off < 0 || off >= sizeof(struct __sk_buff)) if (off < 0 || off >= sizeof(struct __sk_buff))
return false; return false;
/* The verifier guarantees that size > 0. */ /* The verifier guarantees that size > 0. */
if (off % size != 0) if (off % size != 0)
return false; return false;
if (size != sizeof(__u32))
return false; switch (off) {
case offsetof(struct __sk_buff, cb[0]) ...
offsetof(struct __sk_buff, cb[4]) + sizeof(__u32) - 1:
if (size == sizeof(__u16) &&
off > offsetof(struct __sk_buff, cb[4]) + sizeof(__u16))
return false;
if (size == sizeof(__u32) &&
off > offsetof(struct __sk_buff, cb[4]))
return false;
if (size == sizeof(__u64) &&
off > offsetof(struct __sk_buff, cb[2]))
return false;
if (size != sizeof(__u8) &&
size != sizeof(__u16) &&
size != sizeof(__u32) &&
size != sizeof(__u64))
return false;
break;
default:
if (size != sizeof(__u32))
return false;
}
return true; return true;
} }
...@@ -2799,7 +2821,7 @@ static bool sk_filter_is_valid_access(int off, int size, ...@@ -2799,7 +2821,7 @@ static bool sk_filter_is_valid_access(int off, int size,
if (type == BPF_WRITE) { if (type == BPF_WRITE) {
switch (off) { switch (off) {
case offsetof(struct __sk_buff, cb[0]) ... case offsetof(struct __sk_buff, cb[0]) ...
offsetof(struct __sk_buff, cb[4]): offsetof(struct __sk_buff, cb[4]) + sizeof(__u32) - 1:
break; break;
default: default:
return false; return false;
...@@ -2823,7 +2845,7 @@ static bool lwt_is_valid_access(int off, int size, ...@@ -2823,7 +2845,7 @@ static bool lwt_is_valid_access(int off, int size,
case offsetof(struct __sk_buff, mark): case offsetof(struct __sk_buff, mark):
case offsetof(struct __sk_buff, priority): case offsetof(struct __sk_buff, priority):
case offsetof(struct __sk_buff, cb[0]) ... case offsetof(struct __sk_buff, cb[0]) ...
offsetof(struct __sk_buff, cb[4]): offsetof(struct __sk_buff, cb[4]) + sizeof(__u32) - 1:
break; break;
default: default:
return false; return false;
...@@ -2915,7 +2937,7 @@ static bool tc_cls_act_is_valid_access(int off, int size, ...@@ -2915,7 +2937,7 @@ static bool tc_cls_act_is_valid_access(int off, int size,
case offsetof(struct __sk_buff, tc_index): case offsetof(struct __sk_buff, tc_index):
case offsetof(struct __sk_buff, priority): case offsetof(struct __sk_buff, priority):
case offsetof(struct __sk_buff, cb[0]) ... case offsetof(struct __sk_buff, cb[0]) ...
offsetof(struct __sk_buff, cb[4]): offsetof(struct __sk_buff, cb[4]) + sizeof(__u32) - 1:
case offsetof(struct __sk_buff, tc_classid): case offsetof(struct __sk_buff, tc_classid):
break; break;
default: default:
...@@ -3066,8 +3088,11 @@ static u32 sk_filter_convert_ctx_access(enum bpf_access_type type, ...@@ -3066,8 +3088,11 @@ static u32 sk_filter_convert_ctx_access(enum bpf_access_type type,
si->dst_reg, si->src_reg, insn); si->dst_reg, si->src_reg, insn);
case offsetof(struct __sk_buff, cb[0]) ... case offsetof(struct __sk_buff, cb[0]) ...
offsetof(struct __sk_buff, cb[4]): offsetof(struct __sk_buff, cb[4]) + sizeof(__u32) - 1:
BUILD_BUG_ON(FIELD_SIZEOF(struct qdisc_skb_cb, data) < 20); BUILD_BUG_ON(FIELD_SIZEOF(struct qdisc_skb_cb, data) < 20);
BUILD_BUG_ON((offsetof(struct sk_buff, cb) +
offsetof(struct qdisc_skb_cb, data)) %
sizeof(__u64));
prog->cb_access = 1; prog->cb_access = 1;
off = si->off; off = si->off;
...@@ -3075,10 +3100,10 @@ static u32 sk_filter_convert_ctx_access(enum bpf_access_type type, ...@@ -3075,10 +3100,10 @@ static u32 sk_filter_convert_ctx_access(enum bpf_access_type type,
off += offsetof(struct sk_buff, cb); off += offsetof(struct sk_buff, cb);
off += offsetof(struct qdisc_skb_cb, data); off += offsetof(struct qdisc_skb_cb, data);
if (type == BPF_WRITE) if (type == BPF_WRITE)
*insn++ = BPF_STX_MEM(BPF_W, si->dst_reg, *insn++ = BPF_STX_MEM(BPF_SIZE(si->code), si->dst_reg,
si->src_reg, off); si->src_reg, off);
else else
*insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, *insn++ = BPF_LDX_MEM(BPF_SIZE(si->code), si->dst_reg,
si->src_reg, off); si->src_reg, off);
break; break;
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment