summaryrefslogtreecommitdiff
path: root/sanitizers.c
diff options
context:
space:
mode:
authorRobert Swiecki <robert@swiecki.net>2017-06-01 13:25:56 +0200
committerRobert Swiecki <robert@swiecki.net>2017-06-01 13:25:56 +0200
commitec7b84536194ae6f3484ac6d28eb8e67cc5ab059 (patch)
treea1b9c4c110ec3882011a9427b0b3d955c0c9fe87 /sanitizers.c
parenta375f4b69fc1dadced11ea6796edde116ae9898b (diff)
downloadhonggfuzz-ec7b84536194ae6f3484ac6d28eb8e67cc5ab059.tar.gz
Move sanitizer modules out of libcommon
Diffstat (limited to 'sanitizers.c')
-rw-r--r--sanitizers.c237
1 files changed, 237 insertions, 0 deletions
diff --git a/sanitizers.c b/sanitizers.c
new file mode 100644
index 00000000..1534a72e
--- /dev/null
+++ b/sanitizers.c
@@ -0,0 +1,237 @@
+#include "libcommon/common.h"
+#include "sanitizers.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "libcommon/files.h"
+#include "libcommon/log.h"
+#include "libcommon/util.h"
+
+/* Stringify */
+#define XSTR(x) #x
+#define STR(x) XSTR(x)
+
+/*
+ * All clang sanitizers, except ASan, can be activated for target binaries
+ * with or without the matching runtime library (libcompiler_rt). If runtime
+ * libraries are included in target fuzzing environment, we can benefit from the
+ * various Die() callbacks and abort/exit logic manipulation. However, some
+ * setups (e.g. Android production ARM/ARM64 devices) enable sanitizers, such as
+ * UBSan, without the runtime libraries. As such, their default ftrap is activated
+ * which is for most cases a SIGABRT. For these cases end-user needs to enable
+ * SIGABRT monitoring flag, otherwise these crashes will be missed.
+ *
+ * Normally SIGABRT is not a wanted signal to monitor for Android, since it produces
+ * lots of useless crashes due to way Android process termination hacks work. As
+ * a result the sanitizer's 'abort_on_error' flag cannot be utilized since it
+ * invokes abort() internally. In order to not lose crashes a custom exitcode can
+ * be registered and monitored. Since exitcode is a global flag, it's assumed
+ * that target is compiled with only one sanitizer type enabled at a time.
+ *
+ * For cases where clang runtime library linking is not an option, SIGABRT should
+ * be monitored even for noisy targets, such as the Android OS, since no viable
+ * alternative exists.
+ *
+ * There might be cases where ASan instrumented targets crash while generating
+ * reports for detected errors (inside __asan_report_error() proc). Under such
+ * scenarios target fails to exit or SIGABRT (AsanDie() proc) as defined in
+ * ASAN_OPTIONS flags, leaving garbage logs. An attempt is made to parse such
+ * logs for cases where enough data are written to identify potentially missed
+ * crashes. If ASan internal error results into a SIGSEGV being raised, it
+ * will get caught from ptrace API, handling the discovered ASan internal crash.
+ */
+
+/* 'log_path' output directory for sanitizer reports */
+#define kSANLOGDIR "log_path="
+
+/* 'coverage_dir' output directory for coverage data files is set dynamically */
+#define kSANCOVDIR "coverage_dir="
+
+/* Raise SIGABRT on error or continue with exitcode logic */
+#define kABORT_ENABLED "abort_on_error=1"
+#define kABORT_DISABLED "abort_on_error=0"
+
+/*
+ * Common sanitizer flags
+ *
+ * symbolize: Disable symbolication since it changes logs (which are parsed) format
+ */
+#define kSAN_COMMON "symbolize=0"
+
+/* --{ ASan }-- */
+/*
+ *Sanitizer specific flags (notice that if enabled 'abort_on_error' has priority
+ * over exitcode')
+ */
+#define kASAN_COMMON_OPTS "allow_user_segv_handler=1:"\
+ "handle_segv=0:"\
+ "allocator_may_return_null=1:"\
+ kSAN_COMMON":exitcode=" STR(HF_SAN_EXIT_CODE)
+/* Platform specific flags */
+#if defined(__ANDROID__)
+/*
+ * start_deactivated: Enable on Android to reduce memory usage (useful when not all
+ * target's DSOs are compiled with sanitizer enabled
+ */
+#define kASAN_OPTS kASAN_COMMON_OPTS":start_deactivated=1"
+#else
+#define kASAN_OPTS kASAN_COMMON_OPTS
+#endif
+
+/* --{ UBSan }-- */
+#define kUBSAN_OPTS kSAN_COMMON":exitcode=" STR(HF_SAN_EXIT_CODE)
+
+/* --{ MSan }-- */
+#define kMSAN_OPTS kSAN_COMMON":exit_code=" STR(HF_SAN_EXIT_CODE) ":"\
+ "wrap_signals=0:print_stats=1"
+
+/* If no sanitzer support was requested, simply make it use abort() on errors */
+#define kSAN_REGULAR "abort_on_error=1:handle_segv=0:allocator_may_return_null=1:symbolize=0:detect_leaks=0"
+
+/*
+ * If the program ends with a signal that ASan does not handle (or can not
+ * handle at all, like SIGKILL), coverage data will be lost. This is a big
+ * problem on Android, where SIGKILL is a normal way of evicting applications
+ * from memory. With 'coverage_direct=1' coverage data is written to a
+ * memory-mapped file as soon as it collected. Non-Android targets can disable
+ * coverage direct when more coverage data collection methods are implemented.
+ */
+#define kSAN_COV_OPTS "coverage=1:coverage_direct=1"
+
+static bool sanitizers_Regular(void)
+{
+ if (setenv("ASAN_OPTIONS", kSAN_REGULAR, 1) == -1) {
+ PLOG_E("setenv(ASAN_OPTIONS=%s", kSAN_REGULAR);
+ return false;
+ }
+ if (setenv("MSAN_OPTIONS", kSAN_REGULAR, 1) == -1) {
+ PLOG_E("setenv(MSAN_OPTIONS=%s", kSAN_REGULAR);
+ return false;
+ }
+ if (setenv("UBSAN_OPTIONS", kSAN_REGULAR, 1) == -1) {
+ PLOG_E("setenv(UBSAN_OPTIONS=%s", kSAN_REGULAR);
+ return false;
+ }
+ return true;
+}
+
+bool sanitizers_Init(honggfuzz_t * hfuzz)
+{
+ if (hfuzz->linux.pid > 0) {
+ return true;
+ }
+
+ if (hfuzz->enableSanitizers == false) {
+ return sanitizers_Regular();
+ }
+
+ /* Set sanitizer flags once to avoid performance overhead per worker spawn */
+ size_t flagsSz = 0;
+
+ /* Larger constant combination + 2 dynamic paths */
+ size_t bufSz =
+ sizeof(kASAN_OPTS) + 1 + sizeof(kABORT_ENABLED) + 1 + sizeof(kSANLOGDIR) + PATH_MAX + 1 +
+ sizeof(kSANCOVDIR) + PATH_MAX + 1;
+ char *san_opts = util_Calloc(bufSz);
+ defer {
+ free(san_opts);
+ };
+
+ char *abortFlag;
+ if (hfuzz->monitorSIGABRT) {
+ abortFlag = kABORT_ENABLED;
+ } else {
+ abortFlag = kABORT_DISABLED;
+ }
+
+ /* Address Sanitizer (ASan) */
+ if (hfuzz->useSanCov) {
+ snprintf(san_opts, bufSz, "%s:%s:%s:%s%s/%s:%s%s/%s", kASAN_OPTS, abortFlag, kSAN_COV_OPTS,
+ kSANCOVDIR, hfuzz->workDir, _HF_SANCOV_DIR, kSANLOGDIR, hfuzz->workDir,
+ kLOGPREFIX);
+ } else {
+ snprintf(san_opts, bufSz, "%s:%s:%s%s/%s", kASAN_OPTS, abortFlag, kSANLOGDIR,
+ hfuzz->workDir, kLOGPREFIX);
+ }
+
+ flagsSz = strlen(san_opts) + 1;
+ hfuzz->sanOpts.asanOpts = util_Calloc(flagsSz);
+ memcpy(hfuzz->sanOpts.asanOpts, san_opts, flagsSz);
+ LOG_D("ASAN_OPTIONS=%s", hfuzz->sanOpts.asanOpts);
+
+ /* Undefined Behavior Sanitizer (UBSan) */
+ memset(san_opts, 0, bufSz);
+ if (hfuzz->useSanCov) {
+ snprintf(san_opts, bufSz, "%s:%s:%s:%s%s/%s:%s%s/%s", kUBSAN_OPTS, abortFlag, kSAN_COV_OPTS,
+ kSANCOVDIR, hfuzz->workDir, _HF_SANCOV_DIR, kSANLOGDIR, hfuzz->workDir,
+ kLOGPREFIX);
+ } else {
+ snprintf(san_opts, bufSz, "%s:%s:%s%s/%s", kUBSAN_OPTS, abortFlag, kSANLOGDIR,
+ hfuzz->workDir, kLOGPREFIX);
+ }
+
+ flagsSz = strlen(san_opts) + 1;
+ hfuzz->sanOpts.ubsanOpts = util_Calloc(flagsSz);
+ memcpy(hfuzz->sanOpts.ubsanOpts, san_opts, flagsSz);
+ LOG_D("UBSAN_OPTIONS=%s", hfuzz->sanOpts.ubsanOpts);
+
+ /* Memory Sanitizer (MSan) */
+ memset(san_opts, 0, bufSz);
+ const char *msan_reports_flag = "report_umrs=0";
+ if (hfuzz->msanReportUMRS) {
+ msan_reports_flag = "report_umrs=1";
+ }
+
+ if (hfuzz->useSanCov) {
+ snprintf(san_opts, bufSz, "%s:%s:%s:%s:%s%s/%s:%s%s/%s", kMSAN_OPTS, abortFlag,
+ msan_reports_flag, kSAN_COV_OPTS, kSANCOVDIR, hfuzz->workDir, _HF_SANCOV_DIR,
+ kSANLOGDIR, hfuzz->workDir, kLOGPREFIX);
+ } else {
+ snprintf(san_opts, bufSz, "%s:%s:%s:%s%s/%s", kMSAN_OPTS, abortFlag, msan_reports_flag,
+ kSANLOGDIR, hfuzz->workDir, kLOGPREFIX);
+ }
+
+ flagsSz = strlen(san_opts) + 1;
+ hfuzz->sanOpts.msanOpts = util_Calloc(flagsSz);
+ memcpy(hfuzz->sanOpts.msanOpts, san_opts, flagsSz);
+ LOG_D("MSAN_OPTIONS=%s", hfuzz->sanOpts.msanOpts);
+
+ return true;
+}
+
+bool sanitizers_prepareExecve(honggfuzz_t * hfuzz)
+{
+ /* Address Sanitizer (ASan) */
+ if (hfuzz->sanOpts.asanOpts) {
+ if (setenv("ASAN_OPTIONS", hfuzz->sanOpts.asanOpts, 1) == -1) {
+ PLOG_E("setenv(ASAN_OPTIONS) failed");
+ return false;
+ }
+ }
+
+ /* Memory Sanitizer (MSan) */
+ if (hfuzz->sanOpts.msanOpts) {
+ if (setenv("MSAN_OPTIONS", hfuzz->sanOpts.msanOpts, 1) == -1) {
+ PLOG_E("setenv(MSAN_OPTIONS) failed");
+ return false;
+ }
+ }
+
+ /* Undefined Behavior Sanitizer (UBSan) */
+ if (hfuzz->sanOpts.ubsanOpts) {
+ if (setenv("UBSAN_OPTIONS", hfuzz->sanOpts.ubsanOpts, 1) == -1) {
+ PLOG_E("setenv(UBSAN_OPTIONS) failed");
+ return false;
+ }
+ }
+
+ return true;
+}