xref: /linux/arch/um/os-Linux/mem.c (revision 32e8eaf263d9be014ba1970444f745682fa9c6c0)
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 	}
425b301409SPatricia Alfonso }
435b301409SPatricia Alfonso 
440d71832eSTristan Schmelcher /* Set by make_tempfile() during early boot. */
45*32e8eaf2SBenjamin Berg char *tempdir = NULL;
460f80bc85SJeff Dike 
470d71832eSTristan Schmelcher /* Check if dir is on tmpfs. Return 0 if yes, -1 if no or error. */
480d71832eSTristan Schmelcher static int __init check_tmpfs(const char *dir)
490f80bc85SJeff Dike {
500d71832eSTristan Schmelcher 	struct statfs st;
510f80bc85SJeff Dike 
52d3878bb8SMasami Hiramatsu 	os_info("Checking if %s is on tmpfs...", dir);
530d71832eSTristan Schmelcher 	if (statfs(dir, &st) < 0) {
54d3878bb8SMasami Hiramatsu 		os_info("%s\n", strerror(errno));
550d71832eSTristan Schmelcher 	} else if (st.f_type != TMPFS_MAGIC) {
56d3878bb8SMasami Hiramatsu 		os_info("no\n");
5774735341STristan Schmelcher 	} else {
58d3878bb8SMasami Hiramatsu 		os_info("OK\n");
590d71832eSTristan Schmelcher 		return 0;
6074735341STristan Schmelcher 	}
610d71832eSTristan Schmelcher 	return -1;
6274735341STristan Schmelcher }
6374735341STristan Schmelcher 
640d71832eSTristan Schmelcher /*
650d71832eSTristan Schmelcher  * Choose the tempdir to use. We want something on tmpfs so that our memory is
660d71832eSTristan Schmelcher  * not subject to the host's vm.dirty_ratio. If a tempdir is specified in the
670d71832eSTristan Schmelcher  * environment, we use that even if it's not on tmpfs, but we warn the user.
680d71832eSTristan Schmelcher  * Otherwise, we try common tmpfs locations, and if no tmpfs directory is found
690d71832eSTristan Schmelcher  * then we fall back to /tmp.
700d71832eSTristan Schmelcher  */
710d71832eSTristan Schmelcher static char * __init choose_tempdir(void)
720d71832eSTristan Schmelcher {
730d71832eSTristan Schmelcher 	static const char * const vars[] = {
740d71832eSTristan Schmelcher 		"TMPDIR",
750d71832eSTristan Schmelcher 		"TMP",
760d71832eSTristan Schmelcher 		"TEMP",
770d71832eSTristan Schmelcher 		NULL
780d71832eSTristan Schmelcher 	};
790d71832eSTristan Schmelcher 	static const char fallback_dir[] = "/tmp";
800d71832eSTristan Schmelcher 	static const char * const tmpfs_dirs[] = {
810d71832eSTristan Schmelcher 		"/dev/shm",
820d71832eSTristan Schmelcher 		fallback_dir,
830d71832eSTristan Schmelcher 		NULL
840d71832eSTristan Schmelcher 	};
850d71832eSTristan Schmelcher 	int i;
860d71832eSTristan Schmelcher 	const char *dir;
870d71832eSTristan Schmelcher 
88d3878bb8SMasami Hiramatsu 	os_info("Checking environment variables for a tempdir...");
890d71832eSTristan Schmelcher 	for (i = 0; vars[i]; i++) {
900d71832eSTristan Schmelcher 		dir = getenv(vars[i]);
910d71832eSTristan Schmelcher 		if ((dir != NULL) && (*dir != '\0')) {
92d3878bb8SMasami Hiramatsu 			os_info("%s\n", dir);
930d71832eSTristan Schmelcher 			if (check_tmpfs(dir) >= 0)
940d71832eSTristan Schmelcher 				goto done;
950d71832eSTristan Schmelcher 			else
960d71832eSTristan Schmelcher 				goto warn;
970d71832eSTristan Schmelcher 		}
980d71832eSTristan Schmelcher 	}
99d3878bb8SMasami Hiramatsu 	os_info("none found\n");
1000d71832eSTristan Schmelcher 
1010d71832eSTristan Schmelcher 	for (i = 0; tmpfs_dirs[i]; i++) {
1020d71832eSTristan Schmelcher 		dir = tmpfs_dirs[i];
1030d71832eSTristan Schmelcher 		if (check_tmpfs(dir) >= 0)
1040d71832eSTristan Schmelcher 			goto done;
105966a082fSRob Landley 	}
106966a082fSRob Landley 
1070d71832eSTristan Schmelcher 	dir = fallback_dir;
1080d71832eSTristan Schmelcher warn:
1090936d4f3SMasami Hiramatsu 	os_warn("Warning: tempdir %s is not on tmpfs\n", dir);
1100d71832eSTristan Schmelcher done:
1110d71832eSTristan Schmelcher 	/* Make a copy since getenv results may not remain valid forever. */
1120d71832eSTristan Schmelcher 	return strdup(dir);
1130d71832eSTristan Schmelcher }
1140d71832eSTristan Schmelcher 
1150d71832eSTristan Schmelcher /*
1160d71832eSTristan Schmelcher  * Create an unlinked tempfile in a suitable tempdir. template must be the
1170d71832eSTristan Schmelcher  * basename part of the template with a leading '/'.
1180d71832eSTristan Schmelcher  */
1190d71832eSTristan Schmelcher static int __init make_tempfile(const char *template)
1200f80bc85SJeff Dike {
12187276f72SPaolo 'Blaisorblade' Giarrusso 	char *tempname;
1220f80bc85SJeff Dike 	int fd;
1230f80bc85SJeff Dike 
1240d71832eSTristan Schmelcher 	if (tempdir == NULL) {
1250d71832eSTristan Schmelcher 		tempdir = choose_tempdir();
1260d71832eSTristan Schmelcher 		if (tempdir == NULL) {
1270936d4f3SMasami Hiramatsu 			os_warn("Failed to choose tempdir: %s\n",
1280d71832eSTristan Schmelcher 				strerror(errno));
1290d71832eSTristan Schmelcher 			return -1;
1300d71832eSTristan Schmelcher 		}
1310d71832eSTristan Schmelcher 	}
1320d71832eSTristan Schmelcher 
1333e46b253SMickaël Salaün #ifdef O_TMPFILE
1343e46b253SMickaël Salaün 	fd = open(tempdir, O_CLOEXEC | O_RDWR | O_EXCL | O_TMPFILE, 0700);
1353e46b253SMickaël Salaün 	/*
1363e46b253SMickaël Salaün 	 * If the running system does not support O_TMPFILE flag then retry
1373e46b253SMickaël Salaün 	 * without it.
1383e46b253SMickaël Salaün 	 */
1393e46b253SMickaël Salaün 	if (fd != -1 || (errno != EINVAL && errno != EISDIR &&
1403e46b253SMickaël Salaün 			errno != EOPNOTSUPP))
1413e46b253SMickaël Salaün 		return fd;
1423e46b253SMickaël Salaün #endif
1433e46b253SMickaël Salaün 
1440d71832eSTristan Schmelcher 	tempname = malloc(strlen(tempdir) + strlen(template) + 1);
14511a7ac23SJim Meyering 	if (tempname == NULL)
14611a7ac23SJim Meyering 		return -1;
14787276f72SPaolo 'Blaisorblade' Giarrusso 
1480f80bc85SJeff Dike 	strcpy(tempname, tempdir);
1490d71832eSTristan Schmelcher 	strcat(tempname, template);
1500f80bc85SJeff Dike 	fd = mkstemp(tempname);
1510f80bc85SJeff Dike 	if (fd < 0) {
1520936d4f3SMasami Hiramatsu 		os_warn("open - cannot create %s: %s\n", tempname,
1530f80bc85SJeff Dike 			strerror(errno));
15487276f72SPaolo 'Blaisorblade' Giarrusso 		goto out;
1550f80bc85SJeff Dike 	}
1560d71832eSTristan Schmelcher 	if (unlink(tempname) < 0) {
1570f80bc85SJeff Dike 		perror("unlink");
1582a6d0ac1SDavidlohr Bueso 		goto close;
1590f80bc85SJeff Dike 	}
16087276f72SPaolo 'Blaisorblade' Giarrusso 	free(tempname);
16181999a01SJeff Dike 	return fd;
1622a6d0ac1SDavidlohr Bueso close:
1632a6d0ac1SDavidlohr Bueso 	close(fd);
16487276f72SPaolo 'Blaisorblade' Giarrusso out:
16587276f72SPaolo 'Blaisorblade' Giarrusso 	free(tempname);
16687276f72SPaolo 'Blaisorblade' Giarrusso 	return -1;
1670f80bc85SJeff Dike }
1680f80bc85SJeff Dike 
1690d71832eSTristan Schmelcher #define TEMPNAME_TEMPLATE "/vm_file-XXXXXX"
1700f80bc85SJeff Dike 
1715134d8feSJeff Dike static int __init create_tmp_file(unsigned long long len)
1720f80bc85SJeff Dike {
1730f80bc85SJeff Dike 	int fd, err;
1740f80bc85SJeff Dike 	char zero;
1750f80bc85SJeff Dike 
1760d71832eSTristan Schmelcher 	fd = make_tempfile(TEMPNAME_TEMPLATE);
1775134d8feSJeff Dike 	if (fd < 0)
1780f80bc85SJeff Dike 		exit(1);
1790f80bc85SJeff Dike 
1805134d8feSJeff Dike 	/*
1815134d8feSJeff Dike 	 * Seek to len - 1 because writing a character there will
182190f4939SJeff Dike 	 * increase the file size by one byte, to the desired length.
183190f4939SJeff Dike 	 */
184190f4939SJeff Dike 	if (lseek64(fd, len - 1, SEEK_SET) < 0) {
185512b6fb1SJeff Dike 		perror("lseek64");
1860f80bc85SJeff Dike 		exit(1);
1870f80bc85SJeff Dike 	}
1880f80bc85SJeff Dike 
1890f80bc85SJeff Dike 	zero = 0;
1900f80bc85SJeff Dike 
191a61f334fSJeff Dike 	err = write(fd, &zero, 1);
1920f80bc85SJeff Dike 	if (err != 1) {
193a61f334fSJeff Dike 		perror("write");
1940f80bc85SJeff Dike 		exit(1);
1950f80bc85SJeff Dike 	}
1960f80bc85SJeff Dike 
19781999a01SJeff Dike 	return fd;
1980f80bc85SJeff Dike }
1990f80bc85SJeff Dike 
20036e45463SJeff Dike int __init create_mem_file(unsigned long long len)
2010f80bc85SJeff Dike {
2020f80bc85SJeff Dike 	int err, fd;
2030f80bc85SJeff Dike 
20402dea087SJeff Dike 	fd = create_tmp_file(len);
2050f80bc85SJeff Dike 
206512b6fb1SJeff Dike 	err = os_set_exec_close(fd);
2070f80bc85SJeff Dike 	if (err < 0) {
2080f80bc85SJeff Dike 		errno = -err;
2090f80bc85SJeff Dike 		perror("exec_close");
2100f80bc85SJeff Dike 	}
21181999a01SJeff Dike 	return fd;
2120f80bc85SJeff Dike }
213966a082fSRob Landley 
21436e45463SJeff Dike void __init check_tmpexec(void)
215966a082fSRob Landley {
216966a082fSRob Landley 	void *addr;
217966a082fSRob Landley 	int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE);
218966a082fSRob Landley 
219966a082fSRob Landley 	addr = mmap(NULL, UM_KERN_PAGE_SIZE,
220966a082fSRob Landley 		    PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0);
221d3878bb8SMasami Hiramatsu 	os_info("Checking PROT_EXEC mmap in %s...", tempdir);
222966a082fSRob Landley 	if (addr == MAP_FAILED) {
223966a082fSRob Landley 		err = errno;
2240936d4f3SMasami Hiramatsu 		os_warn("%s\n", strerror(err));
225c9a3072dSWANG Cong 		close(fd);
226966a082fSRob Landley 		if (err == EPERM)
2270936d4f3SMasami Hiramatsu 			os_warn("%s must be not mounted noexec\n", tempdir);
228966a082fSRob Landley 		exit(1);
229966a082fSRob Landley 	}
230d3878bb8SMasami Hiramatsu 	os_info("OK\n");
231966a082fSRob Landley 	munmap(addr, UM_KERN_PAGE_SIZE);
232966a082fSRob Landley 
233966a082fSRob Landley 	close(fd);
234966a082fSRob Landley }
235