Skip to content

Commit 98c5b73

Browse files
liu-song-6Kernel Patches Daemon
authored andcommitted
bpf: fs/xattr: Add BPF kfuncs to set and remove xattrs
Add the following kfuncs to set and remove xattrs from BPF programs: bpf_set_dentry_xattr bpf_remove_dentry_xattr bpf_set_dentry_xattr_locked bpf_remove_dentry_xattr_locked The _locked version of these kfuncs are called from hooks where dentry->d_inode is already locked. Instead of requiring the user to know which version of the kfuncs to use, the verifier will pick the proper kfunc based on the calling hook. Signed-off-by: Song Liu <song@kernel.org>
1 parent 915393f commit 98c5b73

File tree

2 files changed

+227
-2
lines changed

2 files changed

+227
-2
lines changed

fs/bpf_fs_kfuncs.c

Lines changed: 225 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
/* Copyright (c) 2024 Google LLC. */
33

44
#include <linux/bpf.h>
5+
#include <linux/bpf_lsm.h>
56
#include <linux/btf.h>
67
#include <linux/btf_ids.h>
78
#include <linux/dcache.h>
89
#include <linux/fs.h>
10+
#include <linux/fsnotify.h>
911
#include <linux/file.h>
1012
#include <linux/mm.h>
1113
#include <linux/xattr.h>
@@ -161,6 +163,164 @@ __bpf_kfunc int bpf_get_file_xattr(struct file *file, const char *name__str,
161163
return bpf_get_dentry_xattr(dentry, name__str, value_p);
162164
}
163165

166+
static int bpf_xattr_write_permission(const char *name, struct inode *inode)
167+
{
168+
if (WARN_ON(!inode))
169+
return -EINVAL;
170+
171+
/* Only allow setting and removing security.bpf. xattrs */
172+
if (!match_security_bpf_prefix(name))
173+
return -EPERM;
174+
175+
return inode_permission(&nop_mnt_idmap, inode, MAY_WRITE);
176+
}
177+
178+
static int __bpf_set_dentry_xattr(struct dentry *dentry, const char *name,
179+
const struct bpf_dynptr *value_p, int flags, bool lock_inode)
180+
{
181+
struct bpf_dynptr_kern *value_ptr = (struct bpf_dynptr_kern *)value_p;
182+
struct inode *inode = d_inode(dentry);
183+
const void *value;
184+
u32 value_len;
185+
int ret;
186+
187+
value_len = __bpf_dynptr_size(value_ptr);
188+
value = __bpf_dynptr_data(value_ptr, value_len);
189+
if (!value)
190+
return -EINVAL;
191+
192+
if (lock_inode)
193+
inode_lock(inode);
194+
195+
ret = bpf_xattr_write_permission(name, inode);
196+
if (ret)
197+
goto out;
198+
199+
ret = __vfs_setxattr(&nop_mnt_idmap, dentry, inode, name,
200+
value, value_len, flags);
201+
if (!ret) {
202+
fsnotify_xattr(dentry);
203+
204+
/* This xattr is set by BPF LSM, so we do not call
205+
* security_inode_post_setxattr. This is the same as
206+
* security_inode_setsecurity().
207+
*/
208+
}
209+
out:
210+
if (lock_inode)
211+
inode_unlock(inode);
212+
return ret;
213+
}
214+
215+
/**
216+
* bpf_set_dentry_xattr - set a xattr of a dentry
217+
* @dentry: dentry to get xattr from
218+
* @name__str: name of the xattr
219+
* @value_p: xattr value
220+
* @flags: flags to pass into filesystem operations
221+
*
222+
* Set xattr *name__str* of *dentry* to the value in *value_ptr*.
223+
*
224+
* For security reasons, only *name__str* with prefix "security.bpf."
225+
* is allowed.
226+
*
227+
* The caller has not locked dentry->d_inode.
228+
*
229+
* Return: 0 on success, a negative value on error.
230+
*/
231+
__bpf_kfunc int bpf_set_dentry_xattr(struct dentry *dentry, const char *name__str,
232+
const struct bpf_dynptr *value_p, int flags)
233+
{
234+
return __bpf_set_dentry_xattr(dentry, name__str, value_p, flags, true);
235+
}
236+
237+
/**
238+
* bpf_set_dentry_xattr_locked - set a xattr of a dentry
239+
* @dentry: dentry to get xattr from
240+
* @name__str: name of the xattr
241+
* @value_p: xattr value
242+
* @flags: flags to pass into filesystem operations
243+
*
244+
* Set xattr *name__str* of *dentry* to the value in *value_ptr*.
245+
*
246+
* For security reasons, only *name__str* with prefix "security.bpf."
247+
* is allowed.
248+
*
249+
* The caller already locked dentry->d_inode.
250+
*
251+
* Return: 0 on success, a negative value on error.
252+
*/
253+
__bpf_kfunc int bpf_set_dentry_xattr_locked(struct dentry *dentry, const char *name__str,
254+
const struct bpf_dynptr *value_p, int flags)
255+
{
256+
return __bpf_set_dentry_xattr(dentry, name__str, value_p, flags, false);
257+
}
258+
259+
static int __bpf_remove_dentry_xattr(struct dentry *dentry, const char *name__str,
260+
bool lock_inode)
261+
{
262+
struct inode *inode = d_inode(dentry);
263+
int ret;
264+
265+
if (lock_inode)
266+
inode_lock(inode);
267+
268+
ret = bpf_xattr_write_permission(name__str, inode);
269+
if (ret)
270+
goto out;
271+
272+
ret = __vfs_removexattr(&nop_mnt_idmap, dentry, name__str);
273+
if (!ret) {
274+
fsnotify_xattr(dentry);
275+
276+
/* This xattr is removed by BPF LSM, so we do not call
277+
* security_inode_post_removexattr.
278+
*/
279+
}
280+
out:
281+
if (lock_inode)
282+
inode_unlock(inode);
283+
return ret;
284+
}
285+
286+
/**
287+
* bpf_remove_dentry_xattr - remove a xattr of a dentry
288+
* @dentry: dentry to get xattr from
289+
* @name__str: name of the xattr
290+
*
291+
* Rmove xattr *name__str* of *dentry*.
292+
*
293+
* For security reasons, only *name__str* with prefix "security.bpf."
294+
* is allowed.
295+
*
296+
* The caller has not locked dentry->d_inode.
297+
*
298+
* Return: 0 on success, a negative value on error.
299+
*/
300+
__bpf_kfunc int bpf_remove_dentry_xattr(struct dentry *dentry, const char *name__str)
301+
{
302+
return __bpf_remove_dentry_xattr(dentry, name__str, true);
303+
}
304+
305+
/**
306+
* bpf_remove_dentry_xattr_locked - remove a xattr of a dentry
307+
* @dentry: dentry to get xattr from
308+
* @name__str: name of the xattr
309+
*
310+
* Rmove xattr *name__str* of *dentry*.
311+
*
312+
* For security reasons, only *name__str* with prefix "security.bpf."
313+
* is allowed.
314+
*
315+
* The caller already locked dentry->d_inode.
316+
*
317+
* Return: 0 on success, a negative value on error.
318+
*/
319+
__bpf_kfunc int bpf_remove_dentry_xattr_locked(struct dentry *dentry, const char *name__str)
320+
{
321+
return __bpf_remove_dentry_xattr(dentry, name__str, false);
322+
}
323+
164324
__bpf_kfunc_end_defs();
165325

166326
BTF_KFUNCS_START(bpf_fs_kfunc_set_ids)
@@ -170,20 +330,83 @@ BTF_ID_FLAGS(func, bpf_put_file, KF_RELEASE)
170330
BTF_ID_FLAGS(func, bpf_path_d_path, KF_TRUSTED_ARGS)
171331
BTF_ID_FLAGS(func, bpf_get_dentry_xattr, KF_SLEEPABLE | KF_TRUSTED_ARGS)
172332
BTF_ID_FLAGS(func, bpf_get_file_xattr, KF_SLEEPABLE | KF_TRUSTED_ARGS)
333+
BTF_ID_FLAGS(func, bpf_set_dentry_xattr, KF_SLEEPABLE | KF_TRUSTED_ARGS)
334+
BTF_ID_FLAGS(func, bpf_remove_dentry_xattr, KF_SLEEPABLE | KF_TRUSTED_ARGS)
173335
BTF_KFUNCS_END(bpf_fs_kfunc_set_ids)
174336

337+
BTF_HIDDEN_KFUNCS_START(bpf_fs_kfunc_hidden_set_ids)
338+
BTF_ID_FLAGS(func, bpf_set_dentry_xattr_locked, KF_SLEEPABLE | KF_TRUSTED_ARGS)
339+
BTF_ID_FLAGS(func, bpf_remove_dentry_xattr_locked, KF_SLEEPABLE | KF_TRUSTED_ARGS)
340+
BTF_KFUNCS_END(bpf_fs_kfunc_hidden_set_ids)
341+
175342
static int bpf_fs_kfuncs_filter(const struct bpf_prog *prog, u32 kfunc_id)
176343
{
177-
if (!btf_id_set8_contains(&bpf_fs_kfunc_set_ids, kfunc_id) ||
178-
prog->type == BPF_PROG_TYPE_LSM)
344+
if (!btf_id_set8_contains(&bpf_fs_kfunc_set_ids, kfunc_id) &&
345+
!btf_id_set8_contains(&bpf_fs_kfunc_hidden_set_ids, kfunc_id))
346+
return 0;
347+
if (prog->type == BPF_PROG_TYPE_LSM)
179348
return 0;
180349
return -EACCES;
181350
}
182351

352+
/* bpf_[set|remove]_dentry_xattr.* hooks have KF_TRUSTED_ARGS and
353+
* KF_SLEEPABLE, so they are only available to sleepable hooks with
354+
* dentry arguments.
355+
*
356+
* Setting and removing xattr requires exclusive lock on dentry->d_inode.
357+
* Some hooks already locked d_inode, while some hooks have not locked
358+
* d_inode. Therefore, we need different kfuncs for different hooks.
359+
* Specifically, hooks in the following list (d_inode_locked_hooks)
360+
* should call bpf_[set|remove]_dentry_xattr_locked; while other hooks
361+
* should call bpf_[set|remove]_dentry_xattr.
362+
*/
363+
BTF_SET_START(d_inode_locked_hooks)
364+
BTF_ID(func, bpf_lsm_inode_post_removexattr)
365+
BTF_ID(func, bpf_lsm_inode_post_setattr)
366+
BTF_ID(func, bpf_lsm_inode_post_setxattr)
367+
BTF_ID(func, bpf_lsm_inode_removexattr)
368+
BTF_ID(func, bpf_lsm_inode_rmdir)
369+
BTF_ID(func, bpf_lsm_inode_setattr)
370+
BTF_ID(func, bpf_lsm_inode_setxattr)
371+
BTF_ID(func, bpf_lsm_inode_unlink)
372+
#ifdef CONFIG_SECURITY_PATH
373+
BTF_ID(func, bpf_lsm_path_unlink)
374+
BTF_ID(func, bpf_lsm_path_rmdir)
375+
#endif /* CONFIG_SECURITY_PATH */
376+
BTF_SET_END(d_inode_locked_hooks)
377+
378+
static bool bpf_lsm_has_d_inode_locked(const struct bpf_prog *prog)
379+
{
380+
return btf_id_set_contains(&d_inode_locked_hooks, prog->aux->attach_btf_id);
381+
}
382+
383+
BTF_ID_LIST(not_locked_fs_kfuncs)
384+
BTF_ID(func, bpf_set_dentry_xattr)
385+
BTF_ID(func, bpf_remove_dentry_xattr)
386+
387+
BTF_ID_LIST(locked_fs_kfuncs)
388+
BTF_ID(func, bpf_set_dentry_xattr_locked)
389+
BTF_ID(func, bpf_remove_dentry_xattr_locked)
390+
391+
static u32 bpf_fs_kfunc_remap(const struct bpf_prog *prog, u32 kfunc_id)
392+
{
393+
if (!bpf_lsm_has_d_inode_locked(prog))
394+
return 0;
395+
396+
if (kfunc_id == not_locked_fs_kfuncs[0])
397+
return locked_fs_kfuncs[0];
398+
if (kfunc_id == not_locked_fs_kfuncs[1])
399+
return locked_fs_kfuncs[1];
400+
401+
return 0;
402+
}
403+
183404
static const struct btf_kfunc_id_set bpf_fs_kfunc_set = {
184405
.owner = THIS_MODULE,
185406
.set = &bpf_fs_kfunc_set_ids,
407+
.hidden_set = &bpf_fs_kfunc_hidden_set_ids,
186408
.filter = bpf_fs_kfuncs_filter,
409+
.remap = bpf_fs_kfunc_remap,
187410
};
188411

189412
static int __init bpf_fs_kfuncs_init(void)

include/linux/bpf_lsm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ void bpf_lsm_find_cgroup_shim(const struct bpf_prog *prog, bpf_func_t *bpf_func)
4848

4949
int bpf_lsm_get_retval_range(const struct bpf_prog *prog,
5050
struct bpf_retval_range *range);
51+
5152
#else /* !CONFIG_BPF_LSM */
5253

5354
static inline bool bpf_lsm_is_sleepable_hook(u32 btf_id)
@@ -86,6 +87,7 @@ static inline int bpf_lsm_get_retval_range(const struct bpf_prog *prog,
8687
{
8788
return -EOPNOTSUPP;
8889
}
90+
8991
#endif /* CONFIG_BPF_LSM */
9092

9193
#endif /* _LINUX_BPF_LSM_H */

0 commit comments

Comments
 (0)