mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 10:04:04 +02:00
selftests/bpf: Add syscall ctx variable offset tests
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>
This commit is contained in:
parent
02f500ce01
commit
5a34139b27
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user