1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) |
4 | */ |
5 | |
6 | #include <stdio.h> |
7 | #include <stdlib.h> |
8 | #include <stdarg.h> |
9 | #include <unistd.h> |
10 | #include <errno.h> |
11 | #include <fcntl.h> |
12 | #include <sched.h> |
13 | #include <signal.h> |
14 | #include <string.h> |
15 | #include <sys/mman.h> |
16 | #include <sys/stat.h> |
17 | #include <sys/wait.h> |
18 | #include <sys/time.h> |
19 | #include <sys/resource.h> |
20 | #include <asm/unistd.h> |
21 | #include <init.h> |
22 | #include <os.h> |
23 | #include <mem_user.h> |
24 | #include <ptrace_user.h> |
25 | #include <registers.h> |
26 | #include <skas.h> |
27 | |
28 | static void ptrace_child(void) |
29 | { |
30 | int ret; |
31 | /* Calling os_getpid because some libcs cached getpid incorrectly */ |
32 | int pid = os_getpid(), ppid = getppid(); |
33 | int sc_result; |
34 | |
35 | if (change_sig(SIGWINCH, 0) < 0 || |
36 | ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) { |
37 | perror("ptrace" ); |
38 | kill(pid, SIGKILL); |
39 | } |
40 | kill(pid, SIGSTOP); |
41 | |
42 | /* |
43 | * This syscall will be intercepted by the parent. Don't call more than |
44 | * once, please. |
45 | */ |
46 | sc_result = os_getpid(); |
47 | |
48 | if (sc_result == pid) |
49 | /* Nothing modified by the parent, we are running normally. */ |
50 | ret = 1; |
51 | else if (sc_result == ppid) |
52 | /* |
53 | * Expected in check_ptrace and check_sysemu when they succeed |
54 | * in modifying the stack frame |
55 | */ |
56 | ret = 0; |
57 | else |
58 | /* Serious trouble! This could be caused by a bug in host 2.6 |
59 | * SKAS3/2.6 patch before release -V6, together with a bug in |
60 | * the UML code itself. |
61 | */ |
62 | ret = 2; |
63 | |
64 | exit(ret); |
65 | } |
66 | |
67 | static void fatal_perror(const char *str) |
68 | { |
69 | perror(str); |
70 | exit(1); |
71 | } |
72 | |
73 | static void fatal(char *fmt, ...) |
74 | { |
75 | va_list list; |
76 | |
77 | va_start(list, fmt); |
78 | vfprintf(stderr, fmt, list); |
79 | va_end(list); |
80 | |
81 | exit(1); |
82 | } |
83 | |
84 | static void non_fatal(char *fmt, ...) |
85 | { |
86 | va_list list; |
87 | |
88 | va_start(list, fmt); |
89 | vfprintf(stderr, fmt, list); |
90 | va_end(list); |
91 | } |
92 | |
93 | static int start_ptraced_child(void) |
94 | { |
95 | int pid, n, status; |
96 | |
97 | fflush(stdout); |
98 | |
99 | pid = fork(); |
100 | if (pid == 0) |
101 | ptrace_child(); |
102 | else if (pid < 0) |
103 | fatal_perror(str: "start_ptraced_child : fork failed" ); |
104 | |
105 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); |
106 | if (n < 0) |
107 | fatal_perror(str: "check_ptrace : waitpid failed" ); |
108 | if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP)) |
109 | fatal(fmt: "check_ptrace : expected SIGSTOP, got status = %d" , |
110 | status); |
111 | |
112 | return pid; |
113 | } |
114 | |
115 | static void stop_ptraced_child(int pid, int exitcode) |
116 | { |
117 | int status, n; |
118 | |
119 | if (ptrace(PTRACE_CONT, pid, 0, 0) < 0) |
120 | fatal_perror(str: "stop_ptraced_child : ptrace failed" ); |
121 | |
122 | CATCH_EINTR(n = waitpid(pid, &status, 0)); |
123 | if (!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) { |
124 | int exit_with = WEXITSTATUS(status); |
125 | fatal(fmt: "stop_ptraced_child : child exited with exitcode %d, " |
126 | "while expecting %d; status 0x%x\n" , exit_with, |
127 | exitcode, status); |
128 | } |
129 | } |
130 | |
131 | static void __init check_sysemu(void) |
132 | { |
133 | int pid, n, status, count=0; |
134 | |
135 | os_info("Checking syscall emulation for ptrace..." ); |
136 | pid = start_ptraced_child(); |
137 | |
138 | if ((ptrace(PTRACE_SETOPTIONS, pid, 0, |
139 | (void *) PTRACE_O_TRACESYSGOOD) < 0)) |
140 | fatal_perror("check_sysemu: PTRACE_SETOPTIONS failed" ); |
141 | |
142 | while (1) { |
143 | count++; |
144 | if (ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0) |
145 | goto fail; |
146 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); |
147 | if (n < 0) |
148 | fatal_perror("check_sysemu: wait failed" ); |
149 | |
150 | if (WIFSTOPPED(status) && |
151 | (WSTOPSIG(status) == (SIGTRAP|0x80))) { |
152 | if (!count) { |
153 | non_fatal("check_sysemu: SYSEMU_SINGLESTEP " |
154 | "doesn't singlestep" ); |
155 | goto fail; |
156 | } |
157 | n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, |
158 | os_getpid()); |
159 | if (n < 0) |
160 | fatal_perror("check_sysemu : failed to modify " |
161 | "system call return" ); |
162 | break; |
163 | } |
164 | else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP)) |
165 | count++; |
166 | else { |
167 | non_fatal("check_sysemu: expected SIGTRAP or " |
168 | "(SIGTRAP | 0x80), got status = %d\n" , |
169 | status); |
170 | goto fail; |
171 | } |
172 | } |
173 | stop_ptraced_child(pid, 0); |
174 | |
175 | os_info("OK\n" ); |
176 | |
177 | return; |
178 | |
179 | fail: |
180 | stop_ptraced_child(pid, 1); |
181 | fatal("missing\n" ); |
182 | } |
183 | |
184 | static void __init check_ptrace(void) |
185 | { |
186 | int pid, syscall, n, status; |
187 | |
188 | os_info("Checking that ptrace can change system call numbers..." ); |
189 | pid = start_ptraced_child(); |
190 | |
191 | if ((ptrace(PTRACE_SETOPTIONS, pid, 0, |
192 | (void *) PTRACE_O_TRACESYSGOOD) < 0)) |
193 | fatal_perror("check_ptrace: PTRACE_SETOPTIONS failed" ); |
194 | |
195 | while (1) { |
196 | if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) |
197 | fatal_perror("check_ptrace : ptrace failed" ); |
198 | |
199 | CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); |
200 | if (n < 0) |
201 | fatal_perror("check_ptrace : wait failed" ); |
202 | |
203 | if (!WIFSTOPPED(status) || |
204 | (WSTOPSIG(status) != (SIGTRAP | 0x80))) |
205 | fatal("check_ptrace : expected (SIGTRAP|0x80), " |
206 | "got status = %d" , status); |
207 | |
208 | syscall = ptrace(PTRACE_PEEKUSER, pid, PT_SYSCALL_NR_OFFSET, |
209 | 0); |
210 | if (syscall == __NR_getpid) { |
211 | n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET, |
212 | __NR_getppid); |
213 | if (n < 0) |
214 | fatal_perror("check_ptrace : failed to modify " |
215 | "system call" ); |
216 | break; |
217 | } |
218 | } |
219 | stop_ptraced_child(pid, 0); |
220 | os_info("OK\n" ); |
221 | check_sysemu(); |
222 | } |
223 | |
224 | extern void check_tmpexec(void); |
225 | |
226 | static void __init check_coredump_limit(void) |
227 | { |
228 | struct rlimit lim; |
229 | int err = getrlimit(RLIMIT_CORE, &lim); |
230 | |
231 | if (err) { |
232 | perror("Getting core dump limit" ); |
233 | return; |
234 | } |
235 | |
236 | os_info("Core dump limits :\n\tsoft - " ); |
237 | if (lim.rlim_cur == RLIM_INFINITY) |
238 | os_info("NONE\n" ); |
239 | else |
240 | os_info("%llu\n" , (unsigned long long)lim.rlim_cur); |
241 | |
242 | os_info("\thard - " ); |
243 | if (lim.rlim_max == RLIM_INFINITY) |
244 | os_info("NONE\n" ); |
245 | else |
246 | os_info("%llu\n" , (unsigned long long)lim.rlim_max); |
247 | } |
248 | |
249 | void __init get_host_cpu_features( |
250 | void (*flags_helper_func)(char *line), |
251 | void (*cache_helper_func)(char *line)) |
252 | { |
253 | FILE *cpuinfo; |
254 | char *line = NULL; |
255 | size_t len = 0; |
256 | int done_parsing = 0; |
257 | |
258 | cpuinfo = fopen("/proc/cpuinfo" , "r" ); |
259 | if (cpuinfo == NULL) { |
260 | os_info("Failed to get host CPU features\n" ); |
261 | } else { |
262 | while ((getline(&line, &len, cpuinfo)) != -1) { |
263 | if (strstr(line, "flags" )) { |
264 | flags_helper_func(line); |
265 | done_parsing++; |
266 | } |
267 | if (strstr(line, "cache_alignment" )) { |
268 | cache_helper_func(line); |
269 | done_parsing++; |
270 | } |
271 | free(line); |
272 | line = NULL; |
273 | if (done_parsing > 1) |
274 | break; |
275 | } |
276 | fclose(cpuinfo); |
277 | } |
278 | } |
279 | |
280 | |
281 | void __init os_early_checks(void) |
282 | { |
283 | int pid; |
284 | |
285 | /* Print out the core dump limits early */ |
286 | check_coredump_limit(); |
287 | |
288 | check_ptrace(); |
289 | |
290 | /* Need to check this early because mmapping happens before the |
291 | * kernel is running. |
292 | */ |
293 | check_tmpexec(); |
294 | |
295 | pid = start_ptraced_child(); |
296 | if (init_pid_registers(pid)) |
297 | fatal("Failed to initialize default registers" ); |
298 | stop_ptraced_child(pid, 1); |
299 | } |
300 | |
301 | int __init parse_iomem(char *str, int *add) |
302 | { |
303 | struct iomem_region *new; |
304 | struct stat64 buf; |
305 | char *file, *driver; |
306 | int fd, size; |
307 | |
308 | driver = str; |
309 | file = strchr(str,','); |
310 | if (file == NULL) { |
311 | os_warn("parse_iomem : failed to parse iomem\n" ); |
312 | goto out; |
313 | } |
314 | *file = '\0'; |
315 | file++; |
316 | fd = open(file, O_RDWR, 0); |
317 | if (fd < 0) { |
318 | perror("parse_iomem - Couldn't open io file" ); |
319 | goto out; |
320 | } |
321 | |
322 | if (fstat64(fd, &buf) < 0) { |
323 | perror("parse_iomem - cannot stat_fd file" ); |
324 | goto out_close; |
325 | } |
326 | |
327 | new = malloc(sizeof(*new)); |
328 | if (new == NULL) { |
329 | perror("Couldn't allocate iomem_region struct" ); |
330 | goto out_close; |
331 | } |
332 | |
333 | size = (buf.st_size + UM_KERN_PAGE_SIZE) & ~(UM_KERN_PAGE_SIZE - 1); |
334 | |
335 | *new = ((struct iomem_region) { .next = iomem_regions, |
336 | .driver = driver, |
337 | .fd = fd, |
338 | .size = size, |
339 | .phys = 0, |
340 | .virt = 0 }); |
341 | iomem_regions = new; |
342 | iomem_size += new->size + UM_KERN_PAGE_SIZE; |
343 | |
344 | return 0; |
345 | out_close: |
346 | close(fd); |
347 | out: |
348 | return 1; |
349 | } |
350 | |