xref: /linux/fs/binfmt_misc_bpf.c (revision b9cba7ebfe539f3e4bbdd03a1e0efa3b30b3f592)
1b4bfe2f6SChristian Brauner // SPDX-License-Identifier: GPL-2.0-only
2b4bfe2f6SChristian Brauner /*
3b4bfe2f6SChristian Brauner  * BPF-backed binary type handlers for binfmt_misc.
4b4bfe2f6SChristian Brauner  *
5b4bfe2f6SChristian Brauner  * A handler is a struct binfmt_misc_ops struct_ops map. Loading and
6b4bfe2f6SChristian Brauner  * registering it makes the handler available under its name in the user
7b4bfe2f6SChristian Brauner  * namespace it was registered in. A binfmt_misc 'B' entry activates it:
8b4bfe2f6SChristian Brauner  *
9b4bfe2f6SChristian Brauner  *   echo ':entry:B::::<handler-name>:' > <binfmt_misc>/register
10*6ec7c96bSChristian Brauner  *
11*6ec7c96bSChristian Brauner  * The entry can bind the interpreters the handler may run its binaries
12*6ec7c96bSChristian Brauner  * with, each opened by the write that binds it and selected by name per
13*6ec7c96bSChristian Brauner  * exec. An entry registered with 'D' is not matchable yet, which is what
14*6ec7c96bSChristian Brauner  * leaves it open to being given them:
15*6ec7c96bSChristian Brauner  *
16*6ec7c96bSChristian Brauner  *   echo ':entry:B::::<handler-name>:D' > <binfmt_misc>/register
17*6ec7c96bSChristian Brauner  *   echo '+<name> <path>' > <binfmt_misc>/entry
18*6ec7c96bSChristian Brauner  *   echo 1 > <binfmt_misc>/entry
19b4bfe2f6SChristian Brauner  */
20b4bfe2f6SChristian Brauner 
21b4bfe2f6SChristian Brauner #include <linux/binfmt_misc.h>
22b4bfe2f6SChristian Brauner #include <linux/binfmts.h>
23b4bfe2f6SChristian Brauner #include <linux/bpf.h>
24b4bfe2f6SChristian Brauner #include <linux/bpf_verifier.h>
25b4bfe2f6SChristian Brauner #include <linux/btf.h>
26b4bfe2f6SChristian Brauner #include <linux/btf_ids.h>
27b4bfe2f6SChristian Brauner #include <linux/cred.h>
28*6ec7c96bSChristian Brauner #include <linux/file.h>
29*6ec7c96bSChristian Brauner #include <linux/fs.h>
30b4bfe2f6SChristian Brauner #include <linux/init.h>
31b4bfe2f6SChristian Brauner #include <linux/limits.h>
32b4bfe2f6SChristian Brauner #include <linux/slab.h>
33b4bfe2f6SChristian Brauner #include <linux/spinlock.h>
34b4bfe2f6SChristian Brauner #include <linux/string.h>
35b4bfe2f6SChristian Brauner #include <linux/user_namespace.h>
36b4bfe2f6SChristian Brauner 
37b4bfe2f6SChristian Brauner struct bm_bpf_ops_reg {
38b4bfe2f6SChristian Brauner 	struct list_head list;
39b4bfe2f6SChristian Brauner 	const struct binfmt_misc_ops *ops;
40b4bfe2f6SChristian Brauner 	struct bpf_link *link;
41b4bfe2f6SChristian Brauner 	struct user_namespace *user_ns;
42b4bfe2f6SChristian Brauner };
43b4bfe2f6SChristian Brauner 
44b4bfe2f6SChristian Brauner static DEFINE_SPINLOCK(bm_bpf_ops_lock);
45b4bfe2f6SChristian Brauner static LIST_HEAD(bm_bpf_ops_list);
46b4bfe2f6SChristian Brauner 
47b4bfe2f6SChristian Brauner static struct bpf_struct_ops bpf_binfmt_misc_ops;
48b4bfe2f6SChristian Brauner 
49b4bfe2f6SChristian Brauner static struct bm_bpf_ops_reg *bm_bpf_ops_find(const struct user_namespace *user_ns,
50b4bfe2f6SChristian Brauner 					      const char *name)
51b4bfe2f6SChristian Brauner {
52b4bfe2f6SChristian Brauner 	struct bm_bpf_ops_reg *reg;
53b4bfe2f6SChristian Brauner 
54b4bfe2f6SChristian Brauner 	lockdep_assert_held(&bm_bpf_ops_lock);
55b4bfe2f6SChristian Brauner 
56b4bfe2f6SChristian Brauner 	list_for_each_entry(reg, &bm_bpf_ops_list, list) {
57b4bfe2f6SChristian Brauner 		if (reg->user_ns == user_ns && !strcmp(reg->ops->name, name))
58b4bfe2f6SChristian Brauner 			return reg;
59b4bfe2f6SChristian Brauner 	}
60b4bfe2f6SChristian Brauner 	return NULL;
61b4bfe2f6SChristian Brauner }
62b4bfe2f6SChristian Brauner 
63b4bfe2f6SChristian Brauner /**
64b4bfe2f6SChristian Brauner  * binfmt_misc_get_ops - look up a bpf binary type handler by name
65b4bfe2f6SChristian Brauner  * @user_ns: user namespace of the binfmt_misc instance
66b4bfe2f6SChristian Brauner  * @name: name the handler was registered under
67b4bfe2f6SChristian Brauner  *
68b4bfe2f6SChristian Brauner  * Look for a handler named @name registered in @user_ns. A handler is not
69b4bfe2f6SChristian Brauner  * inherited from ancestor user namespaces: an entry can only name a handler
70b4bfe2f6SChristian Brauner  * registered in the same user namespace as its instance. The returned handler
71b4bfe2f6SChristian Brauner  * stays callable until binfmt_misc_put_ops() even if the backing struct_ops
72b4bfe2f6SChristian Brauner  * map is detached or deleted in the meantime.
73b4bfe2f6SChristian Brauner  *
74b4bfe2f6SChristian Brauner  * Return: the handler on success, NULL on failure
75b4bfe2f6SChristian Brauner  */
76b4bfe2f6SChristian Brauner const struct binfmt_misc_ops *binfmt_misc_get_ops(struct user_namespace *user_ns,
77b4bfe2f6SChristian Brauner 						  const char *name)
78b4bfe2f6SChristian Brauner {
79b4bfe2f6SChristian Brauner 	struct bm_bpf_ops_reg *reg;
80b4bfe2f6SChristian Brauner 
81b4bfe2f6SChristian Brauner 	guard(spinlock)(&bm_bpf_ops_lock);
82b4bfe2f6SChristian Brauner 
83b4bfe2f6SChristian Brauner 	reg = bm_bpf_ops_find(user_ns, name);
84b4bfe2f6SChristian Brauner 	if (!reg)
85b4bfe2f6SChristian Brauner 		return NULL;
86b4bfe2f6SChristian Brauner 	if (!bpf_struct_ops_get(reg->ops))
87b4bfe2f6SChristian Brauner 		return NULL;
88b4bfe2f6SChristian Brauner 	return reg->ops;
89b4bfe2f6SChristian Brauner }
90b4bfe2f6SChristian Brauner 
91b4bfe2f6SChristian Brauner void binfmt_misc_put_ops(const struct binfmt_misc_ops *ops)
92b4bfe2f6SChristian Brauner {
93b4bfe2f6SChristian Brauner 	bpf_struct_ops_put(ops);
94b4bfe2f6SChristian Brauner }
95b4bfe2f6SChristian Brauner 
96b4bfe2f6SChristian Brauner bool bpf_prog_is_binfmt_misc_ops(const struct bpf_prog *prog)
97b4bfe2f6SChristian Brauner {
98b4bfe2f6SChristian Brauner 	return prog->type == BPF_PROG_TYPE_STRUCT_OPS &&
99b4bfe2f6SChristian Brauner 	       prog->aux->st_ops == &bpf_binfmt_misc_ops;
100b4bfe2f6SChristian Brauner }
101b4bfe2f6SChristian Brauner 
102*6ec7c96bSChristian Brauner /*
103*6ec7c96bSChristian Brauner  * Replace the staged interpreter selection: naming a path drops a bound
104*6ec7c96bSChristian Brauner  * file, selecting a bound interpreter carries its file along.
105*6ec7c96bSChristian Brauner  */
106*6ec7c96bSChristian Brauner static void bm_bpf_stage_selection(struct linux_binprm *bprm, char *path,
107*6ec7c96bSChristian Brauner 				   struct file *f)
108*6ec7c96bSChristian Brauner {
109*6ec7c96bSChristian Brauner 	if (bprm->bpf_interp_file)
110*6ec7c96bSChristian Brauner 		fput(bprm->bpf_interp_file);
111*6ec7c96bSChristian Brauner 	kfree(bprm->bpf_interp);
112*6ec7c96bSChristian Brauner 	bprm->bpf_interp = path;
113*6ec7c96bSChristian Brauner 	bprm->bpf_interp_file = f;
114*6ec7c96bSChristian Brauner }
115*6ec7c96bSChristian Brauner 
116b4bfe2f6SChristian Brauner __bpf_kfunc_start_defs();
117b4bfe2f6SChristian Brauner 
118b4bfe2f6SChristian Brauner /**
119b4bfe2f6SChristian Brauner  * bpf_binprm_set_interp - select the interpreter for the current exec
120b4bfe2f6SChristian Brauner  * @bprm: binary that is being executed
121b4bfe2f6SChristian Brauner  * @path: absolute path to the interpreter
122b4bfe2f6SChristian Brauner  * @path__sz: size of the @path buffer, including the terminating NUL
123b4bfe2f6SChristian Brauner  *
124b4bfe2f6SChristian Brauner  * To be called from the load program of a struct binfmt_misc_ops handler
125b4bfe2f6SChristian Brauner  * before returning zero; the verifier rejects the call from any other
126b4bfe2f6SChristian Brauner  * program, including the handler's own match program. The path is opened
127b4bfe2f6SChristian Brauner  * with the credentials of the task doing the exec after the program
128*6ec7c96bSChristian Brauner  * returns. Calling it again replaces the selection, as does selecting an
129*6ec7c96bSChristian Brauner  * interpreter the entry bound with bpf_binprm_select_interp().
130b4bfe2f6SChristian Brauner  *
131b4bfe2f6SChristian Brauner  * Return: 0 on success, a negative errno on failure
132b4bfe2f6SChristian Brauner  */
133b4bfe2f6SChristian Brauner __bpf_kfunc int bpf_binprm_set_interp(struct linux_binprm *bprm,
134b4bfe2f6SChristian Brauner 				      const char *path, size_t path__sz)
135b4bfe2f6SChristian Brauner {
136b4bfe2f6SChristian Brauner 	size_t len;
137b4bfe2f6SChristian Brauner 	char *interp;
138b4bfe2f6SChristian Brauner 
139b4bfe2f6SChristian Brauner 	if (!path__sz)
140b4bfe2f6SChristian Brauner 		return -EINVAL;
141b4bfe2f6SChristian Brauner 	len = strnlen(path, path__sz);
142b4bfe2f6SChristian Brauner 	if (len == path__sz)
143b4bfe2f6SChristian Brauner 		return -EINVAL;
144b4bfe2f6SChristian Brauner 	if (path[0] != '/')
145b4bfe2f6SChristian Brauner 		return -EINVAL;
146b4bfe2f6SChristian Brauner 	if (len >= PATH_MAX)
147b4bfe2f6SChristian Brauner 		return -ENAMETOOLONG;
148b4bfe2f6SChristian Brauner 
149b4bfe2f6SChristian Brauner 	interp = kmemdup_nul(path, len, GFP_KERNEL);
150b4bfe2f6SChristian Brauner 	if (!interp)
151b4bfe2f6SChristian Brauner 		return -ENOMEM;
152b4bfe2f6SChristian Brauner 
153*6ec7c96bSChristian Brauner 	bm_bpf_stage_selection(bprm, interp, NULL);
154*6ec7c96bSChristian Brauner 	return 0;
155*6ec7c96bSChristian Brauner }
156*6ec7c96bSChristian Brauner 
157*6ec7c96bSChristian Brauner /**
158*6ec7c96bSChristian Brauner  * bpf_binprm_select_interp - run this exec under an interpreter the entry bound
159*6ec7c96bSChristian Brauner  * @bprm: binary that is being executed
160*6ec7c96bSChristian Brauner  * @name: name the interpreter was registered under
161*6ec7c96bSChristian Brauner  * @name__sz: size of the @name buffer, including the terminating NUL
162*6ec7c96bSChristian Brauner  *
163*6ec7c96bSChristian Brauner  * To be called from the load program of a struct binfmt_misc_ops handler
164*6ec7c96bSChristian Brauner  * instead of bpf_binprm_set_interp(). It selects one of the interpreters
165*6ec7c96bSChristian Brauner  * the matched entry was registered with, each of which was opened once when
166*6ec7c96bSChristian Brauner  * the entry was registered. Nothing is resolved at exec time, so no
167*6ec7c96bSChristian Brauner  * filesystem view can redirect the interpreter.
168*6ec7c96bSChristian Brauner  *
169*6ec7c96bSChristian Brauner  * The interpreter runs under the path the entry registered it under.
170*6ec7c96bSChristian Brauner  * Calling it again replaces the selection.
171*6ec7c96bSChristian Brauner  *
172*6ec7c96bSChristian Brauner  * Return: 0 on success, -ENOENT if the matched entry bound no interpreter
173*6ec7c96bSChristian Brauner  * of that name, a negative errno on failure
174*6ec7c96bSChristian Brauner  */
175*6ec7c96bSChristian Brauner __bpf_kfunc int bpf_binprm_select_interp(struct linux_binprm *bprm,
176*6ec7c96bSChristian Brauner 					 const char *name, size_t name__sz)
177*6ec7c96bSChristian Brauner {
178*6ec7c96bSChristian Brauner 	const struct binfmt_misc_interp *interp;
179*6ec7c96bSChristian Brauner 	size_t len;
180*6ec7c96bSChristian Brauner 	char *path;
181*6ec7c96bSChristian Brauner 
182*6ec7c96bSChristian Brauner 	if (!name__sz)
183*6ec7c96bSChristian Brauner 		return -EINVAL;
184*6ec7c96bSChristian Brauner 	len = strnlen(name, name__sz);
185*6ec7c96bSChristian Brauner 	if (len == name__sz || !len)
186*6ec7c96bSChristian Brauner 		return -EINVAL;
187*6ec7c96bSChristian Brauner 
188*6ec7c96bSChristian Brauner 	interp = binfmt_misc_find_interp(bprm->bpf_interps, name);
189*6ec7c96bSChristian Brauner 	if (!interp)
190*6ec7c96bSChristian Brauner 		return -ENOENT;
191*6ec7c96bSChristian Brauner 
192*6ec7c96bSChristian Brauner 	path = kstrdup(interp->path, GFP_KERNEL);
193*6ec7c96bSChristian Brauner 	if (!path)
194*6ec7c96bSChristian Brauner 		return -ENOMEM;
195*6ec7c96bSChristian Brauner 
196*6ec7c96bSChristian Brauner 	bm_bpf_stage_selection(bprm, path, get_file(interp->file));
197b4bfe2f6SChristian Brauner 	return 0;
198b4bfe2f6SChristian Brauner }
199b4bfe2f6SChristian Brauner 
200b4bfe2f6SChristian Brauner /**
201b4bfe2f6SChristian Brauner  * bpf_binprm_set_interp_arg - set a single argument for the interpreter
202b4bfe2f6SChristian Brauner  * @bprm: binary that is being executed
203b4bfe2f6SChristian Brauner  * @arg: argument to pass to the interpreter
204b4bfe2f6SChristian Brauner  * @arg__sz: size of the @arg buffer, including the terminating NUL
205b4bfe2f6SChristian Brauner  *
206b4bfe2f6SChristian Brauner  * To be called from the load program of a struct binfmt_misc_ops handler. The
207b4bfe2f6SChristian Brauner  * argument is passed to the interpreter ahead of the binary, mirroring the
208b4bfe2f6SChristian Brauner  * single optional argument of a #! interpreter line. Calling it again
209b4bfe2f6SChristian Brauner  * replaces the argument.
210b4bfe2f6SChristian Brauner  *
211b4bfe2f6SChristian Brauner  * Return: 0 on success, a negative errno on failure
212b4bfe2f6SChristian Brauner  */
213b4bfe2f6SChristian Brauner __bpf_kfunc int bpf_binprm_set_interp_arg(struct linux_binprm *bprm,
214b4bfe2f6SChristian Brauner 					  const char *arg, size_t arg__sz)
215b4bfe2f6SChristian Brauner {
216b4bfe2f6SChristian Brauner 	size_t len;
217b4bfe2f6SChristian Brauner 	char *val;
218b4bfe2f6SChristian Brauner 
219b4bfe2f6SChristian Brauner 	if (!arg__sz)
220b4bfe2f6SChristian Brauner 		return -EINVAL;
221b4bfe2f6SChristian Brauner 	len = strnlen(arg, arg__sz);
222b4bfe2f6SChristian Brauner 	if (len == arg__sz)
223b4bfe2f6SChristian Brauner 		return -EINVAL;
224b4bfe2f6SChristian Brauner 	if (!len)
225b4bfe2f6SChristian Brauner 		return -EINVAL;
226b4bfe2f6SChristian Brauner 
227b4bfe2f6SChristian Brauner 	val = kmemdup_nul(arg, len, GFP_KERNEL);
228b4bfe2f6SChristian Brauner 	if (!val)
229b4bfe2f6SChristian Brauner 		return -ENOMEM;
230b4bfe2f6SChristian Brauner 
231b4bfe2f6SChristian Brauner 	kfree(bprm->bpf_interp_arg);
232b4bfe2f6SChristian Brauner 	bprm->bpf_interp_arg = val;
233b4bfe2f6SChristian Brauner 	return 0;
234b4bfe2f6SChristian Brauner }
235b4bfe2f6SChristian Brauner 
236b4bfe2f6SChristian Brauner /**
237b4bfe2f6SChristian Brauner  * bpf_binprm_set_flags - choose the interpreter invocation flags for this exec
238b4bfe2f6SChristian Brauner  * @bprm: binary that is being executed
239b4bfe2f6SChristian Brauner  * @flags: an OR of enum bpf_binprm_flags values
240b4bfe2f6SChristian Brauner  *
241b4bfe2f6SChristian Brauner  * To be called from the load program of a struct binfmt_misc_ops handler. It
242375e8a31SChristian Brauner  * decides per exec what a static entry fixes at registration with the P, C,
243375e8a31SChristian Brauner  * O, T and L flags: BPF_BINPRM_PRESERVE_ARGV0 keeps the caller's argv[0],
244b4bfe2f6SChristian Brauner  * BPF_BINPRM_CREDENTIALS computes credentials from the binary, and
245b4bfe2f6SChristian Brauner  * BPF_BINPRM_EXECFD hands the binary to the interpreter through AT_EXECFD.
24621e04378SChristian Brauner  * BPF_BINPRM_TRANSPARENT additionally leaves the argument vector untouched,
247375e8a31SChristian Brauner  * making the exec look like a direct execution of the binary.
248375e8a31SChristian Brauner  * BPF_BINPRM_LOADER substitutes the interpreter for the binary's PT_INTERP
249375e8a31SChristian Brauner  * and runs the binary as a native exec; it excludes every other flag.
250375e8a31SChristian Brauner  * Calling it again replaces the flags, passing zero clears them again.
251b4bfe2f6SChristian Brauner  *
25221e04378SChristian Brauner  * Return: 0 on success, -EINVAL if @flags contains an unknown bit or an
25321e04378SChristian Brauner  * invalid combination
254b4bfe2f6SChristian Brauner  */
255b4bfe2f6SChristian Brauner __bpf_kfunc int bpf_binprm_set_flags(struct linux_binprm *bprm,
256b4bfe2f6SChristian Brauner 				     enum bpf_binprm_flags flags)
257b4bfe2f6SChristian Brauner {
258b4bfe2f6SChristian Brauner 	if (flags & ~(BPF_BINPRM_PRESERVE_ARGV0 | BPF_BINPRM_CREDENTIALS |
259375e8a31SChristian Brauner 		      BPF_BINPRM_EXECFD | BPF_BINPRM_TRANSPARENT |
260375e8a31SChristian Brauner 		      BPF_BINPRM_LOADER))
261375e8a31SChristian Brauner 		return -EINVAL;
262375e8a31SChristian Brauner 
263375e8a31SChristian Brauner 	/* Loader substitution is a native exec: no splice, execfd or creds work. */
264375e8a31SChristian Brauner 	if ((flags & BPF_BINPRM_LOADER) && (flags & ~BPF_BINPRM_LOADER))
26521e04378SChristian Brauner 		return -EINVAL;
26621e04378SChristian Brauner 
26721e04378SChristian Brauner 	/* Transparency preserves the whole argv, argv[0] included. */
26821e04378SChristian Brauner 	if ((flags & BPF_BINPRM_TRANSPARENT) && (flags & BPF_BINPRM_PRESERVE_ARGV0))
269b4bfe2f6SChristian Brauner 		return -EINVAL;
270b4bfe2f6SChristian Brauner 
271b4bfe2f6SChristian Brauner 	bprm->bpf_flags = flags;
272b4bfe2f6SChristian Brauner 	return 0;
273b4bfe2f6SChristian Brauner }
274b4bfe2f6SChristian Brauner 
275b4bfe2f6SChristian Brauner __bpf_kfunc_end_defs();
276b4bfe2f6SChristian Brauner 
277b4bfe2f6SChristian Brauner BTF_KFUNCS_START(bm_bpf_kfunc_ids)
278b4bfe2f6SChristian Brauner BTF_ID_FLAGS(func, bpf_binprm_set_interp, KF_SLEEPABLE)
279*6ec7c96bSChristian Brauner BTF_ID_FLAGS(func, bpf_binprm_select_interp, KF_SLEEPABLE)
280b4bfe2f6SChristian Brauner BTF_ID_FLAGS(func, bpf_binprm_set_interp_arg, KF_SLEEPABLE)
281b4bfe2f6SChristian Brauner BTF_ID_FLAGS(func, bpf_binprm_set_flags, KF_SLEEPABLE)
282b4bfe2f6SChristian Brauner BTF_KFUNCS_END(bm_bpf_kfunc_ids)
283b4bfe2f6SChristian Brauner 
284b4bfe2f6SChristian Brauner static int bm_bpf_kfunc_filter(const struct bpf_prog *prog, u32 kfunc_id)
285b4bfe2f6SChristian Brauner {
286b4bfe2f6SChristian Brauner 	if (!btf_id_set8_contains(&bm_bpf_kfunc_ids, kfunc_id))
287b4bfe2f6SChristian Brauner 		return 0;
288b4bfe2f6SChristian Brauner 	if (prog->type != BPF_PROG_TYPE_STRUCT_OPS)
289b4bfe2f6SChristian Brauner 		return -EACCES;
290b4bfe2f6SChristian Brauner 	/* ->st_ops is unset during the cfg pass; enforced once it is set. */
291b4bfe2f6SChristian Brauner 	if (!prog->aux->st_ops)
292b4bfe2f6SChristian Brauner 		return 0;
293b4bfe2f6SChristian Brauner 	/* Only the load program decides how a binary is run. */
294b4bfe2f6SChristian Brauner 	if (bpf_prog_is_binfmt_misc_ops(prog) &&
295b4bfe2f6SChristian Brauner 	    prog->aux->attach_st_ops_member_off == offsetof(struct binfmt_misc_ops, load))
296b4bfe2f6SChristian Brauner 		return 0;
297b4bfe2f6SChristian Brauner 	return -EACCES;
298b4bfe2f6SChristian Brauner }
299b4bfe2f6SChristian Brauner 
300b4bfe2f6SChristian Brauner static const struct btf_kfunc_id_set bm_bpf_kfunc_set = {
301b4bfe2f6SChristian Brauner 	.owner	= THIS_MODULE,
302b4bfe2f6SChristian Brauner 	.set	= &bm_bpf_kfunc_ids,
303b4bfe2f6SChristian Brauner 	.filter	= bm_bpf_kfunc_filter,
304b4bfe2f6SChristian Brauner };
305b4bfe2f6SChristian Brauner 
306b4bfe2f6SChristian Brauner static bool bm_bpf_ops__match(struct linux_binprm *bprm)
307b4bfe2f6SChristian Brauner {
308b4bfe2f6SChristian Brauner 	return false;
309b4bfe2f6SChristian Brauner }
310b4bfe2f6SChristian Brauner 
311b4bfe2f6SChristian Brauner static int bm_bpf_ops__load(struct linux_binprm *bprm)
312b4bfe2f6SChristian Brauner {
313b4bfe2f6SChristian Brauner 	return 0;
314b4bfe2f6SChristian Brauner }
315b4bfe2f6SChristian Brauner 
316b4bfe2f6SChristian Brauner static struct binfmt_misc_ops bm_bpf_ops_stubs = {
317b4bfe2f6SChristian Brauner 	.match = bm_bpf_ops__match,
318b4bfe2f6SChristian Brauner 	.load = bm_bpf_ops__load,
319b4bfe2f6SChristian Brauner };
320b4bfe2f6SChristian Brauner 
321b4bfe2f6SChristian Brauner static int bm_bpf_init(struct btf *btf)
322b4bfe2f6SChristian Brauner {
323b4bfe2f6SChristian Brauner 	return register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS,
324b4bfe2f6SChristian Brauner 					 &bm_bpf_kfunc_set);
325b4bfe2f6SChristian Brauner }
326b4bfe2f6SChristian Brauner 
327b4bfe2f6SChristian Brauner static int bm_bpf_check_member(const struct btf_type *t,
328b4bfe2f6SChristian Brauner 			       const struct btf_member *member,
329b4bfe2f6SChristian Brauner 			       const struct bpf_prog *prog)
330b4bfe2f6SChristian Brauner {
331b4bfe2f6SChristian Brauner 	u32 moff = __btf_member_bit_offset(t, member) / 8;
332b4bfe2f6SChristian Brauner 
333b4bfe2f6SChristian Brauner 	switch (moff) {
334b4bfe2f6SChristian Brauner 	case offsetof(struct binfmt_misc_ops, match):
335b4bfe2f6SChristian Brauner 	case offsetof(struct binfmt_misc_ops, load):
336b4bfe2f6SChristian Brauner 		/* Reliable file reads at exec time require sleeping. */
337b4bfe2f6SChristian Brauner 		if (!prog->sleepable)
338b4bfe2f6SChristian Brauner 			return -EINVAL;
339b4bfe2f6SChristian Brauner 		break;
340b4bfe2f6SChristian Brauner 	}
341b4bfe2f6SChristian Brauner 	return 0;
342b4bfe2f6SChristian Brauner }
343b4bfe2f6SChristian Brauner 
344b4bfe2f6SChristian Brauner static int bm_bpf_init_member(const struct btf_type *t,
345b4bfe2f6SChristian Brauner 			      const struct btf_member *member,
346b4bfe2f6SChristian Brauner 			      void *kdata, const void *udata)
347b4bfe2f6SChristian Brauner {
348b4bfe2f6SChristian Brauner 	const struct binfmt_misc_ops *uops = udata;
349b4bfe2f6SChristian Brauner 	struct binfmt_misc_ops *ops = kdata;
350b4bfe2f6SChristian Brauner 	u32 moff = __btf_member_bit_offset(t, member) / 8;
351b4bfe2f6SChristian Brauner 
352b4bfe2f6SChristian Brauner 	switch (moff) {
353b4bfe2f6SChristian Brauner 	case offsetof(struct binfmt_misc_ops, name):
354b4bfe2f6SChristian Brauner 		if (bpf_obj_name_cpy(ops->name, uops->name,
355b4bfe2f6SChristian Brauner 				     sizeof(ops->name)) <= 0)
356b4bfe2f6SChristian Brauner 			return -EINVAL;
357b4bfe2f6SChristian Brauner 		return 1;
358b4bfe2f6SChristian Brauner 	}
359b4bfe2f6SChristian Brauner 	return 0;
360b4bfe2f6SChristian Brauner }
361b4bfe2f6SChristian Brauner 
362b4bfe2f6SChristian Brauner static int bm_bpf_validate(void *kdata)
363b4bfe2f6SChristian Brauner {
364b4bfe2f6SChristian Brauner 	struct binfmt_misc_ops *ops = kdata;
365b4bfe2f6SChristian Brauner 
366b4bfe2f6SChristian Brauner 	if (!ops->match || !ops->load)
367b4bfe2f6SChristian Brauner 		return -EINVAL;
368b4bfe2f6SChristian Brauner 	return 0;
369b4bfe2f6SChristian Brauner }
370b4bfe2f6SChristian Brauner 
371b4bfe2f6SChristian Brauner static int bm_bpf_reg(void *kdata, struct bpf_link *link)
372b4bfe2f6SChristian Brauner {
373b4bfe2f6SChristian Brauner 	struct binfmt_misc_ops *ops = kdata;
374b4bfe2f6SChristian Brauner 	struct bm_bpf_ops_reg *reg;
375b4bfe2f6SChristian Brauner 
376b4bfe2f6SChristian Brauner 	reg = kzalloc_obj(*reg, GFP_KERNEL_ACCOUNT);
377b4bfe2f6SChristian Brauner 	if (!reg)
378b4bfe2f6SChristian Brauner 		return -ENOMEM;
379b4bfe2f6SChristian Brauner 
380b4bfe2f6SChristian Brauner 	reg->ops = ops;
381b4bfe2f6SChristian Brauner 	reg->link = link;
382b4bfe2f6SChristian Brauner 	reg->user_ns = get_user_ns(current_user_ns());
383b4bfe2f6SChristian Brauner 
384b4bfe2f6SChristian Brauner 	guard(spinlock)(&bm_bpf_ops_lock);
385b4bfe2f6SChristian Brauner 
386b4bfe2f6SChristian Brauner 	if (bm_bpf_ops_find(reg->user_ns, ops->name)) {
387b4bfe2f6SChristian Brauner 		put_user_ns(reg->user_ns);
388b4bfe2f6SChristian Brauner 		kfree(reg);
389b4bfe2f6SChristian Brauner 		return -EEXIST;
390b4bfe2f6SChristian Brauner 	}
391b4bfe2f6SChristian Brauner 
392b4bfe2f6SChristian Brauner 	list_add(&reg->list, &bm_bpf_ops_list);
393b4bfe2f6SChristian Brauner 	return 0;
394b4bfe2f6SChristian Brauner }
395b4bfe2f6SChristian Brauner 
396b4bfe2f6SChristian Brauner static void bm_bpf_unreg(void *kdata, struct bpf_link *link)
397b4bfe2f6SChristian Brauner {
398b4bfe2f6SChristian Brauner 	struct bm_bpf_ops_reg *reg;
399b4bfe2f6SChristian Brauner 
400b4bfe2f6SChristian Brauner 	guard(spinlock)(&bm_bpf_ops_lock);
401b4bfe2f6SChristian Brauner 
402b4bfe2f6SChristian Brauner 	list_for_each_entry(reg, &bm_bpf_ops_list, list) {
403b4bfe2f6SChristian Brauner 		if (reg->ops == kdata && reg->link == link) {
404b4bfe2f6SChristian Brauner 			list_del(&reg->list);
405b4bfe2f6SChristian Brauner 			put_user_ns(reg->user_ns);
406b4bfe2f6SChristian Brauner 			kfree(reg);
407b4bfe2f6SChristian Brauner 			return;
408b4bfe2f6SChristian Brauner 		}
409b4bfe2f6SChristian Brauner 	}
410b4bfe2f6SChristian Brauner }
411b4bfe2f6SChristian Brauner 
412b4bfe2f6SChristian Brauner static const struct bpf_verifier_ops bm_bpf_verifier_ops = {
413b4bfe2f6SChristian Brauner 	.get_func_proto		= bpf_base_func_proto,
414b4bfe2f6SChristian Brauner 	.is_valid_access	= bpf_tracing_btf_ctx_access,
415b4bfe2f6SChristian Brauner };
416b4bfe2f6SChristian Brauner 
417b4bfe2f6SChristian Brauner static struct bpf_struct_ops bpf_binfmt_misc_ops = {
418b4bfe2f6SChristian Brauner 	.verifier_ops	= &bm_bpf_verifier_ops,
419b4bfe2f6SChristian Brauner 	.init		= bm_bpf_init,
420b4bfe2f6SChristian Brauner 	.check_member	= bm_bpf_check_member,
421b4bfe2f6SChristian Brauner 	.init_member	= bm_bpf_init_member,
422b4bfe2f6SChristian Brauner 	.validate	= bm_bpf_validate,
423b4bfe2f6SChristian Brauner 	.reg		= bm_bpf_reg,
424b4bfe2f6SChristian Brauner 	.unreg		= bm_bpf_unreg,
425b4bfe2f6SChristian Brauner 	.cfi_stubs	= &bm_bpf_ops_stubs,
426b4bfe2f6SChristian Brauner 	.name		= "binfmt_misc_ops",
427b4bfe2f6SChristian Brauner 	.owner		= THIS_MODULE,
428b4bfe2f6SChristian Brauner };
429b4bfe2f6SChristian Brauner 
430b4bfe2f6SChristian Brauner static int __init bm_bpf_struct_ops_init(void)
431b4bfe2f6SChristian Brauner {
432b4bfe2f6SChristian Brauner 	return register_bpf_struct_ops(&bpf_binfmt_misc_ops, binfmt_misc_ops);
433b4bfe2f6SChristian Brauner }
434b4bfe2f6SChristian Brauner late_initcall(bm_bpf_struct_ops_init);
435