1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ 3 #define _GNU_SOURCE 4 #include <test_progs.h> 5 #include <bpf/btf.h> 6 #include <fcntl.h> 7 #include <unistd.h> 8 #include <linux/unistd.h> 9 #include <linux/mount.h> 10 #include <sys/syscall.h> 11 12 static inline int sys_fsopen(const char *fsname, unsigned flags) 13 { 14 return syscall(__NR_fsopen, fsname, flags); 15 } 16 17 static inline int sys_fsconfig(int fs_fd, unsigned cmd, const char *key, const void *val, int aux) 18 { 19 return syscall(__NR_fsconfig, fs_fd, cmd, key, val, aux); 20 } 21 22 static inline int sys_fsmount(int fs_fd, unsigned flags, unsigned ms_flags) 23 { 24 return syscall(__NR_fsmount, fs_fd, flags, ms_flags); 25 } 26 27 __attribute__((unused)) 28 static inline int sys_move_mount(int from_dfd, const char *from_path, 29 int to_dfd, const char *to_path, 30 unsigned int ms_flags) 31 { 32 return syscall(__NR_move_mount, from_dfd, from_path, to_dfd, to_path, ms_flags); 33 } 34 35 static void bpf_obj_pinning_detached(void) 36 { 37 LIBBPF_OPTS(bpf_obj_pin_opts, pin_opts); 38 LIBBPF_OPTS(bpf_obj_get_opts, get_opts); 39 int fs_fd = -1, mnt_fd = -1; 40 int map_fd = -1, map_fd2 = -1; 41 int zero = 0, src_value, dst_value, err; 42 const char *map_name = "fsmount_map"; 43 44 /* A bunch of below UAPI calls are constructed based on reading: 45 * https://brauner.io/2023/02/28/mounting-into-mount-namespaces.html 46 */ 47 48 /* create VFS context */ 49 fs_fd = sys_fsopen("bpf", 0); 50 if (!ASSERT_GE(fs_fd, 0, "fs_fd")) 51 goto cleanup; 52 53 /* instantiate FS object */ 54 err = sys_fsconfig(fs_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0); 55 if (!ASSERT_OK(err, "fs_create")) 56 goto cleanup; 57 58 /* create O_PATH fd for detached mount */ 59 mnt_fd = sys_fsmount(fs_fd, 0, 0); 60 if (!ASSERT_GE(mnt_fd, 0, "mnt_fd")) 61 goto cleanup; 62 63 /* If we wanted to expose detached mount in the file system, we'd do 64 * something like below. But the whole point is that we actually don't 65 * even have to expose BPF FS in the file system to be able to work 66 * (pin/get objects) with it. 67 * 68 * err = sys_move_mount(mnt_fd, "", -EBADF, mnt_path, MOVE_MOUNT_F_EMPTY_PATH); 69 * if (!ASSERT_OK(err, "move_mount")) 70 * goto cleanup; 71 */ 72 73 /* create BPF map to pin */ 74 map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, map_name, 4, 4, 1, NULL); 75 if (!ASSERT_GE(map_fd, 0, "map_fd")) 76 goto cleanup; 77 78 /* pin BPF map into detached BPF FS through mnt_fd */ 79 pin_opts.file_flags = BPF_F_PATH_FD; 80 pin_opts.path_fd = mnt_fd; 81 err = bpf_obj_pin_opts(map_fd, map_name, &pin_opts); 82 if (!ASSERT_OK(err, "map_pin")) 83 goto cleanup; 84 85 /* get BPF map from detached BPF FS through mnt_fd */ 86 get_opts.file_flags = BPF_F_PATH_FD; 87 get_opts.path_fd = mnt_fd; 88 map_fd2 = bpf_obj_get_opts(map_name, &get_opts); 89 if (!ASSERT_GE(map_fd2, 0, "map_get")) 90 goto cleanup; 91 92 /* update map through one FD */ 93 src_value = 0xcafebeef; 94 err = bpf_map_update_elem(map_fd, &zero, &src_value, 0); 95 ASSERT_OK(err, "map_update"); 96 97 /* check values written/read through different FDs do match */ 98 dst_value = 0; 99 err = bpf_map_lookup_elem(map_fd2, &zero, &dst_value); 100 ASSERT_OK(err, "map_lookup"); 101 ASSERT_EQ(dst_value, src_value, "map_value_eq1"); 102 ASSERT_EQ(dst_value, 0xcafebeef, "map_value_eq2"); 103 104 cleanup: 105 if (map_fd >= 0) 106 ASSERT_OK(close(map_fd), "close_map_fd"); 107 if (map_fd2 >= 0) 108 ASSERT_OK(close(map_fd2), "close_map_fd2"); 109 if (fs_fd >= 0) 110 ASSERT_OK(close(fs_fd), "close_fs_fd"); 111 if (mnt_fd >= 0) 112 ASSERT_OK(close(mnt_fd), "close_mnt_fd"); 113 } 114 115 enum path_kind 116 { 117 PATH_STR_ABS, 118 PATH_STR_REL, 119 PATH_FD_REL, 120 }; 121 122 static void validate_pin(int map_fd, const char *map_name, int src_value, 123 enum path_kind path_kind) 124 { 125 LIBBPF_OPTS(bpf_obj_pin_opts, pin_opts); 126 char abs_path[PATH_MAX], old_cwd[PATH_MAX]; 127 const char *pin_path = NULL; 128 int zero = 0, dst_value, map_fd2, err; 129 130 snprintf(abs_path, sizeof(abs_path), "/sys/fs/bpf/%s", map_name); 131 old_cwd[0] = '\0'; 132 133 switch (path_kind) { 134 case PATH_STR_ABS: 135 /* absolute path */ 136 pin_path = abs_path; 137 break; 138 case PATH_STR_REL: 139 /* cwd + relative path */ 140 ASSERT_OK_PTR(getcwd(old_cwd, sizeof(old_cwd)), "getcwd"); 141 ASSERT_OK(chdir("/sys/fs/bpf"), "chdir"); 142 pin_path = map_name; 143 break; 144 case PATH_FD_REL: 145 /* dir fd + relative path */ 146 pin_opts.file_flags = BPF_F_PATH_FD; 147 pin_opts.path_fd = open("/sys/fs/bpf", O_PATH); 148 ASSERT_GE(pin_opts.path_fd, 0, "path_fd"); 149 pin_path = map_name; 150 break; 151 } 152 153 /* pin BPF map using specified path definition */ 154 err = bpf_obj_pin_opts(map_fd, pin_path, &pin_opts); 155 ASSERT_OK(err, "obj_pin"); 156 157 /* cleanup */ 158 if (pin_opts.path_fd >= 0) 159 close(pin_opts.path_fd); 160 if (old_cwd[0]) 161 ASSERT_OK(chdir(old_cwd), "restore_cwd"); 162 163 map_fd2 = bpf_obj_get(abs_path); 164 if (!ASSERT_GE(map_fd2, 0, "map_get")) 165 goto cleanup; 166 167 /* update map through one FD */ 168 err = bpf_map_update_elem(map_fd, &zero, &src_value, 0); 169 ASSERT_OK(err, "map_update"); 170 171 /* check values written/read through different FDs do match */ 172 dst_value = 0; 173 err = bpf_map_lookup_elem(map_fd2, &zero, &dst_value); 174 ASSERT_OK(err, "map_lookup"); 175 ASSERT_EQ(dst_value, src_value, "map_value_eq"); 176 cleanup: 177 if (map_fd2 >= 0) 178 ASSERT_OK(close(map_fd2), "close_map_fd2"); 179 unlink(abs_path); 180 } 181 182 static void validate_get(int map_fd, const char *map_name, int src_value, 183 enum path_kind path_kind) 184 { 185 LIBBPF_OPTS(bpf_obj_get_opts, get_opts); 186 char abs_path[PATH_MAX], old_cwd[PATH_MAX]; 187 const char *pin_path = NULL; 188 int zero = 0, dst_value, map_fd2, err; 189 190 snprintf(abs_path, sizeof(abs_path), "/sys/fs/bpf/%s", map_name); 191 /* pin BPF map using specified path definition */ 192 err = bpf_obj_pin(map_fd, abs_path); 193 if (!ASSERT_OK(err, "pin_map")) 194 return; 195 196 old_cwd[0] = '\0'; 197 198 switch (path_kind) { 199 case PATH_STR_ABS: 200 /* absolute path */ 201 pin_path = abs_path; 202 break; 203 case PATH_STR_REL: 204 /* cwd + relative path */ 205 ASSERT_OK_PTR(getcwd(old_cwd, sizeof(old_cwd)), "getcwd"); 206 ASSERT_OK(chdir("/sys/fs/bpf"), "chdir"); 207 pin_path = map_name; 208 break; 209 case PATH_FD_REL: 210 /* dir fd + relative path */ 211 get_opts.file_flags = BPF_F_PATH_FD; 212 get_opts.path_fd = open("/sys/fs/bpf", O_PATH); 213 ASSERT_GE(get_opts.path_fd, 0, "path_fd"); 214 pin_path = map_name; 215 break; 216 } 217 218 map_fd2 = bpf_obj_get_opts(pin_path, &get_opts); 219 if (!ASSERT_GE(map_fd2, 0, "map_get")) 220 goto cleanup; 221 222 /* cleanup */ 223 if (get_opts.path_fd >= 0) 224 close(get_opts.path_fd); 225 if (old_cwd[0]) 226 ASSERT_OK(chdir(old_cwd), "restore_cwd"); 227 228 /* update map through one FD */ 229 err = bpf_map_update_elem(map_fd, &zero, &src_value, 0); 230 ASSERT_OK(err, "map_update"); 231 232 /* check values written/read through different FDs do match */ 233 dst_value = 0; 234 err = bpf_map_lookup_elem(map_fd2, &zero, &dst_value); 235 ASSERT_OK(err, "map_lookup"); 236 ASSERT_EQ(dst_value, src_value, "map_value_eq"); 237 cleanup: 238 if (map_fd2 >= 0) 239 ASSERT_OK(close(map_fd2), "close_map_fd2"); 240 unlink(abs_path); 241 } 242 243 static void bpf_obj_pinning_mounted(enum path_kind path_kind) 244 { 245 const char *map_name = "mounted_map"; 246 int map_fd; 247 248 /* create BPF map to pin */ 249 map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, map_name, 4, 4, 1, NULL); 250 if (!ASSERT_GE(map_fd, 0, "map_fd")) 251 return; 252 253 validate_pin(map_fd, map_name, 100 + (int)path_kind, path_kind); 254 validate_get(map_fd, map_name, 200 + (int)path_kind, path_kind); 255 ASSERT_OK(close(map_fd), "close_map_fd"); 256 } 257 258 void test_bpf_obj_pinning() 259 { 260 if (test__start_subtest("detached")) 261 bpf_obj_pinning_detached(); 262 if (test__start_subtest("mounted-str-abs")) 263 bpf_obj_pinning_mounted(PATH_STR_ABS); 264 if (test__start_subtest("mounted-str-rel")) 265 bpf_obj_pinning_mounted(PATH_STR_REL); 266 if (test__start_subtest("mounted-fd-rel")) 267 bpf_obj_pinning_mounted(PATH_FD_REL); 268 } 269