Re: [PATCH v2 11/13] KVM: arm64: Prevent the host from specifying a different command queue
[email protected] Fri, 07 Aug 2026 17:28:21 +0000
| Newsgroups | dev.linux.lists.kvmarm |
|---|---|
| Message-ID | <[email protected]> |
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: New issues: - [Critical] Malicious host can bypass ITS command sanitization by setting Page_Size to 64KB in GITS_CBASER, forcing the hardware to read unsanitized commands from host memory. - [Critical] A malicious host can bypass ITS command sanitization by programming GITS_CBASER with Non-cacheable attributes, exploiting a flawed `needs_flush` calculation. Pre-existing issues: - [Critical] Missing interception of GITS_BASER registers allows a malicious host to direct hardware ITS tables to arbitrary physical memory, breaking isolation. - [High] Infinite loop and out-of-bounds memory unsharing in the error handling path of `pkvm_setup_its_shadow_cmdq` due to an unsigned integer underflow. -- --- Patch [11]: [PATCH v2 11/13] KVM: arm64: Prevent the host from specifying a different command queue --- commit 3cac1c2e6af2a0edec0047b75fa76ac659338bc2 Author: Sebastian Ene <[email protected]> KVM: arm64: Prevent the host from specifying a different command queue This patch secures the command queue register (GITS_CBASER) from being re-programmed by a malicious host to bypass command sanitization. It restricts updates to the GITS_CBASER register while the ITS is enabled or not in a quiescent state. > diff --git a/arch/arm64/kvm/hyp/nvhe/its_emulate.c b/arch/arm64/kvm/hyp/nvhe/its_emulate.c > index b9b71aa18d48b..97cfa31d90d11 100644 > --- a/arch/arm64/kvm/hyp/nvhe/its_emulate.c > +++ b/arch/arm64/kvm/hyp/nvhe/its_emulate.c > @@ -429,9 +429,41 @@ static void ctlr_write(struct pkvm_protected_reg *region, u64 offset, u64 value) > writel_relaxed(value, its->base + GITS_CTLR); > } > > +static void cbaser_write(struct pkvm_protected_reg *region, u64 offset, u64 value) > +{ > + struct its_priv_state *its = region->priv; > + int num_pages; > + u64 ctlr; > + > + ctlr = readl_relaxed(its->base + GITS_CTLR); > + if ((ctlr & GITS_CTLR_ENABLE) || !(ctlr & GITS_CTLR_QUIESCENT)) > + return; > + > + num_pages = its->host_state->cmdq_len / SZ_4K; > + > + /* Don't let the host program a different command queue */ > + value &= ~(GENMASK(7, 0) | GENMASK_ULL(51, 12)); > + value |= (num_pages - 1) & GENMASK(7, 0); > + value |= __hyp_pa(its->cmd_original) & GENMASK_ULL(51, 12); [Severity: Critical] Does this masking logic allow the host to modify the Page_Size field? By not clearing bits 9:8 (Page_Size) from the host's GITS_CBASER value, could a malicious host pass a Page_Size of 64KB (0b10)? According to the GICv3 architecture, when a 64KB page size is used, the hardware treats bits 15:12 of the physical address as RES0, effectively rounding down the base address to a 64KB boundary. If the host donates a command queue buffer (cmd_original) that is 4KB-aligned but not 64KB-aligned, the address truncation by the hardware would cause it to fetch commands from an adjacent memory region still under host control, bypassing the hypervisor's command sanitization entirely. > + its->needs_flush = (value & GITS_CBASER_SHAREABILITY_MASK) != GITS_CBASER_InnerShareable; [Severity: Critical] Does this calculation safely account for cacheability attributes? Since this only checks the Shareability mask (bits 11:10) of the raw host value, could a malicious host request an Inner Cacheability of Non-cacheable (0b000 in bits 61:59) while maintaining an InnerShareable state? This would cause needs_flush to evaluate to false. If the hardware ITS operates in Non-cacheable mode, it reads physical memory directly without waiting for the hypervisor to flush the data cache. A host could pre-seed the physical memory with malicious commands before donating the page, forcing the hardware to execute those instead of the sanitized commands. > + > + writeq_relaxed(value, its->base + GITS_CBASER); > + > + /* Restart the CMDQ to read from 0 */ > + its->cmd_offset = 0; > + writeq_relaxed(0, its->base + GITS_CWRITER); > +} [ ... ] > static struct its_handler its_handlers[] = { > ITS_HANDLER(GITS_CWRITER, sizeof(u64), cwriter_write, cwriter_read), > ITS_HANDLER(GITS_CTLR, sizeof(u32), ctlr_write, ctlr_read), > + ITS_HANDLER(GITS_CBASER, sizeof(u64), cbaser_write, cbaser_read), > {}, > }; [Severity: Critical] This is a pre-existing issue, but does the omission of GITS_BASER from the its_handlers array allow the host to bypass isolation? Because GITS_BASER is missing here, any host writes to these registers fall through to its_emulate_forward_req() and are directly written to the hardware ITS. Could a malicious host write an arbitrary physical address (such as hypervisor private memory or guest memory) into GITS_BASER? When the host subsequently issues a MAPD command, the hardware ITS would execute it using the maliciously programmed address, allowing the host to overwrite arbitrary physical memory. [Severity: High] This isn't a bug introduced by this patch, but is there an unsigned integer underflow in the error handling path of pkvm_setup_its_shadow_cmdq()? Looking at unshare_cmd_host: arch/arm64/kvm/hyp/nvhe/its_emulate.c:pkvm_setup_its_shadow_cmdq() { ... unshare_cmd_host: if (i == 0) return ret; for (i = i - 1; i >= 0; i--) __pkvm_host_unshare_hyp(start_pfn + i); return ret; ... } Since the loop variable i is declared as an unsigned 64-bit integer (u64 i), the condition i >= 0 is always true. When i reaches 0, the i-- decrement underflows it to 0xFFFFFFFFFFFFFFFF, which would cause an infinite loop sequentially calling __pkvm_host_unshare_hyp() with invalid out-of-bounds page frame numbers. -- Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=11