[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