From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B0FD223394D; Sat, 17 Oct 2026 15:56:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792252591; cv=none; b=IIxwYwTqUckiCRNOm7znxLIQd6ngVAzuOBzAEJpw5DW4pEhneV4sEfkCD1jprg4yr4f2EPA3L7K4txIyT+LpnxVsc7zKnahg8ehPPtB3pHkuIqNSY7g3WRwFlPBuWjbzxv32iCFfOQHBSfxf0sDqg9oGv41RHWUyzSEJNpacLWI= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792252591; c=relaxed/simple; bh=2AthjvnBSHiEfTZEBwruM1K8WNw+9la5SnHkeRFkpxc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=R0gJM4EiutUGl3SyM0zjiwQFVr4vJuJWZPl8XfqAnSdSK30geWahmK1GCH3vlrSPJ8yNKWdAJG7r/NkI5kx4M7u/aRmmHwha+HxJSa1zINi48YXbwCwZ68bVzxldYxM8zo4VTrK51h0Xunf0vsf2UrTW5DLZsU7bQW+rrHb9Hag= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b=zu+Cv6L2; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b="zu+Cv6L2" Received: by smtp.kernel.org (Postfix) with ESMTPSA id ED59E1F000C4; Sat, 17 Oct 2026 15:56:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1792252589; bh=LiTlOMqFvKuYbeuKCeM3XtXrD72whUe0GXpe7i/j+UY=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=zu+Cv6L28dRj01LcuZCiTQ7i8sC+0Qy3ys//xQN3o66vReg5sXpNRJpFYcLtplnJk CKnueEvaCIobvIfe1YRc7M+j/sEOzwlk5Nb5Gv1voFEYussrBSzPlqFxV8Ec9iArph KRDDJyyYBwg//R7RsbReN6HLo3cCQb3xFfJjWPso= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Karel Srot , Mimi Zohar , Paul Moore , Coiby Xu , Sasha Levin Subject: [PATCH 6.18 31/52] ima: Access decompressed kernel module to verify appended signature Date: Sat, 17 Oct 2026 17:54:32 +0200 Message-ID: <20261017155105.851092003@linuxfoundation.org> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261017155104.830766652@linuxfoundation.org> References: <20261017155104.830766652@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore Precedence: bulk X-Mailing-List: patches@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 6.18-stable review patch. If anyone has any objections, please let me know. ------------------ From: Coiby Xu [ Upstream commit c200892b46ba3df3dd210b7117a463ec283600c3 ] Currently, when in-kernel module decompression (CONFIG_MODULE_DECOMPRESS) is enabled, IMA has no way to verify the appended module signature as it can't decompress the module. Define a new kernel_read_file_id enumerate READING_MODULE_COMPRESSED so IMA can calculate the compressed kernel module data hash on READING_MODULE_COMPRESSED and defer appraising/measuring it until on READING_MODULE when the module has been decompressed. Before enabling in-kernel module decompression, a kernel module in initramfs can still be loaded with ima_policy=secure_boot. So adjust the kernel module rule in secure_boot policy to allow either an IMA signature OR an appended signature i.e. to use "appraise func=MODULE_CHECK appraise_type=imasig|modsig". Reported-by: Karel Srot Suggested-by: Mimi Zohar Suggested-by: Paul Moore Signed-off-by: Coiby Xu Signed-off-by: Mimi Zohar Stable-dep-of: 377cae9851e8 ("ima: Fix stack-out-of-bounds in is_bprm_creds_for_exec()") Signed-off-by: Sasha Levin --- include/linux/kernel_read_file.h | 1 + kernel/module/main.c | 17 ++++++++++++++--- security/integrity/ima/ima_main.c | 24 ++++++++++++++++-------- security/integrity/ima/ima_policy.c | 3 ++- security/ipe/hooks.c | 1 + security/selinux/hooks.c | 5 +++-- 6 files changed, 37 insertions(+), 14 deletions(-) diff --git a/include/linux/kernel_read_file.h b/include/linux/kernel_read_file.h index 90451e2e12bd1..d613a7b4dd35a 100644 --- a/include/linux/kernel_read_file.h +++ b/include/linux/kernel_read_file.h @@ -14,6 +14,7 @@ id(KEXEC_INITRAMFS, kexec-initramfs) \ id(POLICY, security-policy) \ id(X509_CERTIFICATE, x509-certificate) \ + id(MODULE_COMPRESSED, kernel-module-compressed) \ id(MAX_ID, ) #define __fid_enumify(ENUM, dummy) READING_ ## ENUM, diff --git a/kernel/module/main.c b/kernel/module/main.c index 769463d2b254b..fc5b17befb0b8 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -3692,24 +3692,35 @@ static int idempotent_wait_for_completion(struct idempotent *u) static int init_module_from_file(struct file *f, const char __user * uargs, int flags) { + bool compressed = !!(flags & MODULE_INIT_COMPRESSED_FILE); struct load_info info = { }; void *buf = NULL; int len; + int err; - len = kernel_read_file(f, 0, &buf, INT_MAX, NULL, READING_MODULE); + len = kernel_read_file(f, 0, &buf, INT_MAX, NULL, + compressed ? READING_MODULE_COMPRESSED : + READING_MODULE); if (len < 0) { mod_stat_inc(&failed_kreads); return len; } - if (flags & MODULE_INIT_COMPRESSED_FILE) { - int err = module_decompress(&info, buf, len); + if (compressed) { + err = module_decompress(&info, buf, len); vfree(buf); /* compressed data is no longer needed */ if (err) { mod_stat_inc(&failed_decompress); mod_stat_add_long(len, &invalid_decompress_bytes); return err; } + err = security_kernel_post_read_file(f, (char *)info.hdr, info.len, + READING_MODULE); + if (err) { + mod_stat_inc(&failed_kreads); + free_copy(&info, flags); + return err; + } } else { info.hdr = buf; info.len = len; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index e51b53b7789b1..cda897267230d 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -235,7 +235,8 @@ static void ima_file_free(struct file *file) static int process_measurement(struct file *file, const struct cred *cred, struct lsm_prop *prop, char *buf, loff_t size, - int mask, enum ima_hooks func) + int mask, enum ima_hooks func, + enum kernel_read_file_id read_id) { struct inode *real_inode, *inode = file_inode(file); struct ima_iint_cache *iint = NULL; @@ -406,6 +407,12 @@ static int process_measurement(struct file *file, const struct cred *cred, if (rc != 0 && rc != -EBADF && rc != -EINVAL) goto out_locked; + /* Defer measuring/appraising kernel modules to READING_MODULE */ + if (read_id == READING_MODULE_COMPRESSED) { + must_appraise = 0; + goto out_locked; + } + if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */ pathname = ima_d_path(&file->f_path, &pathbuf, filename); @@ -486,14 +493,14 @@ static int ima_file_mmap(struct file *file, unsigned long reqprot, if (reqprot & PROT_EXEC) { ret = process_measurement(file, current_cred(), &prop, NULL, - 0, MAY_EXEC, MMAP_CHECK_REQPROT); + 0, MAY_EXEC, MMAP_CHECK_REQPROT, 0); if (ret) return ret; } if (prot & PROT_EXEC) return process_measurement(file, current_cred(), &prop, NULL, - 0, MAY_EXEC, MMAP_CHECK); + 0, MAY_EXEC, MMAP_CHECK, 0); return 0; } @@ -577,7 +584,7 @@ static int ima_bprm_check(struct linux_binprm *bprm) security_current_getlsmprop_subj(&prop); return process_measurement(bprm->file, current_cred(), - &prop, NULL, 0, MAY_EXEC, BPRM_CHECK); + &prop, NULL, 0, MAY_EXEC, BPRM_CHECK, 0); } /** @@ -607,7 +614,7 @@ static int ima_creds_check(struct linux_binprm *bprm, const struct file *file) security_current_getlsmprop_subj(&prop); return process_measurement((struct file *)file, bprm->cred, &prop, NULL, - 0, MAY_EXEC, CREDS_CHECK); + 0, MAY_EXEC, CREDS_CHECK, 0); } /** @@ -655,7 +662,7 @@ static int ima_file_check(struct file *file, int mask) security_current_getlsmprop_subj(&prop); return process_measurement(file, current_cred(), &prop, NULL, 0, mask & (MAY_READ | MAY_WRITE | MAY_EXEC | - MAY_APPEND), FILE_CHECK); + MAY_APPEND), FILE_CHECK, 0); } /* @@ -911,12 +918,13 @@ static int ima_read_file(struct file *file, enum kernel_read_file_id read_id, func = read_idmap[read_id] ?: FILE_CHECK; security_current_getlsmprop_subj(&prop); return process_measurement(file, current_cred(), &prop, NULL, 0, - MAY_READ, func); + MAY_READ, func, 0); } const int read_idmap[READING_MAX_ID] = { [READING_FIRMWARE] = FIRMWARE_CHECK, [READING_MODULE] = MODULE_CHECK, + [READING_MODULE_COMPRESSED] = MODULE_CHECK, [READING_KEXEC_IMAGE] = KEXEC_KERNEL_CHECK, [READING_KEXEC_INITRAMFS] = KEXEC_INITRAMFS_CHECK, [READING_POLICY] = POLICY_CHECK @@ -954,7 +962,7 @@ static int ima_post_read_file(struct file *file, char *buf, loff_t size, func = read_idmap[read_id] ?: FILE_CHECK; security_current_getlsmprop_subj(&prop); return process_measurement(file, current_cred(), &prop, buf, size, - MAY_READ, func); + MAY_READ, func, read_id); } /** diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index db6d55af5a80b..d63af617d4586 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -241,7 +241,8 @@ static struct ima_rule_entry build_appraise_rules[] __ro_after_init = { static struct ima_rule_entry secure_boot_rules[] __ro_after_init = { {.action = APPRAISE, .func = MODULE_CHECK, - .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, + .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED | + IMA_CHECK_BLACKLIST}, {.action = APPRAISE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, {.action = APPRAISE, .func = KEXEC_KERNEL_CHECK, diff --git a/security/ipe/hooks.c b/security/ipe/hooks.c index d3092933abe21..7cbb1df0a76bc 100644 --- a/security/ipe/hooks.c +++ b/security/ipe/hooks.c @@ -119,6 +119,7 @@ int ipe_kernel_read_file(struct file *file, enum kernel_read_file_id id, op = IPE_OP_FIRMWARE; break; case READING_MODULE: + case READING_MODULE_COMPRESSED: op = IPE_OP_KERNEL_MODULE; break; case READING_KEXEC_INITRAMFS: diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 86c82a6df54c7..03ecfc6de2c3e 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4514,7 +4514,7 @@ static int selinux_kernel_read_file(struct file *file, { int rc = 0; - BUILD_BUG_ON_MSG(READING_MAX_ID > 7, + BUILD_BUG_ON_MSG(READING_MAX_ID > 8, "New kernel_read_file_id introduced; update SELinux!"); switch (id) { @@ -4522,6 +4522,7 @@ static int selinux_kernel_read_file(struct file *file, rc = selinux_kernel_load_from_file(file, SYSTEM__FIRMWARE_LOAD); break; case READING_MODULE: + case READING_MODULE_COMPRESSED: rc = selinux_kernel_load_from_file(file, SYSTEM__MODULE_LOAD); break; case READING_KEXEC_IMAGE: @@ -4550,7 +4551,7 @@ static int selinux_kernel_load_data(enum kernel_load_data_id id, bool contents) { int rc = 0; - BUILD_BUG_ON_MSG(LOADING_MAX_ID > 7, + BUILD_BUG_ON_MSG(LOADING_MAX_ID > 8, "New kernel_load_data_id introduced; update SELinux!"); switch (id) { -- 2.53.0