* [PATCH bpf-next 0/2] propagate nullness information for reg to reg comparisons
@ 2022-08-26 17:29 Eduard Zingerman
2022-08-26 17:29 ` [PATCH bpf-next 1/2] bpf: " Eduard Zingerman
2022-08-26 17:29 ` [PATCH bpf-next 2/2] selftests/bpf: check nullness propagation " Eduard Zingerman
0 siblings, 2 replies; 11+ messages in thread
From: Eduard Zingerman @ 2022-08-26 17:29 UTC (permalink / raw)
To: bpf, ast, andrii, daniel, kernel-team, yhs, john.fastabend
Cc: Eduard Zingerman
Changes since RFC:
- newly added if block in `check_cond_jmp_op` is moved down to keep
`make_ptr_not_null_reg` actions together;
- tests rewritten to have a single `r0 = 0; exit;` block.
Original message follows:
Hi Everyone,
This patchset adds ability to propagates nullness information for
branches of register to register equality compare instructions. The
following rules are used:
- suppose register A maybe null
- suppose register B is not null
- for JNE A, B, ... - A is not null in the false branch
- for JEQ A, B, ... - A is not null in the true branch
E.g. for program like below:
r6 = skb->sk;
r7 = sk_fullsock(r6);
r0 = sk_fullsock(r6);
if (r0 == 0) return 0; (a)
if (r0 != r7) return 0; (b)
*r7->type; (c)
return 0;
It is safe to dereference r7 at point (c), because of (a) and (b).
The utility of this change came up while working on BPF CLang backend
issue [1]. Specifically, while debugging issue with selftest
`test_sk_lookup.c`. This test has the following structure:
int access_ctx_sk(struct bpf_sk_lookup *ctx __CTX__)
{
struct bpf_sock *sk1 = NULL, *sk2 = NULL;
...
sk1 = bpf_map_lookup_elem(&redir_map, &KEY_SERVER_A);
if (!sk1) // (a)
goto out;
...
if (ctx->sk != sk1) // (b)
goto out;
...
if (ctx->sk->family != AF_INET || // (c)
ctx->sk->type != SOCK_STREAM ||
ctx->sk->state != BPF_TCP_LISTEN)
goto out;
...
}
- at (a) `sk1` is checked to be not null;
- at (b) `ctx->sk` is verified to be equal to `sk1`;
- at (c) `ctx->sk` is accessed w/o nullness check.
Currently Global Value Numbering pass considers expressions `sk1` and
`ctx->sk` to be identical at point (c) and replaces `ctx->sk` with
`sk1` (not expressions themselves but corresponding SSA values).
Since `sk1` is known to be not null after (b) verifier allows
execution of the program.
However, such optimization is not guaranteed to happen. When it does
not happen verifier reports an error.
[1] https://reviews.llvm.org/D131633#3722231
Thanks,
Eduard
Eduard Zingerman (2):
bpf: propagate nullness information for reg to reg comparisons
selftests/bpf: check nullness propagation for reg to reg comparisons
kernel/bpf/verifier.c | 41 ++++-
.../bpf/verifier/jeq_infer_not_null.c | 166 ++++++++++++++++++
2 files changed, 205 insertions(+), 2 deletions(-)
create mode 100644 tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
--
2.37.2
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH bpf-next 1/2] bpf: propagate nullness information for reg to reg comparisons
2022-08-26 17:29 [PATCH bpf-next 0/2] propagate nullness information for reg to reg comparisons Eduard Zingerman
@ 2022-08-26 17:29 ` Eduard Zingerman
2022-08-29 14:23 ` Daniel Borkmann
2022-08-26 17:29 ` [PATCH bpf-next 2/2] selftests/bpf: check nullness propagation " Eduard Zingerman
1 sibling, 1 reply; 11+ messages in thread
From: Eduard Zingerman @ 2022-08-26 17:29 UTC (permalink / raw)
To: bpf, ast, andrii, daniel, kernel-team, yhs, john.fastabend
Cc: Eduard Zingerman
Propagate nullness information for branches of register to register
equality compare instructions. The following rules are used:
- suppose register A maybe null
- suppose register B is not null
- for JNE A, B, ... - A is not null in the false branch
- for JEQ A, B, ... - A is not null in the true branch
E.g. for program like below:
r6 = skb->sk;
r7 = sk_fullsock(r6);
r0 = sk_fullsock(r6);
if (r0 == 0) return 0; (a)
if (r0 != r7) return 0; (b)
*r7->type; (c)
return 0;
It is safe to dereference r7 at point (c), because of (a) and (b).
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Acked-by: Yonghong Song <yhs@fb.com>
---
kernel/bpf/verifier.c | 41 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 39 insertions(+), 2 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 0194a36d0b36..7585288e035b 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -472,6 +472,11 @@ static bool type_may_be_null(u32 type)
return type & PTR_MAYBE_NULL;
}
+static bool type_is_pointer(enum bpf_reg_type type)
+{
+ return type != NOT_INIT && type != SCALAR_VALUE;
+}
+
static bool is_acquire_function(enum bpf_func_id func_id,
const struct bpf_map *map)
{
@@ -10064,6 +10069,7 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
struct bpf_verifier_state *other_branch;
struct bpf_reg_state *regs = this_branch->frame[this_branch->curframe]->regs;
struct bpf_reg_state *dst_reg, *other_branch_regs, *src_reg = NULL;
+ struct bpf_reg_state *eq_branch_regs;
u8 opcode = BPF_OP(insn->code);
bool is_jmp32;
int pred = -1;
@@ -10173,8 +10179,8 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
/* detect if we are comparing against a constant value so we can adjust
* our min/max values for our dst register.
* this is only legit if both are scalars (or pointers to the same
- * object, I suppose, but we don't support that right now), because
- * otherwise the different base pointers mean the offsets aren't
+ * object, I suppose, see the PTR_MAYBE_NULL related if block below),
+ * because otherwise the different base pointers mean the offsets aren't
* comparable.
*/
if (BPF_SRC(insn->code) == BPF_X) {
@@ -10223,6 +10229,37 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
find_equal_scalars(other_branch, &other_branch_regs[insn->dst_reg]);
}
+ /* if one pointer register is compared to another pointer
+ * register check if PTR_MAYBE_NULL could be lifted.
+ * E.g. register A - maybe null
+ * register B - not null
+ * for JNE A, B, ... - A is not null in the false branch;
+ * for JEQ A, B, ... - A is not null in the true branch.
+ */
+ if (!is_jmp32 &&
+ BPF_SRC(insn->code) == BPF_X &&
+ type_is_pointer(src_reg->type) && type_is_pointer(dst_reg->type) &&
+ type_may_be_null(src_reg->type) != type_may_be_null(dst_reg->type)) {
+ eq_branch_regs = NULL;
+ switch (opcode) {
+ case BPF_JEQ:
+ eq_branch_regs = other_branch_regs;
+ break;
+ case BPF_JNE:
+ eq_branch_regs = regs;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ if (eq_branch_regs) {
+ if (type_may_be_null(src_reg->type))
+ mark_ptr_not_null_reg(&eq_branch_regs[insn->src_reg]);
+ else
+ mark_ptr_not_null_reg(&eq_branch_regs[insn->dst_reg]);
+ }
+ }
+
/* detect if R == 0 where R is returned from bpf_map_lookup_elem().
* NOTE: these optimizations below are related with pointer comparison
* which will never be JMP32.
--
2.37.2
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH bpf-next 2/2] selftests/bpf: check nullness propagation for reg to reg comparisons
2022-08-26 17:29 [PATCH bpf-next 0/2] propagate nullness information for reg to reg comparisons Eduard Zingerman
2022-08-26 17:29 ` [PATCH bpf-next 1/2] bpf: " Eduard Zingerman
@ 2022-08-26 17:29 ` Eduard Zingerman
2022-11-14 18:01 ` Alexei Starovoitov
1 sibling, 1 reply; 11+ messages in thread
From: Eduard Zingerman @ 2022-08-26 17:29 UTC (permalink / raw)
To: bpf, ast, andrii, daniel, kernel-team, yhs, john.fastabend
Cc: Eduard Zingerman
Verify that nullness information is porpagated in the branches of
register to register JEQ and JNE operations.
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Acked-by: Yonghong Song <yhs@fb.com>
---
.../bpf/verifier/jeq_infer_not_null.c | 166 ++++++++++++++++++
1 file changed, 166 insertions(+)
create mode 100644 tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
diff --git a/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c b/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
new file mode 100644
index 000000000000..d73f6198d544
--- /dev/null
+++ b/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
@@ -0,0 +1,166 @@
+{
+ /* This is equivalent to the following program:
+ *
+ * r6 = skb->sk;
+ * r7 = sk_fullsock(r6);
+ * r0 = sk_fullsock(r6);
+ * if (r0 == 0) return 0; (a)
+ * if (r0 != r7) return 0; (b)
+ * *r7->type; (c)
+ * return 0;
+ *
+ * It is safe to dereference r7 at point (c), because of (a) and (b).
+ * The test verifies that relation r0 == r7 is propagated from (b) to (c).
+ */
+ "jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL -> PTR_TO_SOCKET for JNE false branch",
+ .insns = {
+ /* r6 = skb->sk; */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+ /* if (r6 == 0) return 0; */
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 8),
+ /* r7 = sk_fullsock(skb); */
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+ BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+ BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),
+ /* r0 = sk_fullsock(skb); */
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+ BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+ /* if (r0 == null) return 0; */
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
+ /* if (r0 == r7) r0 = *(r7->type); */
+ BPF_JMP_REG(BPF_JNE, BPF_REG_0, BPF_REG_7, 1), /* Use ! JNE ! */
+ BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, offsetof(struct bpf_sock, type)),
+ /* return 0 */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .result = ACCEPT,
+},
+{
+ /* Same as above, but verify that another branch of JNE still
+ * prohibits access to PTR_MAYBE_NULL.
+ */
+ "jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL unchanged for JNE true branch",
+ .insns = {
+ /* r6 = skb->sk */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+ /* if (r6 == 0) return 0; */
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 9),
+ /* r7 = sk_fullsock(skb); */
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+ BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+ BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),
+ /* r0 = sk_fullsock(skb); */
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+ BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+ /* if (r0 == null) return 0; */
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 3),
+ /* if (r0 == r7) return 0; */
+ BPF_JMP_REG(BPF_JNE, BPF_REG_0, BPF_REG_7, 1), /* Use ! JNE ! */
+ BPF_JMP_IMM(BPF_JA, 0, 0, 1),
+ /* r0 = *(r7->type); */
+ BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, offsetof(struct bpf_sock, type)),
+ /* return 0 */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .result = REJECT,
+ .errstr = "R7 invalid mem access 'sock_or_null'",
+},
+{
+ /* Same as a first test, but not null should be inferred for JEQ branch */
+ "jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL -> PTR_TO_SOCKET for JEQ true branch",
+ .insns = {
+ /* r6 = skb->sk; */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+ /* if (r6 == null) return 0; */
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 9),
+ /* r7 = sk_fullsock(skb); */
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+ BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+ BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),
+ /* r0 = sk_fullsock(skb); */
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+ BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+ /* if (r0 == null) return 0; */
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3),
+ /* if (r0 != r7) return 0; */
+ BPF_JMP_REG(BPF_JEQ, BPF_REG_0, BPF_REG_7, 1), /* Use ! JEQ ! */
+ BPF_JMP_IMM(BPF_JA, 0, 0, 1),
+ /* r0 = *(r7->type); */
+ BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, offsetof(struct bpf_sock, type)),
+ /* return 0; */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .result = ACCEPT,
+},
+{
+ /* Same as above, but verify that another branch of JNE still
+ * prohibits access to PTR_MAYBE_NULL.
+ */
+ "jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL unchanged for JEQ false branch",
+ .insns = {
+ /* r6 = skb->sk; */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, offsetof(struct __sk_buff, sk)),
+ /* if (r6 == null) return 0; */
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 8),
+ /* r7 = sk_fullsock(skb); */
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+ BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+ BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),
+ /* r0 = sk_fullsock(skb); */
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+ BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
+ /* if (r0 == null) return 0; */
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
+ /* if (r0 != r7) r0 = *(r7->type); */
+ BPF_JMP_REG(BPF_JEQ, BPF_REG_0, BPF_REG_7, 1), /* Use ! JEQ ! */
+ BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, offsetof(struct bpf_sock, type)),
+ /* return 0; */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .result = REJECT,
+ .errstr = "R7 invalid mem access 'sock_or_null'",
+},
+{
+ /* Maps are treated in a different branch of `mark_ptr_not_null_reg`,
+ * so separate test for maps case.
+ */
+ "jne/jeq infer not null, PTR_TO_MAP_VALUE_OR_NULL -> PTR_TO_MAP_VALUE",
+ .insns = {
+ /* r9 = &some stack to use as key */
+ BPF_ST_MEM(BPF_W, BPF_REG_10, -8, 0),
+ BPF_MOV64_REG(BPF_REG_9, BPF_REG_10),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_9, -8),
+ /* r8 = process local map */
+ BPF_LD_MAP_FD(BPF_REG_8, 0),
+ /* r6 = map_lookup_elem(r8, r9); */
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_8),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_9),
+ BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+ /* r7 = map_lookup_elem(r8, r9); */
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_8),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_9),
+ BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
+ BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),
+ /* if (r6 == 0) return 0; */
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 2),
+ /* if (r6 != r7) return 0; */
+ BPF_JMP_REG(BPF_JNE, BPF_REG_6, BPF_REG_7, 1),
+ /* read *r7; */
+ BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_7, offsetof(struct bpf_xdp_sock, queue_id)),
+ /* return 0; */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .fixup_map_xskmap = { 3 },
+ .prog_type = BPF_PROG_TYPE_XDP,
+ .result = ACCEPT,
+},
--
2.37.2
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH bpf-next 1/2] bpf: propagate nullness information for reg to reg comparisons
2022-08-26 17:29 ` [PATCH bpf-next 1/2] bpf: " Eduard Zingerman
@ 2022-08-29 14:23 ` Daniel Borkmann
2022-08-30 10:41 ` Eduard Zingerman
0 siblings, 1 reply; 11+ messages in thread
From: Daniel Borkmann @ 2022-08-29 14:23 UTC (permalink / raw)
To: Eduard Zingerman, bpf, ast, andrii, kernel-team, yhs,
john.fastabend
On 8/26/22 7:29 PM, Eduard Zingerman wrote:
> Propagate nullness information for branches of register to register
> equality compare instructions. The following rules are used:
> - suppose register A maybe null
> - suppose register B is not null
> - for JNE A, B, ... - A is not null in the false branch
> - for JEQ A, B, ... - A is not null in the true branch
>
[...]
> kernel/bpf/verifier.c | 41 +++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 39 insertions(+), 2 deletions(-)
>
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 0194a36d0b36..7585288e035b 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -472,6 +472,11 @@ static bool type_may_be_null(u32 type)
> return type & PTR_MAYBE_NULL;
> }
>
> +static bool type_is_pointer(enum bpf_reg_type type)
> +{
> + return type != NOT_INIT && type != SCALAR_VALUE;
> +}
We also have is_pointer_value(), semantics there are a bit different (and mainly to
prevent leakage under unpriv), but I wonder if this can be refactored to accommodate
both. My worry is that if in future we extend one but not the other bugs might slip
in.
> static bool is_acquire_function(enum bpf_func_id func_id,
> const struct bpf_map *map)
> {
> @@ -10064,6 +10069,7 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
> struct bpf_verifier_state *other_branch;
> struct bpf_reg_state *regs = this_branch->frame[this_branch->curframe]->regs;
> struct bpf_reg_state *dst_reg, *other_branch_regs, *src_reg = NULL;
> + struct bpf_reg_state *eq_branch_regs;
> u8 opcode = BPF_OP(insn->code);
> bool is_jmp32;
> int pred = -1;
> @@ -10173,8 +10179,8 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
> /* detect if we are comparing against a constant value so we can adjust
> * our min/max values for our dst register.
> * this is only legit if both are scalars (or pointers to the same
> - * object, I suppose, but we don't support that right now), because
> - * otherwise the different base pointers mean the offsets aren't
> + * object, I suppose, see the PTR_MAYBE_NULL related if block below),
> + * because otherwise the different base pointers mean the offsets aren't
> * comparable.
> */
> if (BPF_SRC(insn->code) == BPF_X) {
> @@ -10223,6 +10229,37 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
> find_equal_scalars(other_branch, &other_branch_regs[insn->dst_reg]);
> }
>
> + /* if one pointer register is compared to another pointer
> + * register check if PTR_MAYBE_NULL could be lifted.
> + * E.g. register A - maybe null
> + * register B - not null
> + * for JNE A, B, ... - A is not null in the false branch;
> + * for JEQ A, B, ... - A is not null in the true branch.
> + */
> + if (!is_jmp32 &&
> + BPF_SRC(insn->code) == BPF_X &&
> + type_is_pointer(src_reg->type) && type_is_pointer(dst_reg->type) &&
> + type_may_be_null(src_reg->type) != type_may_be_null(dst_reg->type)) {
> + eq_branch_regs = NULL;
> + switch (opcode) {
> + case BPF_JEQ:
> + eq_branch_regs = other_branch_regs;
> + break;
> + case BPF_JNE:
> + eq_branch_regs = regs;
> + break;
> + default:
> + /* do nothing */
> + break;
> + }
> + if (eq_branch_regs) {
> + if (type_may_be_null(src_reg->type))
> + mark_ptr_not_null_reg(&eq_branch_regs[insn->src_reg]);
> + else
> + mark_ptr_not_null_reg(&eq_branch_regs[insn->dst_reg]);
> + }
> + }
> +
Could we consolidate the logic above with the one below which deals with R == 0 checks?
There are some similarities, e.g. !is_jmp32, both test for jeq/jne and while one is based
on K, the other one on X, though we could also add check X == 0 for below. Anyway, just
a though that it may be nice to consolidate the handling.
> /* detect if R == 0 where R is returned from bpf_map_lookup_elem().
> * NOTE: these optimizations below are related with pointer comparison
> * which will never be JMP32.
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH bpf-next 1/2] bpf: propagate nullness information for reg to reg comparisons
2022-08-29 14:23 ` Daniel Borkmann
@ 2022-08-30 10:41 ` Eduard Zingerman
2022-09-01 8:13 ` Shung-Hsi Yu
0 siblings, 1 reply; 11+ messages in thread
From: Eduard Zingerman @ 2022-08-30 10:41 UTC (permalink / raw)
To: Daniel Borkmann, bpf, ast, andrii, kernel-team, yhs,
john.fastabend
Hi Daniel,
Thank you for commenting.
> On Mon, 2022-08-29 at 16:23 +0200, Daniel Borkmann wrote:
> [...]
> > kernel/bpf/verifier.c | 41 +++++++++++++++++++++++++++++++++++++++--
> > 1 file changed, 39 insertions(+), 2 deletions(-)
> >
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 0194a36d0b36..7585288e035b 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -472,6 +472,11 @@ static bool type_may_be_null(u32 type)
> > return type & PTR_MAYBE_NULL;
> > }
> >
> > +static bool type_is_pointer(enum bpf_reg_type type)
> > +{
> > + return type != NOT_INIT && type != SCALAR_VALUE;
> > +}
>
> We also have is_pointer_value(), semantics there are a bit different (and mainly to
> prevent leakage under unpriv), but I wonder if this can be refactored to accommodate
> both. My worry is that if in future we extend one but not the other bugs might slip
> in.
John was concerned about this as well, guess I won't not dodging it :)
Suppose I do the following modification:
static bool type_is_pointer(enum bpf_reg_type type)
{
return type != NOT_INIT && type != SCALAR_VALUE;
}
static bool __is_pointer_value(bool allow_ptr_leaks,
const struct bpf_reg_state *reg)
{
if (allow_ptr_leaks)
return false;
- return reg->type != SCALAR_VALUE;
+ return type_is_pointer(reg->type);
}
And check if there are test cases that have to be added because of the
change in the __is_pointer_value behavior (it does not check for
`NOT_INIT` right now). Does this sound like a plan?
[...]
> Could we consolidate the logic above with the one below which deals with R == 0 checks?
> There are some similarities, e.g. !is_jmp32, both test for jeq/jne and while one is based
> on K, the other one on X, though we could also add check X == 0 for below. Anyway, just
> a though that it may be nice to consolidate the handling.
Ok, I will try to consolidate those.
Thanks,
Eduard
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH bpf-next 1/2] bpf: propagate nullness information for reg to reg comparisons
2022-08-30 10:41 ` Eduard Zingerman
@ 2022-09-01 8:13 ` Shung-Hsi Yu
2022-09-01 9:01 ` Shung-Hsi Yu
0 siblings, 1 reply; 11+ messages in thread
From: Shung-Hsi Yu @ 2022-09-01 8:13 UTC (permalink / raw)
To: Eduard Zingerman
Cc: Daniel Borkmann, bpf, ast, andrii, kernel-team, yhs,
john.fastabend
On Tue, Aug 30, 2022 at 01:41:28PM +0300, Eduard Zingerman wrote:
> Hi Daniel,
>
> Thank you for commenting.
>
> > On Mon, 2022-08-29 at 16:23 +0200, Daniel Borkmann wrote:
> > [...]
> > > kernel/bpf/verifier.c | 41 +++++++++++++++++++++++++++++++++++++++--
> > > 1 file changed, 39 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > > index 0194a36d0b36..7585288e035b 100644
> > > --- a/kernel/bpf/verifier.c
> > > +++ b/kernel/bpf/verifier.c
> > > @@ -472,6 +472,11 @@ static bool type_may_be_null(u32 type)
> > > return type & PTR_MAYBE_NULL;
> > > }
> > >
> > > +static bool type_is_pointer(enum bpf_reg_type type)
> > > +{
> > > + return type != NOT_INIT && type != SCALAR_VALUE;
> > > +}
> >
> > We also have is_pointer_value(), semantics there are a bit different (and mainly to
> > prevent leakage under unpriv), but I wonder if this can be refactored to accommodate
> > both. My worry is that if in future we extend one but not the other bugs might slip
> > in.
>
> John was concerned about this as well, guess I won't not dodging it :)
> Suppose I do the following modification:
>
> static bool type_is_pointer(enum bpf_reg_type type)
> {
> return type != NOT_INIT && type != SCALAR_VALUE;
> }
>
> static bool __is_pointer_value(bool allow_ptr_leaks,
> const struct bpf_reg_state *reg)
> {
> if (allow_ptr_leaks)
> return false;
>
> - return reg->type != SCALAR_VALUE;
> + return type_is_pointer(reg->type);
> }
The verifier is using the wrapped is_pointer_value() to guard against
pointer leak.
static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regno,
int off, int bpf_size, enum bpf_access_type t,
int value_regno, bool strict_alignment_once)
{
...
if (reg->type == PTR_TO_MAP_KEY) {
...
} else if (reg->type == PTR_TO_MAP_VALUE) {
struct bpf_map_value_off_desc *kptr_off_desc = NULL;
if (t == BPF_WRITE && value_regno >= 0 &&
is_pointer_value(env, value_regno)) {
verbose(env, "R%d leaks addr into map\n", value_regno);
return -EACCES;
...
}
...
}
In the check_mem_access() case the semantic of is_pointer_value() is check
whether or not the value *might* be a pointer, and since NON_INIT can be
potentially anything, it should not be excluded.
Since the use case seems different, perhaps we could split them up, e.g. a
maybe_pointer_value() and a is_pointer_value(), or something along that
line.
The former is equivalent to type != SCALAR_VALUE, and the latter equivalent
to type != NOT_INIT && type != SCALAR_VALUE. The latter can be used here for
implementing nullness propogation.
> And check if there are test cases that have to be added because of the
> change in the __is_pointer_value behavior (it does not check for
> `NOT_INIT` right now). Does this sound like a plan?
>
> [...]
> > Could we consolidate the logic above with the one below which deals with R == 0 checks?
> > There are some similarities, e.g. !is_jmp32, both test for jeq/jne and while one is based
> > on K, the other one on X, though we could also add check X == 0 for below. Anyway, just
> > a though that it may be nice to consolidate the handling.
>
> Ok, I will try to consolidate those.
>
> Thanks,
> Eduard
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH bpf-next 1/2] bpf: propagate nullness information for reg to reg comparisons
2022-09-01 8:13 ` Shung-Hsi Yu
@ 2022-09-01 9:01 ` Shung-Hsi Yu
2022-10-27 22:18 ` Eduard Zingerman
0 siblings, 1 reply; 11+ messages in thread
From: Shung-Hsi Yu @ 2022-09-01 9:01 UTC (permalink / raw)
To: Eduard Zingerman
Cc: Daniel Borkmann, bpf, ast, andrii, kernel-team, yhs,
john.fastabend
On Thu, Sep 01, 2022 at 04:13:22PM +0800, Shung-Hsi Yu wrote:
> On Tue, Aug 30, 2022 at 01:41:28PM +0300, Eduard Zingerman wrote:
> > Hi Daniel,
> >
> > Thank you for commenting.
> >
> > > On Mon, 2022-08-29 at 16:23 +0200, Daniel Borkmann wrote:
> > > [...]
> > > > kernel/bpf/verifier.c | 41 +++++++++++++++++++++++++++++++++++++++--
> > > > 1 file changed, 39 insertions(+), 2 deletions(-)
> > > >
> > > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > > > index 0194a36d0b36..7585288e035b 100644
> > > > --- a/kernel/bpf/verifier.c
> > > > +++ b/kernel/bpf/verifier.c
> > > > @@ -472,6 +472,11 @@ static bool type_may_be_null(u32 type)
> > > > return type & PTR_MAYBE_NULL;
> > > > }
> > > >
> > > > +static bool type_is_pointer(enum bpf_reg_type type)
> > > > +{
> > > > + return type != NOT_INIT && type != SCALAR_VALUE;
> > > > +}
> > >
> > > We also have is_pointer_value(), semantics there are a bit different (and mainly to
> > > prevent leakage under unpriv), but I wonder if this can be refactored to accommodate
> > > both. My worry is that if in future we extend one but not the other bugs might slip
> > > in.
> >
> > John was concerned about this as well, guess I won't not dodging it :)
> > Suppose I do the following modification:
> >
> > static bool type_is_pointer(enum bpf_reg_type type)
> > {
> > return type != NOT_INIT && type != SCALAR_VALUE;
> > }
> >
> > static bool __is_pointer_value(bool allow_ptr_leaks,
> > const struct bpf_reg_state *reg)
> > {
> > if (allow_ptr_leaks)
> > return false;
> >
> > - return reg->type != SCALAR_VALUE;
> > + return type_is_pointer(reg->type);
> > }
>
> The verifier is using the wrapped is_pointer_value() to guard against
> pointer leak.
>
> static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regno,
> int off, int bpf_size, enum bpf_access_type t,
> int value_regno, bool strict_alignment_once)
> {
> ...
> if (reg->type == PTR_TO_MAP_KEY) {
> ...
> } else if (reg->type == PTR_TO_MAP_VALUE) {
> struct bpf_map_value_off_desc *kptr_off_desc = NULL;
>
> if (t == BPF_WRITE && value_regno >= 0 &&
> is_pointer_value(env, value_regno)) {
> verbose(env, "R%d leaks addr into map\n", value_regno);
> return -EACCES;
> ...
> }
> ...
> }
>
> In the check_mem_access() case the semantic of is_pointer_value() is check
> whether or not the value *might* be a pointer, and since NON_INIT can be
> potentially anything, it should not be excluded.
I wasn't reading the threads carefully enough, apologies, just realized
Daniel had already mention the above point further up.
Also, after going back to the previous RFC thread I saw John mention that
after making the is_pointer_value() changes to exclude NOT_INIT, the tests
still passes.
I guess that comes down to how the verifier rigorously check that the
registers are not NOT_INIT using check_reg_arg(..., SRC_OP), before moving
on to more specific checks. So I'm a bit less sure about the split
{maybe,is}_pointer_value() approach proposed below now.
> Since the use case seems different, perhaps we could split them up, e.g. a
> maybe_pointer_value() and a is_pointer_value(), or something along that
> line.
>
> The former is equivalent to type != SCALAR_VALUE, and the latter equivalent
> to type != NOT_INIT && type != SCALAR_VALUE. The latter can be used here for
> implementing nullness propogation.
>
> > And check if there are test cases that have to be added because of the
> > change in the __is_pointer_value behavior (it does not check for
> > `NOT_INIT` right now). Does this sound like a plan?
> >
> > [...]
> > > Could we consolidate the logic above with the one below which deals with R == 0 checks?
> > > There are some similarities, e.g. !is_jmp32, both test for jeq/jne and while one is based
> > > on K, the other one on X, though we could also add check X == 0 for below. Anyway, just
> > > a though that it may be nice to consolidate the handling.
> >
> > Ok, I will try to consolidate those.
> >
> > Thanks,
> > Eduard
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH bpf-next 1/2] bpf: propagate nullness information for reg to reg comparisons
2022-09-01 9:01 ` Shung-Hsi Yu
@ 2022-10-27 22:18 ` Eduard Zingerman
0 siblings, 0 replies; 11+ messages in thread
From: Eduard Zingerman @ 2022-10-27 22:18 UTC (permalink / raw)
To: Shung-Hsi Yu
Cc: Daniel Borkmann, bpf, ast, andrii, kernel-team, yhs,
john.fastabend
On Thu, 2022-09-01 at 17:01 +0800, Shung-Hsi Yu wrote:
> On Thu, Sep 01, 2022 at 04:13:22PM +0800, Shung-Hsi Yu wrote:
> > On Tue, Aug 30, 2022 at 01:41:28PM +0300, Eduard Zingerman wrote:
> > > Hi Daniel,
> > >
> > > Thank you for commenting.
> > >
> > > > On Mon, 2022-08-29 at 16:23 +0200, Daniel Borkmann wrote:
> > > > [...]
> > > > > kernel/bpf/verifier.c | 41 +++++++++++++++++++++++++++++++++++++++--
> > > > > 1 file changed, 39 insertions(+), 2 deletions(-)
> > > > >
> > > > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > > > > index 0194a36d0b36..7585288e035b 100644
> > > > > --- a/kernel/bpf/verifier.c
> > > > > +++ b/kernel/bpf/verifier.c
> > > > > @@ -472,6 +472,11 @@ static bool type_may_be_null(u32 type)
> > > > > return type & PTR_MAYBE_NULL;
> > > > > }
> > > > >
> > > > > +static bool type_is_pointer(enum bpf_reg_type type)
> > > > > +{
> > > > > + return type != NOT_INIT && type != SCALAR_VALUE;
> > > > > +}
> > > >
> > > > We also have is_pointer_value(), semantics there are a bit different (and mainly to
> > > > prevent leakage under unpriv), but I wonder if this can be refactored to accommodate
> > > > both. My worry is that if in future we extend one but not the other bugs might slip
> > > > in.
> > >
> > > John was concerned about this as well, guess I won't not dodging it :)
> > > Suppose I do the following modification:
> > >
> > > static bool type_is_pointer(enum bpf_reg_type type)
> > > {
> > > return type != NOT_INIT && type != SCALAR_VALUE;
> > > }
> > >
> > > static bool __is_pointer_value(bool allow_ptr_leaks,
> > > const struct bpf_reg_state *reg)
> > > {
> > > if (allow_ptr_leaks)
> > > return false;
> > >
> > > - return reg->type != SCALAR_VALUE;
> > > + return type_is_pointer(reg->type);
> > > }
> >
> > The verifier is using the wrapped is_pointer_value() to guard against
> > pointer leak.
> >
> > static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regno,
> > int off, int bpf_size, enum bpf_access_type t,
> > int value_regno, bool strict_alignment_once)
> > {
> > ...
> > if (reg->type == PTR_TO_MAP_KEY) {
> > ...
> > } else if (reg->type == PTR_TO_MAP_VALUE) {
> > struct bpf_map_value_off_desc *kptr_off_desc = NULL;
> >
> > if (t == BPF_WRITE && value_regno >= 0 &&
> > is_pointer_value(env, value_regno)) {
> > verbose(env, "R%d leaks addr into map\n", value_regno);
> > return -EACCES;
> > ...
> > }
> > ...
> > }
> >
> > In the check_mem_access() case the semantic of is_pointer_value() is check
> > whether or not the value *might* be a pointer, and since NON_INIT can be
> > potentially anything, it should not be excluded.
>
> I wasn't reading the threads carefully enough, apologies, just realized
> Daniel had already mention the above point further up.
>
> Also, after going back to the previous RFC thread I saw John mention that
> after making the is_pointer_value() changes to exclude NOT_INIT, the tests
> still passes.
>
> I guess that comes down to how the verifier rigorously check that the
> registers are not NOT_INIT using check_reg_arg(..., SRC_OP), before moving
> on to more specific checks. So I'm a bit less sure about the split
> {maybe,is}_pointer_value() approach proposed below now.
Hi Shung-Hsi, Daniel,
Sorry for a long delay. I'd like to revive this small change.
Thank you for pointing out the part regarding rigorous checks and
check_reg_arg. I've examined all places where __is_pointer_value(...)
and is_pointer_value(...) are invoked in the verifier code and came to
the conclusion that NOT_INIT can never reach the __is_pointer_value.
I also double checked this by modifying __is_pointer_value as follows:
static bool __is_pointer_value(bool allow_ptr_leaks,
const struct bpf_reg_state *reg)
{
+ BUG_ON(reg->type == NOT_INIT);
...
}
And running the BPF selftests. None triggered the BUG_ON condition.
The place where I use type_is_pointer in check_cond_jmp_op is after
the check_reg_arg(..., SRC_OP) for both src and dst registers. Thus I
want to delete the type_is_pointer function from the patch and use
__is_pointer_value(false, ...) instead (as NOT_INIT check was
unnecessary from the beginning).
>
> > Since the use case seems different, perhaps we could split them up, e.g. a
> > maybe_pointer_value() and a is_pointer_value(), or something along that
> > line.
> >
> > The former is equivalent to type != SCALAR_VALUE, and the latter equivalent
> > to type != NOT_INIT && type != SCALAR_VALUE. The latter can be used here for
> > implementing nullness propogation.
> >
> > > And check if there are test cases that have to be added because of the
> > > change in the __is_pointer_value behavior (it does not check for
> > > `NOT_INIT` right now). Does this sound like a plan?
> > >
> > > [...]
> > > > Could we consolidate the logic above with the one below which deals with R == 0 checks?
> > > > There are some similarities, e.g. !is_jmp32, both test for jeq/jne and while one is based
> > > > on K, the other one on X, though we could also add check X == 0 for below. Anyway, just
> > > > a though that it may be nice to consolidate the handling.
> > >
> > > Ok, I will try to consolidate those.
After some contemplating I don't think that it would be good to
consolidate these two parts.
The part that I want to add merely propagates the nullness
information:
if (!is_jmp32 && BPF_SRC(insn->code) == BPF_X &&
__is_pointer_value(false, src_reg) && __is_pointer_value(false, dst_reg) &&
type_may_be_null(src_reg->type) != type_may_be_null(dst_reg->type)) {
// ... save non-null part for one of the regs ...
}
However, the part that is already present is actually a pointer leak
check that exempts comparison with zero (and exemption for comparison
with zero is stated as goal of commit 1be7f75d1668 that added
is_pointer_value back in 2015):
/* detect if R == 0 where R is returned from bpf_map_lookup_elem()...
*/
if (!is_jmp32 && BPF_SRC(insn->code) == BPF_K &&
insn->imm == 0 && (opcode == BPF_JEQ || opcode == BPF_JNE) &&
type_may_be_null(dst_reg->type)) {
/* Mark all identical registers in each branch as either
* safe or unknown depending R == 0 or R != 0 conditional.
*/
// ...
} else if (!try_match_pkt_pointers(insn, dst_reg, ®s[insn->src_reg],
this_branch, other_branch) &&
leak check --> is_pointer_value(env, insn->dst_reg)) {
verbose(env, "R%d pointer comparison prohibited\n",
insn->dst_reg);
return -EACCES;
}
Merging these conditionals would be confusing, imo.
If you don't have objections I will post the v2 removing
type_is_pointer from the patch.
Thanks,
Eduard
> > >
> > > Thanks,
> > > Eduard
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH bpf-next 2/2] selftests/bpf: check nullness propagation for reg to reg comparisons
2022-08-26 17:29 ` [PATCH bpf-next 2/2] selftests/bpf: check nullness propagation " Eduard Zingerman
@ 2022-11-14 18:01 ` Alexei Starovoitov
2022-11-15 20:31 ` Eduard Zingerman
0 siblings, 1 reply; 11+ messages in thread
From: Alexei Starovoitov @ 2022-11-14 18:01 UTC (permalink / raw)
To: Eduard Zingerman
Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Kernel Team, Yonghong Song, John Fastabend
On Fri, Aug 26, 2022 at 10:30 AM Eduard Zingerman <eddyz87@gmail.com> wrote:
>
> Verify that nullness information is porpagated in the branches of
> register to register JEQ and JNE operations.
>
> Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
> Acked-by: Yonghong Song <yhs@fb.com>
> ---
> .../bpf/verifier/jeq_infer_not_null.c | 166 ++++++++++++++++++
> 1 file changed, 166 insertions(+)
> create mode 100644 tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
These 4 new tests are failing in unpriv.
Pls fix and respin.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH bpf-next 2/2] selftests/bpf: check nullness propagation for reg to reg comparisons
2022-11-14 18:01 ` Alexei Starovoitov
@ 2022-11-15 20:31 ` Eduard Zingerman
2022-11-15 20:51 ` Alexei Starovoitov
0 siblings, 1 reply; 11+ messages in thread
From: Eduard Zingerman @ 2022-11-15 20:31 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Kernel Team, Yonghong Song, John Fastabend
On Mon, 2022-11-14 at 10:01 -0800, Alexei Starovoitov wrote:
> On Fri, Aug 26, 2022 at 10:30 AM Eduard Zingerman <eddyz87@gmail.com> wrote:
> >
> > Verify that nullness information is porpagated in the branches of
> > register to register JEQ and JNE operations.
> >
> > Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
> > Acked-by: Yonghong Song <yhs@fb.com>
> > ---
> > .../bpf/verifier/jeq_infer_not_null.c | 166 ++++++++++++++++++
> > 1 file changed, 166 insertions(+)
> > create mode 100644 tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
>
> These 4 new tests are failing in unpriv.
This is interesting. 'test_verifier' passed for me because of
kernel.unprivileged_bpf_disabled = 1 on my test VM.
But It also passed on CI ([1]) with the following log:
2022-11-06T21:15:53.2873411Z #686/u jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL -> PTR_TO_SOCKET for JNE false branch SKIP
2022-11-06T21:15:53.2908232Z #686/p jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL -> PTR_TO_SOCKET for JNE false branch OK
To skip or not to skip is decided by test_verifier.c:do_test:
if (test_as_unpriv(test) && unpriv_disabled) {
printf("#%d/u %s SKIP\n", i, test->descr);
skips++;
}
The 'test_as_unpriv(test)' is true for my tests because of the
.prog_type == BPF_PROG_TYPE_CGROUP_SKB.
'unpriv_disabled' is a global set by test_verifier.c:get_unpriv_disabled:
static void get_unpriv_disabled()
{
char buf[2];
FILE *fd;
fd = fopen("/proc/sys/"UNPRIV_SYSCTL, "r");
// ...
if (fgets(buf, 2, fd) == buf && atoi(buf))
unpriv_disabled = true;
fclose(fd);
}
Might it be the case that CI configuration needs an update as below:
sysctl kernel.unprivileged_bpf_disabled=0
?
[1] https://github.com/kernel-patches/bpf/actions/runs/3405978658/jobs/5664441527
>
> Pls fix and respin.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH bpf-next 2/2] selftests/bpf: check nullness propagation for reg to reg comparisons
2022-11-15 20:31 ` Eduard Zingerman
@ 2022-11-15 20:51 ` Alexei Starovoitov
0 siblings, 0 replies; 11+ messages in thread
From: Alexei Starovoitov @ 2022-11-15 20:51 UTC (permalink / raw)
To: Eduard Zingerman, Alexei Starovoitov
Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Kernel Team, Yonghong Song, John Fastabend
On 11/15/22 12:31 PM, Eduard Zingerman wrote:
> On Mon, 2022-11-14 at 10:01 -0800, Alexei Starovoitov wrote:
>> On Fri, Aug 26, 2022 at 10:30 AM Eduard Zingerman <eddyz87@gmail.com> wrote:
>>>
>>> Verify that nullness information is porpagated in the branches of
>>> register to register JEQ and JNE operations.
>>>
>>> Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
>>> Acked-by: Yonghong Song <yhs@fb.com>
>>> ---
>>> .../bpf/verifier/jeq_infer_not_null.c | 166 ++++++++++++++++++
>>> 1 file changed, 166 insertions(+)
>>> create mode 100644 tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
>>
>> These 4 new tests are failing in unpriv.
>
> This is interesting. 'test_verifier' passed for me because of
> kernel.unprivileged_bpf_disabled = 1 on my test VM.
> But It also passed on CI ([1]) with the following log:
>
> 2022-11-06T21:15:53.2873411Z #686/u jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL -> PTR_TO_SOCKET for JNE false branch SKIP
> 2022-11-06T21:15:53.2908232Z #686/p jne/jeq infer not null, PTR_TO_SOCKET_OR_NULL -> PTR_TO_SOCKET for JNE false branch OK
>
> To skip or not to skip is decided by test_verifier.c:do_test:
>
> if (test_as_unpriv(test) && unpriv_disabled) {
> printf("#%d/u %s SKIP\n", i, test->descr);
> skips++;
> }
>
> The 'test_as_unpriv(test)' is true for my tests because of the
> .prog_type == BPF_PROG_TYPE_CGROUP_SKB.
> 'unpriv_disabled' is a global set by test_verifier.c:get_unpriv_disabled:
>
> static void get_unpriv_disabled()
> {
> char buf[2];
> FILE *fd;
>
> fd = fopen("/proc/sys/"UNPRIV_SYSCTL, "r");
> // ...
> if (fgets(buf, 2, fd) == buf && atoi(buf))
> unpriv_disabled = true;
> fclose(fd);
> }
>
> Might it be the case that CI configuration needs an update as below:
> sysctl kernel.unprivileged_bpf_disabled=0
> ?
Yeah. Makes sense to enable unpriv in CI, since it's missing tests
in the current form.
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2022-11-15 20:52 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-08-26 17:29 [PATCH bpf-next 0/2] propagate nullness information for reg to reg comparisons Eduard Zingerman
2022-08-26 17:29 ` [PATCH bpf-next 1/2] bpf: " Eduard Zingerman
2022-08-29 14:23 ` Daniel Borkmann
2022-08-30 10:41 ` Eduard Zingerman
2022-09-01 8:13 ` Shung-Hsi Yu
2022-09-01 9:01 ` Shung-Hsi Yu
2022-10-27 22:18 ` Eduard Zingerman
2022-08-26 17:29 ` [PATCH bpf-next 2/2] selftests/bpf: check nullness propagation " Eduard Zingerman
2022-11-14 18:01 ` Alexei Starovoitov
2022-11-15 20:31 ` Eduard Zingerman
2022-11-15 20:51 ` Alexei Starovoitov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox