On Wed, 11 Mar 2026 at 22:31, Frederick Lawler <[email protected]> wrote:
>
> The primary use case is to provide LSM designers a direct API to report
> access allow/denies through the audit subsystem similar to how LSM's
> traditionally log their accesses.
>
> Left out from this API are functions that are potentially abuseable such as
> audit_log_format() where users may fill any field=value pair. Instead, the
> API mostly follows what is exposed through security/lsm_audit.c for
> consistency with user space audit expectations. Further calls to functions
> report once to avoid repeated-call abuse.
>
> Lastly, each audit record corresponds to the loaded BPF program's ID to
> track which program reported the log entry. This helps remove
> ambiguity in the event multiple programs are registered to the same
> security hook.
>
> Exposed functions:
>
> bpf_audit_log_start()
> bpf_audit_log_end()
> bpf_audit_log_cause()
> bpf_audit_log_cap()
> bpf_audit_log_path()
> bpf_audit_log_file()
> bpf_audit_log_ioctl_op()
> bpf_audit_log_dentry()
> bpf_audit_log_inode()
> bpf_audit_log_task()
> bpf_audit_log_net_sock()
> bpf_audit_log_net_sockaddr()
>
> Signed-off-by: Frederick Lawler <[email protected]>
> ---
> include/linux/lsm_audit.h | 1 +
> include/uapi/linux/audit.h | 1 +
> security/lsm_audit_kfuncs.c | 306
> ++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 308 insertions(+)
>
> diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h
> index
> 382c56a97bba1d0e5efe082553338229d541e267..859f51590de417ac246309eb75a760b8632224be
> 100644
> --- a/include/linux/lsm_audit.h
> +++ b/include/linux/lsm_audit.h
> @@ -78,6 +78,7 @@ struct common_audit_data {
> #define LSM_AUDIT_DATA_NOTIFICATION 16
> #define LSM_AUDIT_DATA_ANONINODE 17
> #define LSM_AUDIT_DATA_NLMSGTYPE 18
> +#define LSM_AUDIT_DATA_CAUSE 19 /* unused */
> union {
> struct path path;
> struct dentry *dentry;
> diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
> index
> 14a1c1fe013acecb12ea6bf81690965421baa7ff..7a22e214fe3e421decfc4109d2e6a3cee996fe51
> 100644
> --- a/include/uapi/linux/audit.h
> +++ b/include/uapi/linux/audit.h
> @@ -150,6 +150,7 @@
> #define AUDIT_LANDLOCK_DOMAIN 1424 /* Landlock domain status */
> #define AUDIT_MAC_TASK_CONTEXTS 1425 /* Multiple LSM task contexts
> */
> #define AUDIT_MAC_OBJ_CONTEXTS 1426 /* Multiple LSM objext contexts */
> +#define AUDIT_BPF_LSM_ACCESS 1427 /* LSM BPF MAC events */
>
> #define AUDIT_FIRST_KERN_ANOM_MSG 1700
> #define AUDIT_LAST_KERN_ANOM_MSG 1799
> diff --git a/security/lsm_audit_kfuncs.c b/security/lsm_audit_kfuncs.c
> new file mode 100644
> index
> 0000000000000000000000000000000000000000..0d4fb20be34a61db29aa2c48d2aefc39131e73bf
> --- /dev/null
> +++ b/security/lsm_audit_kfuncs.c
> @@ -0,0 +1,306 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (c) 2026 Cloudflare */
> +
> +#include <linux/audit.h>
> +#include <linux/bpf_mem_alloc.h>
> +#include <linux/gfp_types.h>
> +#include <linux/in6.h>
> +#include <linux/lsm_audit.h>
> +#include <linux/socket.h>
> +#include <linux/types.h>
> +
> +struct bpf_audit_context {
> + struct audit_buffer *ab;
> + u64 log_once_mask;
> +};
> +
> +static struct bpf_mem_alloc bpf_audit_context_ma;
bpf_mem_alloc will be deprecated, please use kmalloc_nolock().
> +
> +static inline u64 log_once(struct bpf_audit_context *ac, u64 mask)
> +{
> + u64 set = (ac->log_once_mask & mask);
> +
> + ac->log_once_mask |= mask;
> + return set;
> +}
> +
> + [...]
> +
> +__bpf_kfunc int
> +bpf_audit_log_net_sockaddr(struct bpf_audit_context *ac, int netif,
> + const struct sockaddr *saddr__nullable,
> + const struct sockaddr *daddr__nullable, int
> addrlen)
> +{
> + struct lsm_network_audit net;
> + struct common_audit_data ad;
> +
> + net.netif = netif;
> +
> + if (!saddr__nullable && !daddr__nullable)
> + return -EINVAL;
Code look a lot cleaner if you just stashed these locally in a better
named variable.
> +
> + if (saddr__nullable && daddr__nullable &&
> + saddr__nullable->sa_family != daddr__nullable->sa_family)
> + return -EINVAL;
> +
> [...]
> +
> +/* The following have a recursion opportunity if a LSM is attached to any of
> + * the following functions, and a bpf_audit_log_*() is called.
> + * security_current_getlsmprop_subj,
> + * security_lsmprop_to_secctx, or
> + * security_release_secctx
> + */
> +BTF_ID_FLAGS(func, bpf_audit_log_cause, KF_DESTRUCTIVE);
> +BTF_ID_FLAGS(func, bpf_audit_log_cap, KF_DESTRUCTIVE);
> +BTF_ID_FLAGS(func, bpf_audit_log_path, KF_DESTRUCTIVE);
> +BTF_ID_FLAGS(func, bpf_audit_log_file, KF_DESTRUCTIVE);
> +BTF_ID_FLAGS(func, bpf_audit_log_ioctl_op, KF_DESTRUCTIVE);
> +BTF_ID_FLAGS(func, bpf_audit_log_dentry, KF_DESTRUCTIVE);
> +BTF_ID_FLAGS(func, bpf_audit_log_inode, KF_DESTRUCTIVE);
> +BTF_ID_FLAGS(func, bpf_audit_log_task, KF_DESTRUCTIVE);
> +BTF_ID_FLAGS(func, bpf_audit_log_net_sock, KF_DESTRUCTIVE);
> +BTF_ID_FLAGS(func, bpf_audit_log_net_sockaddr, KF_DESTRUCTIVE);
> +
> +BTF_KFUNCS_END(lsm_audit_set_ids)
> +
> +static int bpf_lsm_audit_kfuncs_filter(const struct bpf_prog *prog,
> + u32 kfunc_id)
> +{
> + if (!btf_id_set8_contains(&lsm_audit_set_ids, kfunc_id))
> + return 0;
> +
> + return prog->type != BPF_PROG_TYPE_LSM ? -EACCES : 0;
> +}
> +
> +static const struct btf_kfunc_id_set bpf_lsm_audit_set = {
> + .owner = THIS_MODULE,
> + .set = &lsm_audit_set_ids,
> + .filter = bpf_lsm_audit_kfuncs_filter,
> +};
> +
> +static int lsm_audit_init_bpf(void)
> +{
> + int ret;
> +
> + ret = bpf_mem_alloc_init(&bpf_audit_context_ma,
> + sizeof(struct bpf_audit_context), false);
One you switch to kmalloc_nolock you can drop this too.
> + return ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM,
> + &bpf_lsm_audit_set);
> +}
> +
> +late_initcall(lsm_audit_init_bpf)
>
> --
> 2.43.0
>
>