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 | void 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. */ |
43 | static char *tempdir = NULL; |
44 | |
45 | /* Check if dir is on tmpfs. Return 0 if yes, -1 if no or error. */ |
46 | static 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 | */ |
69 | 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 | 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; |
106 | warn: |
107 | 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 | 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 | static 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; |
160 | close: |
161 | close(fd); |
162 | out: |
163 | free(tempname); |
164 | return -1; |
165 | } |
166 | |
167 | #define TEMPNAME_TEMPLATE "/vm_file-XXXXXX" |
168 | |
169 | static 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 | |
198 | int __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 | |
212 | void __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 | |