diff options
Diffstat (limited to 'lib/dfsan/dfsan.cc')
-rw-r--r-- | lib/dfsan/dfsan.cc | 119 |
1 files changed, 113 insertions, 6 deletions
diff --git a/lib/dfsan/dfsan.cc b/lib/dfsan/dfsan.cc index 72d05c68604b..941edc5eba7a 100644 --- a/lib/dfsan/dfsan.cc +++ b/lib/dfsan/dfsan.cc @@ -63,17 +63,50 @@ SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL dfsan_label __dfsan_arg_tls[64]; // account for the double byte representation of shadow labels and move the // address into the shadow memory range. See the function shadow_for below. +// On Linux/MIPS64, memory is laid out as follows: +// +// +--------------------+ 0x10000000000 (top of memory) +// | application memory | +// +--------------------+ 0xF000008000 (kAppAddr) +// | | +// | unused | +// | | +// +--------------------+ 0x2200000000 (kUnusedAddr) +// | union table | +// +--------------------+ 0x2000000000 (kUnionTableAddr) +// | shadow memory | +// +--------------------+ 0x0000010000 (kShadowAddr) +// | reserved by kernel | +// +--------------------+ 0x0000000000 + typedef atomic_dfsan_label dfsan_union_table_t[kNumLabels][kNumLabels]; +#if defined(__x86_64__) static const uptr kShadowAddr = 0x10000; static const uptr kUnionTableAddr = 0x200000000000; static const uptr kUnusedAddr = kUnionTableAddr + sizeof(dfsan_union_table_t); static const uptr kAppAddr = 0x700000008000; +#elif defined(__mips64) +static const uptr kShadowAddr = 0x10000; +static const uptr kUnionTableAddr = 0x2000000000; +static const uptr kUnusedAddr = kUnionTableAddr + sizeof(dfsan_union_table_t); +static const uptr kAppAddr = 0xF000008000; +#else +# error "DFSan not supported for this platform!" +#endif static atomic_dfsan_label *union_table(dfsan_label l1, dfsan_label l2) { return &(*(dfsan_union_table_t *) kUnionTableAddr)[l1][l2]; } +// Checks we do not run out of labels. +static void dfsan_check_label(dfsan_label label) { + if (label == kInitializingLabel) { + Report("FATAL: DataFlowSanitizer: out of labels\n"); + Die(); + } +} + // Resolves the union of two unequal labels. Nonequality is a precondition for // this function (the instrumentation pass inlines the equality test). extern "C" SANITIZER_INTERFACE_ATTRIBUTE @@ -106,7 +139,7 @@ dfsan_label __dfsan_union(dfsan_label l1, dfsan_label l2) { } else { label = atomic_fetch_add(&__dfsan_last_label, 1, memory_order_relaxed) + 1; - CHECK_NE(label, kInitializingLabel); + dfsan_check_label(label); __dfsan_label_info[label].l1 = l1; __dfsan_label_info[label].l2 = l2; } @@ -147,6 +180,15 @@ extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __dfsan_nonzero_label() { Report("WARNING: DataFlowSanitizer: saw nonzero label\n"); } +// Indirect call to an uninstrumented vararg function. We don't have a way of +// handling these at the moment. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void +__dfsan_vararg_wrapper(const char *fname) { + Report("FATAL: DataFlowSanitizer: unsupported indirect call to vararg " + "function %s\n", fname); + Die(); +} + // Like __dfsan_union, but for use from the client or custom functions. Hence // the equality comparison is done here before calling __dfsan_union. SANITIZER_INTERFACE_ATTRIBUTE dfsan_label @@ -160,7 +202,7 @@ extern "C" SANITIZER_INTERFACE_ATTRIBUTE dfsan_label dfsan_create_label(const char *desc, void *userdata) { dfsan_label label = atomic_fetch_add(&__dfsan_last_label, 1, memory_order_relaxed) + 1; - CHECK_NE(label, kInitializingLabel); + dfsan_check_label(label); __dfsan_label_info[label].l1 = __dfsan_label_info[label].l2 = 0; __dfsan_label_info[label].desc = desc; __dfsan_label_info[label].userdata = userdata; @@ -169,8 +211,20 @@ dfsan_label dfsan_create_label(const char *desc, void *userdata) { extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __dfsan_set_label(dfsan_label label, void *addr, uptr size) { - for (dfsan_label *labelp = shadow_for(addr); size != 0; --size, ++labelp) + for (dfsan_label *labelp = shadow_for(addr); size != 0; --size, ++labelp) { + // Don't write the label if it is already the value we need it to be. + // In a program where most addresses are not labeled, it is common that + // a page of shadow memory is entirely zeroed. The Linux copy-on-write + // implementation will share all of the zeroed pages, making a copy of a + // page when any value is written. The un-sharing will happen even if + // the value written does not change the value in memory. Avoiding the + // write when both |label| and |*labelp| are zero dramatically reduces + // the amount of real memory used by large programs. + if (label == *labelp) + continue; + *labelp = label; + } } SANITIZER_INTERFACE_ATTRIBUTE @@ -230,12 +284,58 @@ dfsan_has_label_with_desc(dfsan_label label, const char *desc) { } } +extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr +dfsan_get_label_count(void) { + dfsan_label max_label_allocated = + atomic_load(&__dfsan_last_label, memory_order_relaxed); + + return static_cast<uptr>(max_label_allocated); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void +dfsan_dump_labels(int fd) { + dfsan_label last_label = + atomic_load(&__dfsan_last_label, memory_order_relaxed); + + for (uptr l = 1; l <= last_label; ++l) { + char buf[64]; + internal_snprintf(buf, sizeof(buf), "%u %u %u ", l, + __dfsan_label_info[l].l1, __dfsan_label_info[l].l2); + internal_write(fd, buf, internal_strlen(buf)); + if (__dfsan_label_info[l].l1 == 0 && __dfsan_label_info[l].desc) { + internal_write(fd, __dfsan_label_info[l].desc, + internal_strlen(__dfsan_label_info[l].desc)); + } + internal_write(fd, "\n", 1); + } +} + static void InitializeFlags(Flags &f, const char *env) { f.warn_unimplemented = true; f.warn_nonzero_labels = false; + f.strict_data_dependencies = true; + f.dump_labels_at_exit = ""; + + ParseFlag(env, &f.warn_unimplemented, "warn_unimplemented", ""); + ParseFlag(env, &f.warn_nonzero_labels, "warn_nonzero_labels", ""); + ParseFlag(env, &f.strict_data_dependencies, "strict_data_dependencies", ""); + ParseFlag(env, &f.dump_labels_at_exit, "dump_labels_at_exit", ""); +} + +static void dfsan_fini() { + if (internal_strcmp(flags().dump_labels_at_exit, "") != 0) { + fd_t fd = OpenFile(flags().dump_labels_at_exit, true /* write */); + if (fd == kInvalidFd) { + Report("WARNING: DataFlowSanitizer: unable to open output file %s\n", + flags().dump_labels_at_exit); + return; + } - ParseFlag(env, &f.warn_unimplemented, "warn_unimplemented"); - ParseFlag(env, &f.warn_nonzero_labels, "warn_nonzero_labels"); + Report("INFO: DataFlowSanitizer: dumping labels to %s\n", + flags().dump_labels_at_exit); + dfsan_dump_labels(fd); + internal_close(fd); + } } #ifdef DFSAN_NOLIBC @@ -257,9 +357,16 @@ static void dfsan_init(int argc, char **argv, char **envp) { InitializeFlags(flags(), GetEnv("DFSAN_OPTIONS")); InitializeInterceptors(); + + // Register the fini callback to run when the program terminates successfully + // or it is killed by the runtime. + Atexit(dfsan_fini); + SetDieCallback(dfsan_fini); + + __dfsan_label_info[kInitializingLabel].desc = "<init label>"; } -#ifndef DFSAN_NOLIBC +#if !defined(DFSAN_NOLIBC) && SANITIZER_CAN_USE_PREINIT_ARRAY __attribute__((section(".preinit_array"), used)) static void (*dfsan_init_ptr)(int, char **, char **) = dfsan_init; #endif |