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
} 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;
}
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;
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;
+ }
+
+ Report("INFO: DataFlowSanitizer: dumping labels to %s\n",
+ flags().dump_labels_at_exit);
+ dfsan_dump_labels(fd);
+ internal_close(fd);
+ }
}
#ifdef DFSAN_NOLIBC
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>";
}
#if !defined(DFSAN_NOLIBC) && SANITIZER_CAN_USE_PREINIT_ARRAY
--- /dev/null
+// RUN: %clang_dfsan -m64 %s -o %t
+// RUN: DFSAN_OPTIONS=dump_labels_at_exit=/dev/stdout %run %t 2>&1 | FileCheck %s
+// RUN: DFSAN_OPTIONS=dump_labels_at_exit=/dev/stdout not %run %t c 2>&1 | FileCheck %s --check-prefix=CHECK-OOL
+// RUN: DFSAN_OPTIONS=dump_labels_at_exit=/dev/stdout not %run %t u 2>&1 | FileCheck %s --check-prefix=CHECK-OOL
+
+// Tests that labels are properly dumped at program termination.
+
+#include <sanitizer/dfsan_interface.h>
+#include <assert.h>
+#include <stdio.h>
+
+int main(int argc, char** argv) {
+ int i = 1;
+ dfsan_label i_label = dfsan_create_label("i", 0);
+ dfsan_set_label(i_label, &i, sizeof(i));
+
+ int j = 2;
+ dfsan_label j_label = dfsan_create_label("j", 0);
+ dfsan_set_label(j_label, &j, sizeof(j));
+
+ int k = 3;
+ dfsan_label k_label = dfsan_create_label("k", 0);
+ dfsan_set_label(k_label, &k, sizeof(k));
+
+ dfsan_label ij_label = dfsan_get_label(i + j);
+ dfsan_label ijk_label = dfsan_get_label(i + j + k);
+
+ fprintf(stderr, "i %d j %d k %d ij %d ijk %d\n", i_label, j_label, k_label,
+ ij_label, ijk_label);
+
+ // CHECK: 1 0 0 i
+ // CHECK: 2 0 0 j
+ // CHECK: 3 0 0 k
+ // CHECK: 4 1 2
+ // CHECK: 5 3 4
+
+ if (argc > 1) {
+ // Exhaust the labels.
+ unsigned long num_labels = 1 << (sizeof(dfsan_label) * 8);
+ for (unsigned long i = ijk_label + 1; i < num_labels - 2; ++i) {
+ dfsan_label l = dfsan_create_label("l", 0);
+ assert(l == i);
+ }
+
+ // Consume the last available label.
+ dfsan_label l = dfsan_union(5, 6);
+ assert(l == num_labels - 2);
+
+ // Try to allocate another label (either explicitly or by unioning two
+ // existing labels), but expect a crash.
+ if (argv[1][0] == 'c') {
+ l = dfsan_create_label("l", 0);
+ } else {
+ l = dfsan_union(6, 7);
+ }
+
+ // CHECK-OOL: FATAL: DataFlowSanitizer: out of labels
+ // CHECK-OOL: 1 0 0 i
+ // CHECK-OOL: 2 0 0 j
+ // CHECK-OOL: 3 0 0 k
+ // CHECK-OOL: 4 1 2
+ // CHECK-OOL: 5 3 4
+ // CHECK-OOL: 6 0 0
+ // CHECK-OOL: 65534 5 6
+ // CHECK-OOL: 65535 0 0 <init label>
+ }
+
+ return 0;
+}