162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <stdio.h> 762306a36Sopenharmony_ci#include <stddef.h> 862306a36Sopenharmony_ci#include <stdlib.h> 962306a36Sopenharmony_ci#include <unistd.h> 1062306a36Sopenharmony_ci#include <errno.h> 1162306a36Sopenharmony_ci#include <fcntl.h> 1262306a36Sopenharmony_ci#include <string.h> 1362306a36Sopenharmony_ci#include <sys/stat.h> 1462306a36Sopenharmony_ci#include <sys/mman.h> 1562306a36Sopenharmony_ci#include <sys/vfs.h> 1662306a36Sopenharmony_ci#include <linux/magic.h> 1762306a36Sopenharmony_ci#include <init.h> 1862306a36Sopenharmony_ci#include <os.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * kasan_map_memory - maps memory from @start with a size of @len. 2262306a36Sopenharmony_ci * The allocated memory is filled with zeroes upon success. 2362306a36Sopenharmony_ci * @start: the start address of the memory to be mapped 2462306a36Sopenharmony_ci * @len: the length of the memory to be mapped 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * This function is used to map shadow memory for KASAN in uml 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_civoid kasan_map_memory(void *start, size_t len) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci if (mmap(start, 3162306a36Sopenharmony_ci len, 3262306a36Sopenharmony_ci PROT_READ|PROT_WRITE, 3362306a36Sopenharmony_ci MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, 3462306a36Sopenharmony_ci -1, 3562306a36Sopenharmony_ci 0) == MAP_FAILED) { 3662306a36Sopenharmony_ci os_info("Couldn't allocate shadow memory: %s\n.", 3762306a36Sopenharmony_ci strerror(errno)); 3862306a36Sopenharmony_ci exit(1); 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* Set by make_tempfile() during early boot. */ 4362306a36Sopenharmony_cistatic char *tempdir = NULL; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* Check if dir is on tmpfs. Return 0 if yes, -1 if no or error. */ 4662306a36Sopenharmony_cistatic int __init check_tmpfs(const char *dir) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct statfs st; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci os_info("Checking if %s is on tmpfs...", dir); 5162306a36Sopenharmony_ci if (statfs(dir, &st) < 0) { 5262306a36Sopenharmony_ci os_info("%s\n", strerror(errno)); 5362306a36Sopenharmony_ci } else if (st.f_type != TMPFS_MAGIC) { 5462306a36Sopenharmony_ci os_info("no\n"); 5562306a36Sopenharmony_ci } else { 5662306a36Sopenharmony_ci os_info("OK\n"); 5762306a36Sopenharmony_ci return 0; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci return -1; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* 6362306a36Sopenharmony_ci * Choose the tempdir to use. We want something on tmpfs so that our memory is 6462306a36Sopenharmony_ci * not subject to the host's vm.dirty_ratio. If a tempdir is specified in the 6562306a36Sopenharmony_ci * environment, we use that even if it's not on tmpfs, but we warn the user. 6662306a36Sopenharmony_ci * Otherwise, we try common tmpfs locations, and if no tmpfs directory is found 6762306a36Sopenharmony_ci * then we fall back to /tmp. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_cistatic char * __init choose_tempdir(void) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci static const char * const vars[] = { 7262306a36Sopenharmony_ci "TMPDIR", 7362306a36Sopenharmony_ci "TMP", 7462306a36Sopenharmony_ci "TEMP", 7562306a36Sopenharmony_ci NULL 7662306a36Sopenharmony_ci }; 7762306a36Sopenharmony_ci static const char fallback_dir[] = "/tmp"; 7862306a36Sopenharmony_ci static const char * const tmpfs_dirs[] = { 7962306a36Sopenharmony_ci "/dev/shm", 8062306a36Sopenharmony_ci fallback_dir, 8162306a36Sopenharmony_ci NULL 8262306a36Sopenharmony_ci }; 8362306a36Sopenharmony_ci int i; 8462306a36Sopenharmony_ci const char *dir; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci os_info("Checking environment variables for a tempdir..."); 8762306a36Sopenharmony_ci for (i = 0; vars[i]; i++) { 8862306a36Sopenharmony_ci dir = getenv(vars[i]); 8962306a36Sopenharmony_ci if ((dir != NULL) && (*dir != '\0')) { 9062306a36Sopenharmony_ci os_info("%s\n", dir); 9162306a36Sopenharmony_ci if (check_tmpfs(dir) >= 0) 9262306a36Sopenharmony_ci goto done; 9362306a36Sopenharmony_ci else 9462306a36Sopenharmony_ci goto warn; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci os_info("none found\n"); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci for (i = 0; tmpfs_dirs[i]; i++) { 10062306a36Sopenharmony_ci dir = tmpfs_dirs[i]; 10162306a36Sopenharmony_ci if (check_tmpfs(dir) >= 0) 10262306a36Sopenharmony_ci goto done; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci dir = fallback_dir; 10662306a36Sopenharmony_ciwarn: 10762306a36Sopenharmony_ci os_warn("Warning: tempdir %s is not on tmpfs\n", dir); 10862306a36Sopenharmony_cidone: 10962306a36Sopenharmony_ci /* Make a copy since getenv results may not remain valid forever. */ 11062306a36Sopenharmony_ci return strdup(dir); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* 11462306a36Sopenharmony_ci * Create an unlinked tempfile in a suitable tempdir. template must be the 11562306a36Sopenharmony_ci * basename part of the template with a leading '/'. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_cistatic int __init make_tempfile(const char *template) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci char *tempname; 12062306a36Sopenharmony_ci int fd; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (tempdir == NULL) { 12362306a36Sopenharmony_ci tempdir = choose_tempdir(); 12462306a36Sopenharmony_ci if (tempdir == NULL) { 12562306a36Sopenharmony_ci os_warn("Failed to choose tempdir: %s\n", 12662306a36Sopenharmony_ci strerror(errno)); 12762306a36Sopenharmony_ci return -1; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#ifdef O_TMPFILE 13262306a36Sopenharmony_ci fd = open(tempdir, O_CLOEXEC | O_RDWR | O_EXCL | O_TMPFILE, 0700); 13362306a36Sopenharmony_ci /* 13462306a36Sopenharmony_ci * If the running system does not support O_TMPFILE flag then retry 13562306a36Sopenharmony_ci * without it. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci if (fd != -1 || (errno != EINVAL && errno != EISDIR && 13862306a36Sopenharmony_ci errno != EOPNOTSUPP)) 13962306a36Sopenharmony_ci return fd; 14062306a36Sopenharmony_ci#endif 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci tempname = malloc(strlen(tempdir) + strlen(template) + 1); 14362306a36Sopenharmony_ci if (tempname == NULL) 14462306a36Sopenharmony_ci return -1; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci strcpy(tempname, tempdir); 14762306a36Sopenharmony_ci strcat(tempname, template); 14862306a36Sopenharmony_ci fd = mkstemp(tempname); 14962306a36Sopenharmony_ci if (fd < 0) { 15062306a36Sopenharmony_ci os_warn("open - cannot create %s: %s\n", tempname, 15162306a36Sopenharmony_ci strerror(errno)); 15262306a36Sopenharmony_ci goto out; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci if (unlink(tempname) < 0) { 15562306a36Sopenharmony_ci perror("unlink"); 15662306a36Sopenharmony_ci goto close; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci free(tempname); 15962306a36Sopenharmony_ci return fd; 16062306a36Sopenharmony_ciclose: 16162306a36Sopenharmony_ci close(fd); 16262306a36Sopenharmony_ciout: 16362306a36Sopenharmony_ci free(tempname); 16462306a36Sopenharmony_ci return -1; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci#define TEMPNAME_TEMPLATE "/vm_file-XXXXXX" 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int __init create_tmp_file(unsigned long long len) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci int fd, err; 17262306a36Sopenharmony_ci char zero; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci fd = make_tempfile(TEMPNAME_TEMPLATE); 17562306a36Sopenharmony_ci if (fd < 0) 17662306a36Sopenharmony_ci exit(1); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* 17962306a36Sopenharmony_ci * Seek to len - 1 because writing a character there will 18062306a36Sopenharmony_ci * increase the file size by one byte, to the desired length. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci if (lseek64(fd, len - 1, SEEK_SET) < 0) { 18362306a36Sopenharmony_ci perror("lseek64"); 18462306a36Sopenharmony_ci exit(1); 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci zero = 0; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci err = write(fd, &zero, 1); 19062306a36Sopenharmony_ci if (err != 1) { 19162306a36Sopenharmony_ci perror("write"); 19262306a36Sopenharmony_ci exit(1); 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return fd; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ciint __init create_mem_file(unsigned long long len) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci int err, fd; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci fd = create_tmp_file(len); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci err = os_set_exec_close(fd); 20562306a36Sopenharmony_ci if (err < 0) { 20662306a36Sopenharmony_ci errno = -err; 20762306a36Sopenharmony_ci perror("exec_close"); 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci return fd; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_civoid __init check_tmpexec(void) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci void *addr; 21562306a36Sopenharmony_ci int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci addr = mmap(NULL, UM_KERN_PAGE_SIZE, 21862306a36Sopenharmony_ci PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); 21962306a36Sopenharmony_ci os_info("Checking PROT_EXEC mmap in %s...", tempdir); 22062306a36Sopenharmony_ci if (addr == MAP_FAILED) { 22162306a36Sopenharmony_ci err = errno; 22262306a36Sopenharmony_ci os_warn("%s\n", strerror(err)); 22362306a36Sopenharmony_ci close(fd); 22462306a36Sopenharmony_ci if (err == EPERM) 22562306a36Sopenharmony_ci os_warn("%s must be not mounted noexec\n", tempdir); 22662306a36Sopenharmony_ci exit(1); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci os_info("OK\n"); 22962306a36Sopenharmony_ci munmap(addr, UM_KERN_PAGE_SIZE); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci close(fd); 23262306a36Sopenharmony_ci} 233