From 595cd39cbc9fb1d90c07cbb27a0d7372b0bd6b8c Mon Sep 17 00:00:00 2001 From: danbugs Date: Fri, 17 Apr 2026 07:24:52 +0000 Subject: [PATCH] fix(whp): respect IO port access size in IoPortAccess exit handler The WHP IoPortAccess exit handler was returning the full 8 bytes of RAX regardless of the guest's operand width. For outb (1-byte writes like serial port console output), this produced 8 bytes per character, resulting in garbled multi-byte sequences in the host's eprint! output. Extract AccessSize from IoPortAccess.AccessInfo (bits 1..3) and truncate the returned data vec to the actual operand width (1 for outb, 2 for outw, 4 for outl). Signed-off-by: danbugs --- .../src/hypervisor/virtual_machine/whp.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/hyperlight_host/src/hypervisor/virtual_machine/whp.rs b/src/hyperlight_host/src/hypervisor/virtual_machine/whp.rs index 3c6ae5a9d..f9ff8aec2 100644 --- a/src/hyperlight_host/src/hypervisor/virtual_machine/whp.rs +++ b/src/hyperlight_host/src/hypervisor/virtual_machine/whp.rs @@ -311,14 +311,17 @@ impl VirtualMachine for WhpVm { let rip = exit_context.VpContext.Rip + instruction_length as u64; let port = exit_context.Anonymous.IoPortAccess.PortNumber; let rax = exit_context.Anonymous.IoPortAccess.Rax; - let is_write = exit_context + let access_info_bits = exit_context .Anonymous .IoPortAccess .AccessInfo .Anonymous - ._bitfield - & 1 - != 0; + ._bitfield; + let is_write = access_info_bits & 1 != 0; + // Bits 1..3 of AccessInfo encode the operand width in + // bytes (1 for outb, 2 for outw, 4 for outl). Clamp to + // 1..=8 to avoid a zero-length slice. + let access_size = (((access_info_bits >> 1) & 0x7) as usize).clamp(1, 8); self.set_registers(&[( WHvX64RegisterRip, @@ -357,7 +360,12 @@ impl VirtualMachine for WhpVm { // Suppress unused variable warnings when hw-interrupts is disabled let _ = is_write; - return Ok(VmExit::IoOut(port, rax.to_le_bytes().to_vec())); + // Only return the bytes that the guest actually wrote + // (1 for outb, 2 for outw, 4 for outl). Previously all + // 8 RAX bytes were returned, producing garbled output + // for single-byte console writes. + let data = rax.to_le_bytes(); + return Ok(VmExit::IoOut(port, data[..access_size].to_vec())); }, WHvRunVpExitReasonX64Halt => { // With software timer active, re-enter the guest.