1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright Intel Corporation, 2023 4 * 5 * Author: Chao Peng <chao.p.peng@linux.intel.com> 6 */ 7 8 #define _GNU_SOURCE 9 #include <stdlib.h> 10 #include <string.h> 11 #include <unistd.h> 12 #include <errno.h> 13 #include <stdio.h> 14 #include <fcntl.h> 15 16 #include <linux/bitmap.h> 17 #include <linux/falloc.h> 18 #include <sys/mman.h> 19 #include <sys/types.h> 20 #include <sys/stat.h> 21 22 #include "test_util.h" 23 #include "kvm_util_base.h" 24 25 static void test_file_read_write(int fd) 26 { 27 char buf[64]; 28 29 TEST_ASSERT(read(fd, buf, sizeof(buf)) < 0, 30 "read on a guest_mem fd should fail"); 31 TEST_ASSERT(write(fd, buf, sizeof(buf)) < 0, 32 "write on a guest_mem fd should fail"); 33 TEST_ASSERT(pread(fd, buf, sizeof(buf), 0) < 0, 34 "pread on a guest_mem fd should fail"); 35 TEST_ASSERT(pwrite(fd, buf, sizeof(buf), 0) < 0, 36 "pwrite on a guest_mem fd should fail"); 37 } 38 39 static void test_mmap(int fd, size_t page_size) 40 { 41 char *mem; 42 43 mem = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 44 TEST_ASSERT_EQ(mem, MAP_FAILED); 45 } 46 47 static void test_file_size(int fd, size_t page_size, size_t total_size) 48 { 49 struct stat sb; 50 int ret; 51 52 ret = fstat(fd, &sb); 53 TEST_ASSERT(!ret, "fstat should succeed"); 54 TEST_ASSERT_EQ(sb.st_size, total_size); 55 TEST_ASSERT_EQ(sb.st_blksize, page_size); 56 } 57 58 static void test_fallocate(int fd, size_t page_size, size_t total_size) 59 { 60 int ret; 61 62 ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, total_size); 63 TEST_ASSERT(!ret, "fallocate with aligned offset and size should succeed"); 64 65 ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, 66 page_size - 1, page_size); 67 TEST_ASSERT(ret, "fallocate with unaligned offset should fail"); 68 69 ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, total_size, page_size); 70 TEST_ASSERT(ret, "fallocate beginning at total_size should fail"); 71 72 ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, total_size + page_size, page_size); 73 TEST_ASSERT(ret, "fallocate beginning after total_size should fail"); 74 75 ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, 76 total_size, page_size); 77 TEST_ASSERT(!ret, "fallocate(PUNCH_HOLE) at total_size should succeed"); 78 79 ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, 80 total_size + page_size, page_size); 81 TEST_ASSERT(!ret, "fallocate(PUNCH_HOLE) after total_size should succeed"); 82 83 ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, 84 page_size, page_size - 1); 85 TEST_ASSERT(ret, "fallocate with unaligned size should fail"); 86 87 ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, 88 page_size, page_size); 89 TEST_ASSERT(!ret, "fallocate(PUNCH_HOLE) with aligned offset and size should succeed"); 90 91 ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, page_size, page_size); 92 TEST_ASSERT(!ret, "fallocate to restore punched hole should succeed"); 93 } 94 95 static void test_invalid_punch_hole(int fd, size_t page_size, size_t total_size) 96 { 97 struct { 98 off_t offset; 99 off_t len; 100 } testcases[] = { 101 {0, 1}, 102 {0, page_size - 1}, 103 {0, page_size + 1}, 104 105 {1, 1}, 106 {1, page_size - 1}, 107 {1, page_size}, 108 {1, page_size + 1}, 109 110 {page_size, 1}, 111 {page_size, page_size - 1}, 112 {page_size, page_size + 1}, 113 }; 114 int ret, i; 115 116 for (i = 0; i < ARRAY_SIZE(testcases); i++) { 117 ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, 118 testcases[i].offset, testcases[i].len); 119 TEST_ASSERT(ret == -1 && errno == EINVAL, 120 "PUNCH_HOLE with !PAGE_SIZE offset (%lx) and/or length (%lx) should fail", 121 testcases[i].offset, testcases[i].len); 122 } 123 } 124 125 static void test_create_guest_memfd_invalid(struct kvm_vm *vm) 126 { 127 size_t page_size = getpagesize(); 128 uint64_t flag; 129 size_t size; 130 int fd; 131 132 for (size = 1; size < page_size; size++) { 133 fd = __vm_create_guest_memfd(vm, size, 0); 134 TEST_ASSERT(fd == -1 && errno == EINVAL, 135 "guest_memfd() with non-page-aligned page size '0x%lx' should fail with EINVAL", 136 size); 137 } 138 139 for (flag = 0; flag; flag <<= 1) { 140 fd = __vm_create_guest_memfd(vm, page_size, flag); 141 TEST_ASSERT(fd == -1 && errno == EINVAL, 142 "guest_memfd() with flag '0x%lx' should fail with EINVAL", 143 flag); 144 } 145 } 146 147 static void test_create_guest_memfd_multiple(struct kvm_vm *vm) 148 { 149 int fd1, fd2, ret; 150 struct stat st1, st2; 151 152 fd1 = __vm_create_guest_memfd(vm, 4096, 0); 153 TEST_ASSERT(fd1 != -1, "memfd creation should succeed"); 154 155 ret = fstat(fd1, &st1); 156 TEST_ASSERT(ret != -1, "memfd fstat should succeed"); 157 TEST_ASSERT(st1.st_size == 4096, "memfd st_size should match requested size"); 158 159 fd2 = __vm_create_guest_memfd(vm, 8192, 0); 160 TEST_ASSERT(fd2 != -1, "memfd creation should succeed"); 161 162 ret = fstat(fd2, &st2); 163 TEST_ASSERT(ret != -1, "memfd fstat should succeed"); 164 TEST_ASSERT(st2.st_size == 8192, "second memfd st_size should match requested size"); 165 166 ret = fstat(fd1, &st1); 167 TEST_ASSERT(ret != -1, "memfd fstat should succeed"); 168 TEST_ASSERT(st1.st_size == 4096, "first memfd st_size should still match requested size"); 169 TEST_ASSERT(st1.st_ino != st2.st_ino, "different memfd should have different inode numbers"); 170 } 171 172 int main(int argc, char *argv[]) 173 { 174 size_t page_size; 175 size_t total_size; 176 int fd; 177 struct kvm_vm *vm; 178 179 TEST_REQUIRE(kvm_has_cap(KVM_CAP_GUEST_MEMFD)); 180 181 page_size = getpagesize(); 182 total_size = page_size * 4; 183 184 vm = vm_create_barebones(); 185 186 test_create_guest_memfd_invalid(vm); 187 test_create_guest_memfd_multiple(vm); 188 189 fd = vm_create_guest_memfd(vm, total_size, 0); 190 191 test_file_read_write(fd); 192 test_mmap(fd, page_size); 193 test_file_size(fd, page_size, total_size); 194 test_fallocate(fd, page_size, total_size); 195 test_invalid_punch_hole(fd, page_size, total_size); 196 197 close(fd); 198 } 199