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 */
kasan_map_memory(void * start,size_t len)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. */
check_tmpfs(const char * dir)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 */
choose_tempdir(void)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 */
make_tempfile(const char * template)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
create_tmp_file(unsigned long long len)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
create_mem_file(unsigned long long len)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
check_tmpexec(void)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