diff --git a/compiler-rt/include/sanitizer/dfsan_interface.h b/compiler-rt/include/sanitizer/dfsan_interface.h --- a/compiler-rt/include/sanitizer/dfsan_interface.h +++ b/compiler-rt/include/sanitizer/dfsan_interface.h @@ -123,6 +123,43 @@ /// on, or the address is not labeled, it prints nothing. void dfsan_print_origin_trace(const void *addr, const char *description); +/// Prints the origin trace of the label at the address \p addr to a +/// pre-allocated output buffer. If origin tracking is not on, or the address is +/// not labeled, it prints nothing. +/// +/// Typical usage: +/// \code +/// char kDescription[] = "..."; +/// char buf[1024]; +/// dfsan_sprint_origin_trace(&tainted_var, kDescription, buf, sizeof(buf)); +/// \endcode +/// +/// Typical usage that handles truncation: +/// \code +/// char buf[1024]; +/// int len = dfsan_sprint_origin_trace(&var, nullptr, buf, sizeof(buf)); +/// +/// if (len < sizeof(buf)) { +/// ProcessOriginTrace(tmpbuf); +/// else { +/// char *tmpbuf = new char[len + 1]; +/// dfsan_sprint_origin_trace(&var, nullptr, tmpbuf, len + 1); +/// ProcessOriginTrace(tmpbuf); +/// delete[] tmpbuf; +/// } +/// \endcode +/// +/// \param addr The tainted memory address whose origin we are printing. +/// \param description A description printed at the beginning of the trace. +/// \param [out] out_buf The output buffer to write the results to. +/// \param out_buf_size The size of \p out_buf. +/// +/// \returns The number of symbols that should have been written to \p out_buf +/// (not including trailing null byte '\0'). Thus, the string is truncated iff +/// return value is not less than \p out_buf_size. +size_t dfsan_sprint_origin_trace(const void *addr, const char *description, + char *out_buf, size_t out_buf_size); + /// Retrieves the very first origin associated with the data at the given /// address. dfsan_origin dfsan_get_init_origin(const void *addr); diff --git a/compiler-rt/lib/dfsan/dfsan.cpp b/compiler-rt/lib/dfsan/dfsan.cpp --- a/compiler-rt/lib/dfsan/dfsan.cpp +++ b/compiler-rt/lib/dfsan/dfsan.cpp @@ -842,49 +842,123 @@ const char *Origin() const { return Magenta(); } }; -extern "C" SANITIZER_INTERFACE_ATTRIBUTE void dfsan_print_origin_trace( - const void *addr, const char *description) { +namespace { + +void PrintNoOriginTrackingWarning() { Decorator d; + Printf( + " %sDFSan: origin tracking is not enabled. Did you specify the " + "-dfsan-track-origins=1 option?%s\n", + d.Warning(), d.Default()); +} - if (!__dfsan_get_track_origins()) { - Printf( - " %sDFSan: origin tracking is not enabled. Did you specify the " - "-dfsan-track-origins=1 option?%s\n", - d.Warning(), d.Default()); - return; - } +void PrintNoTaintWarning(const void *address) { + Decorator d; + Printf(" %sDFSan: no tainted value at %x%s\n", d.Warning(), address, + d.Default()); +} + +void PrintInvalidOriginWarning(dfsan_label label, const void *address) { + Decorator d; + Printf( + " %sTaint value 0x%x (at %p) has invalid origin tracking. This can " + "be a DFSan bug.%s\n", + d.Warning(), label, address, d.Default()); +} + +bool PrintOriginTraceToStr(const void *addr, const char *description, + InternalScopedString *out) { + CHECK(out); + CHECK(__dfsan_get_track_origins()); + Decorator d; const dfsan_label label = *__dfsan::shadow_for(addr); - if (!label) { - Printf(" %sDFSan: no tainted value at %x%s\n", d.Warning(), addr, - d.Default()); - return; - } + CHECK(label); const dfsan_origin origin = *__dfsan::origin_for(addr); - Printf(" %sTaint value 0x%x (at %p) origin tracking (%s)%s\n", d.Origin(), - label, addr, description ? description : "", d.Default()); + out->append(" %sTaint value 0x%x (at %p) origin tracking (%s)%s\n", + d.Origin(), label, addr, description ? description : "", + d.Default()); + Origin o = Origin::FromRawId(origin); bool found = false; + while (o.isChainedOrigin()) { StackTrace stack; dfsan_origin origin_id = o.raw_id(); o = o.getNextChainedOrigin(&stack); if (o.isChainedOrigin()) - Printf(" %sOrigin value: 0x%x, Taint value was stored to memory at%s\n", - d.Origin(), origin_id, d.Default()); + out->append( + " %sOrigin value: 0x%x, Taint value was stored to memory at%s\n", + d.Origin(), origin_id, d.Default()); else - Printf(" %sOrigin value: 0x%x, Taint value was created at%s\n", - d.Origin(), origin_id, d.Default()); - stack.Print(); + out->append(" %sOrigin value: 0x%x, Taint value was created at%s\n", + d.Origin(), origin_id, d.Default()); + + // Includes a trailing newline, so no need to add it again. + stack.PrintTo(out); found = true; } - if (!found) - Printf( - " %sTaint value 0x%x (at %p) has invalid origin tracking. This can " - "be a DFSan bug.%s\n", - d.Warning(), label, addr, d.Default()); + + return found; +} + +} // namespace + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void dfsan_print_origin_trace( + const void *addr, const char *description) { + if (!__dfsan_get_track_origins()) { + PrintNoOriginTrackingWarning(); + return; + } + + const dfsan_label label = *__dfsan::shadow_for(addr); + if (!label) { + PrintNoTaintWarning(addr); + return; + } + + InternalScopedString trace; + bool success = PrintOriginTraceToStr(addr, description, &trace); + + if (trace.length()) + Printf("%s", trace.data()); + + if (!success) + PrintInvalidOriginWarning(label, addr); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE size_t +dfsan_sprint_origin_trace(const void *addr, const char *description, + char *out_buf, size_t out_buf_size) { + CHECK(out_buf); + + if (!__dfsan_get_track_origins()) { + PrintNoOriginTrackingWarning(); + return 0; + } + + const dfsan_label label = *__dfsan::shadow_for(addr); + if (!label) { + PrintNoTaintWarning(addr); + return 0; + } + + InternalScopedString trace; + bool success = PrintOriginTraceToStr(addr, description, &trace); + + if (!success) { + PrintInvalidOriginWarning(label, addr); + return 0; + } + + if (out_buf_size) { + internal_strncpy(out_buf, trace.data(), out_buf_size - 1); + out_buf[out_buf_size - 1] = '\0'; + } + + return trace.length(); } extern "C" SANITIZER_INTERFACE_ATTRIBUTE dfsan_origin diff --git a/compiler-rt/lib/dfsan/done_abilist.txt b/compiler-rt/lib/dfsan/done_abilist.txt --- a/compiler-rt/lib/dfsan/done_abilist.txt +++ b/compiler-rt/lib/dfsan/done_abilist.txt @@ -30,6 +30,8 @@ fun:dfsan_flush=discard fun:dfsan_print_origin_trace=uninstrumented fun:dfsan_print_origin_trace=discard +fun:dfsan_sprint_origin_trace=uninstrumented +fun:dfsan_sprint_origin_trace=discard fun:dfsan_get_origin=uninstrumented fun:dfsan_get_origin=custom fun:dfsan_get_init_origin=uninstrumented diff --git a/compiler-rt/test/dfsan/origin_stack_trace.c b/compiler-rt/test/dfsan/origin_stack_trace.c --- a/compiler-rt/test/dfsan/origin_stack_trace.c +++ b/compiler-rt/test/dfsan/origin_stack_trace.c @@ -1,14 +1,16 @@ // RUN: %clang_dfsan -gmlt -mllvm -dfsan-track-origins=1 -mllvm -dfsan-fast-16-labels=true %s -o %t && \ // RUN: %run %t >%t.out 2>&1 -// RUN: FileCheck %s --check-prefix=CHECK < %t.out +// RUN: FileCheck %s < %t.out // // RUN: %clang_dfsan -gmlt -mllvm -dfsan-track-origins=1 -mllvm -dfsan-fast-16-labels=true -mllvm -dfsan-instrument-with-call-threshold=0 %s -o %t && \ // RUN: %run %t >%t.out 2>&1 -// RUN: FileCheck %s --check-prefix=CHECK < %t.out +// RUN: FileCheck %s < %t.out // // REQUIRES: x86_64-target-arch #include +#include +#include #define NOINLINE __attribute__((noinline)) @@ -32,13 +34,67 @@ baz(8, &a, sizeof(a)); int c = foo(a, b); dfsan_print_origin_trace(&c, NULL); -} + // CHECK: Taint value 0x1 {{.*}} origin tracking () + // CHECK: Origin value: {{.*}}, Taint value was stored to memory at + // CHECK: #0 {{.*}} in main {{.*}}origin_stack_trace.c:[[@LINE-4]] + + // CHECK: Origin value: {{.*}}, Taint value was created at + // CHECK: #0 {{.*}} in dfs$bar {{.*}}origin_stack_trace.c:[[@LINE-19]] + // CHECK-COUNT-8: #{{[0-9]+}} {{.*}} in dfs$bar {{.*}}origin_stack_trace.c:[[@LINE-22]] + // CHECK: #9 {{.*}} in dfs$baz {{.*}}origin_stack_trace.c:[[@LINE-16]] + + char buf[3000]; + size_t length = dfsan_sprint_origin_trace(&c, NULL, buf, sizeof(buf)); + + printf("==OUTPUT==\n\n%s==EOS==\n", buf); + // CHECK: ==OUTPUT== + // CHECK: Taint value 0x1 {{.*}} origin tracking () + // CHECK: Origin value: {{.*}}, Taint value was stored to memory at + // CHECK: #0 {{.*}} in main {{.*}}origin_stack_trace.c:[[@LINE-18]] + + // CHECK: Origin value: {{.*}}, Taint value was created at + // CHECK: #0 {{.*}} in dfs$bar {{.*}}origin_stack_trace.c:[[@LINE-33]] + // CHECK-COUNT-8: #{{[0-9]+}} {{.*}} in dfs$bar {{.*}}origin_stack_trace.c:[[@LINE-36]] + // CHECK: #9 {{.*}} in dfs$baz {{.*}}origin_stack_trace.c:[[@LINE-30]] + // CHECK: ==EOS== + + char tinybuf[18]; + size_t same_length = dfsan_sprint_origin_trace(&c, NULL, tinybuf, sizeof(tinybuf)); + + printf("==TRUNCATED OUTPUT==\n\n%s==EOS==\n", tinybuf); + // CHECK: ==TRUNCATED OUTPUT== + // CHECK: Taint value 0x1==EOS== -// CHECK: Taint value 0x1 {{.*}} origin tracking () -// CHECK: Origin value: {{.*}}, Taint value was stored to memory at -// CHECK: #0 {{.*}} in main {{.*}}origin_stack_trace.c:[[@LINE-6]] + printf("Returned length: %zu\n", length); + printf("Actual length: %zu\n", strlen(buf)); + printf("Returned length with truncation: %zu\n", same_length); -// CHECK: Origin value: {{.*}}, Taint value was created at -// CHECK: #0 {{.*}} in dfs$bar {{.*}}origin_stack_trace.c:[[@LINE-21]] -// CHECK-COUNT-8: #{{[0-9]+}} {{.*}} in dfs$bar {{.*}}origin_stack_trace.c:[[@LINE-24]] -// CHECK: #9 {{.*}} in dfs$baz {{.*}}origin_stack_trace.c:[[@LINE-18]] + // CHECK: Returned length: [[#LEN:]] + // CHECK: Actual length: [[#LEN]] + // CHECK: Returned length with truncation: [[#LEN]] + + size_t length_with_desc = dfsan_sprint_origin_trace(&c, "DESCRIPTION", buf, sizeof(buf)); + + printf("==OUTPUT==\n\n%s==EOS==\n", buf); + // CHECK: ==OUTPUT== + // CHECK: Taint value 0x1 {{.*}} origin tracking (DESCRIPTION) + // CHECK: Origin value: {{.*}}, Taint value was stored to memory at + // CHECK: #0 {{.*}} in main {{.*}}origin_stack_trace.c:[[@LINE-47]] + + // CHECK: Origin value: {{.*}}, Taint value was created at + // CHECK: #0 {{.*}} in dfs$bar {{.*}}origin_stack_trace.c:[[@LINE-62]] + // CHECK-COUNT-8: #{{[0-9]+}} {{.*}} in dfs$bar {{.*}}origin_stack_trace.c:[[@LINE-65]] + // CHECK: #9 {{.*}} in dfs$baz {{.*}}origin_stack_trace.c:[[@LINE-59]] + // CHECK: ==EOS== + + printf("Returned length: %zu\n", length_with_desc); + // COMM: Message length is increased by 11: the length of "DESCRIPTION". + // CHECK: Returned length: [[#LEN + 11]] + + buf[0] = '\0'; + length = dfsan_sprint_origin_trace(&c, NULL, buf, 0); + printf("Output=\"%s\"\n", buf); + printf("Returned length: %zu\n", length); + // CHECK: Output="" + // CHECK: Returned length: [[#LEN]] +}