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
next prev 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.