1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 4 */ 5 6 #include <stdio.h> 7 #include <stddef.h> 8 #include <stdlib.h> 9 #include <unistd.h> 10 #include <errno.h> 11 #include <fcntl.h> 12 #include <string.h> 13 #include <sys/stat.h> 14 #include <sys/mman.h> 15 #include <sys/vfs.h> 16 #include <linux/magic.h> 17 #include <init.h> 18 #include <kern_util.h> 19 #include <os.h> 20 #include "internal.h" 21 22 /* 23 * kasan_map_memory - maps memory from @start with a size of @len. 24 * The allocated memory is filled with zeroes upon success. 25 * @start: the start address of the memory to be mapped 26 * @len: the length of the memory to be mapped 27 * 28 * This function is used to map shadow memory for KASAN in uml 29 */ 30 void kasan_map_memory(void *start, size_t len) 31 { 32 if (mmap(start, 33 len, 34 PROT_READ|PROT_WRITE, 35 MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, 36 -1, 37 0) == MAP_FAILED) { 38 os_info("Couldn't allocate shadow memory: %s\n.", 39 strerror(errno)); 40 exit(1); 41 } 42 } 43 44 /* Set by make_tempfile() during early boot. */ 45 static char *tempdir = NULL; 46 47 /* Check if dir is on tmpfs. Return 0 if yes, -1 if no or error. */ 48 static int __init check_tmpfs(const char *dir) 49 { 50 struct statfs st; 51 52 os_info("Checking if %s is on tmpfs...", dir); 53 if (statfs(dir, &st) < 0) { 54 os_info("%s\n", strerror(errno)); 55 } else if (st.f_type != TMPFS_MAGIC) { 56 os_info("no\n"); 57 } else { 58 os_info("OK\n"); 59 return 0; 60 } 61 return -1; 62 } 63 64 /* 65 * Choose the tempdir to use. We want something on tmpfs so that our memory is 66 * not subject to the host's vm.dirty_ratio. If a tempdir is specified in the 67 * environment, we use that even if it's not on tmpfs, but we warn the user. 68 * Otherwise, we try common tmpfs locations, and if no tmpfs directory is found 69 * then we fall back to /tmp. 70 */ 71 static char * __init choose_tempdir(void) 72 { 73 static const char * const vars[] = { 74 "TMPDIR", 75 "TMP", 76 "TEMP", 77 NULL 78 }; 79 static const char fallback_dir[] = "/tmp"; 80 static const char * const tmpfs_dirs[] = { 81 "/dev/shm", 82 fallback_dir, 83 NULL 84 }; 85 int i; 86 const char *dir; 87 88 os_info("Checking environment variables for a tempdir..."); 89 for (i = 0; vars[i]; i++) { 90 dir = getenv(vars[i]); 91 if ((dir != NULL) && (*dir != '\0')) { 92 os_info("%s\n", dir); 93 if (check_tmpfs(dir) >= 0) 94 goto done; 95 else 96 goto warn; 97 } 98 } 99 os_info("none found\n"); 100 101 for (i = 0; tmpfs_dirs[i]; i++) { 102 dir = tmpfs_dirs[i]; 103 if (check_tmpfs(dir) >= 0) 104 goto done; 105 } 106 107 dir = fallback_dir; 108 warn: 109 os_warn("Warning: tempdir %s is not on tmpfs\n", dir); 110 done: 111 /* Make a copy since getenv results may not remain valid forever. */ 112 return strdup(dir); 113 } 114 115 /* 116 * Create an unlinked tempfile in a suitable tempdir. template must be the 117 * basename part of the template with a leading '/'. 118 */ 119 static int __init make_tempfile(const char *template) 120 { 121 char *tempname; 122 int fd; 123 124 if (tempdir == NULL) { 125 tempdir = choose_tempdir(); 126 if (tempdir == NULL) { 127 os_warn("Failed to choose tempdir: %s\n", 128 strerror(errno)); 129 return -1; 130 } 131 } 132 133 #ifdef O_TMPFILE 134 fd = open(tempdir, O_CLOEXEC | O_RDWR | O_EXCL | O_TMPFILE, 0700); 135 /* 136 * If the running system does not support O_TMPFILE flag then retry 137 * without it. 138 */ 139 if (fd != -1 || (errno != EINVAL && errno != EISDIR && 140 errno != EOPNOTSUPP)) 141 return fd; 142 #endif 143 144 tempname = malloc(strlen(tempdir) + strlen(template) + 1); 145 if (tempname == NULL) 146 return -1; 147 148 strcpy(tempname, tempdir); 149 strcat(tempname, template); 150 fd = mkstemp(tempname); 151 if (fd < 0) { 152 os_warn("open - cannot create %s: %s\n", tempname, 153 strerror(errno)); 154 goto out; 155 } 156 if (unlink(tempname) < 0) { 157 perror("unlink"); 158 goto close; 159 } 160 free(tempname); 161 return fd; 162 close: 163 close(fd); 164 out: 165 free(tempname); 166 return -1; 167 } 168 169 #define TEMPNAME_TEMPLATE "/vm_file-XXXXXX" 170 171 static int __init create_tmp_file(unsigned long long len) 172 { 173 int fd, err; 174 char zero; 175 176 fd = make_tempfile(TEMPNAME_TEMPLATE); 177 if (fd < 0) 178 exit(1); 179 180 /* 181 * Seek to len - 1 because writing a character there will 182 * increase the file size by one byte, to the desired length. 183 */ 184 if (lseek64(fd, len - 1, SEEK_SET) < 0) { 185 perror("lseek64"); 186 exit(1); 187 } 188 189 zero = 0; 190 191 err = write(fd, &zero, 1); 192 if (err != 1) { 193 perror("write"); 194 exit(1); 195 } 196 197 return fd; 198 } 199 200 int __init create_mem_file(unsigned long long len) 201 { 202 int err, fd; 203 204 fd = create_tmp_file(len); 205 206 err = os_set_exec_close(fd); 207 if (err < 0) { 208 errno = -err; 209 perror("exec_close"); 210 } 211 return fd; 212 } 213 214 void __init check_tmpexec(void) 215 { 216 void *addr; 217 int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE); 218 219 addr = mmap(NULL, UM_KERN_PAGE_SIZE, 220 PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); 221 os_info("Checking PROT_EXEC mmap in %s...", tempdir); 222 if (addr == MAP_FAILED) { 223 err = errno; 224 os_warn("%s\n", strerror(err)); 225 close(fd); 226 if (err == EPERM) 227 os_warn("%s must be not mounted noexec\n", tempdir); 228 exit(1); 229 } 230 os_info("OK\n"); 231 munmap(addr, UM_KERN_PAGE_SIZE); 232 233 close(fd); 234 } 235