[RFC PATCH] ipe: support multiple BPF integrity verification LSMs
Paul Moore
paul at paul-moore.com
Sat May 23 23:05:46 UTC 2026
On May 23, 2026 3:09:05 PM Paul Moore <paul at paul-moore.com> wrote:
> Currently IPE always records the last BPF integrity verification verdict,
> which is reasonable with only a single BPF verification LSM, but it
> becomes problematic when multiple mechanisms end up submitting BPF
> program integrity verdicts.
>
> This patch updates IPE to record all of the received BPF program
> integrity verdicts, along with their associated LSM IDs, ultimately using
> the "worst" verdict in the policy enforcement engine. Policy support for
> selecting individual integrity verdicts was intentionally omitted from
> this patch to keep things simple both from a code and policy developer
> perspective, however future work to add selector support should be
> trivial.
>
> Signed-off-by: Paul Moore <paul at paul-moore.com>
> ---
> include/linux/security.h | 14 +++++++-------
> security/ipe/eval.h | 7 ++++++-
> security/ipe/hooks.c | 20 +++++++++++++++++---
> 3 files changed, 30 insertions(+), 11 deletions(-)
>
> diff --git a/include/linux/security.h b/include/linux/security.h
> index 598cd2eb1dcd..6a987a0347a0 100644
> --- a/include/linux/security.h
> +++ b/include/linux/security.h
> @@ -103,13 +103,13 @@ enum lsm_integrity_type {
>
> enum lsm_integrity_verdict {
> LSM_INT_VERDICT_NONE = 0,
> - LSM_INT_VERDICT_OK,
> - LSM_INT_VERDICT_UNSIGNED,
> - LSM_INT_VERDICT_PARTIALSIG,
> - LSM_INT_VERDICT_UNKNOWNKEY,
> - LSM_INT_VERDICT_UNEXPECTED,
> - LSM_INT_VERDICT_FAULT,
> - LSM_INT_VERDICT_BADSIG,
> + LSM_INT_VERDICT_OK = 1,
> + LSM_INT_VERDICT_UNSIGNED = 2,
> + LSM_INT_VERDICT_PARTIALSIG = 3,
> + LSM_INT_VERDICT_UNKNOWNKEY = 4,
> + LSM_INT_VERDICT_UNEXPECTED = 5,
> + LSM_INT_VERDICT_FAULT = 6,
> + LSM_INT_VERDICT_BADSIG = 7,
> };
>
> /*
> diff --git a/security/ipe/eval.h b/security/ipe/eval.h
> index b061cb5ade27..90c7b66b9ca8 100644
> --- a/security/ipe/eval.h
> +++ b/security/ipe/eval.h
> @@ -8,6 +8,7 @@
>
> #include <linux/file.h>
> #include <linux/types.h>
> +#include <linux/lsm_count.h>
>
> #include "policy.h"
> #include "hooks.h"
> @@ -39,7 +40,11 @@ struct ipe_inode {
>
> #ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
> struct ipe_bpf_prog {
> - enum lsm_integrity_verdict verdict;
> + struct {
> + const struct lsm_id *lsmid;
> + enum lsm_integrity_verdict verdict;
> + } verdicts[MAX_LSM_COUNT];
> + unsigned int count;
> };
> #endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
>
> diff --git a/security/ipe/hooks.c b/security/ipe/hooks.c
> index 9271e129a2cf..143bb2ae2b12 100644
> --- a/security/ipe/hooks.c
> +++ b/security/ipe/hooks.c
> @@ -9,6 +9,7 @@
> #include <linux/binfmts.h>
> #include <linux/mman.h>
> #include <linux/blk_types.h>
> +#include <linux/lsm_count.h>
>
> #include "ipe.h"
> #include "hooks.h"
> @@ -355,7 +356,8 @@ int ipe_inode_setintegrity(const struct inode *inode,
> * so that ipe_bpf_prog_load() can later read it for policy evaluation.
> *
> * Return:
> - * * %0 - Always succeeds (policy is evaluated in bpf_prog_load)
> + * * %0 - Recorded the verdict (policy is evaluated in bpf_prog_load)
> + * * %-ENOMEM - Exhausted room for recording verdicts
> */
> int ipe_bpf_prog_load_post_integrity(struct bpf_prog *prog,
> union bpf_attr *attr,
> @@ -365,8 +367,14 @@ int ipe_bpf_prog_load_post_integrity(struct bpf_prog
> *prog,
> enum lsm_integrity_verdict verdict)
> {
> struct ipe_bpf_prog *blob = ipe_bpf_prog(prog);
> + unsigned int count = blob->count;
>
> - blob->verdict = verdict;
> + if (count == MAX_LSM_COUNT)
> + return -ENOMEM;
> +
> + blob->verdicts[count].lsmid = lsmid;
> + blob->verdicts[count].verdict = verdict;
> + blob->count++;
>
> return 0;
> }
> @@ -391,12 +399,18 @@ int ipe_bpf_prog_load(struct bpf_prog *prog,
> struct bpf_token *token,
> bool kernel)
> {
> + unsigned int iter;
> struct ipe_bpf_prog *blob = ipe_bpf_prog(prog);
> struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
>
> ctx.op = IPE_OP_BPF_PROG_LOAD;
> ctx.hook = IPE_HOOK_BPF_PROG_LOAD;
> - ctx.bpf_verdict = blob->verdict;
> + ctx.bpf_verdict = LSM_INT_VERDICT_NONE;
> + for (iter = 0; iter < blob->count; iter++) {
> + /* pick the "wosrt" verdict */
That should obviously be "worst" :)
> + if (blob->verdicts[iter].verdict > ctx.bpf_verdict)
> + ctx.bpf_verdict = blob->verdicts[iter].verdict;
> + }
> ctx.bpf_keyring_id = attr->keyring_id;
> ctx.bpf_kernel = kernel;
>
> --
> 2.54.0
--
paul-moore.com
More information about the Linux-security-module-archive
mailing list