Skip to content

Commit ff358ad

Browse files
mattbobrowskiAlexei Starovoitov
authored and
Alexei Starovoitov
committed
selftests/bpf: add negative tests for new VFS based BPF kfuncs
Add a bunch of negative selftests responsible for asserting that the BPF verifier successfully rejects a BPF program load when the underlying BPF program misuses one of the newly introduced VFS based BPF kfuncs. The following VFS based BPF kfuncs are extensively tested within this new selftest: * struct file *bpf_get_task_exe_file(struct task_struct *); * void bpf_put_file(struct file *); * int bpf_path_d_path(struct path *, char *, size_t); Acked-by: Christian Brauner <[email protected]> Acked-by: Song Liu <[email protected]> Signed-off-by: Matt Bobrowski <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent d08e204 commit ff358ad

File tree

3 files changed

+189
-0
lines changed

3 files changed

+189
-0
lines changed

tools/testing/selftests/bpf/bpf_experimental.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,32 @@ extern void bpf_iter_task_vma_destroy(struct bpf_iter_task_vma *it) __ksym;
195195
*/
196196
extern void bpf_throw(u64 cookie) __ksym;
197197

198+
/* Description
199+
* Acquire a reference on the exe_file member field belonging to the
200+
* mm_struct that is nested within the supplied task_struct. The supplied
201+
* task_struct must be trusted/referenced.
202+
* Returns
203+
* A referenced file pointer pointing to the exe_file member field of the
204+
* mm_struct nested in the supplied task_struct, or NULL.
205+
*/
206+
extern struct file *bpf_get_task_exe_file(struct task_struct *task) __ksym;
207+
208+
/* Description
209+
* Release a reference on the supplied file. The supplied file must be
210+
* acquired.
211+
*/
212+
extern void bpf_put_file(struct file *file) __ksym;
213+
214+
/* Description
215+
* Resolve a pathname for the supplied path and store it in the supplied
216+
* buffer. The supplied path must be trusted/referenced.
217+
* Returns
218+
* A positive integer corresponding to the length of the resolved pathname,
219+
* including the NULL termination character, stored in the supplied
220+
* buffer. On error, a negative integer is returned.
221+
*/
222+
extern int bpf_path_d_path(struct path *path, char *buf, size_t buf__sz) __ksym;
223+
198224
/* This macro must be used to mark the exception callback corresponding to the
199225
* main program. For example:
200226
*

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
#include "verifier_value_or_null.skel.h"
8686
#include "verifier_value_ptr_arith.skel.h"
8787
#include "verifier_var_off.skel.h"
88+
#include "verifier_vfs_reject.skel.h"
8889
#include "verifier_xadd.skel.h"
8990
#include "verifier_xdp.skel.h"
9091
#include "verifier_xdp_direct_packet_access.skel.h"
@@ -205,6 +206,7 @@ void test_verifier_value(void) { RUN(verifier_value); }
205206
void test_verifier_value_illegal_alu(void) { RUN(verifier_value_illegal_alu); }
206207
void test_verifier_value_or_null(void) { RUN(verifier_value_or_null); }
207208
void test_verifier_var_off(void) { RUN(verifier_var_off); }
209+
void test_verifier_vfs_reject(void) { RUN(verifier_vfs_reject); }
208210
void test_verifier_xadd(void) { RUN(verifier_xadd); }
209211
void test_verifier_xdp(void) { RUN(verifier_xdp); }
210212
void test_verifier_xdp_direct_packet_access(void) { RUN(verifier_xdp_direct_packet_access); }
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2024 Google LLC. */
3+
4+
#include <vmlinux.h>
5+
#include <bpf/bpf_helpers.h>
6+
#include <bpf/bpf_tracing.h>
7+
#include <linux/limits.h>
8+
9+
#include "bpf_misc.h"
10+
#include "bpf_experimental.h"
11+
12+
static char buf[PATH_MAX];
13+
14+
SEC("lsm.s/file_open")
15+
__failure __msg("Possibly NULL pointer passed to trusted arg0")
16+
int BPF_PROG(get_task_exe_file_kfunc_null)
17+
{
18+
struct file *acquired;
19+
20+
/* Can't pass a NULL pointer to bpf_get_task_exe_file(). */
21+
acquired = bpf_get_task_exe_file(NULL);
22+
if (!acquired)
23+
return 0;
24+
25+
bpf_put_file(acquired);
26+
return 0;
27+
}
28+
29+
SEC("lsm.s/inode_getxattr")
30+
__failure __msg("arg#0 pointer type STRUCT task_struct must point to scalar, or struct with scalar")
31+
int BPF_PROG(get_task_exe_file_kfunc_fp)
32+
{
33+
u64 x;
34+
struct file *acquired;
35+
struct task_struct *task;
36+
37+
task = (struct task_struct *)&x;
38+
/* Can't pass random frame pointer to bpf_get_task_exe_file(). */
39+
acquired = bpf_get_task_exe_file(task);
40+
if (!acquired)
41+
return 0;
42+
43+
bpf_put_file(acquired);
44+
return 0;
45+
}
46+
47+
SEC("lsm.s/file_open")
48+
__failure __msg("R1 must be referenced or trusted")
49+
int BPF_PROG(get_task_exe_file_kfunc_untrusted)
50+
{
51+
struct file *acquired;
52+
struct task_struct *parent;
53+
54+
/* Walking a trusted struct task_struct returned from
55+
* bpf_get_current_task_btf() yields an untrusted pointer.
56+
*/
57+
parent = bpf_get_current_task_btf()->parent;
58+
/* Can't pass untrusted pointer to bpf_get_task_exe_file(). */
59+
acquired = bpf_get_task_exe_file(parent);
60+
if (!acquired)
61+
return 0;
62+
63+
bpf_put_file(acquired);
64+
return 0;
65+
}
66+
67+
SEC("lsm.s/file_open")
68+
__failure __msg("Unreleased reference")
69+
int BPF_PROG(get_task_exe_file_kfunc_unreleased)
70+
{
71+
struct file *acquired;
72+
73+
acquired = bpf_get_task_exe_file(bpf_get_current_task_btf());
74+
if (!acquired)
75+
return 0;
76+
77+
/* Acquired but never released. */
78+
return 0;
79+
}
80+
81+
SEC("lsm.s/file_open")
82+
__failure __msg("release kernel function bpf_put_file expects")
83+
int BPF_PROG(put_file_kfunc_unacquired, struct file *file)
84+
{
85+
/* Can't release an unacquired pointer. */
86+
bpf_put_file(file);
87+
return 0;
88+
}
89+
90+
SEC("lsm.s/file_open")
91+
__failure __msg("Possibly NULL pointer passed to trusted arg0")
92+
int BPF_PROG(path_d_path_kfunc_null)
93+
{
94+
/* Can't pass NULL value to bpf_path_d_path() kfunc. */
95+
bpf_path_d_path(NULL, buf, sizeof(buf));
96+
return 0;
97+
}
98+
99+
SEC("lsm.s/task_alloc")
100+
__failure __msg("R1 must be referenced or trusted")
101+
int BPF_PROG(path_d_path_kfunc_untrusted_from_argument, struct task_struct *task)
102+
{
103+
struct path *root;
104+
105+
/* Walking a trusted argument typically yields an untrusted
106+
* pointer. This is one example of that.
107+
*/
108+
root = &task->fs->root;
109+
bpf_path_d_path(root, buf, sizeof(buf));
110+
return 0;
111+
}
112+
113+
SEC("lsm.s/file_open")
114+
__failure __msg("R1 must be referenced or trusted")
115+
int BPF_PROG(path_d_path_kfunc_untrusted_from_current)
116+
{
117+
struct path *pwd;
118+
struct task_struct *current;
119+
120+
current = bpf_get_current_task_btf();
121+
/* Walking a trusted pointer returned from bpf_get_current_task_btf()
122+
* yields an untrusted pointer.
123+
*/
124+
pwd = &current->fs->pwd;
125+
bpf_path_d_path(pwd, buf, sizeof(buf));
126+
return 0;
127+
}
128+
129+
SEC("lsm.s/file_open")
130+
__failure __msg("kernel function bpf_path_d_path args#0 expected pointer to STRUCT path but R1 has a pointer to STRUCT file")
131+
int BPF_PROG(path_d_path_kfunc_type_mismatch, struct file *file)
132+
{
133+
bpf_path_d_path((struct path *)&file->f_task_work, buf, sizeof(buf));
134+
return 0;
135+
}
136+
137+
SEC("lsm.s/file_open")
138+
__failure __msg("invalid access to map value, value_size=4096 off=0 size=8192")
139+
int BPF_PROG(path_d_path_kfunc_invalid_buf_sz, struct file *file)
140+
{
141+
/* bpf_path_d_path() enforces a constraint on the buffer size supplied
142+
* by the BPF LSM program via the __sz annotation. buf here is set to
143+
* PATH_MAX, so let's ensure that the BPF verifier rejects BPF_PROG_LOAD
144+
* attempts if the supplied size and the actual size of the buffer
145+
* mismatches.
146+
*/
147+
bpf_path_d_path(&file->f_path, buf, PATH_MAX * 2);
148+
return 0;
149+
}
150+
151+
SEC("fentry/vfs_open")
152+
__failure __msg("calling kernel function bpf_path_d_path is not allowed")
153+
int BPF_PROG(path_d_path_kfunc_non_lsm, struct path *path, struct file *f)
154+
{
155+
/* Calling bpf_path_d_path() from a non-LSM BPF program isn't permitted.
156+
*/
157+
bpf_path_d_path(path, buf, sizeof(buf));
158+
return 0;
159+
}
160+
161+
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)