1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <stdlib.h> 4 #include <unistd.h> 5 #include <fcntl.h> 6 #include <stdint.h> 7 #include <sys/stat.h> 8 #include <stdbool.h> 9 #include <linux/bpf.h> 10 #include <bpf/libbpf.h> 11 #include <bpftool_helpers.h> 12 #include <test_progs.h> 13 #include <bpf/bpf.h> 14 #include "security_bpf_map.skel.h" 15 16 #define PROTECTED_MAP_NAME "prot_map" 17 #define UNPROTECTED_MAP_NAME "not_prot_map" 18 #define BPF_ITER_FILE "bpf_iter_map_elem.bpf.o" 19 #define BPFFS_PIN_DIR "/sys/fs/bpf/test_bpftool_map" 20 #define INNER_MAP_NAME "inner_map_tt" 21 #define OUTER_MAP_NAME "outer_map_tt" 22 23 #define MAP_NAME_MAX_LEN 64 24 #define PATH_MAX_LEN 128 25 26 enum map_protection { 27 PROTECTED, 28 UNPROTECTED 29 }; 30 31 struct test_desc { 32 char *name; 33 enum map_protection protection; 34 struct bpf_map *map; 35 char *map_name; 36 bool pinned; 37 char pin_path[PATH_MAX_LEN]; 38 bool write_must_fail; 39 }; 40 41 static struct security_bpf_map *general_setup(void) 42 { 43 struct security_bpf_map *skel; 44 uint32_t key, value; 45 int ret, i; 46 47 skel = security_bpf_map__open_and_load(); 48 if (!ASSERT_OK_PTR(skel, "open and load skeleton")) 49 goto end; 50 51 struct bpf_map *maps[] = {skel->maps.prot_map, skel->maps.not_prot_map}; 52 53 ret = security_bpf_map__attach(skel); 54 if (!ASSERT_OK(ret, "attach maps security programs")) 55 goto end_destroy; 56 57 for (i = 0; i < sizeof(maps)/sizeof(struct bpf_map *); i++) { 58 for (key = 0; key < 2; key++) { 59 int ret = bpf_map__update_elem(maps[i], &key, 60 sizeof(key), &key, sizeof(key), 61 0); 62 if (!ASSERT_OK(ret, "set initial map value")) 63 goto end_destroy; 64 } 65 } 66 67 key = 0; 68 value = 1; 69 ret = bpf_map__update_elem(skel->maps.prot_status_map, &key, 70 sizeof(key), &value, sizeof(value), 0); 71 if (!ASSERT_OK(ret, "configure map protection")) 72 goto end_destroy; 73 74 if (!ASSERT_OK(mkdir(BPFFS_PIN_DIR, S_IFDIR), "create bpffs pin dir")) 75 goto end_destroy; 76 77 return skel; 78 end_destroy: 79 security_bpf_map__destroy(skel); 80 end: 81 return NULL; 82 } 83 84 static void general_cleanup(struct security_bpf_map *skel) 85 { 86 rmdir(BPFFS_PIN_DIR); 87 security_bpf_map__destroy(skel); 88 } 89 90 static void update_test_desc(struct security_bpf_map *skel, 91 struct test_desc *test) 92 { 93 /* Now that the skeleton is loaded, update all missing fields to 94 * have the subtest properly configured 95 */ 96 if (test->protection == PROTECTED) { 97 test->map = skel->maps.prot_map; 98 test->map_name = PROTECTED_MAP_NAME; 99 } else { 100 test->map = skel->maps.not_prot_map; 101 test->map_name = UNPROTECTED_MAP_NAME; 102 } 103 } 104 105 static int test_setup(struct security_bpf_map *skel, struct test_desc *desc) 106 { 107 int ret; 108 109 update_test_desc(skel, desc); 110 111 if (desc->pinned) { 112 ret = snprintf(desc->pin_path, PATH_MAX_LEN, "%s/%s", BPFFS_PIN_DIR, 113 desc->name); 114 if (!ASSERT_GT(ret, 0, "format pin path")) 115 return 1; 116 ret = bpf_map__pin(desc->map, desc->pin_path); 117 if (!ASSERT_OK(ret, "pin map")) 118 return 1; 119 } 120 121 return 0; 122 } 123 124 static void test_cleanup(struct test_desc *desc) 125 { 126 if (desc->pinned) 127 bpf_map__unpin(desc->map, NULL); 128 } 129 130 static int lookup_map_value(char *map_handle) 131 { 132 char cmd[MAX_BPFTOOL_CMD_LEN]; 133 int ret = 0; 134 135 ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "map lookup %s key 0 0 0 0", 136 map_handle); 137 if (!ASSERT_GT(ret, 0, "format map lookup cmd")) 138 return 1; 139 return run_bpftool_command(cmd); 140 } 141 142 static int read_map_btf_data(char *map_handle) 143 { 144 char cmd[MAX_BPFTOOL_CMD_LEN]; 145 int ret = 0; 146 147 ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "btf dump map %s", 148 map_handle); 149 if (!ASSERT_GT(ret, 0, "format map btf dump cmd")) 150 return 1; 151 return run_bpftool_command(cmd); 152 } 153 154 static int write_map_value(char *map_handle) 155 { 156 char cmd[MAX_BPFTOOL_CMD_LEN]; 157 int ret = 0; 158 159 ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, 160 "map update %s key 0 0 0 0 value 1 1 1 1", map_handle); 161 if (!ASSERT_GT(ret, 0, "format value write cmd")) 162 return 1; 163 return run_bpftool_command(cmd); 164 } 165 166 static int delete_map_value(char *map_handle) 167 { 168 char cmd[MAX_BPFTOOL_CMD_LEN]; 169 int ret = 0; 170 171 ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, 172 "map delete %s key 0 0 0 0", map_handle); 173 if (!ASSERT_GT(ret, 0, "format value deletion cmd")) 174 return 1; 175 return run_bpftool_command(cmd); 176 } 177 178 static int iterate_on_map_values(char *map_handle, char *iter_pin_path) 179 { 180 char cmd[MAX_BPFTOOL_CMD_LEN]; 181 int ret = 0; 182 183 184 ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "iter pin %s %s map %s", 185 BPF_ITER_FILE, iter_pin_path, map_handle); 186 if (!ASSERT_GT(ret, 0, "format iterator creation cmd")) 187 return 1; 188 ret = run_bpftool_command(cmd); 189 if (ret) 190 return ret; 191 ret = snprintf(cmd, MAP_NAME_MAX_LEN, "cat %s", iter_pin_path); 192 if (ret < 0) 193 goto cleanup; 194 ret = system(cmd); 195 196 cleanup: 197 unlink(iter_pin_path); 198 return ret; 199 } 200 201 static int create_inner_map(void) 202 { 203 char cmd[MAX_BPFTOOL_CMD_LEN]; 204 int ret = 0; 205 206 ret = snprintf( 207 cmd, MAX_BPFTOOL_CMD_LEN, 208 "map create %s/%s type array key 4 value 4 entries 4 name %s", 209 BPFFS_PIN_DIR, INNER_MAP_NAME, INNER_MAP_NAME); 210 if (!ASSERT_GT(ret, 0, "format inner map create cmd")) 211 return 1; 212 return run_bpftool_command(cmd); 213 } 214 215 static int create_outer_map(void) 216 { 217 char cmd[MAX_BPFTOOL_CMD_LEN]; 218 int ret = 0; 219 220 ret = snprintf( 221 cmd, MAX_BPFTOOL_CMD_LEN, 222 "map create %s/%s type hash_of_maps key 4 value 4 entries 2 name %s inner_map name %s", 223 BPFFS_PIN_DIR, OUTER_MAP_NAME, OUTER_MAP_NAME, INNER_MAP_NAME); 224 if (!ASSERT_GT(ret, 0, "format outer map create cmd")) 225 return 1; 226 return run_bpftool_command(cmd); 227 } 228 229 static void delete_pinned_map(char *map_name) 230 { 231 char pin_path[PATH_MAX_LEN]; 232 int ret; 233 234 ret = snprintf(pin_path, PATH_MAX_LEN, "%s/%s", BPFFS_PIN_DIR, 235 map_name); 236 if (ret >= 0) 237 unlink(pin_path); 238 } 239 240 static int add_outer_map_entry(int key) 241 { 242 char cmd[MAX_BPFTOOL_CMD_LEN]; 243 int ret = 0; 244 245 ret = snprintf( 246 cmd, MAX_BPFTOOL_CMD_LEN, 247 "map update pinned %s/%s key %d 0 0 0 value name %s", 248 BPFFS_PIN_DIR, OUTER_MAP_NAME, key, INNER_MAP_NAME); 249 if (!ASSERT_GT(ret, 0, "format outer map value addition cmd")) 250 return 1; 251 return run_bpftool_command(cmd); 252 } 253 254 static void test_basic_access(struct test_desc *desc) 255 { 256 char map_handle[MAP_NAME_MAX_LEN]; 257 char iter_pin_path[PATH_MAX_LEN]; 258 int ret; 259 260 if (desc->pinned) 261 ret = snprintf(map_handle, MAP_NAME_MAX_LEN, "pinned %s", 262 desc->pin_path); 263 else 264 ret = snprintf(map_handle, MAP_NAME_MAX_LEN, "name %s", 265 desc->map_name); 266 if (!ASSERT_GT(ret, 0, "format map handle")) 267 return; 268 269 ret = lookup_map_value(map_handle); 270 ASSERT_OK(ret, "read map value"); 271 272 ret = read_map_btf_data(map_handle); 273 ASSERT_OK(ret, "read map btf data"); 274 275 ret = write_map_value(map_handle); 276 ASSERT_OK(desc->write_must_fail ? !ret : ret, "write map value"); 277 278 ret = delete_map_value(map_handle); 279 ASSERT_OK(desc->write_must_fail ? !ret : ret, "delete map value"); 280 /* Restore deleted value */ 281 if (!ret) 282 write_map_value(map_handle); 283 284 ret = snprintf(iter_pin_path, PATH_MAX_LEN, "%s/iter", BPFFS_PIN_DIR); 285 if (ASSERT_GT(ret, 0, "format iter pin path")) { 286 ret = iterate_on_map_values(map_handle, iter_pin_path); 287 ASSERT_OK(ret, "iterate on map values"); 288 } 289 } 290 291 static void test_create_nested_maps(void) 292 { 293 if (!ASSERT_OK(create_inner_map(), "create inner map")) 294 return; 295 if (!ASSERT_OK(create_outer_map(), "create outer map")) 296 goto end_cleanup_inner; 297 ASSERT_OK(add_outer_map_entry(0), "add a first entry in outer map"); 298 ASSERT_OK(add_outer_map_entry(1), "add a second entry in outer map"); 299 ASSERT_NEQ(add_outer_map_entry(2), 0, "add a third entry in outer map"); 300 301 delete_pinned_map(OUTER_MAP_NAME); 302 end_cleanup_inner: 303 delete_pinned_map(INNER_MAP_NAME); 304 } 305 306 static void test_btf_list(void) 307 { 308 ASSERT_OK(run_bpftool_command("btf list"), "list btf data"); 309 } 310 311 static struct test_desc tests[] = { 312 { 313 .name = "unprotected_unpinned", 314 .protection = UNPROTECTED, 315 .map_name = UNPROTECTED_MAP_NAME, 316 .pinned = false, 317 .write_must_fail = false, 318 }, 319 { 320 .name = "unprotected_pinned", 321 .protection = UNPROTECTED, 322 .map_name = UNPROTECTED_MAP_NAME, 323 .pinned = true, 324 .write_must_fail = false, 325 }, 326 { 327 .name = "protected_unpinned", 328 .protection = PROTECTED, 329 .map_name = UNPROTECTED_MAP_NAME, 330 .pinned = false, 331 .write_must_fail = true, 332 }, 333 { 334 .name = "protected_pinned", 335 .protection = PROTECTED, 336 .map_name = UNPROTECTED_MAP_NAME, 337 .pinned = true, 338 .write_must_fail = true, 339 } 340 }; 341 342 static const size_t tests_count = ARRAY_SIZE(tests); 343 344 void test_bpftool_maps_access(void) 345 { 346 struct security_bpf_map *skel; 347 struct test_desc *current; 348 int i; 349 350 skel = general_setup(); 351 if (!ASSERT_OK_PTR(skel, "prepare programs")) 352 goto cleanup; 353 354 for (i = 0; i < tests_count; i++) { 355 current = &tests[i]; 356 if (!test__start_subtest(current->name)) 357 continue; 358 if (ASSERT_OK(test_setup(skel, current), "subtest setup")) { 359 test_basic_access(current); 360 test_cleanup(current); 361 } 362 } 363 if (test__start_subtest("nested_maps")) 364 test_create_nested_maps(); 365 if (test__start_subtest("btf_list")) 366 test_btf_list(); 367 368 cleanup: 369 general_cleanup(skel); 370 } 371 372