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