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