Skip to content

Commit 1d528e7

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf-fix-bpf_d_path-helper-prototype'
Shuran Liu says: ==================== bpf: fix bpf_d_path() helper prototype Hi, This series fixes a verifier issue with bpf_d_path() and adds a regression test to cover its use within a hook function. Patch 1 updates the bpf_d_path() helper prototype so that the second argument is marked as MEM_WRITE. This makes it explicit to the verifier that the helper writes into the provided buffer. Patch 2 extends the existing d_path selftest to cover incorrect verifier assumptions caused by an incorrect function prototype. The test program calls bpf_d_path() and checks if the first character of the path can be read. It ensures the verifier does not assume the buffer remains unwritten. Changelog ========= v5: - Moved the temporary file for the fallocate test from /tmp to /dev/shm Since bpf CI's 9P filesystem under /tmp does not support fallocate. v4: - Use the fallocate hook instead of an LSM hook to simplify the selftest, as suggested by Matt and Alexei. - Add a utility function in test_d_path.c to load the BPF program, improving code reuse. v3: - Switch the pathname prefix loop to use bpf_for() instead of #pragma unroll, as suggested by Matt. - Remove /tmp/bpf_d_path_test in the test cleanup path. - Add the missing Reviewed-by tags. v2: - Merge the new test into the existing d_path selftest rather than creating new files. - Add PID filtering in the LSM program to avoid nondeterministic failures due to unrelated processes triggering bprm_check_security. - Synchronize child execution using a pipe to ensure deterministic updates to the PID. Thanks for your time and reviews. ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 9489d45 + 79e247d commit 1d528e7

File tree

3 files changed

+96
-18
lines changed

3 files changed

+96
-18
lines changed

kernel/trace/bpf_trace.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,7 @@ static const struct bpf_func_proto bpf_d_path_proto = {
965965
.ret_type = RET_INTEGER,
966966
.arg1_type = ARG_PTR_TO_BTF_ID,
967967
.arg1_btf_id = &bpf_d_path_btf_ids[0],
968-
.arg2_type = ARG_PTR_TO_MEM,
968+
.arg2_type = ARG_PTR_TO_MEM | MEM_WRITE,
969969
.arg3_type = ARG_CONST_SIZE_OR_ZERO,
970970
.allowed = bpf_d_path_allowed,
971971
};

tools/testing/selftests/bpf/prog_tests/d_path.c

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ static int set_pathname(int fd, pid_t pid)
3838
return readlink(buf, src.paths[src.cnt++], MAX_PATH_LEN);
3939
}
4040

41+
static inline long syscall_close(int fd)
42+
{
43+
return syscall(__NR_close_range,
44+
(unsigned int)fd,
45+
(unsigned int)fd,
46+
0u);
47+
}
48+
4149
static int trigger_fstat_events(pid_t pid)
4250
{
4351
int sockfd = -1, procfd = -1, devfd = -1;
@@ -104,18 +112,34 @@ static int trigger_fstat_events(pid_t pid)
104112
/* sys_close no longer triggers filp_close, but we can
105113
* call sys_close_range instead which still does
106114
*/
107-
#define close(fd) syscall(__NR_close_range, fd, fd, 0)
115+
syscall_close(pipefd[0]);
116+
syscall_close(pipefd[1]);
117+
syscall_close(sockfd);
118+
syscall_close(procfd);
119+
syscall_close(devfd);
120+
syscall_close(localfd);
121+
syscall_close(indicatorfd);
122+
return ret;
123+
}
108124

109-
close(pipefd[0]);
110-
close(pipefd[1]);
111-
close(sockfd);
112-
close(procfd);
113-
close(devfd);
114-
close(localfd);
115-
close(indicatorfd);
125+
static void attach_and_load(struct test_d_path **skel)
126+
{
127+
int err;
116128

117-
#undef close
118-
return ret;
129+
*skel = test_d_path__open_and_load();
130+
if (CHECK(!*skel, "setup", "d_path skeleton failed\n"))
131+
goto cleanup;
132+
133+
err = test_d_path__attach(*skel);
134+
if (CHECK(err, "setup", "attach failed: %d\n", err))
135+
goto cleanup;
136+
137+
(*skel)->bss->my_pid = getpid();
138+
return;
139+
140+
cleanup:
141+
test_d_path__destroy(*skel);
142+
*skel = NULL;
119143
}
120144

121145
static void test_d_path_basic(void)
@@ -124,16 +148,11 @@ static void test_d_path_basic(void)
124148
struct test_d_path *skel;
125149
int err;
126150

127-
skel = test_d_path__open_and_load();
128-
if (CHECK(!skel, "setup", "d_path skeleton failed\n"))
129-
goto cleanup;
130-
131-
err = test_d_path__attach(skel);
132-
if (CHECK(err, "setup", "attach failed: %d\n", err))
151+
attach_and_load(&skel);
152+
if (!skel)
133153
goto cleanup;
134154

135155
bss = skel->bss;
136-
bss->my_pid = getpid();
137156

138157
err = trigger_fstat_events(bss->my_pid);
139158
if (err < 0)
@@ -195,6 +214,39 @@ static void test_d_path_check_types(void)
195214
test_d_path_check_types__destroy(skel);
196215
}
197216

217+
/* Check if the verifier correctly generates code for
218+
* accessing the memory modified by d_path helper.
219+
*/
220+
static void test_d_path_mem_access(void)
221+
{
222+
int localfd = -1;
223+
char path_template[] = "/dev/shm/d_path_loadgen.XXXXXX";
224+
struct test_d_path__bss *bss;
225+
struct test_d_path *skel;
226+
227+
attach_and_load(&skel);
228+
if (!skel)
229+
goto cleanup;
230+
231+
bss = skel->bss;
232+
233+
localfd = mkstemp(path_template);
234+
if (CHECK(localfd < 0, "trigger", "mkstemp failed\n"))
235+
goto cleanup;
236+
237+
if (CHECK(fallocate(localfd, 0, 0, 1024) < 0, "trigger", "fallocate failed\n"))
238+
goto cleanup;
239+
remove(path_template);
240+
241+
if (CHECK(!bss->path_match_fallocate, "check",
242+
"failed to read fallocate path"))
243+
goto cleanup;
244+
245+
cleanup:
246+
syscall_close(localfd);
247+
test_d_path__destroy(skel);
248+
}
249+
198250
void test_d_path(void)
199251
{
200252
if (test__start_subtest("basic"))
@@ -205,4 +257,7 @@ void test_d_path(void)
205257

206258
if (test__start_subtest("check_alloc_mem"))
207259
test_d_path_check_types();
260+
261+
if (test__start_subtest("check_mem_access"))
262+
test_d_path_mem_access();
208263
}

tools/testing/selftests/bpf/progs/test_d_path.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ int rets_close[MAX_FILES] = {};
1717

1818
int called_stat = 0;
1919
int called_close = 0;
20+
int path_match_fallocate = 0;
2021

2122
SEC("fentry/security_inode_getattr")
2223
int BPF_PROG(prog_stat, struct path *path, struct kstat *stat,
@@ -62,4 +63,26 @@ int BPF_PROG(prog_close, struct file *file, void *id)
6263
return 0;
6364
}
6465

66+
SEC("fentry/vfs_fallocate")
67+
int BPF_PROG(prog_fallocate, struct file *file, int mode, loff_t offset, loff_t len)
68+
{
69+
pid_t pid = bpf_get_current_pid_tgid() >> 32;
70+
int ret = 0;
71+
char path_fallocate[MAX_PATH_LEN] = {};
72+
73+
if (pid != my_pid)
74+
return 0;
75+
76+
ret = bpf_d_path(&file->f_path,
77+
path_fallocate, MAX_PATH_LEN);
78+
if (ret < 0)
79+
return 0;
80+
81+
if (!path_fallocate[0])
82+
return 0;
83+
84+
path_match_fallocate = 1;
85+
return 0;
86+
}
87+
6588
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)