mirror of
https://github.com/torvalds/linux.git
synced 2026-09-29 04:22:03 +02:00
The trampoline reads the __arena flag from the btf_func_model per argument but stores the ctx one register slot at a time, so the two only line up if every preceding argument occupies exactly one slot. Every arena-bearing member of bpf_testmod_ops3 takes single-slot arguments, so nothing exercises the mapping and a mis-indexed arg_flags lookup would go unnoticed on any architecture. Add test_arena_multislot(), whose first argument is a 16-byte struct passed by value. It fills ctx[0] and ctx[1], putting the arena pointer at argument index one but slot two. The callback checks both halves of the struct before dereferencing ctx[2], so a JIT that walks registers instead of arguments converts the wrong slot and fails the test. Signed-off-by: Puranjay Mohan <puranjay@kernel.org> Acked-by: Xu Kuohai <xukuohai@huawei.com> Link: https://lore.kernel.org/bpf/20260813190356.335181-8-puranjay@kernel.org Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
140 lines
3.2 KiB
C
140 lines
3.2 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
|
|
|
|
#define BPF_NO_KFUNC_PROTOTYPES
|
|
#include <vmlinux.h>
|
|
#include <bpf/bpf_helpers.h>
|
|
#include "bpf_experimental.h"
|
|
#include <bpf_arena_common.h>
|
|
#include "../test_kmods/bpf_testmod.h"
|
|
#include "../test_kmods/bpf_testmod_kfunc.h"
|
|
|
|
char _license[] SEC("license") = "GPL";
|
|
|
|
struct {
|
|
__uint(type, BPF_MAP_TYPE_ARENA);
|
|
__uint(map_flags, BPF_F_MMAPABLE);
|
|
/* page 0 hosts the arena globals, page 1 is for allocations */
|
|
__uint(max_entries, 2);
|
|
} arena SEC(".maps");
|
|
|
|
/* also associates the callbacks with the arena */
|
|
u64 __arena arena_touch;
|
|
/* raw value of the last __arena ctx argument, captured by test_arena_cb */
|
|
u64 __arena cb_ptr_val;
|
|
|
|
SEC("struct_ops/test_arena")
|
|
int test_arena_cb(unsigned long long *ctx)
|
|
{
|
|
u64 __arena *ptr = (u64 __arena *)ctx[0];
|
|
|
|
arena_touch++;
|
|
cb_ptr_val = ctx[0];
|
|
*ptr += 1;
|
|
return 0;
|
|
}
|
|
|
|
SEC("struct_ops/test_arena_nullable")
|
|
int test_arena_nullable_cb(unsigned long long *ctx)
|
|
{
|
|
u64 __arena *ptr = (u64 __arena *)ctx[0];
|
|
|
|
arena_touch++;
|
|
if (!ptr)
|
|
return 0xbee;
|
|
*ptr += 1;
|
|
return 0;
|
|
}
|
|
|
|
SEC("struct_ops/test_arena_stack")
|
|
int test_arena_stack_cb(unsigned long long *ctx)
|
|
{
|
|
u64 __arena *ptr = (u64 __arena *)ctx[8];
|
|
|
|
arena_touch++;
|
|
/* pin the slot layout: the leading args fill ctx[0]..ctx[7] */
|
|
if (ctx[0] != 1 || ctx[7] != 8)
|
|
return 0xbad;
|
|
*ptr += 1;
|
|
return 0;
|
|
}
|
|
|
|
SEC("struct_ops/test_arena_multislot")
|
|
int test_arena_multislot_cb(unsigned long long *ctx)
|
|
{
|
|
u64 __arena *ptr = (u64 __arena *)ctx[2];
|
|
|
|
arena_touch++;
|
|
/*
|
|
* The 16-byte struct occupies ctx[0] and ctx[1], so @ptr is argument
|
|
* one but slot two. Getting that wrong hands the callback a scalar.
|
|
*/
|
|
if (ctx[0] != 11 || ctx[1] != 22)
|
|
return 0xbad;
|
|
*ptr += 1;
|
|
return 0;
|
|
}
|
|
|
|
SEC(".struct_ops.link")
|
|
struct bpf_testmod_ops3 testmod_arena = {
|
|
.test_arena = (void *)test_arena_cb,
|
|
.test_arena_nullable = (void *)test_arena_nullable_cb,
|
|
.test_arena_stack = (void *)test_arena_stack_cb,
|
|
.test_arena_multislot = (void *)test_arena_multislot_cb,
|
|
};
|
|
|
|
SEC("syscall")
|
|
int trigger(void *ctx)
|
|
{
|
|
#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
|
|
u64 __arena *val;
|
|
int ret;
|
|
|
|
val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
|
|
if (!val)
|
|
return 1;
|
|
|
|
*val = 41;
|
|
ret = bpf_testmod_ops3_call_test_arena((u64 *)val);
|
|
if (ret)
|
|
return 2;
|
|
if (*val != 42)
|
|
return 3;
|
|
|
|
/*
|
|
* The callback must have seen exactly (u32)(kaddr - kern_vm_start),
|
|
* which is the arena offset of val with the upper 32 bits clear.
|
|
*/
|
|
if (cb_ptr_val != (u32)(u64)val)
|
|
return 4;
|
|
|
|
ret = bpf_testmod_ops3_call_test_arena_nullable((u64 *)val);
|
|
if (ret)
|
|
return 5;
|
|
if (*val != 43)
|
|
return 6;
|
|
|
|
/* NULL survives the nullable kfunc and the trampoline as NULL */
|
|
ret = bpf_testmod_ops3_call_test_arena_nullable(NULL);
|
|
if (ret != 0xbee)
|
|
return 7;
|
|
|
|
/* the arena pointer is stack-passed into the trampoline here */
|
|
ret = bpf_testmod_ops3_call_test_arena_stack((u64 *)val);
|
|
if (ret)
|
|
return 8;
|
|
if (*val != 44)
|
|
return 9;
|
|
|
|
/* a multi-slot arg precedes the arena pointer here */
|
|
ret = bpf_testmod_ops3_call_test_arena_multislot((u64 *)val);
|
|
if (ret)
|
|
return 10;
|
|
if (*val != 45)
|
|
return 11;
|
|
|
|
bpf_arena_free_pages(&arena, (void __arena *)val, 1);
|
|
#endif
|
|
return 0;
|
|
}
|