Skip to content

Commit 446751f

Browse files
committed
PTR_TO_MEM for multi-level pointer parameters
1 parent 8d76c66 commit 446751f

File tree

10 files changed

+763
-3
lines changed

10 files changed

+763
-3
lines changed

kernel/bpf/btf.c

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,20 @@ const struct btf_type *btf_type_resolve_func_ptr(const struct btf *btf,
760760
return NULL;
761761
}
762762

763+
static bool is_multilevel_ptr(const struct btf *btf, const struct btf_type *t)
764+
{
765+
int i;
766+
u32 depth = btf_type_is_ptr(t) ? 1 : 0;
767+
768+
t = btf_type_resolve_ptr(btf, t->type, NULL);
769+
for (i = 0; !!t && depth < 2; i++) {
770+
depth += 1;
771+
t = btf_type_resolve_ptr(btf, t->type, NULL);
772+
}
773+
774+
return depth > 1;
775+
}
776+
763777
/* Types that act only as a source, not sink or intermediate
764778
* type when resolving.
765779
*/
@@ -6790,6 +6804,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
67906804
const char *tag_value;
67916805
u32 nr_args, arg;
67926806
int i, ret;
6807+
bool trusted, nullable;
67936808

67946809
if (off % 8) {
67956810
bpf_log(log, "func '%s' offset %d is not multiple of 8\n",
@@ -6927,11 +6942,35 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
69276942
}
69286943
}
69296944

6945+
trusted = prog_args_trusted(prog);
6946+
nullable = btf_param_match_suffix(btf, &args[arg], "__nullable");
6947+
6948+
if (is_multilevel_ptr(btf, t)) {
6949+
info->reg_type = PTR_TO_MEM | MEM_RDONLY;
6950+
if (!trusted)
6951+
info->reg_type |= PTR_UNTRUSTED;
6952+
6953+
if (nullable)
6954+
info->reg_type |= PTR_MAYBE_NULL;
6955+
6956+
/* The memory size cannot be determined without following the t->type. */
6957+
while (btf_type_is_modifier(t)) {
6958+
t = btf_type_by_id(btf, t->type);
6959+
}
6960+
6961+
WARN_ON(!btf_type_is_ptr(t));
6962+
BUG_ON(!btf_type_is_ptr(t)); // remove before submitting patches
6963+
6964+
info->btf_id = t->type;
6965+
info->btf = btf;
6966+
return true;
6967+
}
6968+
69306969
info->reg_type = PTR_TO_BTF_ID;
6931-
if (prog_args_trusted(prog))
6970+
if (trusted)
69326971
info->reg_type |= PTR_TRUSTED;
69336972

6934-
if (btf_param_match_suffix(btf, &args[arg], "__nullable"))
6973+
if (nullable)
69356974
info->reg_type |= PTR_MAYBE_NULL;
69366975

69376976
if (prog->expected_attach_type == BPF_TRACE_RAW_TP) {
@@ -7002,6 +7041,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
70027041
info->btf_id = t->type;
70037042
t = btf_type_by_id(btf, t->type);
70047043
}
7044+
70057045
if (!btf_type_is_struct(t)) {
70067046
bpf_log(log,
70077047
"func '%s' arg%d type %s is not a struct\n",

kernel/bpf/verifier.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7789,6 +7789,16 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
77897789
regs[value_regno].btf = info.btf;
77907790
regs[value_regno].btf_id = info.btf_id;
77917791
regs[value_regno].ref_obj_id = info.ref_obj_id;
7792+
} else if (base_type(info.reg_type) == PTR_TO_MEM) {
7793+
u32 mem_size;
7794+
const struct btf_type *t;
7795+
t = btf_resolve_size(info.btf, btf_type_by_id(info.btf, info.btf_id), &mem_size);
7796+
if (IS_ERR(t)) {
7797+
verbose(env, "R%d mem size cannot be determined\n", value_regno);
7798+
return -EACCES;
7799+
}
7800+
BUG_ON(mem_size != sizeof(void*)); // remove before submitting a patch
7801+
regs[value_regno].mem_size = mem_size;
77927802
}
77937803
}
77947804
regs[value_regno].type = info.reg_type;

net/bpf/test_run.c

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <net/netdev_rx_queue.h>
2525
#include <net/xdp.h>
2626
#include <net/netfilter/nf_bpf_link.h>
27+
#include <linux/set_memory.h>
2728

2829
#define CREATE_TRACE_POINTS
2930
#include <trace/events/bpf_test_run.h>
@@ -563,6 +564,41 @@ noinline int bpf_fentry_test10(const void *a)
563564
return (long)a;
564565
}
565566

567+
struct bpf_fentry_test_dptr_t {
568+
int value;
569+
};
570+
571+
/* Test fentry/fexit with nullable double pointer parameter */
572+
noinline int bpf_fentry_test11_double_ptr_nullable(struct bpf_fentry_test_dptr_t **dptr__nullable)
573+
{
574+
if (!dptr__nullable) {
575+
return -1;
576+
}
577+
578+
return (*dptr__nullable)->value;
579+
}
580+
581+
/* Test fentry/fexit with non-nullable double pointer parameter */
582+
noinline int bpf_fentry_test12_double_ptr(struct bpf_fentry_test_dptr_t **dptr)
583+
{
584+
return (long)dptr;
585+
}
586+
587+
/* Test fentry/fexit with void double pointer parameter and address validation */
588+
noinline int bpf_fentry_test13_void_double_ptr(void **dptr)
589+
{
590+
return virt_addr_valid(dptr);
591+
}
592+
593+
/* Test fentry/fexit with void triple pointer parameter. The volatile specifier
594+
* doesn't affect function parameter behavior, but it is retained in the BTF type
595+
* descriptor, forcing the verifier to walk through the type modifier chain.
596+
*/
597+
noinline int bpf_fentry_test14_void_triple_ptr(void **const *volatile dptr)
598+
{
599+
return (long)dptr;
600+
}
601+
566602
noinline void bpf_fentry_test_sinfo(struct skb_shared_info *sinfo)
567603
{
568604
}
@@ -670,18 +706,103 @@ static void *bpf_test_init(const union bpf_attr *kattr, u32 user_size,
670706
return data;
671707
}
672708

709+
/* Create a guaranteed-invalid address for testing copy_from_kernel_nofault.
710+
* Returns a valid pointer to memory that has been made non-present,
711+
* so any access will fault. Must be freed with free_invalid_test_address().
712+
*/
713+
static void *create_invalid_test_address(void)
714+
{
715+
#if defined(CONFIG_ARCH_HAS_SET_MEMORY) && \
716+
(defined(CONFIG_X86) || defined(CONFIG_PPC))
717+
void *addr;
718+
719+
addr = vmalloc(PAGE_SIZE);
720+
if (!addr)
721+
return NULL;
722+
723+
/* Make it non-present - any access will fault */
724+
if (set_memory_np((unsigned long)addr, 1)) {
725+
vfree(addr);
726+
return NULL;
727+
}
728+
729+
return addr;
730+
#elif defined(CONFIG_ARCH_HAS_SET_DIRECT_MAP)
731+
struct page *page;
732+
733+
page = alloc_page(GFP_KERNEL);
734+
if (!page)
735+
return NULL;
736+
737+
/* Remove from direct map - any access will fault */
738+
if (set_direct_map_invalid_noflush(page)) {
739+
__free_page(page);
740+
return NULL;
741+
}
742+
flush_tlb_kernel_range((unsigned long)page_address(page),
743+
(unsigned long)page_address(page) + PAGE_SIZE);
744+
745+
return page_address(page);
746+
#else
747+
/* Fallback: return a canonical invalid address.
748+
* On 64-bit: middle of address space (non-canonical hole on x86-64).
749+
* On 32-bit: high user address unlikely to be mapped.
750+
* This is just a number, not allocated memory.
751+
*/
752+
if (!virt_addr_valid(VMALLOC_END + 0x1000))
753+
return VMALLOC_END + 0x1000;
754+
else
755+
#ifdef CONFIG_64BIT
756+
return (void *)0x0010000000000000UL;
757+
#else
758+
return (void *)0xA0000000UL;
759+
#endif
760+
#endif
761+
}
762+
763+
/* Free an invalid test address created by create_invalid_test_address().
764+
* Restores the page to present state before freeing.
765+
*/
766+
static void free_invalid_test_address(void *addr)
767+
{
768+
if (!addr)
769+
return;
770+
771+
#if defined(CONFIG_ARCH_HAS_SET_MEMORY) && \
772+
(defined(CONFIG_X86) || defined(CONFIG_PPC))
773+
set_memory_p((unsigned long)addr, 1);
774+
vfree(addr);
775+
#elif defined(CONFIG_ARCH_HAS_SET_DIRECT_MAP)
776+
struct page *page = virt_to_page(addr);
777+
778+
set_direct_map_default_noflush(page);
779+
flush_tlb_kernel_range((unsigned long)addr,
780+
(unsigned long)addr + PAGE_SIZE);
781+
__free_page(page);
782+
#else
783+
/* Fallback case: addr is just a constant, nothing to free */
784+
#endif
785+
}
786+
673787
int bpf_prog_test_run_tracing(struct bpf_prog *prog,
674788
const union bpf_attr *kattr,
675789
union bpf_attr __user *uattr)
676790
{
677791
struct bpf_fentry_test_t arg = {};
792+
struct bpf_fentry_test_dptr_t dptr_struct = { .value = 1979 };
793+
struct bpf_fentry_test_dptr_t *dptr_ptr = &dptr_struct;
794+
void *invalid_addr;
678795
u16 side_effect = 0, ret = 0;
679796
int b = 2, err = -EFAULT;
680797
u32 retval = 0;
681798

682799
if (kattr->test.flags || kattr->test.cpu || kattr->test.batch_size)
683800
return -EINVAL;
684801

802+
invalid_addr = create_invalid_test_address();
803+
if (!invalid_addr)
804+
return -ENOMEM;
805+
685806
switch (prog->expected_attach_type) {
686807
case BPF_TRACE_FENTRY:
687808
case BPF_TRACE_FEXIT:
@@ -695,7 +816,13 @@ int bpf_prog_test_run_tracing(struct bpf_prog *prog,
695816
bpf_fentry_test7((struct bpf_fentry_test_t *)0) != 0 ||
696817
bpf_fentry_test8(&arg) != 0 ||
697818
bpf_fentry_test9(&retval) != 0 ||
698-
bpf_fentry_test10((void *)0) != 0)
819+
bpf_fentry_test10((void *)0) != 0 ||
820+
bpf_fentry_test11_double_ptr_nullable(&dptr_ptr) != 1979 ||
821+
bpf_fentry_test12_double_ptr((struct bpf_fentry_test_dptr_t **)17) != 17 ||
822+
bpf_fentry_test13_void_double_ptr((void**)19) != virt_addr_valid((void*)19) ||
823+
bpf_fentry_test13_void_double_ptr((void**)invalid_addr) != virt_addr_valid(invalid_addr) ||
824+
bpf_fentry_test13_void_double_ptr((void**)ERR_PTR(-ENOMEM)) != virt_addr_valid(ERR_PTR(-ENOMEM)) ||
825+
bpf_fentry_test14_void_triple_ptr((void**const *volatile)ERR_PTR(-ENOMEM)) != (int)(long)ERR_PTR(-ENOMEM))
699826
goto out;
700827
break;
701828
case BPF_MODIFY_RETURN:
@@ -717,6 +844,7 @@ int bpf_prog_test_run_tracing(struct bpf_prog *prog,
717844

718845
err = 0;
719846
out:
847+
free_invalid_test_address(invalid_addr);
720848
trace_bpf_test_finish(&err);
721849
return err;
722850
}

0 commit comments

Comments
 (0)