• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * honggfuzz - architecture dependent code (LINUX)
4  * -----------------------------------------
5  *
6  * Author: Robert Swiecki <swiecki@google.com>
7  *
8  * Copyright 2010-2018 by Google Inc. All Rights Reserved.
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License"); you may
11  * not use this file except in compliance with the License. You may obtain
12  * a copy of the License at
13  *
14  * http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
19  * implied. See the License for the specific language governing
20  * permissions and limitations under the License.
21  *
22  */
23 
24 #include "arch.h"
25 
26 #include <ctype.h>
27 #include <dlfcn.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <inttypes.h>
31 #include <locale.h>
32 #include <setjmp.h>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/cdefs.h>
38 #include <sys/personality.h>
39 #include <sys/prctl.h>
40 #include <sys/syscall.h>
41 #include <sys/time.h>
42 #include <sys/types.h>
43 #include <sys/user.h>
44 #include <sys/utsname.h>
45 #include <sys/wait.h>
46 #include <time.h>
47 #include <unistd.h>
48 
49 #include "fuzz.h"
50 #include "libhfcommon/common.h"
51 #include "libhfcommon/files.h"
52 #include "libhfcommon/log.h"
53 #include "libhfcommon/ns.h"
54 #include "libhfcommon/util.h"
55 #include "linux/perf.h"
56 #include "linux/trace.h"
57 #include "sanitizers.h"
58 #include "subproc.h"
59 
60 static uint8_t arch_clone_stack[128 * 1024] __attribute__((aligned(__BIGGEST_ALIGNMENT__)));
61 static __thread jmp_buf env;
62 
63 HF_ATTR_NO_SANITIZE_ADDRESS
64 HF_ATTR_NO_SANITIZE_MEMORY
65 __attribute__((noreturn)) static int arch_cloneFunc(void* arg HF_ATTR_UNUSED) {
66     longjmp(env, 1);
67 }
68 
69 /* Avoid problem with caching of PID/TID in glibc */
70 static pid_t arch_clone(uintptr_t flags) {
71     if (flags & CLONE_VM) {
72         LOG_E("Cannot use clone(flags & CLONE_VM)");
73         return -1;
74     }
75 
76     if (setjmp(env) == 0) {
77         void* stack_mid = &arch_clone_stack[sizeof(arch_clone_stack) / 2];
78         /* Parent */
79         return clone(arch_cloneFunc, stack_mid, flags, NULL, NULL, NULL);
80     }
81     /* Child */
82     return 0;
83 }
84 
85 pid_t arch_fork(run_t* run) {
86     pid_t pid = run->global->linux.useClone ? arch_clone(CLONE_UNTRACED | SIGCHLD) : fork();
87     if (pid == -1) {
88         return pid;
89     }
90     if (pid == 0) {
91         logMutexReset();
92         if (prctl(PR_SET_PDEATHSIG, (unsigned long)SIGKILL, 0UL, 0UL, 0UL) == -1) {
93             PLOG_W("prctl(PR_SET_PDEATHSIG, SIGKILL)");
94         }
95         return pid;
96     }
97     return pid;
98 }
99 
100 bool arch_launchChild(run_t* run) {
101     if ((run->global->linux.cloneFlags & CLONE_NEWNET) && (nsIfaceUp("lo") == false)) {
102         LOG_W("Cannot bring interface 'lo' up");
103     }
104 
105     /*
106      * Make it attach-able by ptrace()
107      */
108     if (prctl(PR_SET_DUMPABLE, 1UL, 0UL, 0UL, 0UL) == -1) {
109         PLOG_E("prctl(PR_SET_DUMPABLE, 1)");
110         return false;
111     }
112 
113     /*
114      * Kill a process which corrupts its own heap (with ABRT)
115      */
116     if (setenv("MALLOC_CHECK_", "7", 0) == -1) {
117         PLOG_E("setenv(MALLOC_CHECK_=7) failed");
118         return false;
119     }
120     if (setenv("MALLOC_PERTURB_", "85", 0) == -1) {
121         PLOG_E("setenv(MALLOC_PERTURB_=85) failed");
122         return false;
123     }
124 
125     /*
126      * Disable ASLR:
127      * This might fail in Docker, as Docker blocks __NR_personality. Consequently
128      * it's just a debug warning
129      */
130     if (run->global->linux.disableRandomization &&
131         syscall(__NR_personality, ADDR_NO_RANDOMIZE) == -1) {
132         PLOG_D("personality(ADDR_NO_RANDOMIZE) failed");
133     }
134 
135 #define ARGS_MAX 512
136     const char* args[ARGS_MAX + 2];
137     char argData[PATH_MAX];
138 
139     char inputFile[PATH_MAX];
140     snprintf(inputFile, sizeof(inputFile), "/dev/fd/%d", run->dynamicFileCopyFd);
141 
142     int x = 0;
143     for (x = 0; x < ARGS_MAX && x < run->global->exe.argc; x++) {
144         if (run->global->exe.persistent || run->global->exe.fuzzStdin) {
145             args[x] = run->global->exe.cmdline[x];
146         } else if (!strcmp(run->global->exe.cmdline[x], _HF_FILE_PLACEHOLDER)) {
147             args[x] = inputFile;
148         } else if (strstr(run->global->exe.cmdline[x], _HF_FILE_PLACEHOLDER)) {
149             const char* off = strstr(run->global->exe.cmdline[x], _HF_FILE_PLACEHOLDER);
150             snprintf(argData, sizeof(argData), "%.*s%s", (int)(off - run->global->exe.cmdline[x]),
151                 run->global->exe.cmdline[x], inputFile);
152             args[x] = argData;
153         } else {
154             args[x] = run->global->exe.cmdline[x];
155         }
156     }
157     args[x++] = NULL;
158 
159     LOG_D("Launching '%s' on file '%s'", args[0],
160         run->global->exe.persistent ? "PERSISTENT_MODE" : inputFile);
161 
162     /* alarms persist across execve(), so disable it here */
163     alarm(0);
164 
165     /* Wait for the ptrace to attach now */
166     if (kill(syscall(__NR_getpid), SIGSTOP) == -1) {
167         LOG_F("Couldn't stop itself");
168     }
169 #if defined(__NR_execveat)
170     syscall(__NR_execveat, run->global->linux.exeFd, "", args, environ, AT_EMPTY_PATH);
171 #endif /* defined__NR_execveat) */
172     execve(args[0], (char* const*)args, environ);
173     int errno_cpy = errno;
174     alarm(1);
175 
176     LOG_E("execve('%s', fd=%d): %s", args[0], run->global->linux.exeFd, strerror(errno_cpy));
177 
178     return false;
179 }
180 
181 static bool arch_attachToNewPid(run_t* run) {
182     if (!arch_traceAttach(run)) {
183         LOG_W("arch_traceAttach(pid=%d) failed", run->pid);
184         return false;
185     }
186 
187     return true;
188 }
189 
190 void arch_prepareParentAfterFork(run_t* run) {
191     /* Parent */
192     if (run->global->exe.persistent) {
193         const struct f_owner_ex fown = {
194             .type = F_OWNER_TID,
195             .pid = syscall(__NR_gettid),
196         };
197         if (fcntl(run->persistentSock, F_SETOWN_EX, &fown)) {
198             PLOG_F("fcntl(%d, F_SETOWN_EX)", run->persistentSock);
199         }
200         if (fcntl(run->persistentSock, F_SETSIG, SIGIO) == -1) {
201             PLOG_F("fcntl(%d, F_SETSIG, SIGIO)", run->persistentSock);
202         }
203         if (fcntl(run->persistentSock, F_SETFL, O_ASYNC) == -1) {
204             PLOG_F("fcntl(%d, F_SETFL, O_ASYNC)", run->persistentSock);
205         }
206     }
207 
208     arch_perfClose(run);
209     if (!arch_perfOpen(run)) {
210         LOG_F("Couldn't open perf event for pid=%d", (int)run->pid);
211     }
212     if (!arch_attachToNewPid(run)) {
213         LOG_F("Couldn't attach to pid=%d", (int)run->pid);
214     }
215 }
216 
217 void arch_prepareParent(run_t* run) {
218     if (!arch_perfEnable(run)) {
219         LOG_F("Couldn't enable perf counters for pid=%d", (int)run->pid);
220     }
221 }
222 
223 static bool arch_checkWait(run_t* run) {
224     /* All queued wait events must be tested when SIGCHLD was delivered */
225     for (;;) {
226         int status;
227         pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, __WALL | __WNOTHREAD | WNOHANG));
228         if (pid == 0) {
229             return false;
230         }
231         if (pid == -1 && errno == ECHILD) {
232             LOG_D("No more processes to track");
233             return true;
234         }
235         if (pid == -1) {
236             PLOG_F("waitpid() failed");
237         }
238 
239         char statusStr[4096];
240         LOG_D("pid=%d returned with status: %s", pid,
241             subproc_StatusToStr(status, statusStr, sizeof(statusStr)));
242 
243         arch_traceAnalyze(run, status, pid);
244 
245         if (pid == run->pid && (WIFEXITED(status) || WIFSIGNALED(status))) {
246             if (run->global->exe.persistent) {
247                 if (!fuzz_isTerminating()) {
248                     LOG_W("Persistent mode: pid=%d exited with status: %s", (int)run->pid,
249                         subproc_StatusToStr(status, statusStr, sizeof(statusStr)));
250                 }
251             }
252             return true;
253         }
254     }
255 }
256 
257 void arch_reapChild(run_t* run) {
258     for (;;) {
259         if (subproc_persistentModeStateMachine(run)) {
260             break;
261         }
262 
263         subproc_checkTimeLimit(run);
264         subproc_checkTermination(run);
265 
266         const struct timespec ts = {
267             .tv_sec = 0ULL,
268             .tv_nsec = (1000ULL * 1000ULL * 250ULL),
269         };
270         /* Return with SIGIO, SIGCHLD and with SIGUSR1 */
271         int sig = sigtimedwait(&run->global->exe.waitSigSet, NULL, &ts /* 0.25s */);
272         if (sig == -1 && (errno != EAGAIN && errno != EINTR)) {
273             PLOG_F("sigwaitinfo(SIGIO|SIGCHLD|SIGUSR1)");
274         }
275 
276         if (arch_checkWait(run)) {
277             run->pid = 0;
278             break;
279         }
280         if (run->global->socketFuzzer.enabled) {
281             // Do not wait for new events
282             break;
283         }
284     }
285     if (run->global->sanitizer.enable) {
286         char crashReport[PATH_MAX];
287         snprintf(crashReport, sizeof(crashReport), "%s/%s.%d", run->global->io.workDir, kLOGPREFIX,
288             run->pid);
289         if (files_exists(crashReport)) {
290             if (run->backtrace) {
291                 unlink(crashReport);
292             } else {
293                 LOG_W("Un-handled ASan report due to compiler-rt internal error - retry with '%s'",
294                     crashReport);
295                 /* Try to parse report file */
296                 arch_traceExitAnalyze(run, run->pid);
297             }
298         }
299     }
300 
301     arch_perfAnalyze(run);
302 }
303 
304 bool arch_archInit(honggfuzz_t* hfuzz) {
305     /* Make %'d work */
306     setlocale(LC_NUMERIC, "en_US.UTF-8");
307 
308     if (access(hfuzz->exe.cmdline[0], X_OK) == -1) {
309         PLOG_E("File '%s' doesn't seem to be executable", hfuzz->exe.cmdline[0]);
310         return false;
311     }
312     if ((hfuzz->linux.exeFd =
313                 TEMP_FAILURE_RETRY(open(hfuzz->exe.cmdline[0], O_RDONLY | O_CLOEXEC))) == -1) {
314         PLOG_E("Cannot open the executable binary: %s)", hfuzz->exe.cmdline[0]);
315         return false;
316     }
317 
318     for (;;) {
319         __attribute__((weak)) const char* gnu_get_libc_version(void);
320         if (!gnu_get_libc_version) {
321             LOG_W("Unknown libc implementation. Using clone() instead of fork()");
322             break;
323         }
324         const char* gversion = gnu_get_libc_version();
325         int major, minor;
326         if (sscanf(gversion, "%d.%d", &major, &minor) != 2) {
327             LOG_W("Unknown glibc version:'%s'. Using clone() instead of fork()", gversion);
328             break;
329         }
330         if ((major < 2) || (major == 2 && minor < 23)) {
331             LOG_W("Your glibc version:'%s' will most likely result in malloc()-related "
332                   "deadlocks. Min. version 2.24 (Or, Ubuntu's 2.23-0ubuntu6) suggested. "
333                   "See https://sourceware.org/bugzilla/show_bug.cgi?id=19431 for explanation. "
334                   "Using clone() instead of fork()",
335                 gversion);
336             break;
337         }
338         LOG_D("Glibc version:'%s', OK", gversion);
339         hfuzz->linux.useClone = false;
340         break;
341     }
342 
343     if (hfuzz->feedback.dynFileMethod != _HF_DYNFILE_NONE) {
344         unsigned long major = 0, minor = 0;
345         char* p = NULL;
346 
347         /*
348          * Check that Linux kernel is compatible
349          *
350          * Compatibility list:
351          *  1) Perf exclude_callchain_kernel requires kernel >= 3.7
352          *     TODO: Runtime logic to disable it for unsupported kernels
353          *           if it doesn't affect perf counters processing
354          *  2) If 'PERF_TYPE_HARDWARE' is not supported by kernel, ENOENT
355          *     is returned from perf_event_open(). Unfortunately, no reliable
356          *     way to detect it here. libperf exports some list functions,
357          *     although small guarantees it's installed. Maybe a more targeted
358          *     message at perf_event_open() error handling will help.
359          *  3) Intel's PT and new Intel BTS format require kernel >= 4.1
360          */
361         unsigned long checkMajor = 3, checkMinor = 7;
362         if ((hfuzz->feedback.dynFileMethod & _HF_DYNFILE_BTS_EDGE) ||
363             (hfuzz->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK)) {
364             checkMajor = 4;
365             checkMinor = 1;
366         }
367 
368         struct utsname uts;
369         if (uname(&uts) == -1) {
370             PLOG_F("uname() failed");
371             return false;
372         }
373 
374         p = uts.release;
375         major = strtoul(p, &p, 10);
376         if (*p++ != '.') {
377             LOG_F("Unsupported kernel version (%s)", uts.release);
378             return false;
379         }
380 
381         minor = strtoul(p, &p, 10);
382         if ((major < checkMajor) || ((major == checkMajor) && (minor < checkMinor))) {
383             LOG_E("Kernel version '%s' not supporting chosen perf method", uts.release);
384             return false;
385         }
386 
387         if (!arch_perfInit(hfuzz)) {
388             return false;
389         }
390     }
391 #if defined(__ANDROID__) && defined(__arm__) && defined(OPENSSL_ARMCAP_ABI)
392     /*
393      * For ARM kernels running Android API <= 21, if fuzzing target links to
394      * libcrypto (OpenSSL), OPENSSL_cpuid_setup initialization is triggering a
395      * SIGILL/ILLOPC at armv7_tick() due to  "mrrc p15, #1, r0, r1, c14)" instruction.
396      * Setups using BoringSSL (API >= 22) are not affected.
397      */
398     if (setenv("OPENSSL_armcap", OPENSSL_ARMCAP_ABI, 1) == -1) {
399         PLOG_E("setenv(OPENSSL_armcap) failed");
400         return false;
401     }
402 #endif
403 
404     /* Updates the important signal array based on input args */
405     arch_traceSignalsInit(hfuzz);
406 
407     /*
408      * If sanitizer fuzzing enabled and SIGABRT is monitored (abort_on_error=1),
409      * increase number of major frames, since top 7-9 frames will be occupied
410      * with sanitizer runtime library & libc symbols
411      */
412     if (hfuzz->sanitizer.enable && hfuzz->cfg.monitorSIGABRT) {
413         hfuzz->linux.numMajorFrames = 14;
414     }
415 
416     if (hfuzz->linux.cloneFlags && unshare(hfuzz->linux.cloneFlags) == -1) {
417         LOG_E("unshare(%tx)", hfuzz->linux.cloneFlags);
418         return false;
419     }
420 
421     return true;
422 }
423 
424 bool arch_archThreadInit(run_t* run) {
425     run->linux.perfMmapBuf = NULL;
426     run->linux.perfMmapAux = NULL;
427     run->linux.cpuInstrFd = -1;
428     run->linux.cpuBranchFd = -1;
429     run->linux.cpuIptBtsFd = -1;
430 
431     if (prctl(PR_SET_CHILD_SUBREAPER, 1UL, 0UL, 0UL, 0UL) == -1) {
432         PLOG_W("prctl(PR_SET_CHILD_SUBREAPER, 1)");
433     }
434 
435     sigset_t ss;
436     sigemptyset(&ss);
437     sigaddset(&ss, SIGUSR1);
438     if (pthread_sigmask(SIG_BLOCK, &ss, NULL) != 0) {
439         PLOG_W("Couldn't block SIGUSR1");
440         return false;
441     }
442 
443     return true;
444 }
445