* [PATCH bpf-next 0/2] bpf: Reject scalar addition from untrusted memory
@ 2026-06-09 14:55 Nuoqi Gui
2026-06-09 14:55 ` [PATCH bpf-next 1/2] " Nuoqi Gui
2026-06-09 14:55 ` [PATCH bpf-next 2/2] selftests/bpf: Cover scalar addition from rdonly " Nuoqi Gui
0 siblings, 2 replies; 7+ messages in thread
From: Nuoqi Gui @ 2026-06-09 14:55 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
Cc: Eduard Zingerman, Kumar Kartikeya Dwivedi, John Fastabend,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan,
bpf, linux-kernel, linux-kselftest, Nuoqi Gui
scalar += rdonly_untrusted_mem reaches adjust_ptr_min_max_vals() with the
pointer as the source register. The untrusted PTR_TO_MEM case returns there
without updating the scalar destination, leaving stale verifier state.
Reject that addition before the early return. Pointer += scalar remains
handled by the existing untrusted-memory rule.
Patch 1 adds the verifier rejection. Patch 2 adds a verifier regression
test for scalar += bpf_rdonly_cast(..., 0).
Validation:
unpatched bpf-next 8496d9020ff3:
BPF_PROG_LOAD -> fd=4 errno=0
RESULT: verifier ACCEPTED (unexpected_accept)
patched bpf-next 8496d9020ff3 + this series:
BPF_PROG_LOAD -> fd=-1 errno=13 (Permission denied)
R1 tried to add from rdonly_untrusted_mem to scalar
RESULT: verifier REJECTED as expected
Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn>
---
Nuoqi Gui (2):
bpf: Reject scalar addition from untrusted memory
selftests/bpf: Cover scalar addition from rdonly untrusted memory
kernel/bpf/verifier.c | 8 ++++++++
.../testing/selftests/bpf/progs/mem_rdonly_untrusted.c | 17 +++++++++++++++++
2 files changed, 25 insertions(+)
---
base-commit: 8496d9020ff37a33c2a7b2fc84350fd03ffbde78
change-id: 20260609-f01-03-scalar-add-bpf-next-0ccf8a54f338
Best regards,
--
Nuoqi Gui <gnq25@mails.tsinghua.edu.cn>
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH bpf-next 1/2] bpf: Reject scalar addition from untrusted memory 2026-06-09 14:55 [PATCH bpf-next 0/2] bpf: Reject scalar addition from untrusted memory Nuoqi Gui @ 2026-06-09 14:55 ` Nuoqi Gui 2026-06-09 15:28 ` bot+bpf-ci 2026-06-09 17:01 ` Eduard Zingerman 2026-06-09 14:55 ` [PATCH bpf-next 2/2] selftests/bpf: Cover scalar addition from rdonly " Nuoqi Gui 1 sibling, 2 replies; 7+ messages in thread From: Nuoqi Gui @ 2026-06-09 14:55 UTC (permalink / raw) To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko Cc: Eduard Zingerman, Kumar Kartikeya Dwivedi, John Fastabend, Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan, bpf, linux-kernel, linux-kselftest, Nuoqi Gui scalar += rdonly_untrusted_mem reaches adjust_ptr_min_max_vals() with the pointer as the source register. The untrusted PTR_TO_MEM case returns there without updating the scalar destination, leaving stale verifier state. Reject that addition before the early return. Pointer += scalar remains handled by the existing untrusted-memory rule. Fixes: f2362a57aeff ("bpf: allow void* cast using bpf_rdonly_cast()") Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn> --- kernel/bpf/verifier.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index c8d980fdd709..c6b350f9585a 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -14823,6 +14823,14 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, * This is legal, but we have to reverse our * src/dest handling in computing the range */ + if (opcode == BPF_ADD && + base_type(src_reg->type) == PTR_TO_MEM && + (src_reg->type & PTR_UNTRUSTED)) { + verbose(env, "R%d tried to add from %s to scalar\n", + insn->dst_reg, + reg_type_str(env, src_reg->type)); + return -EACCES; + } err = mark_chain_precision(env, insn->dst_reg); if (err) return err; -- 2.34.1 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf-next 1/2] bpf: Reject scalar addition from untrusted memory 2026-06-09 14:55 ` [PATCH bpf-next 1/2] " Nuoqi Gui @ 2026-06-09 15:28 ` bot+bpf-ci 2026-06-09 17:01 ` Eduard Zingerman 1 sibling, 0 replies; 7+ messages in thread From: bot+bpf-ci @ 2026-06-09 15:28 UTC (permalink / raw) To: gnq25, ast, daniel, andrii Cc: eddyz87, memxor, john.fastabend, martin.lau, song, yonghong.song, jolsa, shuah, bpf, linux-kernel, linux-kselftest, gnq25, ast, andrii, daniel, martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai [-- Attachment #1: Type: text/plain, Size: 2095 bytes --] > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index ed7ba0e6a9ce..514322aca6ed 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c > @@ -14758,6 +14758,14 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, > * This is legal, but we have to reverse our > * src/dest handling in computing the range > */ > + if (opcode == BPF_ADD && > + base_type(src_reg->type) == PTR_TO_MEM && > + (src_reg->type & PTR_UNTRUSTED)) { > + verbose(env, "R%d tried to add from %s to scalar\n", > + insn->dst_reg, > + reg_type_str(env, src_reg->type)); > + return -EACCES; > + } The patch only prevents scalar += untrusted_ptr (BPF_ADD) but fails to reject other ALU operations like BPF_SUB, BPF_XOR, and BPF_MUL. This allows attackers to trivially bypass the new check by using operations like scalar -= untrusted_ptr or scalar ^= untrusted_ptr. When an attacker uses these alternative operations, the code will: 1. Bypass the new BPF_ADD check added by this patch 2. Call adjust_ptr_min_max_vals() with the untrusted pointer 3. Hit the early return for untrusted PTR_TO_MEM in adjust_ptr_min_max_vals() 4. Return success without updating the scalar's verifier state This creates the same verifier state desynchronization vulnerability that the patch was meant to fix. The scalar register's tracked bounds remain stale while its actual runtime value has been manipulated by the pointer value. Can this verifier state desynchronization be leveraged to achieve out-of-bounds access? This concern was raised by sashiko-bot@kernel.org in the v1 review: https://lore.kernel.org/bpf/20260609151019.678141F00893@smtp.kernel.org/ The author has not responded to this concern. > err = mark_chain_precision(env, insn->dst_reg); > if (err) > return err; --- AI reviewed your patch. Please fix the bug or email reply why it's not a bug. See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md CI run summary: https://github.com/kernel-patches/bpf/actions/runs/27215521040 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf-next 1/2] bpf: Reject scalar addition from untrusted memory 2026-06-09 14:55 ` [PATCH bpf-next 1/2] " Nuoqi Gui 2026-06-09 15:28 ` bot+bpf-ci @ 2026-06-09 17:01 ` Eduard Zingerman 2026-06-09 18:21 ` Emil Tsalapatis 1 sibling, 1 reply; 7+ messages in thread From: Eduard Zingerman @ 2026-06-09 17:01 UTC (permalink / raw) To: Nuoqi Gui, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko Cc: Kumar Kartikeya Dwivedi, John Fastabend, Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan, bpf, linux-kernel, linux-kselftest On Tue, 2026-06-09 at 22:55 +0800, Nuoqi Gui wrote: > scalar += rdonly_untrusted_mem reaches adjust_ptr_min_max_vals() with the > pointer as the source register. The untrusted PTR_TO_MEM case returns there > without updating the scalar destination, leaving stale verifier state. > > Reject that addition before the early return. Pointer += scalar remains > handled by the existing untrusted-memory rule. > > Fixes: f2362a57aeff ("bpf: allow void* cast using bpf_rdonly_cast()") > Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn> > --- > kernel/bpf/verifier.c | 8 ++++++++ > 1 file changed, 8 insertions(+) > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index c8d980fdd709..c6b350f9585a 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c > @@ -14823,6 +14823,14 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, > * This is legal, but we have to reverse our > * src/dest handling in computing the range > */ > + if (opcode == BPF_ADD && > + base_type(src_reg->type) == PTR_TO_MEM && > + (src_reg->type & PTR_UNTRUSTED)) { > + verbose(env, "R%d tried to add from %s to scalar\n", > + insn->dst_reg, > + reg_type_str(env, src_reg->type)); > + return -EACCES; > + } > err = mark_chain_precision(env, insn->dst_reg); > if (err) > return err; Should the fix be like this: diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 7d27ba396d32..9c85dd680a46 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -13593,8 +13593,10 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, * Accesses to untrusted PTR_TO_MEM are done through probe * instructions, hence no need to track offsets. */ - if (base_type(ptr_reg->type) == PTR_TO_MEM && (ptr_reg->type & PTR_UNTRUSTED)) + if (base_type(ptr_reg->type) == PTR_TO_MEM && (ptr_reg->type & PTR_UNTRUSTED)) { + *dst_reg = *ptr_reg; return 0; + } switch (base_type(ptr_reg->type)) { case PTR_TO_CTX: Instead? ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf-next 1/2] bpf: Reject scalar addition from untrusted memory 2026-06-09 17:01 ` Eduard Zingerman @ 2026-06-09 18:21 ` Emil Tsalapatis 2026-06-10 12:16 ` Nuoqi Gui 0 siblings, 1 reply; 7+ messages in thread From: Emil Tsalapatis @ 2026-06-09 18:21 UTC (permalink / raw) To: Eduard Zingerman, Nuoqi Gui, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko Cc: Kumar Kartikeya Dwivedi, John Fastabend, Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan, bpf, linux-kernel, linux-kselftest On Tue Jun 9, 2026 at 1:01 PM EDT, Eduard Zingerman wrote: > On Tue, 2026-06-09 at 22:55 +0800, Nuoqi Gui wrote: >> scalar += rdonly_untrusted_mem reaches adjust_ptr_min_max_vals() with the >> pointer as the source register. The untrusted PTR_TO_MEM case returns there >> without updating the scalar destination, leaving stale verifier state. >> >> Reject that addition before the early return. Pointer += scalar remains >> handled by the existing untrusted-memory rule. >> >> Fixes: f2362a57aeff ("bpf: allow void* cast using bpf_rdonly_cast()") >> Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn> >> --- >> kernel/bpf/verifier.c | 8 ++++++++ >> 1 file changed, 8 insertions(+) >> >> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c >> index c8d980fdd709..c6b350f9585a 100644 >> --- a/kernel/bpf/verifier.c >> +++ b/kernel/bpf/verifier.c >> @@ -14823,6 +14823,14 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, >> * This is legal, but we have to reverse our >> * src/dest handling in computing the range >> */ >> + if (opcode == BPF_ADD && >> + base_type(src_reg->type) == PTR_TO_MEM && >> + (src_reg->type & PTR_UNTRUSTED)) { >> + verbose(env, "R%d tried to add from %s to scalar\n", >> + insn->dst_reg, >> + reg_type_str(env, src_reg->type)); >> + return -EACCES; >> + } >> err = mark_chain_precision(env, insn->dst_reg); >> if (err) >> return err; > > Should the fix be like this: > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index 7d27ba396d32..9c85dd680a46 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c > @@ -13593,8 +13593,10 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, > * Accesses to untrusted PTR_TO_MEM are done through probe > * instructions, hence no need to track offsets. > */ > - if (base_type(ptr_reg->type) == PTR_TO_MEM && (ptr_reg->type & PTR_UNTRUSTED)) > + if (base_type(ptr_reg->type) == PTR_TO_MEM && (ptr_reg->type & PTR_UNTRUSTED)) { > + *dst_reg = *ptr_reg; > return 0; > + } > > switch (base_type(ptr_reg->type)) { > case PTR_TO_CTX: > > Instead? Seconded, AFAICT there is no reason to fail verification since we just the pointer's value to the scalar. Whether the operation makes sense is up to the programmer to decide. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Re: [PATCH bpf-next 1/2] bpf: Reject scalar addition from untrusted memory 2026-06-09 18:21 ` Emil Tsalapatis @ 2026-06-10 12:16 ` Nuoqi Gui 0 siblings, 0 replies; 7+ messages in thread From: Nuoqi Gui @ 2026-06-10 12:16 UTC (permalink / raw) To: Emil Tsalapatis Cc: Eduard Zingerman, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Kumar Kartikeya Dwivedi, John Fastabend, Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan, bpf, linux-kernel, linux-kselftest > -----Original Messages----- > From: "Emil Tsalapatis" <emil@etsalapatis.com> > Send time:Wednesday, 10/06/2026 02:21:49 > To: "Eduard Zingerman" <eddyz87@gmail.com>, "Nuoqi Gui" <gnq25@mails.tsinghua.edu.cn>, "Alexei Starovoitov" <ast@kernel.org>, "Daniel Borkmann" <daniel@iogearbox.net>, "Andrii Nakryiko" <andrii@kernel.org> > Cc: "Kumar Kartikeya Dwivedi" <memxor@gmail.com>, "John Fastabend" <john.fastabend@gmail.com>, "Martin KaFai Lau" <martin.lau@linux.dev>, "Song Liu" <song@kernel.org>, "Yonghong Song" <yonghong.song@linux.dev>, "Jiri Olsa" <jolsa@kernel.org>, "Shuah Khan" <shuah@kernel.org>, bpf@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org > Subject: Re: [PATCH bpf-next 1/2] bpf: Reject scalar addition from untrusted memory > > On Tue Jun 9, 2026 at 1:01 PM EDT, Eduard Zingerman wrote: > > On Tue, 2026-06-09 at 22:55 +0800, Nuoqi Gui wrote: > >> scalar += rdonly_untrusted_mem reaches adjust_ptr_min_max_vals() with the > >> pointer as the source register. The untrusted PTR_TO_MEM case returns there > >> without updating the scalar destination, leaving stale verifier state. > >> > >> Reject that addition before the early return. Pointer += scalar remains > >> handled by the existing untrusted-memory rule. > >> > >> Fixes: f2362a57aeff ("bpf: allow void* cast using bpf_rdonly_cast()") > >> Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn> > >> --- > >> kernel/bpf/verifier.c | 8 ++++++++ > >> 1 file changed, 8 insertions(+) > >> > >> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > >> index c8d980fdd709..c6b350f9585a 100644 > >> --- a/kernel/bpf/verifier.c > >> +++ b/kernel/bpf/verifier.c > >> @@ -14823,6 +14823,14 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, > >> * This is legal, but we have to reverse our > >> * src/dest handling in computing the range > >> */ > >> + if (opcode == BPF_ADD && > >> + base_type(src_reg->type) == PTR_TO_MEM && > >> + (src_reg->type & PTR_UNTRUSTED)) { > >> + verbose(env, "R%d tried to add from %s to scalar\n", > >> + insn->dst_reg, > >> + reg_type_str(env, src_reg->type)); > >> + return -EACCES; > >> + } > >> err = mark_chain_precision(env, insn->dst_reg); > >> if (err) > >> return err; > > > > Should the fix be like this: > > > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > > index 7d27ba396d32..9c85dd680a46 100644 > > --- a/kernel/bpf/verifier.c > > +++ b/kernel/bpf/verifier.c > > @@ -13593,8 +13593,10 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, > > * Accesses to untrusted PTR_TO_MEM are done through probe > > * instructions, hence no need to track offsets. > > */ > > - if (base_type(ptr_reg->type) == PTR_TO_MEM && (ptr_reg->type & PTR_UNTRUSTED)) > > + if (base_type(ptr_reg->type) == PTR_TO_MEM && (ptr_reg->type & PTR_UNTRUSTED)) { > > + *dst_reg = *ptr_reg; > > return 0; > > + } > > > > switch (base_type(ptr_reg->type)) { > > case PTR_TO_CTX: > > > > Instead? > > Seconded, AFAICT there is no reason to fail verification since we just > the pointer's value to the scalar. Whether the operation makes sense is > up to the programmer to decide. Thanks. Agreed, rejecting scalar += pointer is not the right semantic fix. The bug is that the untrusted PTR_TO_MEM early return leaves dst state stale. I'll send v2 moving the fix into adjust_ptr_min_max_vals(). ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH bpf-next 2/2] selftests/bpf: Cover scalar addition from rdonly untrusted memory 2026-06-09 14:55 [PATCH bpf-next 0/2] bpf: Reject scalar addition from untrusted memory Nuoqi Gui 2026-06-09 14:55 ` [PATCH bpf-next 1/2] " Nuoqi Gui @ 2026-06-09 14:55 ` Nuoqi Gui 1 sibling, 0 replies; 7+ messages in thread From: Nuoqi Gui @ 2026-06-09 14:55 UTC (permalink / raw) To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko Cc: Eduard Zingerman, Kumar Kartikeya Dwivedi, John Fastabend, Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, Shuah Khan, bpf, linux-kernel, linux-kselftest, Nuoqi Gui Add a verifier test for scalar += rdonly_untrusted_mem. The program gets a read-only untrusted memory value from bpf_rdonly_cast(..., 0). It then adds that value to a scalar destination. The verifier should reject this instead of preserving stale scalar state. Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn> --- .../testing/selftests/bpf/progs/mem_rdonly_untrusted.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c b/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c index 5b4453747c23..303b8ed3e70b 100644 --- a/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c +++ b/tools/testing/selftests/bpf/progs/mem_rdonly_untrusted.c @@ -77,6 +77,23 @@ int offset_not_tracked(void *ctx) return s; } +SEC("socket") +__failure +__msg("R1 tried to add from rdonly_untrusted_mem to scalar") +__naked void scalar_add_not_ok(void) +{ + asm volatile ("r1 = 0;" + "r2 = 0;" + "call %[bpf_rdonly_cast];" + "r1 = 0;" + "r1 += r0;" + "r0 = 0;" + "exit;" + : + : __imm(bpf_rdonly_cast) + : __clobber_all); +} + SEC("socket") __failure __msg("cannot write into rdonly_untrusted_mem") -- 2.34.1 ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-06-10 12:16 UTC | newest] Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-06-09 14:55 [PATCH bpf-next 0/2] bpf: Reject scalar addition from untrusted memory Nuoqi Gui 2026-06-09 14:55 ` [PATCH bpf-next 1/2] " Nuoqi Gui 2026-06-09 15:28 ` bot+bpf-ci 2026-06-09 17:01 ` Eduard Zingerman 2026-06-09 18:21 ` Emil Tsalapatis 2026-06-10 12:16 ` Nuoqi Gui 2026-06-09 14:55 ` [PATCH bpf-next 2/2] selftests/bpf: Cover scalar addition from rdonly " Nuoqi Gui
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®