1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */ 3 4 #define BPF_NO_KFUNC_PROTOTYPES 5 #include <vmlinux.h> 6 #include <errno.h> 7 #include <bpf/bpf_helpers.h> 8 #include <bpf/bpf_tracing.h> 9 #include "bpf_misc.h" 10 #include "bpf_experimental.h" 11 #include "bpf_arena_common.h" 12 13 struct { 14 __uint(type, BPF_MAP_TYPE_ARENA); 15 __uint(map_flags, BPF_F_MMAPABLE); 16 __uint(max_entries, 2); /* arena of two pages close to 32-bit boundary*/ 17 #ifdef __TARGET_ARCH_arm64 18 __ulong(map_extra, (1ull << 32) | (~0u - __PAGE_SIZE * 2 + 1)); /* start of mmap() region */ 19 #else 20 __ulong(map_extra, (1ull << 44) | (~0u - __PAGE_SIZE * 2 + 1)); /* start of mmap() region */ 21 #endif 22 } arena SEC(".maps"); 23 24 SEC("syscall") 25 __success __retval(0) 26 int basic_alloc1(void *ctx) 27 { 28 #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) 29 volatile int __arena *page1, *page2, *no_page, *page3; 30 31 page1 = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); 32 if (!page1) 33 return 1; 34 *page1 = 1; 35 page2 = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); 36 if (!page2) 37 return 2; 38 *page2 = 2; 39 no_page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); 40 if (no_page) 41 return 3; 42 if (*page1 != 1) 43 return 4; 44 if (*page2 != 2) 45 return 5; 46 bpf_arena_free_pages(&arena, (void __arena *)page2, 1); 47 if (*page1 != 1) 48 return 6; 49 if (*page2 != 0) /* use-after-free should return 0 */ 50 return 7; 51 page3 = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); 52 if (!page3) 53 return 8; 54 *page3 = 3; 55 if (page2 != page3) 56 return 9; 57 if (*page1 != 1) 58 return 10; 59 #endif 60 return 0; 61 } 62 63 SEC("syscall") 64 __success __retval(0) 65 int basic_alloc2(void *ctx) 66 { 67 #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) 68 volatile char __arena *page1, *page2, *page3, *page4; 69 70 page1 = bpf_arena_alloc_pages(&arena, NULL, 2, NUMA_NO_NODE, 0); 71 if (!page1) 72 return 1; 73 page2 = page1 + __PAGE_SIZE; 74 page3 = page1 + __PAGE_SIZE * 2; 75 page4 = page1 - __PAGE_SIZE; 76 *page1 = 1; 77 *page2 = 2; 78 *page3 = 3; 79 *page4 = 4; 80 if (*page1 != 1) 81 return 1; 82 if (*page2 != 2) 83 return 2; 84 if (*page3 != 0) 85 return 3; 86 if (*page4 != 0) 87 return 4; 88 bpf_arena_free_pages(&arena, (void __arena *)page1, 2); 89 if (*page1 != 0) 90 return 5; 91 if (*page2 != 0) 92 return 6; 93 if (*page3 != 0) 94 return 7; 95 if (*page4 != 0) 96 return 8; 97 #endif 98 return 0; 99 } 100 101 struct bpf_arena___l { 102 struct bpf_map map; 103 } __attribute__((preserve_access_index)); 104 105 SEC("syscall") 106 __success __retval(0) __log_level(2) 107 int basic_alloc3(void *ctx) 108 { 109 struct bpf_arena___l *ar = (struct bpf_arena___l *)&arena; 110 volatile char __arena *pages; 111 112 pages = bpf_arena_alloc_pages(&ar->map, NULL, ar->map.max_entries, NUMA_NO_NODE, 0); 113 if (!pages) 114 return 1; 115 return 0; 116 } 117 118 SEC("syscall") 119 __success __retval(0) 120 int basic_reserve1(void *ctx) 121 { 122 #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) 123 char __arena *page; 124 int ret; 125 126 page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); 127 if (!page) 128 return 1; 129 130 page += __PAGE_SIZE; 131 132 /* Reserve the second page */ 133 ret = bpf_arena_reserve_pages(&arena, page, 1); 134 if (ret) 135 return 2; 136 137 /* Try to explicitly allocate the reserved page. */ 138 page = bpf_arena_alloc_pages(&arena, page, 1, NUMA_NO_NODE, 0); 139 if (page) 140 return 3; 141 142 /* Try to implicitly allocate the page (since there's only 2 of them). */ 143 page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); 144 if (page) 145 return 4; 146 #endif 147 return 0; 148 } 149 150 SEC("syscall") 151 __success __retval(0) 152 int basic_reserve2(void *ctx) 153 { 154 #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) 155 char __arena *page; 156 int ret; 157 158 page = arena_base(&arena); 159 ret = bpf_arena_reserve_pages(&arena, page, 1); 160 if (ret) 161 return 1; 162 163 page = bpf_arena_alloc_pages(&arena, page, 1, NUMA_NO_NODE, 0); 164 if ((u64)page) 165 return 2; 166 #endif 167 return 0; 168 } 169 170 /* Reserve the same page twice, should return -EBUSY. */ 171 SEC("syscall") 172 __success __retval(0) 173 int reserve_twice(void *ctx) 174 { 175 #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) 176 char __arena *page; 177 int ret; 178 179 page = arena_base(&arena); 180 181 ret = bpf_arena_reserve_pages(&arena, page, 1); 182 if (ret) 183 return 1; 184 185 ret = bpf_arena_reserve_pages(&arena, page, 1); 186 if (ret != -EBUSY) 187 return 2; 188 #endif 189 return 0; 190 } 191 192 /* Try to reserve past the end of the arena. */ 193 SEC("syscall") 194 __success __retval(0) 195 int reserve_invalid_region(void *ctx) 196 { 197 #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) 198 char __arena *page; 199 int ret; 200 201 /* Try a NULL pointer. */ 202 ret = bpf_arena_reserve_pages(&arena, NULL, 3); 203 if (ret != -EINVAL) 204 return 1; 205 206 page = arena_base(&arena); 207 208 ret = bpf_arena_reserve_pages(&arena, page, 3); 209 if (ret != -EINVAL) 210 return 2; 211 212 ret = bpf_arena_reserve_pages(&arena, page, 4096); 213 if (ret != -EINVAL) 214 return 3; 215 216 ret = bpf_arena_reserve_pages(&arena, page, (1ULL << 32) - 1); 217 if (ret != -EINVAL) 218 return 4; 219 #endif 220 return 0; 221 } 222 223 SEC("iter.s/bpf_map") 224 __success __log_level(2) 225 int iter_maps1(struct bpf_iter__bpf_map *ctx) 226 { 227 struct bpf_map *map = ctx->map; 228 229 if (!map) 230 return 0; 231 bpf_arena_alloc_pages(map, NULL, map->max_entries, 0, 0); 232 return 0; 233 } 234 235 SEC("iter.s/bpf_map") 236 __failure __msg("expected pointer to STRUCT bpf_map") 237 int iter_maps2(struct bpf_iter__bpf_map *ctx) 238 { 239 struct seq_file *seq = ctx->meta->seq; 240 241 bpf_arena_alloc_pages((void *)seq, NULL, 1, 0, 0); 242 return 0; 243 } 244 245 SEC("iter.s/bpf_map") 246 __failure __msg("untrusted_ptr_bpf_map") 247 int iter_maps3(struct bpf_iter__bpf_map *ctx) 248 { 249 struct bpf_map *map = ctx->map; 250 251 if (!map) 252 return 0; 253 bpf_arena_alloc_pages(map->inner_map_meta, NULL, map->max_entries, 0, 0); 254 return 0; 255 } 256 257 char _license[] SEC("license") = "GPL"; 258