diff options
Diffstat (limited to 'compiler-rt/lib/nsan/nsan.cpp')
-rw-r--r-- | compiler-rt/lib/nsan/nsan.cpp | 808 |
1 files changed, 808 insertions, 0 deletions
diff --git a/compiler-rt/lib/nsan/nsan.cpp b/compiler-rt/lib/nsan/nsan.cpp new file mode 100644 index 0000000..f7b2ce2 --- /dev/null +++ b/compiler-rt/lib/nsan/nsan.cpp @@ -0,0 +1,808 @@ +//===-- nsan.cc -----------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// NumericalStabilitySanitizer runtime. +// +// This implements: +// - The public nsan interface (include/sanitizer/nsan_interface.h). +// - The private nsan interface (./nsan.h). +// - The internal instrumentation interface. These are function emitted by the +// instrumentation pass: +// * __nsan_get_shadow_ptr_for_{float,double,longdouble}_load +// These return the shadow memory pointer for loading the shadow value, +// after checking that the types are consistent. If the types are not +// consistent, returns nullptr. +// * __nsan_get_shadow_ptr_for_{float,double,longdouble}_store +// Sets the shadow types appropriately and returns the shadow memory +// pointer for storing the shadow value. +// * __nsan_internal_check_{float,double,long double}_{f,d,l} checks the +// accuracy of a value against its shadow and emits a warning depending +// on the runtime configuration. The middle part indicates the type of +// the application value, the suffix (f,d,l) indicates the type of the +// shadow, and depends on the instrumentation configuration. +// * __nsan_fcmp_fail_* emits a warning for an fcmp instruction whose +// corresponding shadow fcmp result differs. +// +//===----------------------------------------------------------------------===// + +#include <assert.h> +#include <math.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_report_decorator.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_symbolizer.h" + +#include "nsan/nsan.h" +#include "nsan/nsan_flags.h" +#include "nsan/nsan_stats.h" +#include "nsan/nsan_suppressions.h" + +using namespace __sanitizer; +using namespace __nsan; + +constexpr int kMaxVectorWidth = 8; + +// When copying application memory, we also copy its shadow and shadow type. +// FIXME: We could provide fixed-size versions that would nicely +// vectorize for known sizes. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void +__nsan_copy_values(const u8 *daddr, const u8 *saddr, uptr size) { + internal_memmove((void *)GetShadowTypeAddrFor(daddr), + GetShadowTypeAddrFor(saddr), size); + internal_memmove((void *)GetShadowAddrFor(daddr), GetShadowAddrFor(saddr), + size * kShadowScale); +} + +// FIXME: We could provide fixed-size versions that would nicely +// vectorize for known sizes. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void +__nsan_set_value_unknown(const u8 *addr, uptr size) { + internal_memset((void *)GetShadowTypeAddrFor(addr), 0, size); +} + + +const char *FTInfo<float>::kCppTypeName = "float"; +const char *FTInfo<double>::kCppTypeName = "double"; +const char *FTInfo<long double>::kCppTypeName = "long double"; +const char *FTInfo<__float128>::kCppTypeName = "__float128"; + +const char FTInfo<float>::kTypePattern[sizeof(float)]; +const char FTInfo<double>::kTypePattern[sizeof(double)]; +const char FTInfo<long double>::kTypePattern[sizeof(long double)]; + +// Helper for __nsan_dump_shadow_mem: Reads the value at address `ptr`, +// identified by its type id. +template <typename ShadowFT> +static __float128 ReadShadowInternal(const u8 *ptr) { + ShadowFT Shadow; + __builtin_memcpy(&Shadow, ptr, sizeof(Shadow)); + return Shadow; +} + +static __float128 ReadShadow(const u8 *ptr, const char ShadowTypeId) { + switch (ShadowTypeId) { + case 'd': + return ReadShadowInternal<double>(ptr); + case 'l': + return ReadShadowInternal<long double>(ptr); + case 'q': + return ReadShadowInternal<__float128>(ptr); + default: + return 0.0; + } +} + +namespace { +class Decorator : public __sanitizer::SanitizerCommonDecorator { +public: + Decorator() : SanitizerCommonDecorator() {} + const char *Warning() { return Red(); } + const char *Name() { return Green(); } + const char *End() { return Default(); } +}; + +// Workaround for the fact that Printf() does not support floats. +struct PrintBuffer { + char Buffer[64]; +}; +template <typename FT> struct FTPrinter {}; + +template <> struct FTPrinter<double> { + static PrintBuffer dec(double value) { + PrintBuffer result; + snprintf(result.Buffer, sizeof(result.Buffer) - 1, "%.20f", value); + return result; + } + static PrintBuffer hex(double value) { + PrintBuffer result; + snprintf(result.Buffer, sizeof(result.Buffer) - 1, "%.20a", value); + return result; + } +}; + +template <> struct FTPrinter<float> : FTPrinter<double> {}; + +template <> struct FTPrinter<long double> { + static PrintBuffer dec(long double value) { + PrintBuffer result; + snprintf(result.Buffer, sizeof(result.Buffer) - 1, "%.20Lf", value); + return result; + } + static PrintBuffer hex(long double value) { + PrintBuffer result; + snprintf(result.Buffer, sizeof(result.Buffer) - 1, "%.20La", value); + return result; + } +}; + +// FIXME: print with full precision. +template <> struct FTPrinter<__float128> : FTPrinter<long double> {}; + +// This is a template so that there are no implicit conversions. +template <typename FT> inline FT ftAbs(FT v); + +template <> inline long double ftAbs(long double v) { return fabsl(v); } +template <> inline double ftAbs(double v) { return fabs(v); } + +// We don't care about nans. +// std::abs(__float128) code is suboptimal and generates a function call to +// __getf2(). +template <typename FT> inline FT ftAbs(FT v) { return v >= FT{0} ? v : -v; } + +template <typename FT1, typename FT2, bool Enable> struct LargestFTImpl { + using type = FT2; +}; + +template <typename FT1, typename FT2> struct LargestFTImpl<FT1, FT2, true> { + using type = FT1; +}; + +template <typename FT1, typename FT2> +using LargestFT = + typename LargestFTImpl<FT1, FT2, (sizeof(FT1) > sizeof(FT2))>::type; + +template <typename T> T max(T a, T b) { return a < b ? b : a; } + +} // end anonymous namespace + +void __sanitizer::BufferedStackTrace::UnwindImpl(uptr pc, uptr bp, + void *context, + bool request_fast, + u32 max_depth) { + using namespace __nsan; + return Unwind(max_depth, pc, bp, context, 0, 0, false); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_print_accumulated_stats() { + if (nsan_stats) + nsan_stats->Print(); +} + +static void NsanAtexit() { + Printf("Numerical Sanitizer exit stats:\n"); + __nsan_print_accumulated_stats(); + nsan_stats = nullptr; +} + +// The next three functions return a pointer for storing a shadow value for `n` +// values, after setting the shadow types. We return the pointer instead of +// storing ourselves because it avoids having to rely on the calling convention +// around long double being the same for nsan and the target application. +// We have to have 3 versions because we need to know which type we are storing +// since we are setting the type shadow memory. +template <typename FT> static u8 *getShadowPtrForStore(u8 *store_addr, uptr n) { + unsigned char *shadow_type = GetShadowTypeAddrFor(store_addr); + for (uptr i = 0; i < n; ++i) { + __builtin_memcpy(shadow_type + i * sizeof(FT), FTInfo<FT>::kTypePattern, + sizeof(FTInfo<FT>::kTypePattern)); + } + return GetShadowAddrFor(store_addr); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE u8 * +__nsan_get_shadow_ptr_for_float_store(u8 *store_addr, uptr n) { + return getShadowPtrForStore<float>(store_addr, n); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE u8 * +__nsan_get_shadow_ptr_for_double_store(u8 *store_addr, uptr n) { + return getShadowPtrForStore<double>(store_addr, n); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE u8 * +__nsan_get_shadow_ptr_for_longdouble_store(u8 *store_addr, uptr n) { + return getShadowPtrForStore<long double>(store_addr, n); +} + +template <typename FT> static bool IsValidShadowType(const u8 *shadow_type) { + return __builtin_memcmp(shadow_type, FTInfo<FT>::kTypePattern, sizeof(FT)) == + 0; +} + +template <int kSize, typename T> static bool IsZero(const T *ptr) { + constexpr const char kZeros[kSize] = {}; // Zero initialized. + return __builtin_memcmp(ptr, kZeros, kSize) == 0; +} + +template <typename FT> static bool IsUnknownShadowType(const u8 *shadow_type) { + return IsZero<sizeof(FTInfo<FT>::kTypePattern)>(shadow_type); +} + +// The three folowing functions check that the address stores a complete +// shadow value of the given type and return a pointer for loading. +// They return nullptr if the type of the value is unknown or incomplete. +template <typename FT> +static const u8 *getShadowPtrForLoad(const u8 *load_addr, uptr n) { + const u8 *const shadow_type = GetShadowTypeAddrFor(load_addr); + for (uptr i = 0; i < n; ++i) { + if (!IsValidShadowType<FT>(shadow_type + i * sizeof(FT))) { + // If loadtracking stats are enabled, log loads with invalid types + // (tampered with through type punning). + if (flags().enable_loadtracking_stats) { + if (IsUnknownShadowType<FT>(shadow_type + i * sizeof(FT))) { + // Warn only if the value is non-zero. Zero is special because + // applications typically initialize large buffers to zero in an + // untyped way. + if (!IsZero<sizeof(FT)>(load_addr)) { + GET_CALLER_PC_BP; + nsan_stats->AddUnknownLoadTrackingEvent(pc, bp); + } + } else { + GET_CALLER_PC_BP; + nsan_stats->AddInvalidLoadTrackingEvent(pc, bp); + } + } + return nullptr; + } + } + return GetShadowAddrFor(load_addr); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE const u8 * +__nsan_get_shadow_ptr_for_float_load(const u8 *load_addr, uptr n) { + return getShadowPtrForLoad<float>(load_addr, n); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE const u8 * +__nsan_get_shadow_ptr_for_double_load(const u8 *load_addr, uptr n) { + return getShadowPtrForLoad<double>(load_addr, n); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE const u8 * +__nsan_get_shadow_ptr_for_longdouble_load(const u8 *load_addr, uptr n) { + return getShadowPtrForLoad<long double>(load_addr, n); +} + +// Returns the raw shadow pointer. The returned pointer should be considered +// opaque. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE u8 * +__nsan_internal_get_raw_shadow_ptr(const u8 *addr) { + return GetShadowAddrFor(const_cast<u8 *>(addr)); +} + +// Returns the raw shadow type pointer. The returned pointer should be +// considered opaque. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE u8 * +__nsan_internal_get_raw_shadow_type_ptr(const u8 *addr) { + return reinterpret_cast<u8 *>(GetShadowTypeAddrFor(const_cast<u8 *>(addr))); +} + +static ValueType getValueType(u8 c) { return static_cast<ValueType>(c & 0x3); } + +static int getValuePos(u8 c) { return c >> kValueSizeSizeBits; } + +// Checks the consistency of the value types at the given type pointer. +// If the value is inconsistent, returns ValueType::kUnknown. Else, return the +// consistent type. +template <typename FT> +static bool checkValueConsistency(const u8 *shadow_type) { + const int pos = getValuePos(*shadow_type); + // Check that all bytes from the start of the value are ordered. + for (uptr i = 0; i < sizeof(FT); ++i) { + const u8 T = *(shadow_type - pos + i); + if (!(getValueType(T) == FTInfo<FT>::kValueType && getValuePos(T) == i)) + return false; + } + return true; +} + +// The instrumentation automatically appends `shadow_value_type_ids`, see +// maybeAddSuffixForNsanInterface. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void +__nsan_dump_shadow_mem(const u8 *addr, size_t size_bytes, size_t bytes_per_line, + size_t shadow_value_type_ids) { + const u8 *const shadow_type = GetShadowTypeAddrFor(addr); + const u8 *const shadow = GetShadowAddrFor(addr); + + constexpr int kMaxNumDecodedValues = 16; + __float128 decoded_values[kMaxNumDecodedValues]; + int num_decoded_values = 0; + if (bytes_per_line > 4 * kMaxNumDecodedValues) + bytes_per_line = 4 * kMaxNumDecodedValues; + + // We keep track of the current type and position as we go. + ValueType LastValueTy = kUnknownValueType; + int LastPos = -1; + size_t Offset = 0; + for (size_t R = 0; R < (size_bytes + bytes_per_line - 1) / bytes_per_line; + ++R) { + printf("%p: ", (void *)(addr + R * bytes_per_line)); + for (size_t C = 0; C < bytes_per_line && Offset < size_bytes; ++C) { + const ValueType ValueTy = getValueType(shadow_type[Offset]); + const int pos = getValuePos(shadow_type[Offset]); + if (ValueTy == LastValueTy && pos == LastPos + 1) { + ++LastPos; + } else { + LastValueTy = ValueTy; + LastPos = pos == 0 ? 0 : -1; + } + + switch (ValueTy) { + case kUnknownValueType: + printf("__ "); + break; + case kFloatValueType: + printf("f%x ", pos); + if (LastPos == sizeof(float) - 1) { + decoded_values[num_decoded_values] = + ReadShadow(shadow + kShadowScale * (Offset + 1 - sizeof(float)), + static_cast<char>(shadow_value_type_ids & 0xff)); + ++num_decoded_values; + } + break; + case kDoubleValueType: + printf("d%x ", pos); + if (LastPos == sizeof(double) - 1) { + decoded_values[num_decoded_values] = ReadShadow( + shadow + kShadowScale * (Offset + 1 - sizeof(double)), + static_cast<char>((shadow_value_type_ids >> 8) & 0xff)); + ++num_decoded_values; + } + break; + case kFp80ValueType: + printf("l%x ", pos); + if (LastPos == sizeof(long double) - 1) { + decoded_values[num_decoded_values] = ReadShadow( + shadow + kShadowScale * (Offset + 1 - sizeof(long double)), + static_cast<char>((shadow_value_type_ids >> 16) & 0xff)); + ++num_decoded_values; + } + break; + } + ++Offset; + } + for (int i = 0; i < num_decoded_values; ++i) { + printf(" (%s)", FTPrinter<__float128>::dec(decoded_values[i]).Buffer); + } + num_decoded_values = 0; + printf("\n"); + } +} + +SANITIZER_INTERFACE_ATTRIBUTE +alignas(16) thread_local uptr __nsan_shadow_ret_tag = 0; + +SANITIZER_INTERFACE_ATTRIBUTE +alignas(16) thread_local char __nsan_shadow_ret_ptr[kMaxVectorWidth * + sizeof(__float128)]; + +SANITIZER_INTERFACE_ATTRIBUTE +alignas(16) thread_local uptr __nsan_shadow_args_tag = 0; + +// Maximum number of args. This should be enough for anyone (tm). An alternate +// scheme is to have the generated code create an alloca and make +// __nsan_shadow_args_ptr point ot the alloca. +constexpr const int kMaxNumArgs = 128; +SANITIZER_INTERFACE_ATTRIBUTE +alignas( + 16) thread_local char __nsan_shadow_args_ptr[kMaxVectorWidth * kMaxNumArgs * + sizeof(__float128)]; + +enum ContinuationType { // Keep in sync with instrumentation pass. + kContinueWithShadow = 0, + kResumeFromValue = 1, +}; + +// Checks the consistency between application and shadow value. Returns true +// when the instrumented code should resume computations from the original value +// rather than the shadow value. This prevents one error to propagate to all +// subsequent operations. This behaviour is tunable with flags. +template <typename FT, typename ShadowFT> +int32_t checkFT(const FT value, ShadowFT Shadow, CheckTypeT CheckType, + uptr CheckArg) { + // We do all comparisons in the InternalFT domain, which is the largest FT + // type. + using InternalFT = LargestFT<FT, ShadowFT>; + const InternalFT check_value = value; + const InternalFT check_shadow = Shadow; + + // See this article for an interesting discussion of how to compare floats: + // https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + static constexpr const FT Eps = FTInfo<FT>::kEpsilon; + + const InternalFT abs_err = ftAbs(check_value - check_shadow); + + if (flags().enable_check_stats) { + GET_CALLER_PC_BP; + // We are re-computing `largest` here because this is a cold branch, and we + // want to avoid having to move the computation of `largest` before the + // absolute value check when this branch is not taken. + const InternalFT largest = max(ftAbs(check_value), ftAbs(check_shadow)); + nsan_stats->AddCheck(CheckType, pc, bp, abs_err / largest); + } + + // Note: writing the comparison that way ensures that when `abs_err` is Nan + // (value and shadow are inf or -inf), we pass the test. + if (!(abs_err >= flags().cached_absolute_error_threshold)) + return kContinueWithShadow; + + const InternalFT largest = max(ftAbs(check_value), ftAbs(check_shadow)); + if (abs_err * (1ull << flags().log2_max_relative_error) <= largest) + return kContinueWithShadow; // No problem here. + + if (!flags().disable_warnings) { + GET_CALLER_PC_BP; + BufferedStackTrace stack; + stack.Unwind(pc, bp, nullptr, false); + if (GetSuppressionForStack(&stack, CheckKind::Consistency)) { + // FIXME: optionally print. + return flags().resume_after_suppression ? kResumeFromValue + : kContinueWithShadow; + } + + Decorator D; + Printf("%s", D.Warning()); + // Printf does not support float formatting. + char RelErrBuf[64] = "inf"; + if (largest > Eps) { + snprintf(RelErrBuf, sizeof(RelErrBuf) - 1, "%.20Lf%% (2^%.0Lf epsilons)", + static_cast<long double>(100.0 * abs_err / largest), + log2l(static_cast<long double>(abs_err / largest / Eps))); + } + char ulp_err_buf[128] = ""; + const double shadow_ulp_diff = GetULPDiff(check_value, check_shadow); + if (shadow_ulp_diff != kMaxULPDiff) { + // This is the ULP diff in the internal domain. The user actually cares + // about that in the original domain. + const double ulp_diff = + shadow_ulp_diff / (u64{1} << (FTInfo<InternalFT>::kMantissaBits - + FTInfo<FT>::kMantissaBits)); + snprintf(ulp_err_buf, sizeof(ulp_err_buf) - 1, + "(%.0f ULPs == %.1f digits == %.1f bits)", ulp_diff, + log10(ulp_diff), log2(ulp_diff)); + } + Printf("WARNING: NumericalStabilitySanitizer: inconsistent shadow results"); + switch (CheckType) { + case CheckTypeT::kUnknown: + case CheckTypeT::kFcmp: + case CheckTypeT::kMaxCheckType: + break; + case CheckTypeT::kRet: + Printf(" while checking return value"); + break; + case CheckTypeT::kArg: + Printf(" while checking call argument #%d", static_cast<int>(CheckArg)); + break; + case CheckTypeT::kLoad: + Printf( + " while checking load from address 0x%lx. This is due to incorrect " + "shadow memory tracking, typically due to uninstrumented code " + "writing to memory.", + CheckArg); + break; + case CheckTypeT::kStore: + Printf(" while checking store to address 0x%lx", CheckArg); + break; + case CheckTypeT::kInsert: + Printf(" while checking vector insert"); + break; + case CheckTypeT::kUser: + Printf(" in user-initiated check"); + break; + } + using ValuePrinter = FTPrinter<FT>; + using ShadowPrinter = FTPrinter<ShadowFT>; + Printf("\n" + "%-12s precision (native): dec: %s hex: %s\n" + "%-12s precision (shadow): dec: %s hex: %s\n" + "shadow truncated to %-12s: dec: %s hex: %s\n" + "Relative error: %s\n" + "Absolute error: %s\n" + "%s\n", + FTInfo<FT>::kCppTypeName, ValuePrinter::dec(value).Buffer, + ValuePrinter::hex(value).Buffer, FTInfo<ShadowFT>::kCppTypeName, + ShadowPrinter::dec(Shadow).Buffer, ShadowPrinter::hex(Shadow).Buffer, + FTInfo<FT>::kCppTypeName, ValuePrinter::dec(Shadow).Buffer, + ValuePrinter::hex(Shadow).Buffer, RelErrBuf, + ValuePrinter::hex(abs_err).Buffer, ulp_err_buf); + stack.Print(); + } + + if (flags().enable_warning_stats) { + GET_CALLER_PC_BP; + nsan_stats->AddWarning(CheckType, pc, bp, abs_err / largest); + } + + if (flags().halt_on_error) { + Printf("Exiting\n"); + Die(); + } + return flags().resume_after_warning ? kResumeFromValue : kContinueWithShadow; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE int32_t __nsan_internal_check_float_d( + float value, double shadow, int32_t check_type, uptr check_arg) { + return checkFT(value, shadow, static_cast<CheckTypeT>(check_type), check_arg); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE int32_t __nsan_internal_check_double_l( + double value, long double shadow, int32_t check_type, uptr check_arg) { + return checkFT(value, shadow, static_cast<CheckTypeT>(check_type), check_arg); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE int32_t __nsan_internal_check_double_q( + double value, __float128 shadow, int32_t check_type, uptr check_arg) { + return checkFT(value, shadow, static_cast<CheckTypeT>(check_type), check_arg); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE int32_t +__nsan_internal_check_longdouble_q(long double value, __float128 shadow, + int32_t check_type, uptr check_arg) { + return checkFT(value, shadow, static_cast<CheckTypeT>(check_type), check_arg); +} + +static const char *GetTruthValueName(bool v) { return v ? "true" : "false"; } + +// This uses the same values as CmpInst::Predicate. +static const char *GetPredicateName(int v) { + switch (v) { + case 0: + return "(false)"; + case 1: + return "=="; + case 2: + return ">"; + case 3: + return ">="; + case 4: + return "<"; + case 5: + return "<="; + case 6: + return "!="; + case 7: + return "(ordered)"; + case 8: + return "(unordered)"; + case 9: + return "=="; + case 10: + return ">"; + case 11: + return ">="; + case 12: + return "<"; + case 13: + return "<="; + case 14: + return "!="; + case 15: + return "(true)"; + } + return "??"; +} + +template <typename FT, typename ShadowFT> +void fCmpFailFT(const FT Lhs, const FT Rhs, ShadowFT LhsShadow, + ShadowFT RhsShadow, int Predicate, bool result, + bool ShadowResult) { + if (result == ShadowResult) { + // When a vector comparison fails, we fail each element of the comparison + // to simplify instrumented code. Skip elements where the shadow comparison + // gave the same result as the original one. + return; + } + + GET_CALLER_PC_BP; + BufferedStackTrace stack; + stack.Unwind(pc, bp, nullptr, false); + + if (GetSuppressionForStack(&stack, CheckKind::Fcmp)) { + // FIXME: optionally print. + return; + } + + if (flags().enable_warning_stats) + nsan_stats->AddWarning(CheckTypeT::kFcmp, pc, bp, 0.0); + + if (flags().disable_warnings) + return; + + // FIXME: ideally we would print the shadow value as FP128. Right now because + // we truncate to long double we can sometimes see stuff like: + // shadow <value> == <value> (false) + using ValuePrinter = FTPrinter<FT>; + using ShadowPrinter = FTPrinter<ShadowFT>; + Decorator D; + const char *const PredicateName = GetPredicateName(Predicate); + Printf("%s", D.Warning()); + Printf("WARNING: NumericalStabilitySanitizer: floating-point comparison " + "results depend on precision\n" + "%-12s precision dec (native): %s %s %s (%s)\n" + "%-12s precision dec (shadow): %s %s %s (%s)\n" + "%-12s precision hex (native): %s %s %s (%s)\n" + "%-12s precision hex (shadow): %s %s %s (%s)\n" + "%s", + // Native, decimal. + FTInfo<FT>::kCppTypeName, ValuePrinter::dec(Lhs).Buffer, PredicateName, + ValuePrinter::dec(Rhs).Buffer, GetTruthValueName(result), + // Shadow, decimal + FTInfo<ShadowFT>::kCppTypeName, ShadowPrinter::dec(LhsShadow).Buffer, + PredicateName, ShadowPrinter::dec(RhsShadow).Buffer, + GetTruthValueName(ShadowResult), + // Native, hex. + FTInfo<FT>::kCppTypeName, ValuePrinter::hex(Lhs).Buffer, PredicateName, + ValuePrinter::hex(Rhs).Buffer, GetTruthValueName(result), + // Shadow, hex + FTInfo<ShadowFT>::kCppTypeName, ShadowPrinter::hex(LhsShadow).Buffer, + PredicateName, ShadowPrinter::hex(RhsShadow).Buffer, + GetTruthValueName(ShadowResult), D.End()); + Printf("%s", D.Default()); + stack.Print(); + if (flags().halt_on_error) { + Printf("Exiting\n"); + Die(); + } +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void +__nsan_fcmp_fail_float_d(float lhs, float rhs, double lhs_shadow, + double rhs_shadow, int predicate, bool result, + bool shadow_result) { + fCmpFailFT(lhs, rhs, lhs_shadow, rhs_shadow, predicate, result, + shadow_result); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void +__nsan_fcmp_fail_double_q(double lhs, double rhs, __float128 lhs_shadow, + __float128 rhs_shadow, int predicate, bool result, + bool shadow_result) { + fCmpFailFT(lhs, rhs, lhs_shadow, rhs_shadow, predicate, result, + shadow_result); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void +__nsan_fcmp_fail_double_l(double lhs, double rhs, long double lhs_shadow, + long double rhs_shadow, int predicate, bool result, + bool shadow_result) { + fCmpFailFT(lhs, rhs, lhs_shadow, rhs_shadow, predicate, result, + shadow_result); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void +__nsan_fcmp_fail_longdouble_q(long double lhs, long double rhs, + __float128 lhs_shadow, __float128 rhs_shadow, + int predicate, bool result, bool shadow_result) { + fCmpFailFT(lhs, rhs, lhs_shadow, rhs_shadow, predicate, result, + shadow_result); +} + +template <typename FT> void checkFTFromShadowStack(const FT value) { + // Get the shadow 2FT value from the shadow stack. Note that + // __nsan_check_{float,double,long double} is a function like any other, so + // the instrumentation will have placed the shadow value on the shadow stack. + using ShadowFT = typename FTInfo<FT>::shadow_type; + ShadowFT Shadow; + __builtin_memcpy(&Shadow, __nsan_shadow_args_ptr, sizeof(ShadowFT)); + checkFT(value, Shadow, CheckTypeT::kUser, 0); +} + +// FIXME: Add suffixes and let the instrumentation pass automatically add +// suffixes. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_check_float(float value) { + assert(__nsan_shadow_args_tag == (uptr)&__nsan_check_float && + "__nsan_check_float called from non-instrumented function"); + checkFTFromShadowStack(value); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void +__nsan_check_double(double value) { + assert(__nsan_shadow_args_tag == (uptr)&__nsan_check_double && + "__nsan_check_double called from non-instrumented function"); + checkFTFromShadowStack(value); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void +__nsan_check_longdouble(long double value) { + assert(__nsan_shadow_args_tag == (uptr)&__nsan_check_longdouble && + "__nsan_check_longdouble called from non-instrumented function"); + checkFTFromShadowStack(value); +} + +template <typename FT> static void dumpFTFromShadowStack(const FT value) { + // Get the shadow 2FT value from the shadow stack. Note that + // __nsan_dump_{float,double,long double} is a function like any other, so + // the instrumentation will have placed the shadow value on the shadow stack. + using ShadowFT = typename FTInfo<FT>::shadow_type; + ShadowFT shadow; + __builtin_memcpy(&shadow, __nsan_shadow_args_ptr, sizeof(ShadowFT)); + using ValuePrinter = FTPrinter<FT>; + using ShadowPrinter = FTPrinter<typename FTInfo<FT>::shadow_type>; + printf("value dec:%s hex:%s\n" + "shadow dec:%s hex:%s\n", + ValuePrinter::dec(value).Buffer, ValuePrinter::hex(value).Buffer, + ShadowPrinter::dec(shadow).Buffer, ShadowPrinter::hex(shadow).Buffer); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_dump_float(float value) { + assert(__nsan_shadow_args_tag == (uptr)&__nsan_dump_float && + "__nsan_dump_float called from non-instrumented function"); + dumpFTFromShadowStack(value); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_dump_double(double value) { + assert(__nsan_shadow_args_tag == (uptr)&__nsan_dump_double && + "__nsan_dump_double called from non-instrumented function"); + dumpFTFromShadowStack(value); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void +__nsan_dump_longdouble(long double value) { + assert(__nsan_shadow_args_tag == (uptr)&__nsan_dump_longdouble && + "__nsan_dump_longdouble called from non-instrumented function"); + dumpFTFromShadowStack(value); +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_dump_shadow_ret() { + printf("ret tag: %lx\n", __nsan_shadow_ret_tag); + double v; + __builtin_memcpy(&v, __nsan_shadow_ret_ptr, sizeof(double)); + printf("double value: %f\n", v); + // FIXME: float128 value. +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_dump_shadow_args() { + printf("args tag: %lx\n", __nsan_shadow_args_tag); +} + +bool __nsan::nsan_initialized; +bool __nsan::nsan_init_is_running; + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_init() { + CHECK(!nsan_init_is_running); + if (nsan_initialized) + return; + nsan_init_is_running = true; + + InitializeFlags(); + InitializeSuppressions(); + InitializePlatformEarly(); + + if (!MmapFixedNoReserve(TypesAddr(), UnusedAddr() - TypesAddr())) + Die(); + + InitializeInterceptors(); + + InitializeStats(); + if (flags().print_stats_on_exit) + Atexit(NsanAtexit); + + nsan_init_is_running = false; + nsan_initialized = true; +} + +#if SANITIZER_CAN_USE_PREINIT_ARRAY +__attribute__((section(".preinit_array"), + used)) static void (*nsan_init_ptr)() = __nsan_init; +#endif |