Extend set_memory_region_test to verify the following properties: * read on RO-memslot succeeds, * execute on RO-memslot backed by executable memory succeeds, and * write on RO-memslot fails with mmio fault.
Signed-off-by: Yohei Kojima <[email protected]> --- .../selftests/kvm/set_memory_region_test.c | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/tools/testing/selftests/kvm/set_memory_region_test.c b/tools/testing/selftests/kvm/set_memory_region_test.c index 8d4fd713347c..2f21bfcbc821 100644 --- a/tools/testing/selftests/kvm/set_memory_region_test.c +++ b/tools/testing/selftests/kvm/set_memory_region_test.c @@ -28,7 +28,10 @@ * Somewhat arbitrary location and slot, intended to not overlap anything. */ #define MEM_REGION_GPA 0xc0000000 +#define MEM_REGION_RO_GPA 0xd0000000 + #define MEM_REGION_SLOT 10 +#define MEM_REGION_RO_SLOT 11 static const uint64_t MMIO_VAL = 0xbeefull; @@ -49,6 +52,8 @@ static inline uint64_t guest_spin_on_val(uint64_t spin_val) return val; } +static int allow_mmio_fault; + static void *vcpu_worker(void *data) { struct kvm_vcpu *vcpu = data; @@ -76,6 +81,13 @@ static void *vcpu_worker(void *data) if (run->exit_reason != KVM_EXIT_MMIO) break; + if (allow_mmio_fault && run->mmio.is_write) + /* + * in this case, skip checking mmio-related assertions + * and exit status + */ + return NULL; + TEST_ASSERT(!run->mmio.is_write, "Unexpected exit mmio write"); TEST_ASSERT(run->mmio.len == 8, "Unexpected exit mmio size = %u", run->mmio.len); @@ -336,6 +348,92 @@ static void test_delete_memory_region(bool disable_slot_zap_quirk) kvm_vm_free(vm); } +static void guest_code_ro_memory_region(void) +{ + uint64_t val; + unsigned char c; + void *instruction_addr; + + GUEST_SYNC(0); + + val = guest_spin_on_val(0); + __GUEST_ASSERT(val == 1, "Expected '1', got '%lx'", val); + + /* RO memory read; should succeed if the backing memory is readable */ + c = *(unsigned char *)MEM_REGION_RO_GPA; + __GUEST_ASSERT(c == 0xab, "Expected '0xab', got '0x%x'", c); + + /* RO memory exec; should succeed if the backing memory is executable */ + instruction_addr = ((unsigned char *)MEM_REGION_RO_GPA) + 8; + val = ((uint32_t (*)(void))instruction_addr)(); + __GUEST_ASSERT(val == 0xbeef, + "Expected 0xbeef, but got '%lx'", val); + + /* Spin until the mmio fault is allowed for RO-memslot write */ + val = guest_spin_on_val(1); + __GUEST_ASSERT(val == 2, "Expected '2', got '%lx'", val); + + /* RO memory write; should fail */ + WRITE_ONCE(*((uint64_t *)MEM_REGION_RO_GPA), 0x12); + __GUEST_ASSERT(0, "RO memory write is expected to fail, but it didn't"); +} + +/* + * On x86 environment, write access to the readonly memslots are trapped as + * a special MMIO fault. This test verifies that write access on the readonly + * memslot is blocked, and read/exec access isn't. + */ +static void test_ro_memory_region(void) +{ + pthread_t vcpu_thread; + uint64_t *hva, *hva_ro; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + + /* + * Equivalent C function (assuming SysV ABI): + * uint32_t some_function(void) { + * return 0xbeef; + * } + */ + unsigned char inst_bytes[] = { + 0x48, 0xc7, 0xc0, 0xef, 0xbe, 0x00, 0x00, // mov %eax, $0xbeef + 0xc3, // ret + }; + + pr_info("Testing write on RO memslot\n"); + + vm = spawn_vm(&vcpu, &vcpu_thread, guest_code_ro_memory_region); + + vm_userspace_mem_region_add_map(vm, + MEM_REGION_RO_GPA, + MEM_REGION_RO_SLOT, + MEM_REGION_SIZE, + KVM_MEM_READONLY); + + hva = addr_gpa2hva(vm, MEM_REGION_GPA); + hva_ro = addr_gpa2hva(vm, MEM_REGION_RO_GPA); + + memset(hva_ro, 0xcccccccc, 0x2000); + WRITE_ONCE(*hva_ro, 0xab); + memcpy(((unsigned char *)hva_ro) + 8, inst_bytes, sizeof(inst_bytes)); + + WRITE_ONCE(*hva, 1); + + /* Wait the vcpu thread to complete read/exec on ro memory */ + usleep(100000); + + allow_mmio_fault = true; + WRITE_ONCE(*hva, 2); + + wait_for_vcpu(); + + pthread_join(vcpu_thread, NULL); + + kvm_vm_free(vm); + allow_mmio_fault = false; +} + static void test_zero_memory_regions(void) { struct kvm_vcpu *vcpu; @@ -629,6 +727,8 @@ int main(int argc, char *argv[]) */ test_zero_memory_regions(); test_mmio_during_vectoring(); + + test_ro_memory_region(); #endif test_invalid_memory_region_flags(); -- 2.43.0

