197870c34SAlex Dewar // SPDX-License-Identifier: GPL-2.0 25134d8feSJeff Dike /* 35134d8feSJeff Dike * Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 45134d8feSJeff Dike */ 55134d8feSJeff Dike 60f80bc85SJeff Dike #include <stdio.h> 70f80bc85SJeff Dike #include <stddef.h> 85134d8feSJeff Dike #include <stdlib.h> 90f80bc85SJeff Dike #include <unistd.h> 100f80bc85SJeff Dike #include <errno.h> 110f80bc85SJeff Dike #include <fcntl.h> 125134d8feSJeff Dike #include <string.h> 13fb967eccSLiu Aleaxander #include <sys/stat.h> 140f80bc85SJeff Dike #include <sys/mman.h> 150d71832eSTristan Schmelcher #include <sys/vfs.h> 160d71832eSTristan Schmelcher #include <linux/magic.h> 1737185b33SAl Viro #include <init.h> 186a85e34cSTiwei Bie #include <kern_util.h> 1937185b33SAl Viro #include <os.h> 20847d3abcSTiwei Bie #include "internal.h" 210f80bc85SJeff Dike 225b301409SPatricia Alfonso /* 235b301409SPatricia Alfonso * kasan_map_memory - maps memory from @start with a size of @len. 245b301409SPatricia Alfonso * The allocated memory is filled with zeroes upon success. 255b301409SPatricia Alfonso * @start: the start address of the memory to be mapped 265b301409SPatricia Alfonso * @len: the length of the memory to be mapped 275b301409SPatricia Alfonso * 285b301409SPatricia Alfonso * This function is used to map shadow memory for KASAN in uml 295b301409SPatricia Alfonso */ 305b301409SPatricia Alfonso void kasan_map_memory(void *start, size_t len) 315b301409SPatricia Alfonso { 325b301409SPatricia Alfonso if (mmap(start, 335b301409SPatricia Alfonso len, 345b301409SPatricia Alfonso PROT_READ|PROT_WRITE, 355b301409SPatricia Alfonso MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, 365b301409SPatricia Alfonso -1, 375b301409SPatricia Alfonso 0) == MAP_FAILED) { 385b301409SPatricia Alfonso os_info("Couldn't allocate shadow memory: %s\n.", 395b301409SPatricia Alfonso strerror(errno)); 405b301409SPatricia Alfonso exit(1); 415b301409SPatricia Alfonso } 42*fce01288SBenjamin Berg 43*fce01288SBenjamin Berg if (madvise(start, len, MADV_DONTDUMP)) { 44*fce01288SBenjamin Berg os_info("Couldn't set MAD_DONTDUMP on shadow memory: %s\n.", 45*fce01288SBenjamin Berg strerror(errno)); 46*fce01288SBenjamin Berg exit(1); 47*fce01288SBenjamin Berg } 48*fce01288SBenjamin Berg 49*fce01288SBenjamin Berg if (madvise(start, len, MADV_DONTFORK)) { 50*fce01288SBenjamin Berg os_info("Couldn't set MADV_DONTFORK on shadow memory: %s\n.", 51*fce01288SBenjamin Berg strerror(errno)); 52*fce01288SBenjamin Berg exit(1); 53*fce01288SBenjamin Berg } 545b301409SPatricia Alfonso } 555b301409SPatricia Alfonso 560d71832eSTristan Schmelcher /* Set by make_tempfile() during early boot. */ 5732e8eaf2SBenjamin Berg char *tempdir = NULL; 580f80bc85SJeff Dike 590d71832eSTristan Schmelcher /* Check if dir is on tmpfs. Return 0 if yes, -1 if no or error. */ 600d71832eSTristan Schmelcher static int __init check_tmpfs(const char *dir) 610f80bc85SJeff Dike { 620d71832eSTristan Schmelcher struct statfs st; 630f80bc85SJeff Dike 64d3878bb8SMasami Hiramatsu os_info("Checking if %s is on tmpfs...", dir); 650d71832eSTristan Schmelcher if (statfs(dir, &st) < 0) { 66d3878bb8SMasami Hiramatsu os_info("%s\n", strerror(errno)); 670d71832eSTristan Schmelcher } else if (st.f_type != TMPFS_MAGIC) { 68d3878bb8SMasami Hiramatsu os_info("no\n"); 6974735341STristan Schmelcher } else { 70d3878bb8SMasami Hiramatsu os_info("OK\n"); 710d71832eSTristan Schmelcher return 0; 7274735341STristan Schmelcher } 730d71832eSTristan Schmelcher return -1; 7474735341STristan Schmelcher } 7574735341STristan Schmelcher 760d71832eSTristan Schmelcher /* 770d71832eSTristan Schmelcher * Choose the tempdir to use. We want something on tmpfs so that our memory is 780d71832eSTristan Schmelcher * not subject to the host's vm.dirty_ratio. If a tempdir is specified in the 790d71832eSTristan Schmelcher * environment, we use that even if it's not on tmpfs, but we warn the user. 800d71832eSTristan Schmelcher * Otherwise, we try common tmpfs locations, and if no tmpfs directory is found 810d71832eSTristan Schmelcher * then we fall back to /tmp. 820d71832eSTristan Schmelcher */ 830d71832eSTristan Schmelcher static char * __init choose_tempdir(void) 840d71832eSTristan Schmelcher { 850d71832eSTristan Schmelcher static const char * const vars[] = { 860d71832eSTristan Schmelcher "TMPDIR", 870d71832eSTristan Schmelcher "TMP", 880d71832eSTristan Schmelcher "TEMP", 890d71832eSTristan Schmelcher NULL 900d71832eSTristan Schmelcher }; 910d71832eSTristan Schmelcher static const char fallback_dir[] = "/tmp"; 920d71832eSTristan Schmelcher static const char * const tmpfs_dirs[] = { 930d71832eSTristan Schmelcher "/dev/shm", 940d71832eSTristan Schmelcher fallback_dir, 950d71832eSTristan Schmelcher NULL 960d71832eSTristan Schmelcher }; 970d71832eSTristan Schmelcher int i; 980d71832eSTristan Schmelcher const char *dir; 990d71832eSTristan Schmelcher 100d3878bb8SMasami Hiramatsu os_info("Checking environment variables for a tempdir..."); 1010d71832eSTristan Schmelcher for (i = 0; vars[i]; i++) { 1020d71832eSTristan Schmelcher dir = getenv(vars[i]); 1030d71832eSTristan Schmelcher if ((dir != NULL) && (*dir != '\0')) { 104d3878bb8SMasami Hiramatsu os_info("%s\n", dir); 1050d71832eSTristan Schmelcher if (check_tmpfs(dir) >= 0) 1060d71832eSTristan Schmelcher goto done; 1070d71832eSTristan Schmelcher else 1080d71832eSTristan Schmelcher goto warn; 1090d71832eSTristan Schmelcher } 1100d71832eSTristan Schmelcher } 111d3878bb8SMasami Hiramatsu os_info("none found\n"); 1120d71832eSTristan Schmelcher 1130d71832eSTristan Schmelcher for (i = 0; tmpfs_dirs[i]; i++) { 1140d71832eSTristan Schmelcher dir = tmpfs_dirs[i]; 1150d71832eSTristan Schmelcher if (check_tmpfs(dir) >= 0) 1160d71832eSTristan Schmelcher goto done; 117966a082fSRob Landley } 118966a082fSRob Landley 1190d71832eSTristan Schmelcher dir = fallback_dir; 1200d71832eSTristan Schmelcher warn: 1210936d4f3SMasami Hiramatsu os_warn("Warning: tempdir %s is not on tmpfs\n", dir); 1220d71832eSTristan Schmelcher done: 1230d71832eSTristan Schmelcher /* Make a copy since getenv results may not remain valid forever. */ 1240d71832eSTristan Schmelcher return strdup(dir); 1250d71832eSTristan Schmelcher } 1260d71832eSTristan Schmelcher 1270d71832eSTristan Schmelcher /* 1280d71832eSTristan Schmelcher * Create an unlinked tempfile in a suitable tempdir. template must be the 1290d71832eSTristan Schmelcher * basename part of the template with a leading '/'. 1300d71832eSTristan Schmelcher */ 1310d71832eSTristan Schmelcher static int __init make_tempfile(const char *template) 1320f80bc85SJeff Dike { 13387276f72SPaolo 'Blaisorblade' Giarrusso char *tempname; 1340f80bc85SJeff Dike int fd; 1350f80bc85SJeff Dike 1360d71832eSTristan Schmelcher if (tempdir == NULL) { 1370d71832eSTristan Schmelcher tempdir = choose_tempdir(); 1380d71832eSTristan Schmelcher if (tempdir == NULL) { 1390936d4f3SMasami Hiramatsu os_warn("Failed to choose tempdir: %s\n", 1400d71832eSTristan Schmelcher strerror(errno)); 1410d71832eSTristan Schmelcher return -1; 1420d71832eSTristan Schmelcher } 1430d71832eSTristan Schmelcher } 1440d71832eSTristan Schmelcher 1453e46b253SMickaël Salaün #ifdef O_TMPFILE 1463e46b253SMickaël Salaün fd = open(tempdir, O_CLOEXEC | O_RDWR | O_EXCL | O_TMPFILE, 0700); 1473e46b253SMickaël Salaün /* 1483e46b253SMickaël Salaün * If the running system does not support O_TMPFILE flag then retry 1493e46b253SMickaël Salaün * without it. 1503e46b253SMickaël Salaün */ 1513e46b253SMickaël Salaün if (fd != -1 || (errno != EINVAL && errno != EISDIR && 1523e46b253SMickaël Salaün errno != EOPNOTSUPP)) 1533e46b253SMickaël Salaün return fd; 1543e46b253SMickaël Salaün #endif 1553e46b253SMickaël Salaün 1560d71832eSTristan Schmelcher tempname = malloc(strlen(tempdir) + strlen(template) + 1); 15711a7ac23SJim Meyering if (tempname == NULL) 15811a7ac23SJim Meyering return -1; 15987276f72SPaolo 'Blaisorblade' Giarrusso 1600f80bc85SJeff Dike strcpy(tempname, tempdir); 1610d71832eSTristan Schmelcher strcat(tempname, template); 1620f80bc85SJeff Dike fd = mkstemp(tempname); 1630f80bc85SJeff Dike if (fd < 0) { 1640936d4f3SMasami Hiramatsu os_warn("open - cannot create %s: %s\n", tempname, 1650f80bc85SJeff Dike strerror(errno)); 16687276f72SPaolo 'Blaisorblade' Giarrusso goto out; 1670f80bc85SJeff Dike } 1680d71832eSTristan Schmelcher if (unlink(tempname) < 0) { 1690f80bc85SJeff Dike perror("unlink"); 1702a6d0ac1SDavidlohr Bueso goto close; 1710f80bc85SJeff Dike } 17287276f72SPaolo 'Blaisorblade' Giarrusso free(tempname); 17381999a01SJeff Dike return fd; 1742a6d0ac1SDavidlohr Bueso close: 1752a6d0ac1SDavidlohr Bueso close(fd); 17687276f72SPaolo 'Blaisorblade' Giarrusso out: 17787276f72SPaolo 'Blaisorblade' Giarrusso free(tempname); 17887276f72SPaolo 'Blaisorblade' Giarrusso return -1; 1790f80bc85SJeff Dike } 1800f80bc85SJeff Dike 1810d71832eSTristan Schmelcher #define TEMPNAME_TEMPLATE "/vm_file-XXXXXX" 1820f80bc85SJeff Dike 1835134d8feSJeff Dike static int __init create_tmp_file(unsigned long long len) 1840f80bc85SJeff Dike { 1850f80bc85SJeff Dike int fd, err; 1860f80bc85SJeff Dike char zero; 1870f80bc85SJeff Dike 1880d71832eSTristan Schmelcher fd = make_tempfile(TEMPNAME_TEMPLATE); 1895134d8feSJeff Dike if (fd < 0) 1900f80bc85SJeff Dike exit(1); 1910f80bc85SJeff Dike 1925134d8feSJeff Dike /* 1935134d8feSJeff Dike * Seek to len - 1 because writing a character there will 194190f4939SJeff Dike * increase the file size by one byte, to the desired length. 195190f4939SJeff Dike */ 196190f4939SJeff Dike if (lseek64(fd, len - 1, SEEK_SET) < 0) { 197512b6fb1SJeff Dike perror("lseek64"); 1980f80bc85SJeff Dike exit(1); 1990f80bc85SJeff Dike } 2000f80bc85SJeff Dike 2010f80bc85SJeff Dike zero = 0; 2020f80bc85SJeff Dike 203a61f334fSJeff Dike err = write(fd, &zero, 1); 2040f80bc85SJeff Dike if (err != 1) { 205a61f334fSJeff Dike perror("write"); 2060f80bc85SJeff Dike exit(1); 2070f80bc85SJeff Dike } 2080f80bc85SJeff Dike 20981999a01SJeff Dike return fd; 2100f80bc85SJeff Dike } 2110f80bc85SJeff Dike 21236e45463SJeff Dike int __init create_mem_file(unsigned long long len) 2130f80bc85SJeff Dike { 2140f80bc85SJeff Dike int err, fd; 2150f80bc85SJeff Dike 21602dea087SJeff Dike fd = create_tmp_file(len); 2170f80bc85SJeff Dike 218512b6fb1SJeff Dike err = os_set_exec_close(fd); 2190f80bc85SJeff Dike if (err < 0) { 2200f80bc85SJeff Dike errno = -err; 2210f80bc85SJeff Dike perror("exec_close"); 2220f80bc85SJeff Dike } 22381999a01SJeff Dike return fd; 2240f80bc85SJeff Dike } 225966a082fSRob Landley 22636e45463SJeff Dike void __init check_tmpexec(void) 227966a082fSRob Landley { 228966a082fSRob Landley void *addr; 229966a082fSRob Landley int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE); 230966a082fSRob Landley 231966a082fSRob Landley addr = mmap(NULL, UM_KERN_PAGE_SIZE, 232966a082fSRob Landley PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); 233d3878bb8SMasami Hiramatsu os_info("Checking PROT_EXEC mmap in %s...", tempdir); 234966a082fSRob Landley if (addr == MAP_FAILED) { 235966a082fSRob Landley err = errno; 2360936d4f3SMasami Hiramatsu os_warn("%s\n", strerror(err)); 237c9a3072dSWANG Cong close(fd); 238966a082fSRob Landley if (err == EPERM) 2390936d4f3SMasami Hiramatsu os_warn("%s must be not mounted noexec\n", tempdir); 240966a082fSRob Landley exit(1); 241966a082fSRob Landley } 242d3878bb8SMasami Hiramatsu os_info("OK\n"); 243966a082fSRob Landley munmap(addr, UM_KERN_PAGE_SIZE); 244966a082fSRob Landley 245966a082fSRob Landley close(fd); 246966a082fSRob Landley } 247