From 7089457a75d5cdbef00c3d04a17357333ce2dcf6 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Thu, 24 Nov 2022 15:23:26 +0000 Subject: [PATCH] ANDROID: KVM: arm64: Block module loading based on cmdline or HVC Disable module loading, based on the cmdline option kvm-arm.protected_modules. If enabled, the feature can later be irreversibly disabled with the HVC __pkvm_close_module_registration(). Bug: 357781595 Bug: 244543039 Bug: 244373730 Change-Id: I693a1db350ae15bcaab59103a68b60f087dcc6b4 Signed-off-by: Vincent Donnefort Signed-off-by: Quentin Perret --- arch/arm64/include/asm/kvm_asm.h | 9 ++-- arch/arm64/include/asm/kvm_hyp.h | 3 ++ arch/arm64/kvm/hyp/include/nvhe/modules.h | 8 +++ arch/arm64/kvm/hyp/nvhe/hyp-main.c | 7 +++ arch/arm64/kvm/hyp/nvhe/mm.c | 34 +++++++++---- arch/arm64/kvm/hyp/nvhe/modules.c | 62 +++++++++++++++++++++-- arch/arm64/kvm/pkvm.c | 11 ++++ 7 files changed, 117 insertions(+), 17 deletions(-) diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h index 41d9b1fe9519..03e99e082bff 100644 --- a/arch/arm64/include/asm/kvm_asm.h +++ b/arch/arm64/include/asm/kvm_asm.h @@ -66,10 +66,6 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid, __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_range, __KVM_HOST_SMCCC_FUNC___kvm_flush_cpu_context, - __KVM_HOST_SMCCC_FUNC___pkvm_alloc_module_va, - __KVM_HOST_SMCCC_FUNC___pkvm_map_module_page, - __KVM_HOST_SMCCC_FUNC___pkvm_unmap_module_page, - __KVM_HOST_SMCCC_FUNC___pkvm_init_module, __KVM_HOST_SMCCC_FUNC___pkvm_prot_finalize, /* Hypercalls available after pKVM finalisation */ @@ -98,6 +94,11 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___pkvm_enable_event, __KVM_HOST_SMCCC_FUNC___pkvm_selftest_event, __KVM_HOST_SMCCC_FUNC___pkvm_register_hcall, + __KVM_HOST_SMCCC_FUNC___pkvm_alloc_module_va, + __KVM_HOST_SMCCC_FUNC___pkvm_map_module_page, + __KVM_HOST_SMCCC_FUNC___pkvm_unmap_module_page, + __KVM_HOST_SMCCC_FUNC___pkvm_init_module, + __KVM_HOST_SMCCC_FUNC___pkvm_close_module_registration, /* * Start of the dynamically registered hypercalls. Start a bit diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h index c83f8d39d15a..931183c7fe3a 100644 --- a/arch/arm64/include/asm/kvm_hyp.h +++ b/arch/arm64/include/asm/kvm_hyp.h @@ -147,4 +147,7 @@ extern u64 kvm_nvhe_sym(id_aa64smfr0_el1_sys_val); extern unsigned long kvm_nvhe_sym(__icache_flags); extern unsigned int kvm_nvhe_sym(kvm_arm_vmid_bits); extern unsigned int kvm_nvhe_sym(kvm_host_sve_max_vl); + +extern bool kvm_nvhe_sym(__pkvm_modules_enabled); + #endif /* __ARM64_KVM_HYP_H__ */ diff --git a/arch/arm64/kvm/hyp/include/nvhe/modules.h b/arch/arm64/kvm/hyp/include/nvhe/modules.h index f7c03cbb5a4b..d69aa744b82f 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/modules.h +++ b/arch/arm64/kvm/hyp/include/nvhe/modules.h @@ -7,6 +7,10 @@ int __pkvm_init_module(void *module_init); int __pkvm_register_hcall(unsigned long hfn_hyp_va); int handle_host_dynamic_hcall(struct kvm_cpu_context *host_ctxt); +void pkvm_modules_lock(void); +void pkvm_modules_unlock(void); +bool pkvm_modules_enabled(void); +int __pkvm_close_module_registration(void); #else static inline int __pkvm_init_module(void *module_init) { return -EOPNOTSUPP; } static inline int @@ -15,4 +19,8 @@ static inline int handle_host_dynamic_hcall(struct kvm_cpu_context *host_ctxt) { return HCALL_UNHANDLED; } +static inline void pkvm_modules_lock(void) { } +static inline void pkvm_modules_unlock(void) { } +static inline bool pkvm_modules_enabled(void) { return false; } +static inline int __pkvm_close_module_registration(void) { return -EOPNOTSUPP; } #endif diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index dd961eb76083..046338ab0df5 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -1318,6 +1318,12 @@ static void handle___pkvm_register_hcall(struct kvm_cpu_context *host_ctxt) cpu_reg(host_ctxt, 1) = __pkvm_register_hcall(hfn_hyp_va); } +static void +handle___pkvm_close_module_registration(struct kvm_cpu_context *host_ctxt) +{ + cpu_reg(host_ctxt, 1) = __pkvm_close_module_registration(); +} + typedef void (*hcall_t)(struct kvm_cpu_context *); #define HANDLE_FUNC(x) [__KVM_HOST_SMCCC_FUNC_##x] = (hcall_t)handle_##x @@ -1368,6 +1374,7 @@ static const hcall_t host_hcall[] = { HANDLE_FUNC(__pkvm_unmap_module_page), HANDLE_FUNC(__pkvm_init_module), HANDLE_FUNC(__pkvm_register_hcall), + HANDLE_FUNC(__pkvm_close_module_registration), }; static void handle_host_hcall(struct kvm_cpu_context *host_ctxt) diff --git a/arch/arm64/kvm/hyp/nvhe/mm.c b/arch/arm64/kvm/hyp/nvhe/mm.c index 73be74784b55..04b9efa7508c 100644 --- a/arch/arm64/kvm/hyp/nvhe/mm.c +++ b/arch/arm64/kvm/hyp/nvhe/mm.c @@ -16,6 +16,7 @@ #include #include #include +#include #include struct kvm_pgtable pkvm_pgtable; @@ -112,29 +113,44 @@ int __pkvm_create_private_mapping(phys_addr_t phys, size_t size, void *__pkvm_alloc_module_va(u64 nr_pages) { - unsigned long addr; - int ret; + unsigned long addr = 0; - ret = pkvm_alloc_private_va_range(nr_pages << PAGE_SHIFT, &addr); + pkvm_modules_lock(); + if (pkvm_modules_enabled()) + pkvm_alloc_private_va_range(nr_pages << PAGE_SHIFT, &addr); + pkvm_modules_unlock(); - return ret ? NULL : (void *)addr; + return (void *)addr; } int __pkvm_map_module_page(u64 pfn, void *va, enum kvm_pgtable_prot prot) { - int ret; + int ret = -EACCES; + + pkvm_modules_lock(); + + if (!pkvm_modules_enabled()) + goto err; ret = __pkvm_host_donate_hyp(pfn, 1); if (ret) - return ret; + goto err; - return __pkvm_create_mappings((unsigned long)va, PAGE_SIZE, hyp_pfn_to_phys(pfn), prot); + ret = __pkvm_create_mappings((unsigned long)va, PAGE_SIZE, hyp_pfn_to_phys(pfn), prot); +err: + pkvm_modules_unlock(); + + return ret; } void __pkvm_unmap_module_page(u64 pfn, void *va) { - WARN_ON(__pkvm_hyp_donate_host(pfn, 1)); - pkvm_remove_mappings(va, va + PAGE_SIZE); + pkvm_modules_lock(); + if (pkvm_modules_enabled()) { + WARN_ON(__pkvm_hyp_donate_host(pfn, 1)); + pkvm_remove_mappings(va, va + PAGE_SIZE); + } + pkvm_modules_unlock(); } int pkvm_create_mappings_locked(void *from, void *to, enum kvm_pgtable_prot prot) diff --git a/arch/arm64/kvm/hyp/nvhe/modules.c b/arch/arm64/kvm/hyp/nvhe/modules.c index e867e6d9a877..78424c3178e2 100644 --- a/arch/arm64/kvm/hyp/nvhe/modules.c +++ b/arch/arm64/kvm/hyp/nvhe/modules.c @@ -11,6 +11,42 @@ #include #include +DEFINE_HYP_SPINLOCK(modules_lock); + +bool __pkvm_modules_enabled __ro_after_init; + +void pkvm_modules_lock(void) +{ + hyp_spin_lock(&modules_lock); +} + +void pkvm_modules_unlock(void) +{ + hyp_spin_unlock(&modules_lock); +} + +bool pkvm_modules_enabled(void) +{ + return __pkvm_modules_enabled; +} + +int __pkvm_close_module_registration(void) +{ + int ret; + + pkvm_modules_lock(); + ret = __pkvm_modules_enabled ? 0 : -EACCES; + if (!ret) { + void *addr = hyp_fixmap_map(__hyp_pa(&__pkvm_modules_enabled)); + *(bool *)addr = false; + hyp_fixmap_unmap(); + } + pkvm_modules_unlock(); + + /* The fuse is blown! No way back until reset */ + return ret; +} + const struct pkvm_module_ops module_ops = { .create_private_mapping = __pkvm_create_private_mapping, .register_serial_driver = __pkvm_register_serial_driver, @@ -24,7 +60,15 @@ int __pkvm_init_module(void *module_init) int (*do_module_init)(const struct pkvm_module_ops *ops) = module_init; int ret; + pkvm_modules_lock(); + if (!pkvm_modules_enabled()) { + ret = -EACCES; + goto err; + } ret = do_module_init(&module_ops); +err: + pkvm_modules_unlock(); + return ret; } @@ -72,15 +116,21 @@ end: int __pkvm_register_hcall(unsigned long hvn_hyp_va) { dyn_hcall_t hfn = (void *)hvn_hyp_va; - int reserved_id; + int reserved_id, ret; + + pkvm_modules_lock(); + if (!pkvm_modules_enabled()) { + ret = -EACCES; + goto err; + } hyp_spin_lock(&dyn_hcall_lock); reserved_id = atomic_read(&num_dynamic_hcalls); if (reserved_id >= MAX_DYNAMIC_HCALLS) { - hyp_spin_unlock(&dyn_hcall_lock); - return -ENOMEM; + ret = -ENOMEM; + goto err_hcall_unlock; } WRITE_ONCE(host_dynamic_hcalls[reserved_id], hfn); @@ -91,7 +141,11 @@ int __pkvm_register_hcall(unsigned long hvn_hyp_va) */ atomic_set_release(&num_dynamic_hcalls, reserved_id + 1); + ret = reserved_id + __KVM_HOST_SMCCC_FUNC___dynamic_hcalls; +err_hcall_unlock: hyp_spin_unlock(&dyn_hcall_lock); +err: + pkvm_modules_unlock(); - return reserved_id + __KVM_HOST_SMCCC_FUNC___dynamic_hcalls; + return ret; }; diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c index 574c9253e63c..8e4a66795845 100644 --- a/arch/arm64/kvm/pkvm.c +++ b/arch/arm64/kvm/pkvm.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -480,6 +481,16 @@ int pkvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap) return 0; } +#ifdef CONFIG_MODULES +static int __init early_pkvm_enable_modules(char *arg) +{ + kvm_nvhe_sym(__pkvm_modules_enabled) = true; + + return 0; +} +early_param("kvm-arm.protected_modules", early_pkvm_enable_modules); +#endif + struct pkvm_mod_sec_mapping { struct pkvm_module_section *sec; enum kvm_pgtable_prot prot;