From 9814077275eca36ebf8d510d2f076d235ff9f51a Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Tue, 22 Sep 2026 20:13:48 -0700 Subject: [PATCH 1/2] ipe: fix use-after-free when auditing a newly loaded policy new_policy() audits the policy after ipe_new_policyfs_node() publishes it and drops the new directory's inode lock. A concurrent delete can free the policy while ipe_audit_policy_load() is still using it. Audit the successful load under that lock. Fixes: f44554b5067b ("audit,ipe: add IPE auditing support") Cc: stable@vger.kernel.org Assisted-by: LLM [FW: remove model name according to latest guideline] Signed-off-by: Fan Wu --- security/ipe/fs.c | 8 +++----- security/ipe/policy_fs.c | 3 +++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/security/ipe/fs.c b/security/ipe/fs.c index 076c111c85c8..847a76afb93d 100644 --- a/security/ipe/fs.c +++ b/security/ipe/fs.c @@ -159,18 +159,16 @@ static ssize_t new_policy(struct file *f, const char __user *data, } rc = ipe_new_policyfs_node(p); - if (rc) - goto out; out: kfree(copy); if (rc < 0) { ipe_free_policy(p); ipe_audit_policy_load(ERR_PTR(rc)); - } else { - ipe_audit_policy_load(p); + return rc; } - return (rc < 0) ? rc : len; + + return len; } static const struct file_operations np_fops = { diff --git a/security/ipe/policy_fs.c b/security/ipe/policy_fs.c index 9d92d8a14b13..a7aeb57483c6 100644 --- a/security/ipe/policy_fs.c +++ b/security/ipe/policy_fs.c @@ -481,6 +481,9 @@ int ipe_new_policyfs_node(struct ipe_policy *p) inode_lock(root); p->policyfs = policyfs; root->i_private = p; + /* Only audit signed policies from userspace */ + if (p->pkcs7) + ipe_audit_policy_load(p); inode_unlock(root); return 0; From 2776e9c28513a1c855a94792b292cbcc533418c8 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Tue, 22 Sep 2026 20:13:49 -0700 Subject: [PATCH 2/2] ipe: protect the dm-verity root hash with RCU ipe_bdev_setintegrity() frees the old root hash when dm-verity publishes a new one on ->preresume, while policy evaluation can still be dereferencing it. Protect the root hash with RCU. The evaluation path already runs under rcu_read_lock(). Fixes: e155858dd995 ("ipe: add support for dm-verity as a trust provider") Cc: stable@vger.kernel.org Assisted-by: LLM [FW: remove model name according to latest guideline] Signed-off-by: Fan Wu --- security/ipe/eval.c | 12 ++++++++---- security/ipe/eval.h | 2 +- security/ipe/hooks.c | 22 +++++++++++++++++----- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/security/ipe/eval.c b/security/ipe/eval.c index 21439c5be336..4a7be96c84f0 100644 --- a/security/ipe/eval.c +++ b/security/ipe/eval.c @@ -134,10 +134,14 @@ static bool evaluate_boot_verified(const struct ipe_eval_ctx *const ctx) static bool evaluate_dmv_roothash(const struct ipe_eval_ctx *const ctx, struct ipe_prop *p) { - return !!ctx->ipe_bdev && - !!ctx->ipe_bdev->root_hash && - ipe_digest_eval(p->value, - ctx->ipe_bdev->root_hash); + const struct digest_info *root_hash; + + if (!ctx->ipe_bdev) + return false; + + root_hash = rcu_dereference(ctx->ipe_bdev->root_hash); + + return root_hash && ipe_digest_eval(p->value, root_hash); } #else static bool evaluate_dmv_roothash(const struct ipe_eval_ctx *const ctx, diff --git a/security/ipe/eval.h b/security/ipe/eval.h index fef65a36468c..b26d1147d070 100644 --- a/security/ipe/eval.h +++ b/security/ipe/eval.h @@ -27,7 +27,7 @@ struct ipe_bdev { #ifdef CONFIG_IPE_PROP_DM_VERITY_SIGNATURE bool dm_verity_signed; #endif /* CONFIG_IPE_PROP_DM_VERITY_SIGNATURE */ - struct digest_info *root_hash; + struct digest_info __rcu *root_hash; }; #endif /* CONFIG_IPE_PROP_DM_VERITY */ diff --git a/security/ipe/hooks.c b/security/ipe/hooks.c index 0ae54a880405..d878b52ceec8 100644 --- a/security/ipe/hooks.c +++ b/security/ipe/hooks.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "ipe.h" #include "hooks.h" @@ -232,7 +233,20 @@ void ipe_bdev_free_security(struct block_device *bdev) { struct ipe_bdev *blob = ipe_bdev(bdev); - ipe_digest_free(blob->root_hash); + ipe_digest_free(rcu_access_pointer(blob->root_hash)); +} + +static void ipe_set_dmverity_roothash(struct ipe_bdev *blob, + struct digest_info *info) +{ + struct digest_info *old; + + /* Protected by device-mapper's md->suspend_lock */ + old = rcu_replace_pointer(blob->root_hash, info, true); + if (old) { + synchronize_rcu(); + ipe_digest_free(old); + } } #ifdef CONFIG_IPE_PROP_DM_VERITY_SIGNATURE @@ -280,8 +294,7 @@ int ipe_bdev_setintegrity(struct block_device *bdev, enum lsm_integrity_type typ return -EINVAL; if (!value) { - ipe_digest_free(blob->root_hash); - blob->root_hash = NULL; + ipe_set_dmverity_roothash(blob, NULL); return 0; } @@ -301,8 +314,7 @@ int ipe_bdev_setintegrity(struct block_device *bdev, enum lsm_integrity_type typ info->digest_len = digest->digest_len; - ipe_digest_free(blob->root_hash); - blob->root_hash = info; + ipe_set_dmverity_roothash(blob, info); return 0; err: