]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
selftests/bpf: Add syscall ctx variable offset tests
authorKumar Kartikeya Dwivedi <memxor@gmail.com>
Mon, 6 Apr 2026 19:43:58 +0000 (21:43 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Mon, 6 Apr 2026 22:27:26 +0000 (15:27 -0700)
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>
Link: https://lore.kernel.org/r/20260406194403.1649608-5-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/progs/verifier_ctx.c
tools/testing/selftests/bpf/test_kmods/bpf_testmod.c

index 4c285ac8fff6ebc3f63aa6778483a80929b1ae85..6e683dd8002a702b477708b9f72e8883ac8b8e65 100644 (file)
@@ -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);
index 061356f10093d35e5f6a89074d6c67e1a074e3fc..d876314a4d67e004061c7100981e764bad5e81d0 100644 (file)
@@ -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)