mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
selftests/bpf: Add verifier tests for the __ctx_out plumbing
Add BPF selftests to cover what a BPF program may and may not hand to bpf_inode_init_xattr() as the inode_init_security hook's args. # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t verifier_lsm_init_xattr [...] #649/1 verifier_lsm_init_xattr/reject_count_write:OK #649/2 verifier_lsm_init_xattr/allow_count_read:OK #649/3 verifier_lsm_init_xattr/reject_unchecked_xattrs:OK #649/4 verifier_lsm_init_xattr/reject_unchecked_qstr:OK #649/5 verifier_lsm_init_xattr/allow_spilled_count:OK #649/6 verifier_lsm_init_xattr/reject_forged_count:OK #649/7 verifier_lsm_init_xattr/reject_stack_count:OK #649/8 verifier_lsm_init_xattr/reject_other_ctx_arg:OK #649/9 verifier_lsm_init_xattr/reject_null_count:OK #649/10 verifier_lsm_init_xattr/reject_shifted_count:OK #649/11 verifier_lsm_init_xattr/reject_var_shifted_count:OK #649/12 verifier_lsm_init_xattr/reject_ctx_forged_count:OK #649/13 verifier_lsm_init_xattr/allow_count_via_subprog:OK #649/14 verifier_lsm_init_xattr/reject_shifted_xattrs:OK #649/15 verifier_lsm_init_xattr/reject_sleepable:OK #649/16 verifier_lsm_init_xattr/reject_lsm_cgroup:OK #649/17 verifier_lsm_init_xattr/reject_wrong_hook:OK #649 verifier_lsm_init_xattr:OK Summary: 1/17 PASSED, 0 SKIPPED, 0/0 FAILED Co-developed-by: David Windsor <dwindsor@gmail.com> Signed-off-by: David Windsor <dwindsor@gmail.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20260915150739.284189-8-daniel@iogearbox.net
This commit is contained in:
parent
e5d960cfe1
commit
2757e9e539
|
|
@ -78,18 +78,29 @@ extern void bpf_key_put(struct bpf_key *key) __ksym;
|
|||
extern int bpf_verify_pkcs7_signature(const struct bpf_dynptr *data_ptr,
|
||||
const struct bpf_dynptr *sig_ptr,
|
||||
struct bpf_key *trusted_keyring) __ksym;
|
||||
|
||||
struct dentry;
|
||||
/* Description
|
||||
/*
|
||||
* Description
|
||||
* Returns xattr of a dentry
|
||||
* Returns
|
||||
* Error code
|
||||
*/
|
||||
struct dentry;
|
||||
extern int bpf_get_dentry_xattr(struct dentry *dentry, const char *name,
|
||||
struct bpf_dynptr *value_ptr) __ksym __weak;
|
||||
|
||||
extern int bpf_set_dentry_xattr(struct dentry *dentry, const char *name__str,
|
||||
const struct bpf_dynptr *value_p, int flags) __ksym __weak;
|
||||
extern int bpf_remove_dentry_xattr(struct dentry *dentry, const char *name__str) __ksym __weak;
|
||||
|
||||
/*
|
||||
* Description
|
||||
* Attach a xattr to an inode that is being created, from a program on the
|
||||
* inode_init_security LSM hook. *xattrs* and *xattr_count* must be the
|
||||
* hook's own arguments, passed through unmodified.
|
||||
* Returns
|
||||
* 0 on success, a negative value on error
|
||||
*/
|
||||
struct xattr;
|
||||
extern int bpf_inode_init_xattr(struct xattr *xattrs, int *xattr_count,
|
||||
const char *name__str,
|
||||
const struct bpf_dynptr *value_p) __ksym __weak;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@
|
|||
#include "verifier_bits_iter.skel.h"
|
||||
#include "verifier_set_retval.skel.h"
|
||||
#include "verifier_lsm.skel.h"
|
||||
#include "verifier_lsm_init_xattr.skel.h"
|
||||
#include "verifier_jit_inline.skel.h"
|
||||
#include "irq.skel.h"
|
||||
#include "verifier_ctx_ptr_param.skel.h"
|
||||
|
|
@ -294,6 +295,7 @@ void test_verifier_xdp_direct_packet_access(void) { RUN(verifier_xdp_direct_pack
|
|||
void test_verifier_bits_iter(void) { RUN(verifier_bits_iter); }
|
||||
void test_verifier_set_retval(void) { RUN(verifier_set_retval); }
|
||||
void test_verifier_lsm(void) { RUN(verifier_lsm); }
|
||||
void test_verifier_lsm_init_xattr(void) { RUN(verifier_lsm_init_xattr); }
|
||||
void test_irq(void) { RUN(irq); }
|
||||
void test_verifier_mtu(void) { RUN(verifier_mtu); }
|
||||
void test_verifier_jit_inline(void) { RUN(verifier_jit_inline); }
|
||||
|
|
|
|||
245
tools/testing/selftests/bpf/progs/verifier_lsm_init_xattr.c
Normal file
245
tools/testing/selftests/bpf/progs/verifier_lsm_init_xattr.c
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "bpf_kfuncs.h"
|
||||
#include "bpf_misc.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
const char xattr_zone[] = "security.bpf.zone";
|
||||
char value_buf[8] = "z";
|
||||
int scratch_count;
|
||||
|
||||
SEC("lsm/inode_init_security")
|
||||
__failure __msg("cannot write into rdonly_trusted_mem")
|
||||
int BPF_PROG(reject_count_write, struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr, struct xattr *xattrs, int *xattr_count)
|
||||
{
|
||||
*xattr_count = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm/inode_init_security")
|
||||
__success
|
||||
int BPF_PROG(allow_count_read, struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr, struct xattr *xattrs, int *xattr_count)
|
||||
{
|
||||
scratch_count = *xattr_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm/inode_init_security")
|
||||
__failure __msg("Possibly NULL pointer passed to trusted")
|
||||
int BPF_PROG(reject_unchecked_xattrs, struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr, struct xattr *xattrs, int *xattr_count)
|
||||
{
|
||||
struct bpf_dynptr value;
|
||||
|
||||
bpf_dynptr_from_mem(value_buf, sizeof(value_buf), 0, &value);
|
||||
bpf_inode_init_xattr(xattrs, xattr_count, xattr_zone, &value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm/inode_init_security")
|
||||
__failure __msg("invalid mem access 'trusted_ptr_or_null_'")
|
||||
int BPF_PROG(reject_unchecked_qstr, struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr, struct xattr *xattrs, int *xattr_count)
|
||||
{
|
||||
scratch_count = qstr->len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm/inode_init_security")
|
||||
__success
|
||||
int BPF_PROG(allow_spilled_count, struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr, struct xattr *xattrs, int *xattr_count)
|
||||
{
|
||||
struct bpf_dynptr value;
|
||||
int *saved = xattr_count;
|
||||
|
||||
if (!xattrs)
|
||||
return 0;
|
||||
|
||||
bpf_dynptr_from_mem(value_buf, sizeof(value_buf), 0, &value);
|
||||
bpf_inode_init_xattr(xattrs, saved, xattr_zone, &value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm/inode_init_security")
|
||||
__failure __msg("expected=rdonly_trusted_mem")
|
||||
int BPF_PROG(reject_forged_count, struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr, struct xattr *xattrs, int *xattr_count)
|
||||
{
|
||||
struct bpf_dynptr value;
|
||||
|
||||
if (!xattrs)
|
||||
return 0;
|
||||
|
||||
bpf_dynptr_from_mem(value_buf, sizeof(value_buf), 0, &value);
|
||||
bpf_inode_init_xattr(xattrs, &scratch_count, xattr_zone, &value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm/inode_init_security")
|
||||
__failure __msg("expected=rdonly_trusted_mem")
|
||||
int BPF_PROG(reject_stack_count, struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr, struct xattr *xattrs, int *xattr_count)
|
||||
{
|
||||
struct bpf_dynptr value;
|
||||
int local = 0;
|
||||
|
||||
if (!xattrs)
|
||||
return 0;
|
||||
|
||||
bpf_dynptr_from_mem(value_buf, sizeof(value_buf), 0, &value);
|
||||
bpf_inode_init_xattr(xattrs, &local, xattr_zone, &value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm/inode_init_security")
|
||||
__failure __msg("expected=rdonly_trusted_mem")
|
||||
int BPF_PROG(reject_other_ctx_arg, struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr, struct xattr *xattrs, int *xattr_count)
|
||||
{
|
||||
struct bpf_dynptr value;
|
||||
|
||||
if (!xattrs)
|
||||
return 0;
|
||||
|
||||
bpf_dynptr_from_mem(value_buf, sizeof(value_buf), 0, &value);
|
||||
bpf_inode_init_xattr(xattrs, (int *)xattrs, xattr_zone, &value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm/inode_init_security")
|
||||
__failure __msg("Possibly NULL pointer passed to trusted")
|
||||
int BPF_PROG(reject_null_count, struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr, struct xattr *xattrs, int *xattr_count)
|
||||
{
|
||||
struct bpf_dynptr value;
|
||||
|
||||
if (!xattrs)
|
||||
return 0;
|
||||
|
||||
bpf_dynptr_from_mem(value_buf, sizeof(value_buf), 0, &value);
|
||||
bpf_inode_init_xattr(xattrs, NULL, xattr_zone, &value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm/inode_init_security")
|
||||
__failure __msg("dereference of modified rdonly_trusted_mem ptr")
|
||||
int BPF_PROG(reject_shifted_count, struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr, struct xattr *xattrs, int *xattr_count)
|
||||
{
|
||||
struct bpf_dynptr value;
|
||||
|
||||
if (!xattrs)
|
||||
return 0;
|
||||
|
||||
bpf_dynptr_from_mem(value_buf, sizeof(value_buf), 0, &value);
|
||||
bpf_inode_init_xattr(xattrs, xattr_count + 1, xattr_zone, &value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm/inode_init_security")
|
||||
__failure __msg("variable rdonly_trusted_mem access var_off=")
|
||||
int BPF_PROG(reject_var_shifted_count, struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr, struct xattr *xattrs, int *xattr_count)
|
||||
{
|
||||
struct bpf_dynptr value;
|
||||
|
||||
if (!xattrs)
|
||||
return 0;
|
||||
|
||||
bpf_dynptr_from_mem(value_buf, sizeof(value_buf), 0, &value);
|
||||
bpf_inode_init_xattr(xattrs, xattr_count + (scratch_count & 1),
|
||||
xattr_zone, &value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm/inode_init_security")
|
||||
__failure __msg("invalid bpf_context access")
|
||||
int reject_ctx_forged_count(unsigned long long *ctx)
|
||||
{
|
||||
volatile unsigned long long *slot = ctx;
|
||||
struct bpf_dynptr value;
|
||||
|
||||
bpf_dynptr_from_mem(value_buf, sizeof(value_buf), 0, &value);
|
||||
slot[4] = 0;
|
||||
bpf_inode_init_xattr((struct xattr *)(long)slot[3],
|
||||
(int *)(long)slot[4], xattr_zone, &value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __noinline int claim_via_subprog(struct xattr *xattrs, int *xattr_count,
|
||||
struct bpf_dynptr *value)
|
||||
{
|
||||
return bpf_inode_init_xattr(xattrs, xattr_count, xattr_zone, value);
|
||||
}
|
||||
|
||||
SEC("lsm/inode_init_security")
|
||||
__success
|
||||
int BPF_PROG(allow_count_via_subprog, struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr, struct xattr *xattrs, int *xattr_count)
|
||||
{
|
||||
struct bpf_dynptr value;
|
||||
|
||||
if (!xattrs)
|
||||
return 0;
|
||||
|
||||
bpf_dynptr_from_mem(value_buf, sizeof(value_buf), 0, &value);
|
||||
claim_via_subprog(xattrs, xattr_count, &value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm/inode_init_security")
|
||||
__failure __msg("access beyond struct xattr at off 24")
|
||||
int BPF_PROG(reject_shifted_xattrs, struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr, struct xattr *xattrs, int *xattr_count)
|
||||
{
|
||||
struct bpf_dynptr value;
|
||||
|
||||
if (!xattrs)
|
||||
return 0;
|
||||
|
||||
bpf_dynptr_from_mem(value_buf, sizeof(value_buf), 0, &value);
|
||||
bpf_inode_init_xattr(xattrs + 1, xattr_count, xattr_zone, &value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm.s/inode_init_security")
|
||||
__failure __msg("bpf_lsm_inode_init_security is not sleepable")
|
||||
int BPF_PROG(reject_sleepable, struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr, struct xattr *xattrs, int *xattr_count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm_cgroup/inode_init_security")
|
||||
__failure __msg("calling kernel function bpf_inode_init_xattr is not allowed")
|
||||
int BPF_PROG(reject_lsm_cgroup, struct inode *inode, struct inode *dir,
|
||||
const struct qstr *qstr, struct xattr *xattrs, int *xattr_count)
|
||||
{
|
||||
struct bpf_dynptr value;
|
||||
|
||||
if (!xattrs)
|
||||
return 0;
|
||||
|
||||
bpf_dynptr_from_mem(value_buf, sizeof(value_buf), 0, &value);
|
||||
bpf_inode_init_xattr(xattrs, xattr_count, xattr_zone, &value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("lsm/inode_setxattr")
|
||||
__failure __msg("calling kernel function bpf_inode_init_xattr is not allowed")
|
||||
int BPF_PROG(reject_wrong_hook, struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
const char *name, const void *value, size_t size, int flags)
|
||||
{
|
||||
struct bpf_dynptr val;
|
||||
|
||||
bpf_dynptr_from_mem(value_buf, sizeof(value_buf), 0, &val);
|
||||
bpf_inode_init_xattr(NULL, &scratch_count, xattr_zone, &val);
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user