2022-12-13 03:14:38

by Hao Sun

[permalink] [raw]
Subject: [PATCH bpf-next v2 2/2] selftests/bpf: check null propagation only neither reg is PTR_TO_BTF_ID

Verify that nullness information is not porpagated in the branches
of register to register JEQ and JNE operations if one of them is
PTR_TO_BTF_ID.

Signed-off-by: Hao Sun <[email protected]>
Acked-by: Yonghong Song <[email protected]>
---
.../bpf/verifier/jeq_infer_not_null.c | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)

diff --git a/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c b/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
index 67a1c07ead34..b2b215227d97 100644
--- a/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
+++ b/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
@@ -172,3 +172,25 @@
.prog_type = BPF_PROG_TYPE_XDP,
.result = ACCEPT,
},
+{
+ "jne/jeq infer not null, PTR_TO_MAP_OR_NULL unchanged with PTR_TO_BTF_ID reg",
+ .insns = {
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+ BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),
+ BPF_LD_MAP_FD(BPF_REG_1, 0),
+ /* r6 = bpf_map->inner_map_meta; */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 8),
+ /* r0 = map_lookup_elem(r1, r2); */
+ BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
+ /* if (r0 == r6) read *r0; */
+ BPF_JMP_REG(BPF_JEQ, BPF_REG_6, BPF_REG_0, 1),
+ BPF_EXIT_INSN(),
+ BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .fixup_map_hash_8b = { 3 },
+ .prog_type = BPF_PROG_TYPE_XDP,
+ .result = REJECT,
+ .errstr = "R0 invalid mem access 'map_value_or_null'",
+},
--
2.38.1


2022-12-19 22:36:23

by Martin KaFai Lau

[permalink] [raw]
Subject: Re: [PATCH bpf-next v2 2/2] selftests/bpf: check null propagation only neither reg is PTR_TO_BTF_ID

On 12/12/22 7:04 PM, Hao Sun wrote:
> Verify that nullness information is not porpagated in the branches
> of register to register JEQ and JNE operations if one of them is
> PTR_TO_BTF_ID.

Thanks for the fix and test.

>
> Signed-off-by: Hao Sun <[email protected]>
> Acked-by: Yonghong Song <[email protected]>
> ---
> .../bpf/verifier/jeq_infer_not_null.c | 22 +++++++++++++++++++
> 1 file changed, 22 insertions(+)
>
> diff --git a/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c b/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
> index 67a1c07ead34..b2b215227d97 100644
> --- a/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
> +++ b/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
> @@ -172,3 +172,25 @@
> .prog_type = BPF_PROG_TYPE_XDP,
> .result = ACCEPT,
> },
> +{
> + "jne/jeq infer not null, PTR_TO_MAP_OR_NULL unchanged with PTR_TO_BTF_ID reg",
> + .insns = {
> + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
> + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
> + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),
> + BPF_LD_MAP_FD(BPF_REG_1, 0),
> + /* r6 = bpf_map->inner_map_meta; */
> + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 8),

This bpf_map->inner_map_meta requires CO-RE. It works now but could be fragile
in different platform and in the future bpf_map changes. Take a look at the
map_ptr_kern.c which uses "__attribute__((preserve_access_index))" at the
"struct bpf_map".

Please translate this verifer test into a proper bpf prog in C code such that it
can use the CO-RE in libbpf. It should run under test_progs instead of
test_verifier. The bpf prog can include the "vmlinux.h" to get the
"__attribute__((preserve_access_index))" for free. Take a look at
https://lore.kernel.org/all/[email protected]/ which
has example on how to check verifier message in test_progs.


> + /* r0 = map_lookup_elem(r1, r2); */
> + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
> + /* if (r0 == r6) read *r0; */
> + BPF_JMP_REG(BPF_JEQ, BPF_REG_6, BPF_REG_0, 1),
> + BPF_EXIT_INSN(),
> + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, 0),
> + BPF_EXIT_INSN(),
> + },
> + .fixup_map_hash_8b = { 3 },
> + .prog_type = BPF_PROG_TYPE_XDP,
> + .result = REJECT,
> + .errstr = "R0 invalid mem access 'map_value_or_null'",
> +},

2022-12-20 02:49:04

by Hao Sun

[permalink] [raw]
Subject: Re: [PATCH bpf-next v2 2/2] selftests/bpf: check null propagation only neither reg is PTR_TO_BTF_ID



> On 20 Dec 2022, at 6:01 AM, Martin KaFai Lau <[email protected]> wrote:
>
> On 12/12/22 7:04 PM, Hao Sun wrote:
>> Verify that nullness information is not porpagated in the branches
>> of register to register JEQ and JNE operations if one of them is
>> PTR_TO_BTF_ID.
>
> Thanks for the fix and test.
>
>> Signed-off-by: Hao Sun <[email protected]>
>> Acked-by: Yonghong Song <[email protected]>
>> ---
>> .../bpf/verifier/jeq_infer_not_null.c | 22 +++++++++++++++++++
>> 1 file changed, 22 insertions(+)
>> diff --git a/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c b/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
>> index 67a1c07ead34..b2b215227d97 100644
>> --- a/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
>> +++ b/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
>> @@ -172,3 +172,25 @@
>> .prog_type = BPF_PROG_TYPE_XDP,
>> .result = ACCEPT,
>> },
>> +{
>> + "jne/jeq infer not null, PTR_TO_MAP_OR_NULL unchanged with PTR_TO_BTF_ID reg",
>> + .insns = {
>> + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
>> + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
>> + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),
>> + BPF_LD_MAP_FD(BPF_REG_1, 0),
>> + /* r6 = bpf_map->inner_map_meta; */
>> + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 8),
>
> This bpf_map->inner_map_meta requires CO-RE. It works now but could be fragile in different platform and in the future bpf_map changes. Take a look at the map_ptr_kern.c which uses "__attribute__((preserve_access_index))" at the "struct bpf_map".
>
> Please translate this verifer test into a proper bpf prog in C code such that it can use the CO-RE in libbpf. It should run under test_progs instead of test_verifier. The bpf prog can include the "vmlinux.h" to get the "__attribute__((preserve_access_index))" for free. Take a look at https://lore.kernel.org/all/[email protected]/ which has example on how to check verifier message in test_progs.
>

Sure, thanks for the hint, will send patch v3 soon.

Thanks
Hao

2022-12-21 14:08:40

by Hao Sun

[permalink] [raw]
Subject: Re: [PATCH bpf-next v2 2/2] selftests/bpf: check null propagation only neither reg is PTR_TO_BTF_ID



> On 20 Dec 2022, at 6:01 AM, Martin KaFai Lau <[email protected]> wrote:
>
> On 12/12/22 7:04 PM, Hao Sun wrote:
>> Verify that nullness information is not porpagated in the branches
>> of register to register JEQ and JNE operations if one of them is
>> PTR_TO_BTF_ID.
>
> Thanks for the fix and test.
>
>> Signed-off-by: Hao Sun <[email protected]>
>> Acked-by: Yonghong Song <[email protected]>
>> ---
>> .../bpf/verifier/jeq_infer_not_null.c | 22 +++++++++++++++++++
>> 1 file changed, 22 insertions(+)
>> diff --git a/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c b/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
>> index 67a1c07ead34..b2b215227d97 100644
>> --- a/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
>> +++ b/tools/testing/selftests/bpf/verifier/jeq_infer_not_null.c
>> @@ -172,3 +172,25 @@
>> .prog_type = BPF_PROG_TYPE_XDP,
>> .result = ACCEPT,
>> },
>> +{
>> + "jne/jeq infer not null, PTR_TO_MAP_OR_NULL unchanged with PTR_TO_BTF_ID reg",
>> + .insns = {
>> + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
>> + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
>> + BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),
>> + BPF_LD_MAP_FD(BPF_REG_1, 0),
>> + /* r6 = bpf_map->inner_map_meta; */
>> + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 8),
>
> This bpf_map->inner_map_meta requires CO-RE. It works now but could be fragile in different platform and in the future bpf_map changes. Take a look at the map_ptr_kern.c which uses "__attribute__((preserve_access_index))" at the "struct bpf_map".
>
> Please translate this verifer test into a proper bpf prog in C code such that it can use the CO-RE in libbpf. It should run under test_progs instead of test_verifier. The bpf prog can include the "vmlinux.h" to get the "__attribute__((preserve_access_index))" for free. Take a look at https://lore.kernel.org/all/[email protected]/ which has example on how to check verifier message in test_progs.
>

Hi,

I’ve tried something like the bellow, but soon realized that this
won’t work because once compiler figures out `inner_map` equals
to `val`, it can choose either reg to write into in the following
path, meaning that this program can be rejected due to writing
into read-only PTR_TO_BTF_ID reg, and this makes the test useless.

Essentially, we want two regs, one points to PTR_TO_BTD_ID, one
points to MAP_VALUR_OR_NULL, then compare them and deref map val.
It’s hard to implement this in C level because compilers decide
which reg to use but not us, maybe we can just drop this test.

thoughts?

+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 1);
+ __type(key, u64);
+ __type(value, u64);
+} m_hash SEC(".maps");
+
+SEC("?raw_tp")
+__failure __msg("invalid mem access 'map_value_or_null")
+int jeq_infer_not_null_ptr_to_btfid(void *ctx)
+{
+ struct bpf_map *map = (struct bpf_map *)&m_hash;
+ struct bpf_map *inner_map = map->inner_map_meta;
+ u64 key = 0, ret = 0, *val;
+
+ val = bpf_map_lookup_elem(map, &key);
+ /* Do not mark ptr as non-null if one of them is
+ * PTR_TO_BTF_ID, reject because of invalid access
+ * to map value.
+ */
+ if (val == inner_map)
+ ret = *val;
+
+ return ret;
+}


2022-12-21 22:15:14

by Martin KaFai Lau

[permalink] [raw]
Subject: Re: [PATCH bpf-next v2 2/2] selftests/bpf: check null propagation only neither reg is PTR_TO_BTF_ID

On 12/21/22 5:46 AM, Hao Sun wrote:
> Hi,
>
> I’ve tried something like the bellow, but soon realized that this
> won’t work because once compiler figures out `inner_map` equals
> to `val`, it can choose either reg to write into in the following
> path, meaning that this program can be rejected due to writing
> into read-only PTR_TO_BTF_ID reg, and this makes the test useless.

hmm... I read the above a few times but I still don't quite get it. In
particular, '...can be rejected due to writing into read-only PTR_TO_BTF_ID
reg...'. Where is it writing into a read-only PTR_TO_BTF_ID reg in the
following bpf prog? Did I overlook something?

>
> Essentially, we want two regs, one points to PTR_TO_BTD_ID, one
> points to MAP_VALUR_OR_NULL, then compare them and deref map val.

If I read this request correctly, I guess the compiler has changed 'ret = *val'
to 'ret = *inner_map'? Thus, the verifier did not reject because it deref a
PTR_TO_BTF_ID?

> It’s hard to implement this in C level because compilers decide
> which reg to use but not us, maybe we can just drop this test.

Have you tried inline assembly. Something like this (untested):

asm volatile (
"r8 = %[val];\n"
"r9 = %[inner_map];\n"
"if r8 != r9 goto +1;\n"
"%[ret] = *(u64 *)(r8 +0);\n"
:[ret] "+r"(ret)
: [inner_map] "r"(inner_map), [val] "r"(val)
:"r8", "r9");

Please attach the verifier output in the future. It will be easier to understand.

>
> thoughts?
>
> +struct {
> + __uint(type, BPF_MAP_TYPE_HASH);
> + __uint(max_entries, 1);
> + __type(key, u64);
> + __type(value, u64);
> +} m_hash SEC(".maps");
> +
> +SEC("?raw_tp")
> +__failure __msg("invalid mem access 'map_value_or_null")
> +int jeq_infer_not_null_ptr_to_btfid(void *ctx)
> +{
> + struct bpf_map *map = (struct bpf_map *)&m_hash;
> + struct bpf_map *inner_map = map->inner_map_meta;
> + u64 key = 0, ret = 0, *val;
> +
> + val = bpf_map_lookup_elem(map, &key);
> + /* Do not mark ptr as non-null if one of them is
> + * PTR_TO_BTF_ID, reject because of invalid access
> + * to map value.
> + */
> + if (val == inner_map)
> + ret = *val;
> +
> + return ret;
> +}

2022-12-22 03:02:28

by Hao Sun

[permalink] [raw]
Subject: Re: [PATCH bpf-next v2 2/2] selftests/bpf: check null propagation only neither reg is PTR_TO_BTF_ID

Martin KaFai Lau <[email protected]> 于2022年12月22日周四 05:21写道:
>
> On 12/21/22 5:46 AM, Hao Sun wrote:
> > Hi,
> >
> > I’ve tried something like the bellow, but soon realized that this
> > won’t work because once compiler figures out `inner_map` equals
> > to `val`, it can choose either reg to write into in the following
> > path, meaning that this program can be rejected due to writing
> > into read-only PTR_TO_BTF_ID reg, and this makes the test useless.
>
> hmm... I read the above a few times but I still don't quite get it. In
> particular, '...can be rejected due to writing into read-only PTR_TO_BTF_ID
> reg...'. Where is it writing into a read-only PTR_TO_BTF_ID reg in the
> following bpf prog? Did I overlook something?
>
> >
> > Essentially, we want two regs, one points to PTR_TO_BTD_ID, one
> > points to MAP_VALUR_OR_NULL, then compare them and deref map val.
>
> If I read this request correctly, I guess the compiler has changed 'ret = *val'
> to 'ret = *inner_map'? Thus, the verifier did not reject because it deref a
> PTR_TO_BTF_ID?
>

Yes, and if we do "*val = 0", it's rejected due to writing to read-only
PTR_TO_BTF_ID reg.

> > It’s hard to implement this in C level because compilers decide
> > which reg to use but not us, maybe we can just drop this test.
>
> Have you tried inline assembly. Something like this (untested):
>
> asm volatile (
> "r8 = %[val];\n"
> "r9 = %[inner_map];\n"
> "if r8 != r9 goto +1;\n"
> "%[ret] = *(u64 *)(r8 +0);\n"
> :[ret] "+r"(ret)
> : [inner_map] "r"(inner_map), [val] "r"(val)
> :"r8", "r9");
>

This would work, didn't realize that I can inline BPF insns this way.
Thanks!

> Please attach the verifier output in the future. It will be easier to understand.
>

Will do the next time.