* [PATCH bpf-next v4 1/7] bpf: Support variable offsets for syscall PTR_TO_CTX
2026-04-01 12:28 [PATCH bpf-next v4 0/7] Allow variable offsets for syscall PTR_TO_CTX Kumar Kartikeya Dwivedi
@ 2026-04-01 12:28 ` Kumar Kartikeya Dwivedi
2026-04-01 12:40 ` Kumar Kartikeya Dwivedi
2026-04-01 12:28 ` [PATCH bpf-next v4 2/7] bpf: Enable unaligned accesses for syscall ctx Kumar Kartikeya Dwivedi
` (5 subsequent siblings)
6 siblings, 1 reply; 13+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-01 12:28 UTC (permalink / raw)
To: bpf
Cc: Emil Tsalapatis, Puranjay Mohan, Mykyta Yatsenko,
Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Tejun Heo, Dan Schatzberg,
kkd, kernel-team
Allow accessing PTR_TO_CTX with variable offsets in syscall programs.
Fixed offsets are already enabled for all program types that do not
convert their ctx accesses, since the changes we made in the commit
de6c7d99f898 ("bpf: Relax fixed offset check for PTR_TO_CTX"). Note
that we also lift the restriction on passing syscall context into
helpers, which was not permitted before, and passing modified syscall
context into kfuncs.
The structure of check_mem_access can be mostly shared and preserved,
but we must use check_mem_region_access to correctly verify access with
variable offsets.
The check made in check_helper_mem_access is hardened to only allow
PTR_TO_CTX for syscall programs to be passed in as helper memory. This
was the original intention of the existing code anyway, and it makes
little sense for other program types' context to be utilized as a memory
buffer. In case a convincing example presents itself in the future, this
check can be relaxed further.
We also no longer use the last-byte access to simulate helper memory
access, but instead go through check_mem_region_access. Since this no
longer updates our max_ctx_offset, we must do so manually, to keep track
of the maximum offset at which the program ctx may be accessed.
Take care to ensure that when arg_type is ARG_PTR_TO_CTX, we do not
relax any fixed or variable offset constraints around PTR_TO_CTX even in
syscall programs, and require them to be passed unmodified. There are
several reasons why this is necessary. First, if we pass a modified ctx,
then the global subprog's accesses will not update the max_ctx_offset to
its true maximum offset, and can lead to out of bounds accesses. Second,
tail called program (or extension program replacing global subprog) where
their max_ctx_offset exceeds the program they are being called from can
also cause issues. For the latter, unmodified PTR_TO_CTX is the first
requirement for the fix, the second is ensuring max_ctx_offset >= the
program they are being called from, which has to be a separate change
not made in this commit.
All in all, we can hint using arg_type when we expect ARG_PTR_TO_CTX and
make our relaxation around offsets conditional on it.
Drop coverage of syscall tests from verifier_ctx.c temporarily for
negative cases until they are updated in subsequent commits.
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
Acked-by: Puranjay Mohan <puranjay@kernel.org>
Acked-by: Mykyta Yatsenko <yatsenko@meta.com>
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
kernel/bpf/verifier.c | 104 +++++++++++-------
.../selftests/bpf/progs/verifier_ctx.c | 1 -
.../bpf/progs/verifier_global_subprogs.c | 1 -
3 files changed, 66 insertions(+), 40 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 8c1cf2eb6cbb..4b34f4198215 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -5897,6 +5897,10 @@ static int __check_mem_access(struct bpf_verifier_env *env, int regno,
verbose(env, "invalid access to packet, off=%d size=%d, R%d(id=%d,off=%d,r=%d)\n",
off, size, regno, reg->id, off, mem_size);
break;
+ case PTR_TO_CTX:
+ verbose(env, "invalid access to context, ctx_size=%d off=%d size=%d\n",
+ mem_size, off, size);
+ break;
case PTR_TO_MEM:
default:
verbose(env, "invalid access to memory, mem_size=%u off=%d size=%d\n",
@@ -6390,9 +6394,14 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off,
return 0;
}
+static bool is_var_ctx_off_allowed(struct bpf_prog *prog)
+{
+ return resolve_prog_type(prog) == BPF_PROG_TYPE_SYSCALL;
+}
+
/* check access to 'struct bpf_context' fields. Supports fixed offsets only */
-static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, int size,
- enum bpf_access_type t, struct bpf_insn_access_aux *info)
+static int __check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, int size,
+ enum bpf_access_type t, struct bpf_insn_access_aux *info)
{
if (env->ops->is_valid_access &&
env->ops->is_valid_access(off, size, t, env->prog, info)) {
@@ -6423,6 +6432,34 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off,
return -EACCES;
}
+static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, u32 regno,
+ int off, int access_size, enum bpf_access_type t,
+ struct bpf_insn_access_aux *info)
+{
+ /*
+ * Program types that don't rewrite ctx accesses can safely
+ * dereference ctx pointers with fixed offsets.
+ */
+ bool var_off_ok = is_var_ctx_off_allowed(env->prog);
+ bool fixed_off_ok = !env->ops->convert_ctx_access;
+ struct bpf_reg_state *regs = cur_regs(env);
+ struct bpf_reg_state *reg = regs + regno;
+ int err;
+
+ if (var_off_ok)
+ err = check_mem_region_access(env, regno, off, access_size, U16_MAX, false);
+ else
+ err = __check_ptr_off_reg(env, reg, regno, fixed_off_ok);
+ if (err)
+ return err;
+ off += reg->umax_value;
+
+ err = __check_ctx_access(env, insn_idx, off, access_size, t, info);
+ if (err)
+ verbose_linfo(env, insn_idx, "; ");
+ return err;
+}
+
static int check_flow_keys_access(struct bpf_verifier_env *env, int off,
int size)
{
@@ -7853,17 +7890,12 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
if (!err && value_regno >= 0 && (t == BPF_READ || rdonly_mem))
mark_reg_unknown(env, regs, value_regno);
} else if (reg->type == PTR_TO_CTX) {
- /*
- * Program types that don't rewrite ctx accesses can safely
- * dereference ctx pointers with fixed offsets.
- */
- bool fixed_off_ok = !env->ops->convert_ctx_access;
- struct bpf_retval_range range;
struct bpf_insn_access_aux info = {
.reg_type = SCALAR_VALUE,
.is_ldsx = is_ldsx,
.log = &env->log,
};
+ struct bpf_retval_range range;
if (t == BPF_WRITE && value_regno >= 0 &&
is_pointer_value(env, value_regno)) {
@@ -7871,19 +7903,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
return -EACCES;
}
- err = __check_ptr_off_reg(env, reg, regno, fixed_off_ok);
- if (err < 0)
- return err;
-
- /*
- * Fold the register's constant offset into the insn offset so
- * that is_valid_access() sees the true effective offset.
- */
- if (fixed_off_ok)
- off += reg->var_off.value;
- err = check_ctx_access(env, insn_idx, off, size, t, &info);
- if (err)
- verbose_linfo(env, insn_idx, "; ");
+ err = check_ctx_access(env, insn_idx, regno, off, size, t, &info);
if (!err && t == BPF_READ && value_regno >= 0) {
/* ctx access returns either a scalar, or a
* PTR_TO_PACKET[_META,_END]. In the latter
@@ -8456,22 +8476,16 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
return check_ptr_to_btf_access(env, regs, regno, 0,
access_size, BPF_READ, -1);
case PTR_TO_CTX:
- /* in case the function doesn't know how to access the context,
- * (because we are in a program of type SYSCALL for example), we
- * can not statically check its size.
- * Dynamically check it now.
- */
- if (!env->ops->convert_ctx_access) {
- int offset = access_size - 1;
-
- /* Allow zero-byte read from PTR_TO_CTX */
- if (access_size == 0)
- return zero_size_allowed ? 0 : -EACCES;
-
- return check_mem_access(env, env->insn_idx, regno, offset, BPF_B,
- access_type, -1, false, false);
+ /* Only permit reading or writing syscall context using helper calls. */
+ if (is_var_ctx_off_allowed(env->prog)) {
+ int err = check_mem_region_access(env, regno, 0, access_size, U16_MAX,
+ zero_size_allowed);
+ if (err)
+ return err;
+ if (env->prog->aux->max_ctx_offset < reg->umax_value + access_size)
+ env->prog->aux->max_ctx_offset = reg->umax_value + access_size;
+ return 0;
}
-
fallthrough;
default: /* scalar_value or invalid ptr */
/* Allow zero-byte read from NULL, regardless of pointer type */
@@ -9415,6 +9429,7 @@ static const struct bpf_reg_types mem_types = {
PTR_TO_MEM | MEM_RINGBUF,
PTR_TO_BUF,
PTR_TO_BTF_ID | PTR_TRUSTED,
+ PTR_TO_CTX,
},
};
@@ -9724,6 +9739,17 @@ static int check_func_arg_reg_off(struct bpf_verifier_env *env,
* still need to do checks instead of returning.
*/
return __check_ptr_off_reg(env, reg, regno, true);
+ case PTR_TO_CTX:
+ /*
+ * Allow fixed and variable offsets for syscall context, but
+ * only when the argument is passed as memory, not ctx,
+ * otherwise we may get modified ctx in tail called programs and
+ * global subprogs (that may act as extension prog hooks).
+ */
+ if (arg_type != ARG_PTR_TO_CTX &&
+ resolve_prog_type(env->prog) == BPF_PROG_TYPE_SYSCALL)
+ return 0;
+ fallthrough;
default:
return __check_ptr_off_reg(env, reg, regno, false);
}
@@ -10771,7 +10797,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
* invalid memory access.
*/
} else if (arg->arg_type == ARG_PTR_TO_CTX) {
- ret = check_func_arg_reg_off(env, reg, regno, ARG_DONTCARE);
+ ret = check_func_arg_reg_off(env, reg, regno, ARG_PTR_TO_CTX);
if (ret < 0)
return ret;
/* If function expects ctx type in BTF check that caller
@@ -13645,7 +13671,6 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
}
}
fallthrough;
- case KF_ARG_PTR_TO_CTX:
case KF_ARG_PTR_TO_DYNPTR:
case KF_ARG_PTR_TO_ITER:
case KF_ARG_PTR_TO_LIST_HEAD:
@@ -13663,6 +13688,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
case KF_ARG_PTR_TO_IRQ_FLAG:
case KF_ARG_PTR_TO_RES_SPIN_LOCK:
break;
+ case KF_ARG_PTR_TO_CTX:
+ arg_type = ARG_PTR_TO_CTX;
+ break;
default:
verifier_bug(env, "unknown kfunc arg type %d", kf_arg_type);
return -EFAULT;
diff --git a/tools/testing/selftests/bpf/progs/verifier_ctx.c b/tools/testing/selftests/bpf/progs/verifier_ctx.c
index 371780290c0d..ae756764ffba 100644
--- a/tools/testing/selftests/bpf/progs/verifier_ctx.c
+++ b/tools/testing/selftests/bpf/progs/verifier_ctx.c
@@ -359,7 +359,6 @@ __naked void syscall_ctx_fixed_off_write(void)
: __clobber_all); \
}
-no_rewrite_ctx_access("syscall", syscall, 4, u32);
no_rewrite_ctx_access("kprobe", kprobe, 8, u64);
no_rewrite_ctx_access("tracepoint", tp, 8, u64);
no_rewrite_ctx_access("raw_tp", raw_tp, 8, u64);
diff --git a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
index f02012a2fbaa..2250fc31574d 100644
--- a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
+++ b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
@@ -134,7 +134,6 @@ __noinline __weak int subprog_user_anon_mem(user_struct_t *t)
SEC("?tracepoint")
__failure __log_level(2)
-__msg("invalid bpf_context access")
__msg("Caller passes invalid args into func#1 ('subprog_user_anon_mem')")
int anon_user_mem_invalid(void *ctx)
{
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH bpf-next v4 1/7] bpf: Support variable offsets for syscall PTR_TO_CTX
2026-04-01 12:28 ` [PATCH bpf-next v4 1/7] bpf: Support " Kumar Kartikeya Dwivedi
@ 2026-04-01 12:40 ` Kumar Kartikeya Dwivedi
0 siblings, 0 replies; 13+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-01 12:40 UTC (permalink / raw)
To: bpf
Cc: Emil Tsalapatis, Puranjay Mohan, Mykyta Yatsenko,
Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Tejun Heo, Dan Schatzberg,
kkd, kernel-team
On Wed, 1 Apr 2026 at 14:28, Kumar Kartikeya Dwivedi <memxor@gmail.com> wrote:
>
> Allow accessing PTR_TO_CTX with variable offsets in syscall programs.
> Fixed offsets are already enabled for all program types that do not
> convert their ctx accesses, since the changes we made in the commit
> de6c7d99f898 ("bpf: Relax fixed offset check for PTR_TO_CTX"). Note
> that we also lift the restriction on passing syscall context into
> helpers, which was not permitted before, and passing modified syscall
> context into kfuncs.
>
> The structure of check_mem_access can be mostly shared and preserved,
> but we must use check_mem_region_access to correctly verify access with
> variable offsets.
>
> The check made in check_helper_mem_access is hardened to only allow
> PTR_TO_CTX for syscall programs to be passed in as helper memory. This
> was the original intention of the existing code anyway, and it makes
> little sense for other program types' context to be utilized as a memory
> buffer. In case a convincing example presents itself in the future, this
> check can be relaxed further.
>
> We also no longer use the last-byte access to simulate helper memory
> access, but instead go through check_mem_region_access. Since this no
> longer updates our max_ctx_offset, we must do so manually, to keep track
> of the maximum offset at which the program ctx may be accessed.
>
> Take care to ensure that when arg_type is ARG_PTR_TO_CTX, we do not
> relax any fixed or variable offset constraints around PTR_TO_CTX even in
> syscall programs, and require them to be passed unmodified. There are
> several reasons why this is necessary. First, if we pass a modified ctx,
> then the global subprog's accesses will not update the max_ctx_offset to
> its true maximum offset, and can lead to out of bounds accesses. Second,
> tail called program (or extension program replacing global subprog) where
> their max_ctx_offset exceeds the program they are being called from can
> also cause issues. For the latter, unmodified PTR_TO_CTX is the first
> requirement for the fix, the second is ensuring max_ctx_offset >= the
> program they are being called from, which has to be a separate change
> not made in this commit.
>
> All in all, we can hint using arg_type when we expect ARG_PTR_TO_CTX and
> make our relaxation around offsets conditional on it.
>
> Drop coverage of syscall tests from verifier_ctx.c temporarily for
> negative cases until they are updated in subsequent commits.
>
> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
> Acked-by: Puranjay Mohan <puranjay@kernel.org>
> Acked-by: Mykyta Yatsenko <yatsenko@meta.com>
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> ---
> [...]
>
> @@ -9724,6 +9739,17 @@ static int check_func_arg_reg_off(struct bpf_verifier_env *env,
> * still need to do checks instead of returning.
> */
> return __check_ptr_off_reg(env, reg, regno, true);
> + case PTR_TO_CTX:
> + /*
> + * Allow fixed and variable offsets for syscall context, but
> + * only when the argument is passed as memory, not ctx,
> + * otherwise we may get modified ctx in tail called programs and
> + * global subprogs (that may act as extension prog hooks).
> + */
> + if (arg_type != ARG_PTR_TO_CTX &&
> + resolve_prog_type(env->prog) == BPF_PROG_TYPE_SYSCALL)
Sigh, this can be is_var_ctx_off_allowed(env->prog) for clarity. I
will fix in respin after waiting for comments.
> [...]
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH bpf-next v4 2/7] bpf: Enable unaligned accesses for syscall ctx
2026-04-01 12:28 [PATCH bpf-next v4 0/7] Allow variable offsets for syscall PTR_TO_CTX Kumar Kartikeya Dwivedi
2026-04-01 12:28 ` [PATCH bpf-next v4 1/7] bpf: Support " Kumar Kartikeya Dwivedi
@ 2026-04-01 12:28 ` Kumar Kartikeya Dwivedi
2026-04-06 1:11 ` Emil Tsalapatis
2026-04-01 12:28 ` [PATCH bpf-next v4 3/7] selftests/bpf: Convert ctx tests from ASM to C Kumar Kartikeya Dwivedi
` (4 subsequent siblings)
6 siblings, 1 reply; 13+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-01 12:28 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Tejun Heo, Dan Schatzberg,
kkd, kernel-team
Don't reject usage of fixed unaligned offsets for syscall ctx. Tests
will be added in later commits. Unaligned offsets already work for
variable offsets.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
kernel/bpf/syscall.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 51ade3cde8bb..f06fe9a9b1d3 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -6348,8 +6348,7 @@ static bool syscall_prog_is_valid_access(int off, int size,
{
if (off < 0 || off >= U16_MAX)
return false;
- if (off % size != 0)
- return false;
+ /* No alignment requirements for syscall ctx accesses. */
return true;
}
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH bpf-next v4 2/7] bpf: Enable unaligned accesses for syscall ctx
2026-04-01 12:28 ` [PATCH bpf-next v4 2/7] bpf: Enable unaligned accesses for syscall ctx Kumar Kartikeya Dwivedi
@ 2026-04-06 1:11 ` Emil Tsalapatis
0 siblings, 0 replies; 13+ messages in thread
From: Emil Tsalapatis @ 2026-04-06 1:11 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi, bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Tejun Heo, Dan Schatzberg,
kkd, kernel-team
On Wed Apr 1, 2026 at 8:28 AM EDT, Kumar Kartikeya Dwivedi wrote:
> Don't reject usage of fixed unaligned offsets for syscall ctx. Tests
> will be added in later commits. Unaligned offsets already work for
> variable offsets.
>
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
> ---
> kernel/bpf/syscall.c | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index 51ade3cde8bb..f06fe9a9b1d3 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -6348,8 +6348,7 @@ static bool syscall_prog_is_valid_access(int off, int size,
> {
> if (off < 0 || off >= U16_MAX)
> return false;
> - if (off % size != 0)
> - return false;
> + /* No alignment requirements for syscall ctx accesses. */
> return true;
Nit: Isn't this now just:
return 0 <= off && off < U16_MAX;
?
> }
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH bpf-next v4 3/7] selftests/bpf: Convert ctx tests from ASM to C
2026-04-01 12:28 [PATCH bpf-next v4 0/7] Allow variable offsets for syscall PTR_TO_CTX Kumar Kartikeya Dwivedi
2026-04-01 12:28 ` [PATCH bpf-next v4 1/7] bpf: Support " Kumar Kartikeya Dwivedi
2026-04-01 12:28 ` [PATCH bpf-next v4 2/7] bpf: Enable unaligned accesses for syscall ctx Kumar Kartikeya Dwivedi
@ 2026-04-01 12:28 ` Kumar Kartikeya Dwivedi
2026-04-06 1:20 ` Emil Tsalapatis
2026-04-01 12:28 ` [PATCH bpf-next v4 4/7] selftests/bpf: Add syscall ctx variable offset tests Kumar Kartikeya Dwivedi
` (3 subsequent siblings)
6 siblings, 1 reply; 13+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-01 12:28 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Tejun Heo, Dan Schatzberg,
kkd, kernel-team
Convert existing tests from ASM to C, in prep for future changes to add
more comprehensive tests.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
.../selftests/bpf/prog_tests/verifier.c | 2 +-
.../selftests/bpf/progs/verifier_ctx.c | 78 ++++++++-----------
2 files changed, 35 insertions(+), 45 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index bcf01cb4cfe4..0c50a4305292 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -174,7 +174,7 @@ void test_verifier_cgroup_skb(void) { RUN(verifier_cgroup_skb); }
void test_verifier_cgroup_storage(void) { RUN(verifier_cgroup_storage); }
void test_verifier_const(void) { RUN(verifier_const); }
void test_verifier_const_or(void) { RUN(verifier_const_or); }
-void test_verifier_ctx(void) { RUN(verifier_ctx); }
+void test_verifier_ctx(void) { RUN_TESTS(verifier_ctx); }
void test_verifier_ctx_sk_msg(void) { RUN(verifier_ctx_sk_msg); }
void test_verifier_d_path(void) { RUN(verifier_d_path); }
void test_verifier_default_trusted_ptr(void) { RUN_TESTS(verifier_default_trusted_ptr); }
diff --git a/tools/testing/selftests/bpf/progs/verifier_ctx.c b/tools/testing/selftests/bpf/progs/verifier_ctx.c
index ae756764ffba..4c285ac8fff6 100644
--- a/tools/testing/selftests/bpf/progs/verifier_ctx.c
+++ b/tools/testing/selftests/bpf/progs/verifier_ctx.c
@@ -295,68 +295,58 @@ padding_access("sk_reuseport", sk_reuseport_md, hash, 4);
SEC("syscall")
__description("syscall: write to ctx with fixed offset")
__success
-__naked void syscall_ctx_fixed_off_write(void)
+int syscall_ctx_fixed_off_write(void *ctx)
{
- asm volatile (" \
- r0 = 0; \
- *(u32*)(r1 + 0) = r0; \
- r1 += 4; \
- *(u32*)(r1 + 0) = r0; \
- exit; \
-" ::: __clobber_all);
+ char *p = ctx;
+
+ *(__u32 *)p = 0;
+ *(__u32 *)(p + 4) = 0;
+ return 0;
}
/*
- * Test that program types without convert_ctx_access can dereference
- * their ctx pointer after adding a fixed offset. Variable and negative
- * offsets should still be rejected.
+ * For non-syscall program types without convert_ctx_access, direct ctx
+ * dereference is still allowed after adding a fixed offset, while variable
+ * and negative direct accesses reject.
+ *
+ * Passing ctx as a helper or kfunc memory argument is only permitted for
+ * syscall programs, so the helper and kfunc cases below validate rejection
+ * for non-syscall ctx pointers at fixed, variable, and zero-sized accesses.
*/
-#define no_rewrite_ctx_access(type, name, off, ld_op) \
+#define no_rewrite_ctx_access(type, name, off, load_t) \
SEC(type) \
__description(type ": read ctx at fixed offset") \
__success \
- __naked void no_rewrite_##name##_fixed(void) \
+ int no_rewrite_##name##_fixed(void *ctx) \
{ \
- asm volatile (" \
- r1 += %[__off]; \
- r0 = *(" #ld_op " *)(r1 + 0); \
- r0 = 0; \
- exit;" \
- : \
- : __imm_const(__off, off) \
- : __clobber_all); \
+ char *p = ctx; \
+ volatile load_t val; \
+ \
+ val = *(load_t *)(p + off); \
+ (void)val; \
+ return 0; \
} \
SEC(type) \
__description(type ": reject variable offset ctx access") \
__failure __msg("variable ctx access var_off=") \
- __naked void no_rewrite_##name##_var(void) \
+ int no_rewrite_##name##_var(void *ctx) \
{ \
- asm volatile (" \
- r6 = r1; \
- call %[bpf_get_prandom_u32]; \
- r1 = r6; \
- r0 &= 4; \
- r1 += r0; \
- r0 = *(" #ld_op " *)(r1 + 0); \
- r0 = 0; \
- exit;" \
- : \
- : __imm(bpf_get_prandom_u32) \
- : __clobber_all); \
+ __u64 off_var = bpf_get_prandom_u32(); \
+ char *p = ctx; \
+ \
+ off_var &= 4; \
+ p += off_var; \
+ return *(load_t *)p; \
} \
SEC(type) \
__description(type ": reject negative offset ctx access") \
- __failure __msg("negative offset ctx ptr") \
- __naked void no_rewrite_##name##_neg(void) \
+ __failure __msg("invalid bpf_context access") \
+ int no_rewrite_##name##_neg(void *ctx) \
{ \
- asm volatile (" \
- r1 += %[__neg_off]; \
- r0 = *(" #ld_op " *)(r1 + 0); \
- r0 = 0; \
- exit;" \
- : \
- : __imm_const(__neg_off, -(off)) \
- : __clobber_all); \
+ char *p = ctx; \
+ \
+ p -= 612; \
+ return *(load_t *)p; \
}
no_rewrite_ctx_access("kprobe", kprobe, 8, u64);
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH bpf-next v4 3/7] selftests/bpf: Convert ctx tests from ASM to C
2026-04-01 12:28 ` [PATCH bpf-next v4 3/7] selftests/bpf: Convert ctx tests from ASM to C Kumar Kartikeya Dwivedi
@ 2026-04-06 1:20 ` Emil Tsalapatis
0 siblings, 0 replies; 13+ messages in thread
From: Emil Tsalapatis @ 2026-04-06 1:20 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi, bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Tejun Heo, Dan Schatzberg,
kkd, kernel-team
On Wed Apr 1, 2026 at 8:28 AM EDT, Kumar Kartikeya Dwivedi wrote:
> Convert existing tests from ASM to C, in prep for future changes to add
> more comprehensive tests.
>
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
> ---
> .../selftests/bpf/prog_tests/verifier.c | 2 +-
> .../selftests/bpf/progs/verifier_ctx.c | 78 ++++++++-----------
> 2 files changed, 35 insertions(+), 45 deletions(-)
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
> index bcf01cb4cfe4..0c50a4305292 100644
> --- a/tools/testing/selftests/bpf/prog_tests/verifier.c
> +++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
> @@ -174,7 +174,7 @@ void test_verifier_cgroup_skb(void) { RUN(verifier_cgroup_skb); }
> void test_verifier_cgroup_storage(void) { RUN(verifier_cgroup_storage); }
> void test_verifier_const(void) { RUN(verifier_const); }
> void test_verifier_const_or(void) { RUN(verifier_const_or); }
> -void test_verifier_ctx(void) { RUN(verifier_ctx); }
> +void test_verifier_ctx(void) { RUN_TESTS(verifier_ctx); }
> void test_verifier_ctx_sk_msg(void) { RUN(verifier_ctx_sk_msg); }
> void test_verifier_d_path(void) { RUN(verifier_d_path); }
> void test_verifier_default_trusted_ptr(void) { RUN_TESTS(verifier_default_trusted_ptr); }
> diff --git a/tools/testing/selftests/bpf/progs/verifier_ctx.c b/tools/testing/selftests/bpf/progs/verifier_ctx.c
> index ae756764ffba..4c285ac8fff6 100644
> --- a/tools/testing/selftests/bpf/progs/verifier_ctx.c
> +++ b/tools/testing/selftests/bpf/progs/verifier_ctx.c
> @@ -295,68 +295,58 @@ padding_access("sk_reuseport", sk_reuseport_md, hash, 4);
> SEC("syscall")
> __description("syscall: write to ctx with fixed offset")
> __success
> -__naked void syscall_ctx_fixed_off_write(void)
> +int syscall_ctx_fixed_off_write(void *ctx)
> {
> - asm volatile (" \
> - r0 = 0; \
> - *(u32*)(r1 + 0) = r0; \
> - r1 += 4; \
> - *(u32*)(r1 + 0) = r0; \
> - exit; \
> -" ::: __clobber_all);
> + char *p = ctx;
> +
> + *(__u32 *)p = 0;
> + *(__u32 *)(p + 4) = 0;
> + return 0;
> }
>
> /*
> - * Test that program types without convert_ctx_access can dereference
> - * their ctx pointer after adding a fixed offset. Variable and negative
> - * offsets should still be rejected.
> + * For non-syscall program types without convert_ctx_access, direct ctx
> + * dereference is still allowed after adding a fixed offset, while variable
> + * and negative direct accesses reject.
> + *
> + * Passing ctx as a helper or kfunc memory argument is only permitted for
> + * syscall programs, so the helper and kfunc cases below validate rejection
> + * for non-syscall ctx pointers at fixed, variable, and zero-sized accesses.
> */
> -#define no_rewrite_ctx_access(type, name, off, ld_op) \
> +#define no_rewrite_ctx_access(type, name, off, load_t) \
> SEC(type) \
> __description(type ": read ctx at fixed offset") \
> __success \
> - __naked void no_rewrite_##name##_fixed(void) \
> + int no_rewrite_##name##_fixed(void *ctx) \
> { \
> - asm volatile (" \
> - r1 += %[__off]; \
> - r0 = *(" #ld_op " *)(r1 + 0); \
> - r0 = 0; \
> - exit;" \
> - : \
> - : __imm_const(__off, off) \
> - : __clobber_all); \
> + char *p = ctx; \
> + volatile load_t val; \
> + \
> + val = *(load_t *)(p + off); \
> + (void)val; \
> + return 0; \
> } \
> SEC(type) \
> __description(type ": reject variable offset ctx access") \
> __failure __msg("variable ctx access var_off=") \
> - __naked void no_rewrite_##name##_var(void) \
> + int no_rewrite_##name##_var(void *ctx) \
> { \
> - asm volatile (" \
> - r6 = r1; \
> - call %[bpf_get_prandom_u32]; \
> - r1 = r6; \
> - r0 &= 4; \
> - r1 += r0; \
> - r0 = *(" #ld_op " *)(r1 + 0); \
> - r0 = 0; \
> - exit;" \
> - : \
> - : __imm(bpf_get_prandom_u32) \
> - : __clobber_all); \
> + __u64 off_var = bpf_get_prandom_u32(); \
> + char *p = ctx; \
> + \
> + off_var &= 4; \
> + p += off_var; \
> + return *(load_t *)p; \
> } \
> SEC(type) \
> __description(type ": reject negative offset ctx access") \
> - __failure __msg("negative offset ctx ptr") \
> - __naked void no_rewrite_##name##_neg(void) \
> + __failure __msg("invalid bpf_context access") \
> + int no_rewrite_##name##_neg(void *ctx) \
> { \
> - asm volatile (" \
> - r1 += %[__neg_off]; \
> - r0 = *(" #ld_op " *)(r1 + 0); \
> - r0 = 0; \
> - exit;" \
> - : \
> - : __imm_const(__neg_off, -(off)) \
> - : __clobber_all); \
> + char *p = ctx; \
> + \
> + p -= 612; \
> + return *(load_t *)p; \
> }
>
> no_rewrite_ctx_access("kprobe", kprobe, 8, u64);
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH bpf-next v4 4/7] selftests/bpf: Add syscall ctx variable offset tests
2026-04-01 12:28 [PATCH bpf-next v4 0/7] Allow variable offsets for syscall PTR_TO_CTX Kumar Kartikeya Dwivedi
` (2 preceding siblings ...)
2026-04-01 12:28 ` [PATCH bpf-next v4 3/7] selftests/bpf: Convert ctx tests from ASM to C Kumar Kartikeya Dwivedi
@ 2026-04-01 12:28 ` Kumar Kartikeya Dwivedi
2026-04-01 12:28 ` [PATCH bpf-next v4 5/7] selftests/bpf: Test modified syscall ctx for ARG_PTR_TO_CTX Kumar Kartikeya Dwivedi
` (2 subsequent siblings)
6 siblings, 0 replies; 13+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-01 12:28 UTC (permalink / raw)
To: bpf
Cc: Emil Tsalapatis, Puranjay Mohan, Mykyta Yatsenko,
Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Tejun Heo, Dan Schatzberg,
kkd, kernel-team
Add various tests to exercise fixed and variable offsets on PTR_TO_CTX
for syscall programs, and cover disallowed cases for other program types
lacking convert_ctx_access callback. Load verifier_ctx with CAP_SYS_ADMIN
so that kfunc related logic can be tested. While at it, convert assembly
tests to C. Unfortunately, ctx_pointer_to_helper_2's unpriv case conflicts
with usage of kfuncs in the file and cannot be run.
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
Acked-by: Puranjay Mohan <puranjay@kernel.org>
Acked-by: Mykyta Yatsenko <yatsenko@meta.com>
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
.../selftests/bpf/progs/verifier_ctx.c | 271 +++++++++++++++++-
.../selftests/bpf/test_kmods/bpf_testmod.c | 2 +-
2 files changed, 267 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/bpf/progs/verifier_ctx.c b/tools/testing/selftests/bpf/progs/verifier_ctx.c
index 4c285ac8fff6..6e683dd8002a 100644
--- a/tools/testing/selftests/bpf/progs/verifier_ctx.c
+++ b/tools/testing/selftests/bpf/progs/verifier_ctx.c
@@ -4,6 +4,10 @@
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"
+#include "../test_kmods/bpf_testmod_kfunc.h"
+
+static const char ctx_strncmp_target[] = "ctx";
+static const char ctx_snprintf_fmt[] = "";
SEC("tc")
__description("context stores via BPF_ATOMIC")
@@ -69,7 +73,6 @@ __naked void ctx_pointer_to_helper_1(void)
SEC("socket")
__description("pass modified ctx pointer to helper, 2")
__failure __msg("negative offset ctx ptr R1 off=-612 disallowed")
-__failure_unpriv __msg_unpriv("negative offset ctx ptr R1 off=-612 disallowed")
__naked void ctx_pointer_to_helper_2(void)
{
asm volatile (" \
@@ -292,7 +295,7 @@ padding_access("cgroup/post_bind4", bpf_sock, dst_port, 2);
__failure __msg("invalid bpf_context access")
padding_access("sk_reuseport", sk_reuseport_md, hash, 4);
-SEC("syscall")
+SEC("?syscall")
__description("syscall: write to ctx with fixed offset")
__success
int syscall_ctx_fixed_off_write(void *ctx)
@@ -304,6 +307,174 @@ int syscall_ctx_fixed_off_write(void *ctx)
return 0;
}
+SEC("?syscall")
+__description("syscall: read ctx with fixed offset")
+__success
+int syscall_ctx_fixed_off_read(void *ctx)
+{
+ char *p = ctx;
+ volatile __u32 val;
+
+ val = *(__u32 *)(p + 4);
+ (void)val;
+ return 0;
+}
+
+SEC("?syscall")
+__description("syscall: read ctx with variable offset")
+__success
+int syscall_ctx_var_off_read(void *ctx)
+{
+ __u64 off = bpf_get_prandom_u32();
+ char *p = ctx;
+ volatile __u32 val;
+
+ off &= 0xfc;
+ p += off;
+ val = *(__u32 *)p;
+ (void)val;
+ return 0;
+}
+
+SEC("?syscall")
+__description("syscall: write ctx with variable offset")
+__success
+int syscall_ctx_var_off_write(void *ctx)
+{
+ __u64 off = bpf_get_prandom_u32();
+ char *p = ctx;
+
+ off &= 0xfc;
+ p += off;
+ *(__u32 *)p = 0;
+ return 0;
+}
+
+SEC("?syscall")
+__description("syscall: reject negative variable offset ctx access")
+__failure __msg("min value is negative")
+int syscall_ctx_neg_var_off(void *ctx)
+{
+ __u64 off = bpf_get_prandom_u32();
+ char *p = ctx;
+
+ off &= 4;
+ p -= off;
+ return *(__u32 *)p;
+}
+
+SEC("?syscall")
+__description("syscall: reject unbounded variable offset ctx access")
+__failure __msg("unbounded memory access")
+int syscall_ctx_unbounded_var_off(void *ctx)
+{
+ __u64 off = (__u32)bpf_get_prandom_u32();
+ char *p = ctx;
+
+ off <<= 2;
+ p += off;
+ return *(__u32 *)p;
+}
+
+SEC("?syscall")
+__description("syscall: helper read ctx with fixed offset")
+__success
+int syscall_ctx_helper_fixed_off_read(void *ctx)
+{
+ char *p = ctx;
+
+ p += 4;
+ return bpf_strncmp(p, 4, ctx_strncmp_target);
+}
+
+SEC("?syscall")
+__description("syscall: helper write ctx with fixed offset")
+__success
+int syscall_ctx_helper_fixed_off_write(void *ctx)
+{
+ char *p = ctx;
+
+ p += 4;
+ return bpf_probe_read_kernel(p, 4, 0);
+}
+
+SEC("?syscall")
+__description("syscall: helper read ctx with variable offset")
+__success
+int syscall_ctx_helper_var_off_read(void *ctx)
+{
+ __u64 off = bpf_get_prandom_u32();
+ char *p = ctx;
+
+ off &= 0xfc;
+ p += off;
+ return bpf_strncmp(p, 4, ctx_strncmp_target);
+}
+
+SEC("?syscall")
+__description("syscall: helper write ctx with variable offset")
+__success
+int syscall_ctx_helper_var_off_write(void *ctx)
+{
+ __u64 off = bpf_get_prandom_u32();
+ char *p = ctx;
+
+ off &= 0xfc;
+ p += off;
+ return bpf_probe_read_kernel(p, 4, 0);
+}
+
+SEC("?syscall")
+__description("syscall: helper read zero-sized ctx access")
+__success
+int syscall_ctx_helper_zero_sized_read(void *ctx)
+{
+ return bpf_snprintf(0, 0, ctx_snprintf_fmt, ctx, 0);
+}
+
+SEC("?syscall")
+__description("syscall: helper write zero-sized ctx access")
+__success
+int syscall_ctx_helper_zero_sized_write(void *ctx)
+{
+ return bpf_probe_read_kernel(ctx, 0, 0);
+}
+
+SEC("?syscall")
+__description("syscall: kfunc access ctx with fixed offset")
+__success
+int syscall_ctx_kfunc_fixed_off(void *ctx)
+{
+ char *p = ctx;
+
+ p += 4;
+ bpf_kfunc_call_test_mem_len_pass1(p, 4);
+ return 0;
+}
+
+SEC("?syscall")
+__description("syscall: kfunc access ctx with variable offset")
+__success
+int syscall_ctx_kfunc_var_off(void *ctx)
+{
+ __u64 off = bpf_get_prandom_u32();
+ char *p = ctx;
+
+ off &= 0xfc;
+ p += off;
+ bpf_kfunc_call_test_mem_len_pass1(p, 4);
+ return 0;
+}
+
+SEC("?syscall")
+__description("syscall: kfunc access zero-sized ctx")
+__success
+int syscall_ctx_kfunc_zero_sized(void *ctx)
+{
+ bpf_kfunc_call_test_mem_len_pass1(ctx, 0);
+ return 0;
+}
+
/*
* For non-syscall program types without convert_ctx_access, direct ctx
* dereference is still allowed after adding a fixed offset, while variable
@@ -314,7 +485,7 @@ int syscall_ctx_fixed_off_write(void *ctx)
* for non-syscall ctx pointers at fixed, variable, and zero-sized accesses.
*/
#define no_rewrite_ctx_access(type, name, off, load_t) \
- SEC(type) \
+ SEC("?" type) \
__description(type ": read ctx at fixed offset") \
__success \
int no_rewrite_##name##_fixed(void *ctx) \
@@ -326,7 +497,7 @@ int syscall_ctx_fixed_off_write(void *ctx)
(void)val; \
return 0; \
} \
- SEC(type) \
+ SEC("?" type) \
__description(type ": reject variable offset ctx access") \
__failure __msg("variable ctx access var_off=") \
int no_rewrite_##name##_var(void *ctx) \
@@ -338,7 +509,7 @@ int syscall_ctx_fixed_off_write(void *ctx)
p += off_var; \
return *(load_t *)p; \
} \
- SEC(type) \
+ SEC("?" type) \
__description(type ": reject negative offset ctx access") \
__failure __msg("invalid bpf_context access") \
int no_rewrite_##name##_neg(void *ctx) \
@@ -347,6 +518,96 @@ int syscall_ctx_fixed_off_write(void *ctx)
\
p -= 612; \
return *(load_t *)p; \
+ } \
+ SEC("?" type) \
+ __description(type ": reject helper read ctx at fixed offset") \
+ __failure __msg("dereference of modified ctx ptr") \
+ int no_rewrite_##name##_helper_read_fixed(void *ctx) \
+ { \
+ char *p = ctx; \
+ \
+ p += off; \
+ return bpf_strncmp(p, 4, ctx_strncmp_target); \
+ } \
+ SEC("?" type) \
+ __description(type ": reject helper write ctx at fixed offset") \
+ __failure __msg("dereference of modified ctx ptr") \
+ int no_rewrite_##name##_helper_write_fixed(void *ctx) \
+ { \
+ char *p = ctx; \
+ \
+ p += off; \
+ return bpf_probe_read_kernel(p, 4, 0); \
+ } \
+ SEC("?" type) \
+ __description(type ": reject helper read ctx with variable offset") \
+ __failure __msg("variable ctx access var_off=") \
+ int no_rewrite_##name##_helper_read_var(void *ctx) \
+ { \
+ __u64 off_var = bpf_get_prandom_u32(); \
+ char *p = ctx; \
+ \
+ off_var &= 4; \
+ p += off_var; \
+ return bpf_strncmp(p, 4, ctx_strncmp_target); \
+ } \
+ SEC("?" type) \
+ __description(type ": reject helper write ctx with variable offset") \
+ __failure __msg("variable ctx access var_off=") \
+ int no_rewrite_##name##_helper_write_var(void *ctx) \
+ { \
+ __u64 off_var = bpf_get_prandom_u32(); \
+ char *p = ctx; \
+ \
+ off_var &= 4; \
+ p += off_var; \
+ return bpf_probe_read_kernel(p, 4, 0); \
+ } \
+ SEC("?" type) \
+ __description(type ": reject helper read zero-sized ctx access") \
+ __failure __msg("R4 type=ctx expected=fp") \
+ int no_rewrite_##name##_helper_read_zero(void *ctx) \
+ { \
+ return bpf_snprintf(0, 0, ctx_snprintf_fmt, ctx, 0); \
+ } \
+ SEC("?" type) \
+ __description(type ": reject helper write zero-sized ctx access") \
+ __failure __msg("R1 type=ctx expected=fp") \
+ int no_rewrite_##name##_helper_write_zero(void *ctx) \
+ { \
+ return bpf_probe_read_kernel(ctx, 0, 0); \
+ } \
+ SEC("?" type) \
+ __description(type ": reject kfunc ctx at fixed offset") \
+ __failure __msg("dereference of modified ctx ptr") \
+ int no_rewrite_##name##_kfunc_fixed(void *ctx) \
+ { \
+ char *p = ctx; \
+ \
+ p += off; \
+ bpf_kfunc_call_test_mem_len_pass1(p, 4); \
+ return 0; \
+ } \
+ SEC("?" type) \
+ __description(type ": reject kfunc ctx with variable offset") \
+ __failure __msg("variable ctx access var_off=") \
+ int no_rewrite_##name##_kfunc_var(void *ctx) \
+ { \
+ __u64 off_var = bpf_get_prandom_u32(); \
+ char *p = ctx; \
+ \
+ off_var &= 4; \
+ p += off_var; \
+ bpf_kfunc_call_test_mem_len_pass1(p, 4); \
+ return 0; \
+ } \
+ SEC("?" type) \
+ __description(type ": reject kfunc zero-sized ctx access") \
+ __failure __msg("R1 type=ctx expected=fp") \
+ int no_rewrite_##name##_kfunc_zero(void *ctx) \
+ { \
+ bpf_kfunc_call_test_mem_len_pass1(ctx, 0); \
+ return 0; \
}
no_rewrite_ctx_access("kprobe", kprobe, 8, u64);
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index 061356f10093..d876314a4d67 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -723,6 +723,7 @@ BTF_ID_FLAGS(func, bpf_iter_testmod_seq_next, KF_ITER_NEXT | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_iter_testmod_seq_destroy, KF_ITER_DESTROY)
BTF_ID_FLAGS(func, bpf_iter_testmod_seq_value)
BTF_ID_FLAGS(func, bpf_kfunc_common_test)
+BTF_ID_FLAGS(func, bpf_kfunc_call_test_mem_len_pass1)
BTF_ID_FLAGS(func, bpf_kfunc_dynptr_test)
BTF_ID_FLAGS(func, bpf_kfunc_nested_acquire_nonzero_offset_test, KF_ACQUIRE)
BTF_ID_FLAGS(func, bpf_kfunc_nested_acquire_zero_offset_test, KF_ACQUIRE)
@@ -1287,7 +1288,6 @@ BTF_ID_FLAGS(func, bpf_kfunc_call_test2)
BTF_ID_FLAGS(func, bpf_kfunc_call_test3)
BTF_ID_FLAGS(func, bpf_kfunc_call_test4)
BTF_ID_FLAGS(func, bpf_kfunc_call_test5)
-BTF_ID_FLAGS(func, bpf_kfunc_call_test_mem_len_pass1)
BTF_ID_FLAGS(func, bpf_kfunc_call_test_mem_len_fail1)
BTF_ID_FLAGS(func, bpf_kfunc_call_test_mem_len_fail2)
BTF_ID_FLAGS(func, bpf_kfunc_call_test_acquire, KF_ACQUIRE | KF_RET_NULL)
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH bpf-next v4 5/7] selftests/bpf: Test modified syscall ctx for ARG_PTR_TO_CTX
2026-04-01 12:28 [PATCH bpf-next v4 0/7] Allow variable offsets for syscall PTR_TO_CTX Kumar Kartikeya Dwivedi
` (3 preceding siblings ...)
2026-04-01 12:28 ` [PATCH bpf-next v4 4/7] selftests/bpf: Add syscall ctx variable offset tests Kumar Kartikeya Dwivedi
@ 2026-04-01 12:28 ` Kumar Kartikeya Dwivedi
2026-04-01 12:28 ` [PATCH bpf-next v4 6/7] selftests/bpf: Add tests for unaligned syscall ctx accesses Kumar Kartikeya Dwivedi
2026-04-01 12:28 ` [PATCH bpf-next v4 7/7] selftests/bpf: Add tests for syscall ctx accesses beyond U16_MAX Kumar Kartikeya Dwivedi
6 siblings, 0 replies; 13+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-01 12:28 UTC (permalink / raw)
To: bpf
Cc: Emil Tsalapatis, Puranjay Mohan, Mykyta Yatsenko,
Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Tejun Heo, Dan Schatzberg,
kkd, kernel-team
Ensure that global subprogs and tail calls can only accept an unmodified
PTR_TO_CTX for syscall programs. For all other program types, fixed or
variable offsets on PTR_TO_CTX is rejected when passed into an argument
of any call instruction type, through the unified logic of
check_func_arg_reg_off.
Finally, add a positive example of a case that should succeed with all
our previous changes.
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
Acked-by: Puranjay Mohan <puranjay@kernel.org>
Acked-by: Mykyta Yatsenko <yatsenko@meta.com>
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
.../bpf/progs/verifier_global_subprogs.c | 94 +++++++++++++++++++
1 file changed, 94 insertions(+)
diff --git a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
index 2250fc31574d..1e08aff7532e 100644
--- a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
+++ b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
@@ -357,6 +357,100 @@ int arg_tag_ctx_syscall(void *ctx)
return tracing_subprog_void(ctx) + tracing_subprog_u64(ctx) + tp_whatever(ctx);
}
+__weak int syscall_array_bpf_for(void *ctx __arg_ctx)
+{
+ int *arr = ctx;
+ int i;
+
+ bpf_for(i, 0, 100)
+ arr[i] *= i;
+
+ return 0;
+}
+
+SEC("?syscall")
+__success __log_level(2)
+int arg_tag_ctx_syscall_bpf_for(void *ctx)
+{
+ return syscall_array_bpf_for(ctx);
+}
+
+SEC("syscall")
+__auxiliary
+int syscall_tailcall_target(void *ctx)
+{
+ return syscall_array_bpf_for(ctx);
+}
+
+struct {
+ __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+ __uint(max_entries, 1);
+ __uint(key_size, sizeof(__u32));
+ __array(values, int (void *));
+} syscall_prog_array SEC(".maps") = {
+ .values = {
+ [0] = (void *)&syscall_tailcall_target,
+ },
+};
+
+SEC("?syscall")
+__success __log_level(2)
+int arg_tag_ctx_syscall_tailcall(void *ctx)
+{
+ bpf_tail_call(ctx, &syscall_prog_array, 0);
+ return 0;
+}
+
+SEC("?syscall")
+__failure __log_level(2)
+__msg("dereference of modified ctx ptr R1 off=8 disallowed")
+int arg_tag_ctx_syscall_tailcall_fixed_off_bad(void *ctx)
+{
+ char *p = ctx;
+
+ p += 8;
+ bpf_tail_call(p, &syscall_prog_array, 0);
+ return 0;
+}
+
+SEC("?syscall")
+__failure __log_level(2)
+__msg("variable ctx access var_off=(0x0; 0x4) disallowed")
+int arg_tag_ctx_syscall_tailcall_var_off_bad(void *ctx)
+{
+ __u64 off = bpf_get_prandom_u32();
+ char *p = ctx;
+
+ off &= 4;
+ p += off;
+ bpf_tail_call(p, &syscall_prog_array, 0);
+ return 0;
+}
+
+SEC("?syscall")
+__failure __log_level(2)
+__msg("dereference of modified ctx ptr R1 off=8 disallowed")
+int arg_tag_ctx_syscall_fixed_off_bad(void *ctx)
+{
+ char *p = ctx;
+
+ p += 8;
+ return subprog_ctx_tag(p);
+}
+
+SEC("?syscall")
+__failure __log_level(2)
+__msg("variable ctx access var_off=(0x0; 0x4) disallowed")
+int arg_tag_ctx_syscall_var_off_bad(void *ctx)
+{
+ __u64 off = bpf_get_prandom_u32();
+ char *p = ctx;
+
+ off &= 4;
+ p += off;
+ return subprog_ctx_tag(p);
+}
+
__weak int subprog_dynptr(struct bpf_dynptr *dptr)
{
long *d, t, buf[1] = {};
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH bpf-next v4 6/7] selftests/bpf: Add tests for unaligned syscall ctx accesses
2026-04-01 12:28 [PATCH bpf-next v4 0/7] Allow variable offsets for syscall PTR_TO_CTX Kumar Kartikeya Dwivedi
` (4 preceding siblings ...)
2026-04-01 12:28 ` [PATCH bpf-next v4 5/7] selftests/bpf: Test modified syscall ctx for ARG_PTR_TO_CTX Kumar Kartikeya Dwivedi
@ 2026-04-01 12:28 ` Kumar Kartikeya Dwivedi
2026-04-06 2:00 ` Emil Tsalapatis
2026-04-01 12:28 ` [PATCH bpf-next v4 7/7] selftests/bpf: Add tests for syscall ctx accesses beyond U16_MAX Kumar Kartikeya Dwivedi
6 siblings, 1 reply; 13+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-01 12:28 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Tejun Heo, Dan Schatzberg,
kkd, kernel-team
Add coverage for unaligned access with fixed offsets and variable
offsets, and through helpers or kfuncs.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
.../selftests/bpf/progs/verifier_ctx.c | 133 ++++++++++++++++++
1 file changed, 133 insertions(+)
diff --git a/tools/testing/selftests/bpf/progs/verifier_ctx.c b/tools/testing/selftests/bpf/progs/verifier_ctx.c
index 6e683dd8002a..887cd07ed885 100644
--- a/tools/testing/selftests/bpf/progs/verifier_ctx.c
+++ b/tools/testing/selftests/bpf/progs/verifier_ctx.c
@@ -320,6 +320,30 @@ int syscall_ctx_fixed_off_read(void *ctx)
return 0;
}
+SEC("?syscall")
+__description("syscall: unaligned read ctx with fixed offset")
+__success
+int syscall_ctx_unaligned_fixed_off_read(void *ctx)
+{
+ char *p = ctx;
+ volatile __u32 val;
+
+ val = *(__u32 *)(p + 2);
+ (void)val;
+ return 0;
+}
+
+SEC("?syscall")
+__description("syscall: unaligned write ctx with fixed offset")
+__success
+int syscall_ctx_unaligned_fixed_off_write(void *ctx)
+{
+ char *p = ctx;
+
+ *(__u32 *)(p + 2) = 0;
+ return 0;
+}
+
SEC("?syscall")
__description("syscall: read ctx with variable offset")
__success
@@ -350,6 +374,38 @@ int syscall_ctx_var_off_write(void *ctx)
return 0;
}
+SEC("?syscall")
+__description("syscall: unaligned read ctx with variable offset")
+__success
+int syscall_ctx_unaligned_var_off_read(void *ctx)
+{
+ __u64 off = bpf_get_prandom_u32();
+ char *p = ctx;
+ volatile __u32 val;
+
+ off &= 0xfc;
+ off += 2;
+ p += off;
+ val = *(__u32 *)p;
+ (void)val;
+ return 0;
+}
+
+SEC("?syscall")
+__description("syscall: unaligned write ctx with variable offset")
+__success
+int syscall_ctx_unaligned_var_off_write(void *ctx)
+{
+ __u64 off = bpf_get_prandom_u32();
+ char *p = ctx;
+
+ off &= 0xfc;
+ off += 2;
+ p += off;
+ *(__u32 *)p = 0;
+ return 0;
+}
+
SEC("?syscall")
__description("syscall: reject negative variable offset ctx access")
__failure __msg("min value is negative")
@@ -398,6 +454,28 @@ int syscall_ctx_helper_fixed_off_write(void *ctx)
return bpf_probe_read_kernel(p, 4, 0);
}
+SEC("?syscall")
+__description("syscall: helper unaligned read ctx with fixed offset")
+__success
+int syscall_ctx_helper_unaligned_fixed_off_read(void *ctx)
+{
+ char *p = ctx;
+
+ p += 2;
+ return bpf_strncmp(p, 4, ctx_strncmp_target);
+}
+
+SEC("?syscall")
+__description("syscall: helper unaligned write ctx with fixed offset")
+__success
+int syscall_ctx_helper_unaligned_fixed_off_write(void *ctx)
+{
+ char *p = ctx;
+
+ p += 2;
+ return bpf_probe_read_kernel(p, 4, 0);
+}
+
SEC("?syscall")
__description("syscall: helper read ctx with variable offset")
__success
@@ -424,6 +502,34 @@ int syscall_ctx_helper_var_off_write(void *ctx)
return bpf_probe_read_kernel(p, 4, 0);
}
+SEC("?syscall")
+__description("syscall: helper unaligned read ctx with variable offset")
+__success
+int syscall_ctx_helper_unaligned_var_off_read(void *ctx)
+{
+ __u64 off = bpf_get_prandom_u32();
+ char *p = ctx;
+
+ off &= 0xfc;
+ off += 2;
+ p += off;
+ return bpf_strncmp(p, 4, ctx_strncmp_target);
+}
+
+SEC("?syscall")
+__description("syscall: helper unaligned write ctx with variable offset")
+__success
+int syscall_ctx_helper_unaligned_var_off_write(void *ctx)
+{
+ __u64 off = bpf_get_prandom_u32();
+ char *p = ctx;
+
+ off &= 0xfc;
+ off += 2;
+ p += off;
+ return bpf_probe_read_kernel(p, 4, 0);
+}
+
SEC("?syscall")
__description("syscall: helper read zero-sized ctx access")
__success
@@ -466,6 +572,33 @@ int syscall_ctx_kfunc_var_off(void *ctx)
return 0;
}
+SEC("?syscall")
+__description("syscall: kfunc unaligned access ctx with fixed offset")
+__success
+int syscall_ctx_kfunc_unaligned_fixed_off(void *ctx)
+{
+ char *p = ctx;
+
+ p += 2;
+ bpf_kfunc_call_test_mem_len_pass1(p, 4);
+ return 0;
+}
+
+SEC("?syscall")
+__description("syscall: kfunc unaligned access ctx with variable offset")
+__success
+int syscall_ctx_kfunc_unaligned_var_off(void *ctx)
+{
+ __u64 off = bpf_get_prandom_u32();
+ char *p = ctx;
+
+ off &= 0xfc;
+ off += 2;
+ p += off;
+ bpf_kfunc_call_test_mem_len_pass1(p, 4);
+ return 0;
+}
+
SEC("?syscall")
__description("syscall: kfunc access zero-sized ctx")
__success
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH bpf-next v4 6/7] selftests/bpf: Add tests for unaligned syscall ctx accesses
2026-04-01 12:28 ` [PATCH bpf-next v4 6/7] selftests/bpf: Add tests for unaligned syscall ctx accesses Kumar Kartikeya Dwivedi
@ 2026-04-06 2:00 ` Emil Tsalapatis
0 siblings, 0 replies; 13+ messages in thread
From: Emil Tsalapatis @ 2026-04-06 2:00 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi, bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Tejun Heo, Dan Schatzberg,
kkd, kernel-team
On Wed Apr 1, 2026 at 8:28 AM EDT, Kumar Kartikeya Dwivedi wrote:
> Add coverage for unaligned access with fixed offsets and variable
> offsets, and through helpers or kfuncs.
>
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
There is some duplication with the variable offset tests, but it
makes the tests self-contianed and readily understandable so no
use trying to refactor or anything.
> ---
> .../selftests/bpf/progs/verifier_ctx.c | 133 ++++++++++++++++++
> 1 file changed, 133 insertions(+)
>
> diff --git a/tools/testing/selftests/bpf/progs/verifier_ctx.c b/tools/testing/selftests/bpf/progs/verifier_ctx.c
> index 6e683dd8002a..887cd07ed885 100644
> --- a/tools/testing/selftests/bpf/progs/verifier_ctx.c
> +++ b/tools/testing/selftests/bpf/progs/verifier_ctx.c
> @@ -320,6 +320,30 @@ int syscall_ctx_fixed_off_read(void *ctx)
> return 0;
> }
>
> +SEC("?syscall")
> +__description("syscall: unaligned read ctx with fixed offset")
> +__success
> +int syscall_ctx_unaligned_fixed_off_read(void *ctx)
> +{
> + char *p = ctx;
> + volatile __u32 val;
> +
> + val = *(__u32 *)(p + 2);
> + (void)val;
> + return 0;
> +}
> +
> +SEC("?syscall")
> +__description("syscall: unaligned write ctx with fixed offset")
> +__success
> +int syscall_ctx_unaligned_fixed_off_write(void *ctx)
> +{
> + char *p = ctx;
> +
> + *(__u32 *)(p + 2) = 0;
> + return 0;
> +}
> +
> SEC("?syscall")
> __description("syscall: read ctx with variable offset")
> __success
> @@ -350,6 +374,38 @@ int syscall_ctx_var_off_write(void *ctx)
> return 0;
> }
>
> +SEC("?syscall")
> +__description("syscall: unaligned read ctx with variable offset")
> +__success
> +int syscall_ctx_unaligned_var_off_read(void *ctx)
> +{
> + __u64 off = bpf_get_prandom_u32();
> + char *p = ctx;
> + volatile __u32 val;
> +
> + off &= 0xfc;
> + off += 2;
> + p += off;
> + val = *(__u32 *)p;
> + (void)val;
> + return 0;
> +}
> +
> +SEC("?syscall")
> +__description("syscall: unaligned write ctx with variable offset")
> +__success
> +int syscall_ctx_unaligned_var_off_write(void *ctx)
> +{
> + __u64 off = bpf_get_prandom_u32();
> + char *p = ctx;
> +
> + off &= 0xfc;
> + off += 2;
> + p += off;
> + *(__u32 *)p = 0;
> + return 0;
> +}
> +
> SEC("?syscall")
> __description("syscall: reject negative variable offset ctx access")
> __failure __msg("min value is negative")
> @@ -398,6 +454,28 @@ int syscall_ctx_helper_fixed_off_write(void *ctx)
> return bpf_probe_read_kernel(p, 4, 0);
> }
>
> +SEC("?syscall")
> +__description("syscall: helper unaligned read ctx with fixed offset")
> +__success
> +int syscall_ctx_helper_unaligned_fixed_off_read(void *ctx)
> +{
> + char *p = ctx;
> +
> + p += 2;
> + return bpf_strncmp(p, 4, ctx_strncmp_target);
> +}
> +
> +SEC("?syscall")
> +__description("syscall: helper unaligned write ctx with fixed offset")
> +__success
> +int syscall_ctx_helper_unaligned_fixed_off_write(void *ctx)
> +{
> + char *p = ctx;
> +
> + p += 2;
> + return bpf_probe_read_kernel(p, 4, 0);
> +}
> +
> SEC("?syscall")
> __description("syscall: helper read ctx with variable offset")
> __success
> @@ -424,6 +502,34 @@ int syscall_ctx_helper_var_off_write(void *ctx)
> return bpf_probe_read_kernel(p, 4, 0);
> }
>
> +SEC("?syscall")
> +__description("syscall: helper unaligned read ctx with variable offset")
> +__success
> +int syscall_ctx_helper_unaligned_var_off_read(void *ctx)
> +{
> + __u64 off = bpf_get_prandom_u32();
> + char *p = ctx;
> +
> + off &= 0xfc;
> + off += 2;
> + p += off;
> + return bpf_strncmp(p, 4, ctx_strncmp_target);
> +}
> +
> +SEC("?syscall")
> +__description("syscall: helper unaligned write ctx with variable offset")
> +__success
> +int syscall_ctx_helper_unaligned_var_off_write(void *ctx)
> +{
> + __u64 off = bpf_get_prandom_u32();
> + char *p = ctx;
> +
> + off &= 0xfc;
> + off += 2;
> + p += off;
> + return bpf_probe_read_kernel(p, 4, 0);
> +}
> +
> SEC("?syscall")
> __description("syscall: helper read zero-sized ctx access")
> __success
> @@ -466,6 +572,33 @@ int syscall_ctx_kfunc_var_off(void *ctx)
> return 0;
> }
>
> +SEC("?syscall")
> +__description("syscall: kfunc unaligned access ctx with fixed offset")
> +__success
> +int syscall_ctx_kfunc_unaligned_fixed_off(void *ctx)
> +{
> + char *p = ctx;
> +
> + p += 2;
> + bpf_kfunc_call_test_mem_len_pass1(p, 4);
> + return 0;
> +}
> +
> +SEC("?syscall")
> +__description("syscall: kfunc unaligned access ctx with variable offset")
> +__success
> +int syscall_ctx_kfunc_unaligned_var_off(void *ctx)
> +{
> + __u64 off = bpf_get_prandom_u32();
> + char *p = ctx;
> +
> + off &= 0xfc;
> + off += 2;
> + p += off;
> + bpf_kfunc_call_test_mem_len_pass1(p, 4);
> + return 0;
> +}
> +
> SEC("?syscall")
> __description("syscall: kfunc access zero-sized ctx")
> __success
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH bpf-next v4 7/7] selftests/bpf: Add tests for syscall ctx accesses beyond U16_MAX
2026-04-01 12:28 [PATCH bpf-next v4 0/7] Allow variable offsets for syscall PTR_TO_CTX Kumar Kartikeya Dwivedi
` (5 preceding siblings ...)
2026-04-01 12:28 ` [PATCH bpf-next v4 6/7] selftests/bpf: Add tests for unaligned syscall ctx accesses Kumar Kartikeya Dwivedi
@ 2026-04-01 12:28 ` Kumar Kartikeya Dwivedi
2026-04-06 2:02 ` Emil Tsalapatis
6 siblings, 1 reply; 13+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-04-01 12:28 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Tejun Heo, Dan Schatzberg,
kkd, kernel-team
Ensure we reject programs that access beyond the maximum syscall ctx
size, i.e. U16_MAX either through direct accesses or helpers/kfuncs.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
.../selftests/bpf/progs/verifier_ctx.c | 108 ++++++++++++++++++
1 file changed, 108 insertions(+)
diff --git a/tools/testing/selftests/bpf/progs/verifier_ctx.c b/tools/testing/selftests/bpf/progs/verifier_ctx.c
index 887cd07ed885..7856dad3d1f3 100644
--- a/tools/testing/selftests/bpf/progs/verifier_ctx.c
+++ b/tools/testing/selftests/bpf/progs/verifier_ctx.c
@@ -406,6 +406,37 @@ int syscall_ctx_unaligned_var_off_write(void *ctx)
return 0;
}
+SEC("?syscall")
+__description("syscall: reject ctx access past U16_MAX with fixed offset")
+__failure __msg("outside of the allowed memory range")
+int syscall_ctx_u16_max_fixed_off(void *ctx)
+{
+ char *p = ctx;
+ volatile __u32 val;
+
+ p += 65535;
+ val = *(__u32 *)p;
+ (void)val;
+ return 0;
+}
+
+SEC("?syscall")
+__description("syscall: reject ctx access past U16_MAX with variable offset")
+__failure __msg("outside of the allowed memory range")
+int syscall_ctx_u16_max_var_off(void *ctx)
+{
+ __u64 off = bpf_get_prandom_u32();
+ char *p = ctx;
+ volatile __u32 val;
+
+ off &= 0xffff;
+ off += 1;
+ p += off;
+ val = *(__u32 *)p;
+ (void)val;
+ return 0;
+}
+
SEC("?syscall")
__description("syscall: reject negative variable offset ctx access")
__failure __msg("min value is negative")
@@ -530,6 +561,56 @@ int syscall_ctx_helper_unaligned_var_off_write(void *ctx)
return bpf_probe_read_kernel(p, 4, 0);
}
+SEC("?syscall")
+__description("syscall: reject helper read ctx past U16_MAX with fixed offset")
+__failure __msg("outside of the allowed memory range")
+int syscall_ctx_helper_u16_max_fixed_off_read(void *ctx)
+{
+ char *p = ctx;
+
+ p += 65535;
+ return bpf_strncmp(p, 4, ctx_strncmp_target);
+}
+
+SEC("?syscall")
+__description("syscall: reject helper write ctx past U16_MAX with fixed offset")
+__failure __msg("outside of the allowed memory range")
+int syscall_ctx_helper_u16_max_fixed_off_write(void *ctx)
+{
+ char *p = ctx;
+
+ p += 65535;
+ return bpf_probe_read_kernel(p, 4, 0);
+}
+
+SEC("?syscall")
+__description("syscall: reject helper read ctx past U16_MAX with variable offset")
+__failure __msg("outside of the allowed memory range")
+int syscall_ctx_helper_u16_max_var_off_read(void *ctx)
+{
+ __u64 off = bpf_get_prandom_u32();
+ char *p = ctx;
+
+ off &= 0xffff;
+ off += 1;
+ p += off;
+ return bpf_strncmp(p, 4, ctx_strncmp_target);
+}
+
+SEC("?syscall")
+__description("syscall: reject helper write ctx past U16_MAX with variable offset")
+__failure __msg("outside of the allowed memory range")
+int syscall_ctx_helper_u16_max_var_off_write(void *ctx)
+{
+ __u64 off = bpf_get_prandom_u32();
+ char *p = ctx;
+
+ off &= 0xffff;
+ off += 1;
+ p += off;
+ return bpf_probe_read_kernel(p, 4, 0);
+}
+
SEC("?syscall")
__description("syscall: helper read zero-sized ctx access")
__success
@@ -599,6 +680,33 @@ int syscall_ctx_kfunc_unaligned_var_off(void *ctx)
return 0;
}
+SEC("?syscall")
+__description("syscall: reject kfunc ctx access past U16_MAX with fixed offset")
+__failure __msg("outside of the allowed memory range")
+int syscall_ctx_kfunc_u16_max_fixed_off(void *ctx)
+{
+ char *p = ctx;
+
+ p += 65535;
+ bpf_kfunc_call_test_mem_len_pass1(p, 4);
+ return 0;
+}
+
+SEC("?syscall")
+__description("syscall: reject kfunc ctx access past U16_MAX with variable offset")
+__failure __msg("outside of the allowed memory range")
+int syscall_ctx_kfunc_u16_max_var_off(void *ctx)
+{
+ __u64 off = bpf_get_prandom_u32();
+ char *p = ctx;
+
+ off &= 0xffff;
+ off += 1;
+ p += off;
+ bpf_kfunc_call_test_mem_len_pass1(p, 4);
+ return 0;
+}
+
SEC("?syscall")
__description("syscall: kfunc access zero-sized ctx")
__success
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH bpf-next v4 7/7] selftests/bpf: Add tests for syscall ctx accesses beyond U16_MAX
2026-04-01 12:28 ` [PATCH bpf-next v4 7/7] selftests/bpf: Add tests for syscall ctx accesses beyond U16_MAX Kumar Kartikeya Dwivedi
@ 2026-04-06 2:02 ` Emil Tsalapatis
0 siblings, 0 replies; 13+ messages in thread
From: Emil Tsalapatis @ 2026-04-06 2:02 UTC (permalink / raw)
To: Kumar Kartikeya Dwivedi, bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
Martin KaFai Lau, Eduard Zingerman, Tejun Heo, Dan Schatzberg,
kkd, kernel-team
On Wed Apr 1, 2026 at 8:28 AM EDT, Kumar Kartikeya Dwivedi wrote:
> Ensure we reject programs that access beyond the maximum syscall ctx
> size, i.e. U16_MAX either through direct accesses or helpers/kfuncs.
>
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
> ---
> .../selftests/bpf/progs/verifier_ctx.c | 108 ++++++++++++++++++
> 1 file changed, 108 insertions(+)
>
> diff --git a/tools/testing/selftests/bpf/progs/verifier_ctx.c b/tools/testing/selftests/bpf/progs/verifier_ctx.c
> index 887cd07ed885..7856dad3d1f3 100644
> --- a/tools/testing/selftests/bpf/progs/verifier_ctx.c
> +++ b/tools/testing/selftests/bpf/progs/verifier_ctx.c
> @@ -406,6 +406,37 @@ int syscall_ctx_unaligned_var_off_write(void *ctx)
> return 0;
> }
>
> +SEC("?syscall")
> +__description("syscall: reject ctx access past U16_MAX with fixed offset")
> +__failure __msg("outside of the allowed memory range")
> +int syscall_ctx_u16_max_fixed_off(void *ctx)
> +{
> + char *p = ctx;
> + volatile __u32 val;
> +
> + p += 65535;
> + val = *(__u32 *)p;
> + (void)val;
> + return 0;
> +}
> +
> +SEC("?syscall")
> +__description("syscall: reject ctx access past U16_MAX with variable offset")
> +__failure __msg("outside of the allowed memory range")
> +int syscall_ctx_u16_max_var_off(void *ctx)
> +{
> + __u64 off = bpf_get_prandom_u32();
> + char *p = ctx;
> + volatile __u32 val;
> +
> + off &= 0xffff;
> + off += 1;
> + p += off;
> + val = *(__u32 *)p;
> + (void)val;
> + return 0;
> +}
> +
> SEC("?syscall")
> __description("syscall: reject negative variable offset ctx access")
> __failure __msg("min value is negative")
> @@ -530,6 +561,56 @@ int syscall_ctx_helper_unaligned_var_off_write(void *ctx)
> return bpf_probe_read_kernel(p, 4, 0);
> }
>
> +SEC("?syscall")
> +__description("syscall: reject helper read ctx past U16_MAX with fixed offset")
> +__failure __msg("outside of the allowed memory range")
> +int syscall_ctx_helper_u16_max_fixed_off_read(void *ctx)
> +{
> + char *p = ctx;
> +
> + p += 65535;
> + return bpf_strncmp(p, 4, ctx_strncmp_target);
> +}
> +
> +SEC("?syscall")
> +__description("syscall: reject helper write ctx past U16_MAX with fixed offset")
> +__failure __msg("outside of the allowed memory range")
> +int syscall_ctx_helper_u16_max_fixed_off_write(void *ctx)
> +{
> + char *p = ctx;
> +
> + p += 65535;
> + return bpf_probe_read_kernel(p, 4, 0);
> +}
> +
> +SEC("?syscall")
> +__description("syscall: reject helper read ctx past U16_MAX with variable offset")
> +__failure __msg("outside of the allowed memory range")
> +int syscall_ctx_helper_u16_max_var_off_read(void *ctx)
> +{
> + __u64 off = bpf_get_prandom_u32();
> + char *p = ctx;
> +
> + off &= 0xffff;
> + off += 1;
> + p += off;
> + return bpf_strncmp(p, 4, ctx_strncmp_target);
> +}
> +
> +SEC("?syscall")
> +__description("syscall: reject helper write ctx past U16_MAX with variable offset")
> +__failure __msg("outside of the allowed memory range")
> +int syscall_ctx_helper_u16_max_var_off_write(void *ctx)
> +{
> + __u64 off = bpf_get_prandom_u32();
> + char *p = ctx;
> +
> + off &= 0xffff;
> + off += 1;
> + p += off;
> + return bpf_probe_read_kernel(p, 4, 0);
> +}
> +
> SEC("?syscall")
> __description("syscall: helper read zero-sized ctx access")
> __success
> @@ -599,6 +680,33 @@ int syscall_ctx_kfunc_unaligned_var_off(void *ctx)
> return 0;
> }
>
> +SEC("?syscall")
> +__description("syscall: reject kfunc ctx access past U16_MAX with fixed offset")
> +__failure __msg("outside of the allowed memory range")
> +int syscall_ctx_kfunc_u16_max_fixed_off(void *ctx)
> +{
> + char *p = ctx;
> +
> + p += 65535;
> + bpf_kfunc_call_test_mem_len_pass1(p, 4);
> + return 0;
> +}
> +
> +SEC("?syscall")
> +__description("syscall: reject kfunc ctx access past U16_MAX with variable offset")
> +__failure __msg("outside of the allowed memory range")
> +int syscall_ctx_kfunc_u16_max_var_off(void *ctx)
> +{
> + __u64 off = bpf_get_prandom_u32();
> + char *p = ctx;
> +
> + off &= 0xffff;
> + off += 1;
> + p += off;
> + bpf_kfunc_call_test_mem_len_pass1(p, 4);
> + return 0;
> +}
> +
> SEC("?syscall")
> __description("syscall: kfunc access zero-sized ctx")
> __success
^ permalink raw reply [flat|nested] 13+ messages in thread