Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions kernel/bpf/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -21136,13 +21136,13 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
} else {
u32 off = insn[1].imm;

if (off >= BPF_MAX_VAR_OFF) {
verbose(env, "direct value offset of %u is not allowed\n", off);
if (!map->ops->map_direct_value_addr) {
verbose(env, "no direct value access support for this map type\n");
return -EINVAL;
}

if (!map->ops->map_direct_value_addr) {
verbose(env, "no direct value access support for this map type\n");
if (off >= BPF_MAX_VAR_OFF && map->map_type != BPF_MAP_TYPE_ARENA) {
verbose(env, "direct value offset of %u is not allowed\n", off);
return -EINVAL;
}

Expand Down
19 changes: 15 additions & 4 deletions tools/lib/bpf/libbpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,7 @@ struct bpf_object {
int arena_map_idx;
void *arena_data;
size_t arena_data_sz;
__u32 arena_data_off;

void *jumptables_data;
size_t jumptables_data_sz;
Expand Down Expand Up @@ -2991,10 +2992,11 @@ static int init_arena_map_data(struct bpf_object *obj, struct bpf_map *map,
void *data, size_t data_sz)
{
const long page_sz = sysconf(_SC_PAGE_SIZE);
const size_t data_alloc_sz = roundup(data_sz, page_sz);
size_t mmap_sz;

mmap_sz = bpf_map_mmap_sz(map);
if (roundup(data_sz, page_sz) > mmap_sz) {
if (data_alloc_sz > mmap_sz) {
pr_warn("elf: sec '%s': declared ARENA map size (%zu) is too small to hold global __arena variables of size %zu\n",
sec_name, mmap_sz, data_sz);
return -E2BIG;
Expand All @@ -3006,6 +3008,9 @@ static int init_arena_map_data(struct bpf_object *obj, struct bpf_map *map,
memcpy(obj->arena_data, data, data_sz);
obj->arena_data_sz = data_sz;

/* place globals at the end of the arena */
obj->arena_data_off = mmap_sz - data_alloc_sz;

/* make bpf_map__init_value() work for ARENA maps */
map->mmaped = obj->arena_data;

Expand Down Expand Up @@ -4663,7 +4668,7 @@ static int bpf_program__record_reloc(struct bpf_program *prog,
reloc_desc->type = RELO_DATA;
reloc_desc->insn_idx = insn_idx;
reloc_desc->map_idx = obj->arena_map_idx;
reloc_desc->sym_off = sym->st_value;
reloc_desc->sym_off = sym->st_value + obj->arena_data_off;

map = &obj->maps[obj->arena_map_idx];
pr_debug("prog '%s': found arena map %d (%s, sec %d, off %zu) for insn %u\n",
Expand Down Expand Up @@ -5624,7 +5629,8 @@ bpf_object__create_maps(struct bpf_object *obj)
return err;
}
if (obj->arena_data) {
memcpy(map->mmaped, obj->arena_data, obj->arena_data_sz);
memcpy(map->mmaped + obj->arena_data_off, obj->arena_data,
obj->arena_data_sz);
zfree(&obj->arena_data);
}
}
Expand Down Expand Up @@ -14383,6 +14389,7 @@ void bpf_object__destroy_subskeleton(struct bpf_object_subskeleton *s)

int bpf_object__load_skeleton(struct bpf_object_skeleton *s)
{
void *mmaped;
int i, err;

err = bpf_object__load(*s->obj);
Expand All @@ -14398,7 +14405,11 @@ int bpf_object__load_skeleton(struct bpf_object_skeleton *s)
if (!map_skel->mmaped)
continue;

*map_skel->mmaped = map->mmaped;
mmaped = map->mmaped;
if (map->def.type == BPF_MAP_TYPE_ARENA)
mmaped += map->obj->arena_data_off;

*map_skel->mmaped = mmaped;
}

return 0;
Expand Down
4 changes: 4 additions & 0 deletions tools/testing/selftests/bpf/prog_tests/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include "verifier_and.skel.h"
#include "verifier_arena.skel.h"
#include "verifier_arena_large.skel.h"
#include "verifier_arena_globals1.skel.h"
#include "verifier_arena_globals2.skel.h"
#include "verifier_array_access.skel.h"
#include "verifier_async_cb_context.skel.h"
#include "verifier_basic_stack.skel.h"
Expand Down Expand Up @@ -147,6 +149,8 @@ static void run_tests_aux(const char *skel_name,
void test_verifier_and(void) { RUN(verifier_and); }
void test_verifier_arena(void) { RUN(verifier_arena); }
void test_verifier_arena_large(void) { RUN(verifier_arena_large); }
void test_verifier_arena_globals1(void) { RUN(verifier_arena_globals1); }
void test_verifier_arena_globals2(void) { RUN(verifier_arena_globals2); }
void test_verifier_basic_stack(void) { RUN(verifier_basic_stack); }
void test_verifier_bitfield_write(void) { RUN(verifier_bitfield_write); }
void test_verifier_bounds(void) { RUN(verifier_bounds); }
Expand Down
58 changes: 58 additions & 0 deletions tools/testing/selftests/bpf/progs/verifier_arena_globals1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */

#define BPF_NO_KFUNC_PROTOTYPES
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "bpf_experimental.h"
#include "bpf_arena_common.h"
#include "bpf_misc.h"

#define ARENA_PAGES (64)
#define GLOBAL_PAGES (16)

struct {
__uint(type, BPF_MAP_TYPE_ARENA);
__uint(map_flags, BPF_F_MMAPABLE);
__uint(max_entries, ARENA_PAGES); /* Arena of 64 pages */
#ifdef __TARGET_ARCH_arm64
__ulong(map_extra, (1ull << 32) | (~0u - __PAGE_SIZE * ARENA_PAGES + 1));
#else
__ulong(map_extra, (1ull << 44) | (~0u - __PAGE_SIZE * ARENA_PAGES + 1));
#endif
} arena SEC(".maps");

/*
* Global data small enough that we can apply the maximum
* offset into the arena. Userspace will also use this to
* ensure the offset doesn't unexpectedly change from
* under us.
*/
char __arena global_data[PAGE_SIZE][GLOBAL_PAGES];

SEC("syscall")
__success __retval(0)
int check_reserve1(void *ctx)
{
#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
__u8 __arena *guard, *globals;
int ret;

guard = (void __arena *)arena_base(&arena);
globals = (void __arena *)(arena_base(&arena) + (ARENA_PAGES - GLOBAL_PAGES) * PAGE_SIZE);

/* Reserve the region we've offset the globals by. */
ret = bpf_arena_reserve_pages(&arena, guard, ARENA_PAGES - GLOBAL_PAGES);
if (ret)
return 1;

/* Make sure the globals are placed GLOBALS_PGOFF pages in. */
ret = bpf_arena_reserve_pages(&arena, globals, 1);
if (!ret)
return 2;
#endif
return 0;
}

char _license[] SEC("license") = "GPL";
49 changes: 49 additions & 0 deletions tools/testing/selftests/bpf/progs/verifier_arena_globals2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */

#define BPF_NO_KFUNC_PROTOTYPES
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "bpf_misc.h"
#include "bpf_experimental.h"
#include "bpf_arena_common.h"

#define ARENA_PAGES (32)

struct {
__uint(type, BPF_MAP_TYPE_ARENA);
__uint(map_flags, BPF_F_MMAPABLE);
__uint(max_entries, ARENA_PAGES); /* Arena of 32 pages (standard offset is 16 pages) */
#ifdef __TARGET_ARCH_arm64
__ulong(map_extra, (1ull << 32) | (~0u - __PAGE_SIZE * ARENA_PAGES + 1));
#else
__ulong(map_extra, (1ull << 44) | (~0u - __PAGE_SIZE * ARENA_PAGES + 1));
#endif
} arena SEC(".maps");

/*
* Fill the entire arena with global data.
* The offset into the arena should be 0.
*/
char __arena global_data[PAGE_SIZE][ARENA_PAGES];

SEC("syscall")
__success __retval(0)
int check_reserve2(void *ctx)
{
#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
void __arena *guard;
int ret;

guard = (void __arena *)arena_base(&arena);

/* Make sure the data at offset 0 case is properly handled. */
ret = bpf_arena_reserve_pages(&arena, guard, 1);
if (!ret)
return 1;
#endif
return 0;
}

char _license[] SEC("license") = "GPL";
21 changes: 17 additions & 4 deletions tools/testing/selftests/bpf/progs/verifier_arena_large.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,31 @@ int big_alloc1(void *ctx)
{
#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
volatile char __arena *page1, *page2, *no_page, *page3;
void __arena *base;
u64 base;

page1 = base = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
base = (u64)arena_base(&arena);

page1 = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
if (!page1)
return 1;

if ((u64)page1 != base)
return 15;

*page1 = 1;
page2 = bpf_arena_alloc_pages(&arena, base + ARENA_SIZE - PAGE_SIZE * 2,
page2 = bpf_arena_alloc_pages(&arena, (void __arena *)(ARENA_SIZE - 2 * PAGE_SIZE),
1, NUMA_NO_NODE, 0);
if (!page2)
return 2;
*page2 = 2;
no_page = bpf_arena_alloc_pages(&arena, base + ARENA_SIZE - PAGE_SIZE,

/* Test for the guard region at the end of the arena. */
no_page = bpf_arena_alloc_pages(&arena, (void __arena *)ARENA_SIZE - PAGE_SIZE,
1, NUMA_NO_NODE, 0);
if (no_page)
return 16;

no_page = bpf_arena_alloc_pages(&arena, (void __arena *)ARENA_SIZE,
1, NUMA_NO_NODE, 0);
if (no_page)
return 3;
Expand Down
Loading