Line data Source code
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 : */
28 0 : void kasan_map_memory(void *start, size_t len)
29 : {
30 0 : 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 0 : os_info("Couldn't allocate shadow memory: %s\n.",
37 0 : strerror(errno));
38 0 : exit(1);
39 : }
40 0 : }
41 :
42 : /* Set by make_tempfile() during early boot. */
43 : static char *tempdir = NULL;
44 :
45 : /* Check if dir is on tmpfs. Return 0 if yes, -1 if no or error. */
46 2 : static int __init check_tmpfs(const char *dir)
47 : {
48 : struct statfs st;
49 :
50 2 : os_info("Checking if %s is on tmpfs...", dir);
51 2 : if (statfs(dir, &st) < 0) {
52 0 : os_info("%s\n", strerror(errno));
53 2 : } else if (st.f_type != TMPFS_MAGIC) {
54 0 : os_info("no\n");
55 : } else {
56 2 : os_info("OK\n");
57 2 : 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 : */
69 2 : static 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 2 : os_info("Checking environment variables for a tempdir...");
87 8 : for (i = 0; vars[i]; i++) {
88 6 : dir = getenv(vars[i]);
89 6 : if ((dir != NULL) && (*dir != '\0')) {
90 0 : os_info("%s\n", dir);
91 0 : if (check_tmpfs(dir) >= 0)
92 : goto done;
93 : else
94 : goto warn;
95 : }
96 : }
97 2 : os_info("none found\n");
98 :
99 2 : for (i = 0; tmpfs_dirs[i]; i++) {
100 2 : dir = tmpfs_dirs[i];
101 2 : if (check_tmpfs(dir) >= 0)
102 : goto done;
103 : }
104 :
105 : dir = fallback_dir;
106 : warn:
107 0 : os_warn("Warning: tempdir %s is not on tmpfs\n", dir);
108 : done:
109 : /* Make a copy since getenv results may not remain valid forever. */
110 2 : 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 : */
117 4 : static int __init make_tempfile(const char *template)
118 : {
119 : char *tempname;
120 : int fd;
121 :
122 4 : if (tempdir == NULL) {
123 2 : tempdir = choose_tempdir();
124 2 : if (tempdir == NULL) {
125 0 : os_warn("Failed to choose tempdir: %s\n",
126 0 : strerror(errno));
127 0 : return -1;
128 : }
129 : }
130 :
131 : #ifdef O_TMPFILE
132 4 : 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 4 : if (fd != -1 || (errno != EINVAL && errno != EISDIR &&
138 : errno != EOPNOTSUPP))
139 : return fd;
140 : #endif
141 :
142 0 : tempname = malloc(strlen(tempdir) + strlen(template) + 1);
143 0 : if (tempname == NULL)
144 : return -1;
145 :
146 0 : strcpy(tempname, tempdir);
147 0 : strcat(tempname, template);
148 0 : fd = mkstemp(tempname);
149 0 : if (fd < 0) {
150 0 : os_warn("open - cannot create %s: %s\n", tempname,
151 : strerror(errno));
152 0 : goto out;
153 : }
154 0 : if (unlink(tempname) < 0) {
155 0 : perror("unlink");
156 : goto close;
157 : }
158 0 : free(tempname);
159 0 : return fd;
160 : close:
161 0 : close(fd);
162 : out:
163 0 : free(tempname);
164 0 : return -1;
165 : }
166 :
167 : #define TEMPNAME_TEMPLATE "/vm_file-XXXXXX"
168 :
169 4 : static int __init create_tmp_file(unsigned long long len)
170 : {
171 : int fd, err;
172 : char zero;
173 :
174 4 : fd = make_tempfile(TEMPNAME_TEMPLATE);
175 4 : if (fd < 0)
176 0 : 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 4 : if (lseek64(fd, len - 1, SEEK_SET) < 0) {
183 0 : perror("lseek64");
184 0 : exit(1);
185 : }
186 :
187 4 : zero = 0;
188 :
189 4 : err = write(fd, &zero, 1);
190 4 : if (err != 1) {
191 0 : perror("write");
192 0 : exit(1);
193 : }
194 :
195 4 : return fd;
196 : }
197 :
198 2 : int __init create_mem_file(unsigned long long len)
199 : {
200 : int err, fd;
201 :
202 2 : fd = create_tmp_file(len);
203 :
204 2 : err = os_set_exec_close(fd);
205 2 : if (err < 0) {
206 0 : errno = -err;
207 0 : perror("exec_close");
208 : }
209 2 : return fd;
210 : }
211 :
212 2 : void __init check_tmpexec(void)
213 : {
214 : void *addr;
215 2 : int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE);
216 :
217 2 : addr = mmap(NULL, UM_KERN_PAGE_SIZE,
218 : PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0);
219 2 : os_info("Checking PROT_EXEC mmap in %s...", tempdir);
220 2 : if (addr == MAP_FAILED) {
221 0 : err = errno;
222 0 : os_warn("%s\n", strerror(err));
223 0 : close(fd);
224 0 : if (err == EPERM)
225 0 : os_warn("%s must be not mounted noexec\n", tempdir);
226 0 : exit(1);
227 : }
228 2 : os_info("OK\n");
229 2 : munmap(addr, UM_KERN_PAGE_SIZE);
230 :
231 2 : close(fd);
232 2 : }
|