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,22 +842,44 @@ const char *Origin() const { return Magenta(); } }; +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()); +} + +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()); +} + +} // namespace + extern "C" SANITIZER_INTERFACE_ATTRIBUTE void dfsan_print_origin_trace( const void *addr, const char *description) { Decorator d; 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()); + PrintNoOriginTrackingWarning(); return; } const dfsan_label label = *__dfsan::shadow_for(addr); if (!label) { - Printf(" %sDFSan: no tainted value at %x%s\n", d.Warning(), addr, - d.Default()); + PrintNoTaintWarning(addr); return; } @@ -880,11 +902,76 @@ stack.Print(); 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()); + 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) { + Decorator d; + + if (!out_buf || !out_buf_size) + return 0; + + if (!__dfsan_get_track_origins()) { + PrintNoOriginTrackingWarning(); + return 0; + } + + const dfsan_label label = *__dfsan::shadow_for(addr); + if (!label) { + PrintNoTaintWarning(addr); + return 0; + } + + const dfsan_origin origin = *__dfsan::origin_for(addr); + + InternalScopedString result; + size_t needed_length = 0; + + result.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()) + result.append( + " %sOrigin value: 0x%x, Taint value was stored to memory at%s\n", + d.Origin(), origin_id, d.Default()); + else + result.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. + size_t length = stack.PrintTo(out_buf, out_buf_size); + result.append("%s", out_buf); + found = true; + + // If truncation happened, increase total length needed accordingly. + if (length >= out_buf_size) { + size_t num_chars_written = out_buf_size - 1; // for terminating '\0' byte + size_t num_truncated_chars = length - num_chars_written; + needed_length += num_truncated_chars; + } + } + + if (!found) { + PrintInvalidOriginWarning(label, addr); + Die(); + } + + needed_length += result.length(); + internal_strncpy(out_buf, result.data(), out_buf_size - 1); + out_buf[out_buf_size - 1] = '\0'; + return needed_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/lib/sanitizer_common/sanitizer_stacktrace.h b/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h --- a/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h @@ -56,6 +56,12 @@ // Prints a symbolized stacktrace, followed by an empty line. void Print() const; + // Prints a symbolized stacktrace to the output buffer, followed by an empty + // line. Returns the number of symbols that should have been written to buffer + // (not including trailing '\0'). Thus, the string is truncated iff return + // value is not less than "out_buf_size". + uptr PrintTo(char *out_buf, uptr out_buf_size) const; + static bool WillUseFastUnwind(bool request_fast_unwind) { if (!SANITIZER_CAN_FAST_UNWIND) return false; diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cpp --- a/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cpp @@ -18,46 +18,210 @@ namespace __sanitizer { +namespace { + +template +class StackTraceTextProcessor { + public: + void InitFromFlags(const CommonFlags *flags) { + SetDedupTokenLength(flags->dedup_token_length); + SetStackTraceFormat(flags->stack_trace_format); + } + + void SetDedupTokenLength(int dedup_token_length) { + dedup_frames_ = dedup_token_length; + } + + void SetStackTraceFormat(const char *fmt) { + stack_trace_fmt_ = fmt; + symbolize_ = RenderNeedsSymbolization(fmt); + } + + // Returns a short string token to simplify deduplication of reports. + const char *GetDedupToken() const { return dedup_token_.data(); } + + uptr GetDedupTokenLength() const { return dedup_token_.length(); } + + bool ProcessAddressFrames(uptr pc) { + SymbolizedStack *frames = Frames(pc); + if (!frames) + return false; + + for (SymbolizedStack *cur = frames; cur; cur = cur->next) { + frame_desc_.clear(); + RenderFrame(&frame_desc_, stack_trace_fmt_, frame_num_++, + cur->info.address, symbolize_ ? &cur->info : nullptr, + common_flags()->symbolize_vs_style, + common_flags()->strip_path_prefix); + + if (!frame_desc_.length()) + continue; + + ProcessText(frame_desc_.data(), frame_desc_.length()); + ExtendDedupToken(cur); + } + frames->ClearAll(); + return true; + } + + protected: + void ProcessText(const char *text, uptr length) { + static_cast(*this).ProcessText(text, length); + } + + // Returns a list of symbolized frames for the given address. + SymbolizedStack *Frames(uptr pc) const { + return symbolize_ ? Symbolizer::GetOrInit()->SymbolizePC(pc) + : SymbolizedStack::New(pc); + } + + // Extend the dedup token by appending a new frame. + void ExtendDedupToken(SymbolizedStack *stack) { + if (dedup_frames_-- > 0) { + if (dedup_token_.length()) + dedup_token_.append("--"); + if (stack->info.function != nullptr) + dedup_token_.append(stack->info.function); + } + } + + private: + const char *stack_trace_fmt_; + bool symbolize_ = false; + int dedup_frames_ = 0; + uptr frame_num_ = 0; + InternalScopedString frame_desc_; + InternalScopedString dedup_token_; +}; + +class StackTraceTextPrinter + : public StackTraceTextProcessor { + protected: + friend class StackTraceTextProcessor; + + void ProcessText(const char *text, uptr) { Printf("%s\n", text); } +}; + +class StackTraceTextCopier + : public StackTraceTextProcessor { + public: + StackTraceTextCopier(char *out_buf, uptr out_buf_size) + : StackTraceTextProcessor(), + out_buf_(out_buf), + out_end_(out_buf + out_buf_size - 1) { + *out_end_ = 0; + } + + void SetFrameDelimiter(char delim) { frame_delimiter_ = delim; } + + uptr GetLength() const { return length_; } + + void Copy(const char *text) { + length_ += internal_strlen(text); + + if (out_buf_ == out_end_) + return; + + uptr n = out_end_ - out_buf_; + internal_strncpy(out_buf_, text, n); + + while (*out_buf_++) { + } + + CHECK(out_buf_ <= out_end_); + CHECK(!*out_end_); + } + + protected: + friend class StackTraceTextProcessor; + + void ProcessText(const char *text, uptr length) { + length_ += length + 1; + + if (!length) + return; + if (out_buf_ == out_end_) + return; + + // Reserve one byte for the frame delimiter. + uptr n = out_end_ - out_buf_ - 1; + internal_strncpy(out_buf_, text, n); + out_buf_ += __sanitizer::Min(n, length); + *out_buf_++ = frame_delimiter_; + + CHECK(out_buf_ <= out_end_); + CHECK(!*out_end_); + } + + private: + // Holds the actual length of the contents, even if it doesn't fit in the + // output buffer. + uptr length_ = 0; + + char *out_buf_; + char *const out_end_; + char frame_delimiter_ = '\0'; +}; + +} // namespace + +uptr StackTrace::PrintTo(char *out_buf, uptr out_buf_size) const { + CHECK(out_buf); + CHECK_GT(out_buf_size, 0); + + StackTraceTextCopier copier(out_buf, out_buf_size); + + if (trace == nullptr || size == 0) { + copier.Copy(" \n\n"); + return copier.GetLength(); + } + + copier.InitFromFlags(common_flags()); + copier.SetFrameDelimiter('\n'); + + for (uptr i = 0; i < size && trace[i]; i++) { + // PCs in stack traces are actually the return addresses, that is, + // addresses of the next instructions after the call. + uptr pc = GetPreviousInstructionPc(trace[i]); + CHECK(copier.ProcessAddressFrames(pc)); + } + + // Always add a trailing empty line after stack trace. + copier.Copy("\n"); + + // Append deduplication token, if non-empty. + if (copier.GetDedupTokenLength()) { + copier.Copy("DEDUP_TOKEN: "); + copier.Copy(copier.GetDedupToken()); + copier.Copy("\n"); + } + + return copier.GetLength(); +} + void StackTrace::Print() const { if (trace == nullptr || size == 0) { Printf(" \n\n"); return; } - InternalScopedString frame_desc; - InternalScopedString dedup_token; - int dedup_frames = common_flags()->dedup_token_length; - bool symbolize = RenderNeedsSymbolization(common_flags()->stack_trace_format); - uptr frame_num = 0; + + StackTraceTextPrinter printer; + printer.InitFromFlags(common_flags()); + for (uptr i = 0; i < size && trace[i]; i++) { // PCs in stack traces are actually the return addresses, that is, // addresses of the next instructions after the call. uptr pc = GetPreviousInstructionPc(trace[i]); - SymbolizedStack *frames; - if (symbolize) - frames = Symbolizer::GetOrInit()->SymbolizePC(pc); - else - frames = SymbolizedStack::New(pc); - CHECK(frames); - for (SymbolizedStack *cur = frames; cur; cur = cur->next) { - frame_desc.clear(); - RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++, - cur->info.address, symbolize ? &cur->info : nullptr, - common_flags()->symbolize_vs_style, - common_flags()->strip_path_prefix); - Printf("%s\n", frame_desc.data()); - if (dedup_frames-- > 0) { - if (dedup_token.length()) - dedup_token.append("--"); - if (cur->info.function != nullptr) - dedup_token.append(cur->info.function); - } - } - frames->ClearAll(); + CHECK(printer.ProcessAddressFrames(pc)); } + // Always print a trailing empty line after stack trace. Printf("\n"); - if (dedup_token.length()) - Printf("DEDUP_TOKEN: %s\n", dedup_token.data()); + + // Print deduplication token, if non-empty. + if (printer.GetDedupTokenLength()) { + Printf("DEDUP_TOKEN: %s\n", printer.GetDedupToken()); + } } void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context, @@ -116,40 +280,16 @@ void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf, uptr out_buf_size) { if (!out_buf_size) return; + pc = StackTrace::GetPreviousInstructionPc(pc); - SymbolizedStack *frame; - bool symbolize = RenderNeedsSymbolization(fmt); - if (symbolize) - frame = Symbolizer::GetOrInit()->SymbolizePC(pc); - else - frame = SymbolizedStack::New(pc); - if (!frame) { - internal_strncpy(out_buf, "", out_buf_size); - out_buf[out_buf_size - 1] = 0; + + StackTraceTextCopier copier(out_buf, out_buf_size); + copier.SetStackTraceFormat(fmt); + + if (!copier.ProcessAddressFrames(pc)) { + copier.Copy(""); return; } - InternalScopedString frame_desc; - uptr frame_num = 0; - // Reserve one byte for the final 0. - char *out_end = out_buf + out_buf_size - 1; - for (SymbolizedStack *cur = frame; cur && out_buf < out_end; - cur = cur->next) { - frame_desc.clear(); - RenderFrame(&frame_desc, fmt, frame_num++, cur->info.address, - symbolize ? &cur->info : nullptr, - common_flags()->symbolize_vs_style, - common_flags()->strip_path_prefix); - if (!frame_desc.length()) - continue; - // Reserve one byte for the terminating 0. - uptr n = out_end - out_buf - 1; - internal_strncpy(out_buf, frame_desc.data(), n); - out_buf += __sanitizer::Min(n, frame_desc.length()); - *out_buf++ = 0; - } - CHECK(out_buf <= out_end); - *out_buf = 0; - frame->ClearAll(); } SANITIZER_INTERFACE_ATTRIBUTE diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cpp b/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cpp --- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cpp +++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cpp @@ -10,9 +10,11 @@ // //===----------------------------------------------------------------------===// -#include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_stacktrace.h" + #include "gtest/gtest.h" +#include "sanitizer_common/sanitizer_common.h" +#include "string.h" namespace __sanitizer { @@ -160,6 +162,107 @@ } } +using StackPrintTest = FastUnwindTest; + +TEST_F(StackPrintTest, SKIP_ON_SPARC(StackPrintToContainsFullTrace)) { + // Override stack trace format to make testing code independent of default + // flag values. + CommonFlags flags; + flags.CopyFrom(*common_flags()); + flags.stack_trace_format = "#%n %p"; + OverrideCommonFlags(flags); + + UnwindFast(); + + char buf[3000]; + uptr len = trace.PrintTo(buf, sizeof(buf)); + + // This is the no-truncation case. + ASSERT_LT(len, sizeof(buf)); + + // Printed contents should always end with an empty line. + EXPECT_EQ(buf[len - 2], '\n'); + EXPECT_EQ(buf[len - 1], '\n'); + EXPECT_EQ(buf[len], '\0'); + + // Buffer contents are delimited by newlines, by default. + char *saveptr; + char *line = strtok_r(buf, "\n", &saveptr); + + // Checks buffer contents line-by-line. + for (u32 i = 0; i < trace.size; ++i) { + char traceline[100]; + + // Should be synced with the stack trace format, set above. + snprintf(traceline, sizeof(traceline) - 1, "#%u 0x%lx", i, + trace.trace[i] - 1); + + EXPECT_STREQ(line, traceline); + line = strtok_r(NULL, "\n", &saveptr); + } + + EXPECT_EQ(line, nullptr); +} + +TEST_F(StackPrintTest, SKIP_ON_SPARC(StackPrintToTruncatesContents)) { + UnwindFast(); + + char buf[3000]; + uptr actual_len = trace.PrintTo(buf, sizeof(buf)); + ASSERT_LT(actual_len, sizeof(buf)); + + char tinybuf[10]; + trace.PrintTo(tinybuf, sizeof(tinybuf)); + + // This the the truncation case. + ASSERT_GT(actual_len, sizeof(tinybuf)); + + // The truncated contents should be a prefix of the full contents. + size_t lastpos = sizeof(tinybuf) - 2; + EXPECT_EQ(strncmp(buf, tinybuf, lastpos), 0); + + // Truncated contents end with newline. + EXPECT_EQ(tinybuf[lastpos], '\n'); + EXPECT_EQ(tinybuf[lastpos + 1], '\0'); + + // Full bufffer has more contents... + EXPECT_NE(buf[lastpos], '\0'); + EXPECT_NE(buf[lastpos], '\n'); +} + +TEST_F(StackPrintTest, SKIP_ON_SPARC(StackPrintToWorksWithEmptyStack)) { + char buf[3000]; + trace.PrintTo(buf, sizeof(buf)); + EXPECT_NE(strstr(buf, ""), nullptr); +} + +TEST_F(StackPrintTest, SKIP_ON_SPARC(StackPrintToReturnsCorrectLength)) { + UnwindFast(); + + char buf[3000]; + uptr len = trace.PrintTo(buf, sizeof(buf)); + size_t actual_len = strlen(buf); + ASSERT_LT(len, sizeof(buf)); + EXPECT_EQ(len, actual_len); + + char tinybuf[5]; + len = trace.PrintTo(tinybuf, sizeof(tinybuf)); + size_t truncated_len = strlen(tinybuf); + ASSERT_GE(len, sizeof(tinybuf)); + EXPECT_EQ(len, actual_len); + EXPECT_EQ(truncated_len, sizeof(tinybuf) - 1); +} + +using StackPrintDeathTest = StackPrintTest; + +TEST_F(StackPrintDeathTest, + SKIP_ON_SPARC(StackPrintRequiresNonNullBufferAndPosSize)) { + UnwindFast(); + char buf[100]; + EXPECT_DEATH(trace.PrintTo(buf, 0), ""); + EXPECT_DEATH(trace.PrintTo(NULL, 100), ""); +} + #endif // SANITIZER_CAN_FAST_UNWIND TEST(SlowUnwindTest, ShortStackTrace) { 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,71 @@ 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, "DESCRIPTION", buf, 0); + printf("Output=\"%s\"\n", buf); + printf("Returned length: %zu\n", length); + // CHECK: Output="" + // CHECK: Returned length: 0 + + length = dfsan_sprint_origin_trace(&c, NULL, NULL, 100); + printf("Returned length: %zu\n", length); + // CHECK: Returned length: 0 +}