[nsan] Fix style issue

The initial check-in of compiler-rt/lib/nsan #94322 has a lot of style
issues. Fix them before the history becomes more useful.

Pull Request: https://github.com/llvm/llvm-project/pull/96142
This commit is contained in:
Fangrui Song
2024-06-20 00:46:10 -07:00
committed by GitHub
parent 11344249e1
commit ef83c25b0e
12 changed files with 841 additions and 869 deletions

View File

@@ -3,11 +3,11 @@ add_compiler_rt_component(nsan)
include_directories(..)
set(NSAN_SOURCES
nsan.cc
nsan_flags.cc
nsan_interceptors.cc
nsan_stats.cc
nsan_suppressions.cc
nsan.cpp
nsan_flags.cpp
nsan_interceptors.cpp
nsan_stats.cpp
nsan_suppressions.cpp
)
set(NSAN_HEADERS

View File

@@ -51,16 +51,16 @@
using namespace __sanitizer;
using namespace __nsan;
static constexpr const int kMaxVectorWidth = 8;
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),
internal_memmove((void *)GetShadowTypeAddrFor(daddr),
GetShadowTypeAddrFor(saddr), size);
internal_memmove((void *)GetShadowAddrFor(daddr), GetShadowAddrFor(saddr),
size * kShadowScale);
}
@@ -68,10 +68,9 @@ __nsan_copy_values(const u8 *daddr, const u8 *saddr, uptr size) {
// 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);
internal_memset((void *)GetShadowTypeAddrFor(addr), 0, size);
}
namespace __nsan {
const char *FTInfo<float>::kCppTypeName = "float";
const char *FTInfo<double>::kCppTypeName = "double";
@@ -82,27 +81,29 @@ 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`,
// Helper for __nsan_dump_shadow_mem: Reads the value at address `ptr`,
// identified by its type id.
template <typename ShadowFT> __float128 readShadowInternal(const u8 *Ptr) {
template <typename ShadowFT>
static __float128 ReadShadowInternal(const u8 *ptr) {
ShadowFT Shadow;
__builtin_memcpy(&Shadow, Ptr, sizeof(Shadow));
__builtin_memcpy(&Shadow, ptr, sizeof(Shadow));
return Shadow;
}
__float128 readShadow(const u8 *Ptr, const char ShadowTypeId) {
static __float128 ReadShadow(const u8 *ptr, const char ShadowTypeId) {
switch (ShadowTypeId) {
case 'd':
return readShadowInternal<double>(Ptr);
return ReadShadowInternal<double>(ptr);
case 'l':
return readShadowInternal<long double>(Ptr);
return ReadShadowInternal<long double>(ptr);
case 'q':
return readShadowInternal<__float128>(Ptr);
return ReadShadowInternal<__float128>(ptr);
default:
return 0.0;
}
}
namespace {
class Decorator : public __sanitizer::SanitizerCommonDecorator {
public:
Decorator() : SanitizerCommonDecorator() {}
@@ -111,8 +112,6 @@ public:
const char *End() { return Default(); }
};
namespace {
// Workaround for the fact that Printf() does not support floats.
struct PrintBuffer {
char Buffer[64];
@@ -120,30 +119,30 @@ struct PrintBuffer {
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 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;
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 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;
static PrintBuffer hex(long double value) {
PrintBuffer result;
snprintf(result.Buffer, sizeof(result.Buffer) - 1, "%.20La", value);
return result;
}
};
@@ -151,15 +150,15 @@ template <> struct FTPrinter<long double> {
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 <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); }
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 FT> inline FT ftAbs(FT v) { return v >= FT{0} ? v : -v; }
template <typename FT1, typename FT2, bool Enable> struct LargestFTImpl {
using type = FT2;
@@ -177,8 +176,6 @@ template <typename T> T max(T a, T b) { return a < b ? b : a; }
} // end anonymous namespace
} // end namespace __nsan
void __sanitizer::BufferedStackTrace::UnwindImpl(uptr pc, uptr bp,
void *context,
bool request_fast,
@@ -189,10 +186,10 @@ void __sanitizer::BufferedStackTrace::UnwindImpl(uptr pc, uptr bp,
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_print_accumulated_stats() {
if (nsan_stats)
nsan_stats->print();
nsan_stats->Print();
}
static void nsanAtexit() {
static void NsanAtexit() {
Printf("Numerical Sanitizer exit stats:\n");
__nsan_print_accumulated_stats();
nsan_stats = nullptr;
@@ -204,13 +201,13 @@ static void nsanAtexit() {
// 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 *StoreAddr, uptr N) {
unsigned char *ShadowType = getShadowTypeAddrFor(StoreAddr);
for (uptr I = 0; I < N; ++I) {
__builtin_memcpy(ShadowType + I * sizeof(FT), FTInfo<FT>::kTypePattern,
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(StoreAddr);
return GetShadowAddrFor(store_addr);
}
extern "C" SANITIZER_INTERFACE_ATTRIBUTE u8 *
@@ -228,48 +225,48 @@ __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 *ShadowType) {
return __builtin_memcmp(ShadowType, FTInfo<FT>::kTypePattern, sizeof(FT)) ==
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) {
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;
return __builtin_memcmp(ptr, kZeros, kSize) == 0;
}
template <typename FT> static bool isUnknownShadowType(const u8 *ShadowType) {
return isZero<sizeof(FTInfo<FT>::kTypePattern)>(ShadowType);
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 *LoadAddr, uptr N) {
const u8 *const ShadowType = getShadowTypeAddrFor(LoadAddr);
for (uptr I = 0; I < N; ++I) {
if (!isValidShadowType<FT>(ShadowType + I * sizeof(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>(ShadowType + I * sizeof(FT))) {
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)>(LoadAddr)) {
if (!IsZero<sizeof(FT)>(load_addr)) {
GET_CALLER_PC_BP;
nsan_stats->addUnknownLoadTrackingEvent(pc, bp);
nsan_stats->AddUnknownLoadTrackingEvent(pc, bp);
}
} else {
GET_CALLER_PC_BP;
nsan_stats->addInvalidLoadTrackingEvent(pc, bp);
nsan_stats->AddInvalidLoadTrackingEvent(pc, bp);
}
}
return nullptr;
}
}
return getShadowAddrFor(LoadAddr);
return GetShadowAddrFor(load_addr);
}
extern "C" SANITIZER_INTERFACE_ATTRIBUTE const u8 *
@@ -291,14 +288,14 @@ __nsan_get_shadow_ptr_for_longdouble_load(const u8 *load_addr, uptr n) {
// opaque.
extern "C" SANITIZER_INTERFACE_ATTRIBUTE u8 *
__nsan_internal_get_raw_shadow_ptr(const u8 *addr) {
return getShadowAddrFor(const_cast<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)));
return reinterpret_cast<u8 *>(GetShadowTypeAddrFor(const_cast<u8 *>(addr)));
}
static ValueType getValueType(u8 c) { return static_cast<ValueType>(c & 0x3); }
@@ -308,14 +305,14 @@ 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 *ShadowType) {
const int Pos = getValuePos(*ShadowType);
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 = *(ShadowType - Pos + I);
if (!(getValueType(T) == FTInfo<FT>::kValueType && getValuePos(T) == I)) {
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;
}
@@ -325,15 +322,14 @@ template <typename FT> static bool checkValueConsistency(const u8 *ShadowType) {
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 ShadowType = getShadowTypeAddrFor(addr);
const u8 *const Shadow = getShadowAddrFor(addr);
const u8 *const shadow_type = GetShadowTypeAddrFor(addr);
const u8 *const shadow = GetShadowAddrFor(addr);
constexpr int kMaxNumDecodedValues = 16;
__float128 DecodedValues[kMaxNumDecodedValues];
int NumDecodedValues = 0;
if (bytes_per_line > 4 * kMaxNumDecodedValues) {
__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;
@@ -343,8 +339,8 @@ __nsan_dump_shadow_mem(const u8 *addr, size_t size_bytes, size_t 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(ShadowType[Offset]);
const int pos = getValuePos(ShadowType[Offset]);
const ValueType ValueTy = getValueType(shadow_type[Offset]);
const int pos = getValuePos(shadow_type[Offset]);
if (ValueTy == LastValueTy && pos == LastPos + 1) {
++LastPos;
} else {
@@ -359,64 +355,59 @@ __nsan_dump_shadow_mem(const u8 *addr, size_t size_bytes, size_t bytes_per_line,
case kFloatValueType:
printf("f%x ", pos);
if (LastPos == sizeof(float) - 1) {
DecodedValues[NumDecodedValues] =
readShadow(Shadow + kShadowScale * (Offset + 1 - sizeof(float)),
decoded_values[num_decoded_values] =
ReadShadow(shadow + kShadowScale * (Offset + 1 - sizeof(float)),
static_cast<char>(shadow_value_type_ids & 0xff));
++NumDecodedValues;
++num_decoded_values;
}
break;
case kDoubleValueType:
printf("d%x ", pos);
if (LastPos == sizeof(double) - 1) {
DecodedValues[NumDecodedValues] = readShadow(
Shadow + kShadowScale * (Offset + 1 - sizeof(double)),
decoded_values[num_decoded_values] = ReadShadow(
shadow + kShadowScale * (Offset + 1 - sizeof(double)),
static_cast<char>((shadow_value_type_ids >> 8) & 0xff));
++NumDecodedValues;
++num_decoded_values;
}
break;
case kFp80ValueType:
printf("l%x ", pos);
if (LastPos == sizeof(long double) - 1) {
DecodedValues[NumDecodedValues] = readShadow(
Shadow + kShadowScale * (Offset + 1 - sizeof(long double)),
decoded_values[num_decoded_values] = ReadShadow(
shadow + kShadowScale * (Offset + 1 - sizeof(long double)),
static_cast<char>((shadow_value_type_ids >> 16) & 0xff));
++NumDecodedValues;
++num_decoded_values;
}
break;
}
++Offset;
}
for (int I = 0; I < NumDecodedValues; ++I) {
printf(" (%s)", FTPrinter<__float128>::dec(DecodedValues[I]).Buffer);
for (int i = 0; i < num_decoded_values; ++i) {
printf(" (%s)", FTPrinter<__float128>::dec(decoded_values[i]).Buffer);
}
NumDecodedValues = 0;
num_decoded_values = 0;
printf("\n");
}
}
SANITIZER_INTERFACE_ATTRIBUTE
ALIGNED(16)
THREADLOCAL
uptr __nsan_shadow_ret_tag = 0;
alignas(16) thread_local uptr __nsan_shadow_ret_tag = 0;
SANITIZER_INTERFACE_ATTRIBUTE
ALIGNED(16)
THREADLOCAL
char __nsan_shadow_ret_ptr[kMaxVectorWidth * sizeof(__float128)];
alignas(16) thread_local char __nsan_shadow_ret_ptr[kMaxVectorWidth *
sizeof(__float128)];
SANITIZER_INTERFACE_ATTRIBUTE
ALIGNED(16)
THREADLOCAL
uptr __nsan_shadow_args_tag = 0;
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
ALIGNED(16)
THREADLOCAL
char __nsan_shadow_args_ptr[kMaxVectorWidth * kMaxNumArgs * sizeof(__float128)];
alignas(
16) thread_local char __nsan_shadow_args_ptr[kMaxVectorWidth * kMaxNumArgs *
sizeof(__float128)];
enum ContinuationType { // Keep in sync with instrumentation pass.
kContinueWithShadow = 0,
@@ -428,36 +419,36 @@ enum ContinuationType { // Keep in sync with instrumentation pass.
// 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,
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 CheckValue = Value;
const InternalFT CheckShadow = Shadow;
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 AbsErr = ftAbs(CheckValue - CheckShadow);
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
// 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(CheckValue), ftAbs(CheckShadow));
nsan_stats->addCheck(CheckType, pc, bp, AbsErr / Largest);
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 `AbsErr` is Nan
// 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 (!(AbsErr >= flags().cached_absolute_error_threshold))
if (!(abs_err >= flags().cached_absolute_error_threshold))
return kContinueWithShadow;
const InternalFT Largest = max(ftAbs(CheckValue), ftAbs(CheckShadow));
if (AbsErr * (1ull << flags().log2_max_relative_error) <= Largest)
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) {
@@ -474,22 +465,22 @@ int32_t checkFT(const FT Value, ShadowFT Shadow, CheckTypeT CheckType,
Printf("%s", D.Warning());
// Printf does not support float formatting.
char RelErrBuf[64] = "inf";
if (Largest > Eps) {
if (largest > Eps) {
snprintf(RelErrBuf, sizeof(RelErrBuf) - 1, "%.20Lf%% (2^%.0Lf epsilons)",
static_cast<long double>(100.0 * AbsErr / Largest),
log2l(static_cast<long double>(AbsErr / Largest / Eps)));
static_cast<long double>(100.0 * abs_err / largest),
log2l(static_cast<long double>(abs_err / largest / Eps)));
}
char UlpErrBuf[128] = "";
const double ShadowUlpDiff = getULPDiff(CheckValue, CheckShadow);
if (ShadowUlpDiff != kMaxULPDiff) {
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 UlpDiff =
ShadowUlpDiff / (u64{1} << (FTInfo<InternalFT>::kMantissaBits -
FTInfo<FT>::kMantissaBits));
snprintf(UlpErrBuf, sizeof(UlpErrBuf) - 1,
"(%.0f ULPs == %.1f digits == %.1f bits)", UlpDiff,
log10(UlpDiff), log2(UlpDiff));
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) {
@@ -529,18 +520,18 @@ int32_t checkFT(const FT Value, ShadowFT Shadow, CheckTypeT CheckType,
"Relative error: %s\n"
"Absolute error: %s\n"
"%s\n",
FTInfo<FT>::kCppTypeName, ValuePrinter::dec(Value).Buffer,
ValuePrinter::hex(Value).Buffer, FTInfo<ShadowFT>::kCppTypeName,
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(AbsErr).Buffer, UlpErrBuf);
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, AbsErr / Largest);
nsan_stats->AddWarning(CheckType, pc, bp, abs_err / largest);
}
if (flags().halt_on_error) {
@@ -571,10 +562,10 @@ __nsan_internal_check_longdouble_q(long double value, __float128 shadow,
return checkFT(value, shadow, static_cast<CheckTypeT>(check_type), check_arg);
}
static const char *getTruthValueName(bool v) { return v ? "true" : "false"; }
static const char *GetTruthValueName(bool v) { return v ? "true" : "false"; }
// This uses the same values as CmpInst::Predicate.
static const char *getPredicateName(int v) {
static const char *GetPredicateName(int v) {
switch (v) {
case 0:
return "(false)";
@@ -614,9 +605,9 @@ static const char *getPredicateName(int v) {
template <typename FT, typename ShadowFT>
void fCmpFailFT(const FT Lhs, const FT Rhs, ShadowFT LhsShadow,
ShadowFT RhsShadow, int Predicate, bool Result,
ShadowFT RhsShadow, int Predicate, bool result,
bool ShadowResult) {
if (Result == 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.
@@ -624,21 +615,19 @@ void fCmpFailFT(const FT Lhs, const FT Rhs, ShadowFT LhsShadow,
}
GET_CALLER_PC_BP;
BufferedStackTrace Stack;
Stack.Unwind(pc, bp, nullptr, false);
BufferedStackTrace stack;
stack.Unwind(pc, bp, nullptr, false);
if (GetSuppressionForStack(&Stack, CheckKind::Fcmp)) {
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().enable_warning_stats)
nsan_stats->AddWarning(CheckTypeT::kFcmp, pc, bp, 0.0);
if (flags().disable_warnings) {
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:
@@ -646,7 +635,7 @@ void fCmpFailFT(const FT Lhs, const FT Rhs, ShadowFT LhsShadow,
using ValuePrinter = FTPrinter<FT>;
using ShadowPrinter = FTPrinter<ShadowFT>;
Decorator D;
const char *const PredicateName = getPredicateName(Predicate);
const char *const PredicateName = GetPredicateName(Predicate);
Printf("%s", D.Warning());
Printf("WARNING: NumericalStabilitySanitizer: floating-point comparison "
"results depend on precision\n"
@@ -657,20 +646,20 @@ void fCmpFailFT(const FT Lhs, const FT Rhs, ShadowFT LhsShadow,
"%s",
// Native, decimal.
FTInfo<FT>::kCppTypeName, ValuePrinter::dec(Lhs).Buffer, PredicateName,
ValuePrinter::dec(Rhs).Buffer, getTruthValueName(Result),
ValuePrinter::dec(Rhs).Buffer, GetTruthValueName(result),
// Shadow, decimal
FTInfo<ShadowFT>::kCppTypeName, ShadowPrinter::dec(LhsShadow).Buffer,
PredicateName, ShadowPrinter::dec(RhsShadow).Buffer,
getTruthValueName(ShadowResult),
GetTruthValueName(ShadowResult),
// Native, hex.
FTInfo<FT>::kCppTypeName, ValuePrinter::hex(Lhs).Buffer, PredicateName,
ValuePrinter::hex(Rhs).Buffer, getTruthValueName(Result),
ValuePrinter::hex(Rhs).Buffer, GetTruthValueName(result),
// Shadow, hex
FTInfo<ShadowFT>::kCppTypeName, ShadowPrinter::hex(LhsShadow).Buffer,
PredicateName, ShadowPrinter::hex(RhsShadow).Buffer,
getTruthValueName(ShadowResult), D.End());
GetTruthValueName(ShadowResult), D.End());
Printf("%s", D.Default());
Stack.Print();
stack.Print();
if (flags().halt_on_error) {
Printf("Exiting\n");
Die();
@@ -709,77 +698,77 @@ __nsan_fcmp_fail_longdouble_q(long double lhs, long double rhs,
shadow_result);
}
template <typename FT> void checkFTFromShadowStack(const FT Value) {
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);
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) {
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);
checkFTFromShadowStack(value);
}
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__nsan_check_double(double Value) {
__nsan_check_double(double value) {
assert(__nsan_shadow_args_tag == (uptr)&__nsan_check_double &&
"__nsan_check_double called from non-instrumented function");
checkFTFromShadowStack(Value);
checkFTFromShadowStack(value);
}
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__nsan_check_longdouble(long double Value) {
__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);
checkFTFromShadowStack(value);
}
template <typename FT> static void dumpFTFromShadowStack(const FT 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));
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);
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) {
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);
dumpFTFromShadowStack(value);
}
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_dump_double(double 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);
dumpFTFromShadowStack(value);
}
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__nsan_dump_longdouble(long double Value) {
__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);
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);
double v;
__builtin_memcpy(&v, __nsan_shadow_ret_ptr, sizeof(double));
printf("double value: %f\n", v);
// FIXME: float128 value.
}
@@ -787,16 +776,14 @@ extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_dump_shadow_args() {
printf("args tag: %lx\n", __nsan_shadow_args_tag);
}
namespace __nsan {
bool NsanInitialized = false;
bool NsanInitIsRunning;
} // end namespace __nsan
bool __nsan::nsan_initialized;
bool __nsan::nsan_init_is_running;
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_init() {
CHECK(!NsanInitIsRunning);
if (NsanInitialized)
CHECK(!nsan_init_is_running);
if (nsan_initialized)
return;
NsanInitIsRunning = true;
nsan_init_is_running = true;
InitializeFlags();
InitializeSuppressions();
@@ -805,14 +792,14 @@ extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_init() {
if (!MmapFixedNoReserve(TypesAddr(), UnusedAddr() - TypesAddr()))
Die();
initializeInterceptors();
InitializeInterceptors();
initializeStats();
InitializeStats();
if (flags().print_stats_on_exit)
Atexit(nsanAtexit);
Atexit(NsanAtexit);
NsanInitIsRunning = false;
NsanInitialized = true;
nsan_init_is_running = false;
nsan_initialized = true;
}
#if SANITIZER_CAN_USE_PREINIT_ARRAY

View File

@@ -51,32 +51,32 @@ __nsan_default_options();
namespace __nsan {
extern bool NsanInitialized;
extern bool NsanInitIsRunning;
extern bool nsan_initialized;
extern bool nsan_init_is_running;
void initializeInterceptors();
void InitializeInterceptors();
// See notes in nsan_platform.
// printf-free (see comment in nsan_interceptors.cc).
inline u8 *getShadowAddrFor(u8 *Ptr) {
inline u8 *GetShadowAddrFor(u8 *Ptr) {
uptr AppOffset = ((uptr)Ptr) & ShadowMask();
return (u8 *)(AppOffset * kShadowScale + ShadowAddr());
}
// printf-free (see comment in nsan_interceptors.cc).
inline const u8 *getShadowAddrFor(const u8 *Ptr) {
return getShadowAddrFor(const_cast<u8 *>(Ptr));
inline const u8 *GetShadowAddrFor(const u8 *Ptr) {
return GetShadowAddrFor(const_cast<u8 *>(Ptr));
}
// printf-free (see comment in nsan_interceptors.cc).
inline u8 *getShadowTypeAddrFor(u8 *Ptr) {
inline u8 *GetShadowTypeAddrFor(u8 *Ptr) {
uptr AppOffset = ((uptr)Ptr) & ShadowMask();
return (u8 *)(AppOffset + TypesAddr());
}
// printf-free (see comment in nsan_interceptors.cc).
inline const u8 *getShadowTypeAddrFor(const u8 *Ptr) {
return getShadowTypeAddrFor(const_cast<u8 *>(Ptr));
inline const u8 *GetShadowTypeAddrFor(const u8 *Ptr) {
return GetShadowTypeAddrFor(const_cast<u8 *>(Ptr));
}
// Information about value types and their shadow counterparts.
@@ -164,60 +164,60 @@ template <> struct FTInfo<__float128> {
constexpr double kMaxULPDiff = INFINITY;
// Helper for getULPDiff that works on bit representations.
template <typename BT> double getULPDiffBits(BT V1Bits, BT V2Bits) {
template <typename BT> double GetULPDiffBits(BT v1_bits, BT v2_bits) {
// If the integer representations of two same-sign floats are subtracted then
// the absolute value of the result is equal to one plus the number of
// representable floats between them.
return V1Bits >= V2Bits ? V1Bits - V2Bits : V2Bits - V1Bits;
return v1_bits >= v2_bits ? v1_bits - v2_bits : v2_bits - v1_bits;
}
// Returns the the number of floating point values between V1 and V2, capped to
// Returns the the number of floating point values between v1 and v2, capped to
// u64max. Return 0 for (-0.0,0.0).
template <typename FT> double getULPDiff(FT V1, FT V2) {
if (V1 == V2) {
template <typename FT> double GetULPDiff(FT v1, FT v2) {
if (v1 == v2) {
return 0; // Typically, -0.0 and 0.0
}
using BT = typename FTInfo<FT>::orig_bits_type;
static_assert(sizeof(FT) == sizeof(BT), "not implemented");
static_assert(sizeof(BT) <= 64, "not implemented");
BT V1Bits;
__builtin_memcpy(&V1Bits, &V1, sizeof(BT));
BT V2Bits;
__builtin_memcpy(&V2Bits, &V2, sizeof(BT));
BT v1_bits;
__builtin_memcpy(&v1_bits, &v1, sizeof(BT));
BT v2_bits;
__builtin_memcpy(&v2_bits, &v2, sizeof(BT));
// Check whether the signs differ. IEEE-754 float types always store the sign
// in the most significant bit. NaNs and infinities are handled by the calling
// code.
constexpr BT kSignMask = BT{1} << (CHAR_BIT * sizeof(BT) - 1);
if ((V1Bits ^ V2Bits) & kSignMask) {
if ((v1_bits ^ v2_bits) & kSignMask) {
// Signs differ. We can get the ULPs as `getULPDiff(negative_number, -0.0)
// + getULPDiff(0.0, positive_number)`.
if (V1Bits & kSignMask) {
return getULPDiffBits<BT>(V1Bits, kSignMask) +
getULPDiffBits<BT>(0, V2Bits);
if (v1_bits & kSignMask) {
return GetULPDiffBits<BT>(v1_bits, kSignMask) +
GetULPDiffBits<BT>(0, v2_bits);
} else {
return getULPDiffBits<BT>(V2Bits, kSignMask) +
getULPDiffBits<BT>(0, V1Bits);
return GetULPDiffBits<BT>(v2_bits, kSignMask) +
GetULPDiffBits<BT>(0, v1_bits);
}
}
return getULPDiffBits(V1Bits, V2Bits);
return GetULPDiffBits(v1_bits, v2_bits);
}
// FIXME: This needs mor work: Because there is no 80-bit integer type, we have
// to go through __uint128_t. Therefore the assumptions about the sign bit do
// not hold.
template <> inline double getULPDiff(long double V1, long double V2) {
template <> inline double GetULPDiff(long double v1, long double v2) {
using BT = __uint128_t;
BT V1Bits = 0;
__builtin_memcpy(&V1Bits, &V1, sizeof(long double));
BT V2Bits = 0;
__builtin_memcpy(&V2Bits, &V2, sizeof(long double));
if ((V1Bits ^ V2Bits) & (BT{1} << (CHAR_BIT * sizeof(BT) - 1)))
return (V1 == V2) ? __sanitizer::u64{0} : kMaxULPDiff; // Signs differ.
BT v1_bits = 0;
__builtin_memcpy(&v1_bits, &v1, sizeof(long double));
BT v2_bits = 0;
__builtin_memcpy(&v2_bits, &v2, sizeof(long double));
if ((v1_bits ^ v2_bits) & (BT{1} << (CHAR_BIT * sizeof(BT) - 1)))
return v1 == v2 ? __sanitizer::u64{0} : kMaxULPDiff; // Signs differ.
// If the integer representations of two same-sign floats are subtracted then
// the absolute value of the result is equal to one plus the number of
// representable floats between them.
BT Diff = V1Bits >= V2Bits ? V1Bits - V2Bits : V2Bits - V1Bits;
return Diff >= kMaxULPDiff ? kMaxULPDiff : Diff;
BT diff = v1_bits >= v2_bits ? v1_bits - v2_bits : v2_bits - v1_bits;
return diff >= kMaxULPDiff ? kMaxULPDiff : diff;
}
} // end namespace __nsan

View File

@@ -15,15 +15,14 @@
#include "sanitizer_common/sanitizer_flag_parser.h"
#include "sanitizer_common/sanitizer_flags.h"
namespace __nsan {
using namespace __sanitizer;
using namespace __nsan;
SANITIZER_INTERFACE_WEAK_DEF(const char *, __nsan_default_options, void) {
return "";
}
using namespace __sanitizer;
Flags flags_data;
Flags __nsan::flags_data;
void Flags::SetDefaults() {
#define NSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
@@ -47,7 +46,7 @@ static const char *MaybeCallNsanDefaultOptions() {
return (&__nsan_default_options) ? __nsan_default_options() : "";
}
void InitializeFlags() {
void __nsan::InitializeFlags() {
SetCommonFlagsDefaults();
{
CommonFlags cf;
@@ -74,5 +73,3 @@ void InitializeFlags() {
flags().PopulateCache();
}
} // namespace __nsan

View File

@@ -1,364 +0,0 @@
//===-- nsan_interceptors.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
//
//===----------------------------------------------------------------------===//
//
// Interceptors for standard library functions.
//
// A note about `printf`: Make sure none of the interceptor code calls any
// part of the nsan framework that can call `printf`, since this could create
// a loop (`printf` itself uses the libc). printf-free functions are documented
// as such in nsan.h.
//
//===----------------------------------------------------------------------===//
#include "interception/interception.h"
#include "nsan/nsan.h"
#include "sanitizer_common/sanitizer_common.h"
#include <wchar.h>
#if SANITIZER_LINUX
extern "C" int mallopt(int param, int value);
#endif
using namespace __sanitizer;
using __nsan::NsanInitialized;
using __nsan::NsanInitIsRunning;
static constexpr uptr kEarlyAllocBufSize = 16384;
static uptr AllocatedBytes;
static char EarlyAllocBuf[kEarlyAllocBufSize];
static bool isInEarlyAllocBuf(const void *Ptr) {
return ((uptr)Ptr >= (uptr)EarlyAllocBuf &&
((uptr)Ptr - (uptr)EarlyAllocBuf) < sizeof(EarlyAllocBuf));
}
static u8 *toU8Ptr(wchar_t *ptr) { return reinterpret_cast<u8 *>(ptr); }
static const u8 *toU8Ptr(const wchar_t *ptr) {
return reinterpret_cast<const u8 *>(ptr);
}
template <typename T> T min(T a, T b) { return a < b ? a : b; }
// Handle allocation requests early (before all interceptors are setup). dlsym,
// for example, calls calloc.
static void *handleEarlyAlloc(uptr Size) {
void *Mem = (void *)&EarlyAllocBuf[AllocatedBytes];
AllocatedBytes += Size;
CHECK_LT(AllocatedBytes, kEarlyAllocBufSize);
return Mem;
}
INTERCEPTOR(void *, memset, void *Dst, int V, uptr Size) {
// NOTE: This guard is needed because nsan's initialization code might call
// memset.
if (!NsanInitialized && REAL(memset) == nullptr)
return internal_memset(Dst, V, Size);
void *Res = REAL(memset)(Dst, V, Size);
__nsan_set_value_unknown(static_cast<u8 *>(Dst), Size);
return Res;
}
INTERCEPTOR(wchar_t *, wmemset, wchar_t *Dst, wchar_t V, uptr Size) {
wchar_t *Res = REAL(wmemset)(Dst, V, Size);
__nsan_set_value_unknown(toU8Ptr(Dst), sizeof(wchar_t) * Size);
return Res;
}
INTERCEPTOR(void *, memmove, void *Dst, const void *Src, uptr Size) {
// NOTE: This guard is needed because nsan's initialization code might call
// memmove.
if (!NsanInitialized && REAL(memmove) == nullptr)
return internal_memmove(Dst, Src, Size);
void *Res = REAL(memmove)(Dst, Src, Size);
__nsan_copy_values(static_cast<u8 *>(Dst), static_cast<const u8 *>(Src),
Size);
return Res;
}
INTERCEPTOR(wchar_t *, wmemmove, wchar_t *Dst, const wchar_t *Src, uptr Size) {
wchar_t *Res = REAL(wmemmove)(Dst, Src, Size);
__nsan_copy_values(toU8Ptr(Dst), toU8Ptr(Src), sizeof(wchar_t) * Size);
return Res;
}
INTERCEPTOR(void *, memcpy, void *Dst, const void *Src, uptr Size) {
// NOTE: This guard is needed because nsan's initialization code might call
// memcpy.
if (!NsanInitialized && REAL(memcpy) == nullptr) {
// memmove is used here because on some platforms this will also
// intercept the memmove implementation.
return internal_memmove(Dst, Src, Size);
}
void *Res = REAL(memcpy)(Dst, Src, Size);
__nsan_copy_values(static_cast<u8 *>(Dst), static_cast<const u8 *>(Src),
Size);
return Res;
}
INTERCEPTOR(wchar_t *, wmemcpy, wchar_t *Dst, const wchar_t *Src, uptr Size) {
wchar_t *Res = REAL(wmemcpy)(Dst, Src, Size);
__nsan_copy_values(toU8Ptr(Dst), toU8Ptr(Src), sizeof(wchar_t) * Size);
return Res;
}
INTERCEPTOR(void *, malloc, uptr Size) {
// NOTE: This guard is needed because nsan's initialization code might call
// malloc.
if (NsanInitIsRunning && REAL(malloc) == nullptr)
return handleEarlyAlloc(Size);
void *Res = REAL(malloc)(Size);
if (Res)
__nsan_set_value_unknown(static_cast<u8 *>(Res), Size);
return Res;
}
INTERCEPTOR(void *, realloc, void *Ptr, uptr Size) {
void *Res = REAL(realloc)(Ptr, Size);
// FIXME: We might want to copy the types from the original allocation
// (although that would require that we know its size).
if (Res)
__nsan_set_value_unknown(static_cast<u8 *>(Res), Size);
return Res;
}
INTERCEPTOR(void *, calloc, uptr Nmemb, uptr Size) {
// NOTE: This guard is needed because nsan's initialization code might call
// calloc.
if (NsanInitIsRunning && REAL(calloc) == nullptr) {
// Note: EarlyAllocBuf is initialized with zeros.
return handleEarlyAlloc(Nmemb * Size);
}
void *Res = REAL(calloc)(Nmemb, Size);
if (Res)
__nsan_set_value_unknown(static_cast<u8 *>(Res), Nmemb * Size);
return Res;
}
INTERCEPTOR(void, free, void *P) {
// There are only a few early allocation requests, so we simply skip the free.
if (isInEarlyAllocBuf(P))
return;
REAL(free)(P);
}
INTERCEPTOR(void *, valloc, uptr Size) {
void *const Res = REAL(valloc)(Size);
if (Res)
__nsan_set_value_unknown(static_cast<u8 *>(Res), Size);
return Res;
}
INTERCEPTOR(void *, memalign, uptr Alignment, uptr Size) {
void *const Res = REAL(memalign)(Alignment, Size);
if (Res)
__nsan_set_value_unknown(static_cast<u8 *>(Res), Size);
return Res;
}
INTERCEPTOR(void *, __libc_memalign, uptr Alignment, uptr Size) {
void *const Res = REAL(__libc_memalign)(Alignment, Size);
if (Res)
__nsan_set_value_unknown(static_cast<u8 *>(Res), Size);
return Res;
}
INTERCEPTOR(void *, pvalloc, uptr Size) {
void *const Res = REAL(pvalloc)(Size);
if (Res)
__nsan_set_value_unknown(static_cast<u8 *>(Res), Size);
return Res;
}
INTERCEPTOR(void *, aligned_alloc, uptr Alignment, uptr Size) {
void *const Res = REAL(aligned_alloc)(Alignment, Size);
if (Res)
__nsan_set_value_unknown(static_cast<u8 *>(Res), Size);
return Res;
}
INTERCEPTOR(int, posix_memalign, void **Memptr, uptr Alignment, uptr Size) {
int Res = REAL(posix_memalign)(Memptr, Alignment, Size);
if (Res == 0 && *Memptr)
__nsan_set_value_unknown(static_cast<u8 *>(*Memptr), Size);
return Res;
}
INTERCEPTOR(char *, strfry, char *S) {
const auto Len = internal_strlen(S);
char *Res = REAL(strfry)(S);
if (Res)
__nsan_set_value_unknown(reinterpret_cast<u8 *>(S), Len);
return Res;
}
INTERCEPTOR(char *, strsep, char **Stringp, const char *Delim) {
char *OrigStringp = REAL(strsep)(Stringp, Delim);
if (Stringp != nullptr) {
// The previous character has been overwritten with a '\0' char.
__nsan_set_value_unknown(reinterpret_cast<u8 *>(*Stringp) - 1, 1);
}
return OrigStringp;
}
INTERCEPTOR(char *, strtok, char *Str, const char *Delim) {
// This is overly conservative, but the probability that modern code is using
// strtok on double data is essentially zero anyway.
if (Str)
__nsan_set_value_unknown(reinterpret_cast<u8 *>(Str), internal_strlen(Str));
return REAL(strtok)(Str, Delim);
}
static void nsanCopyZeroTerminated(char *Dst, const char *Src, uptr N) {
__nsan_copy_values(reinterpret_cast<u8 *>(Dst),
reinterpret_cast<const u8 *>(Src), N); // Data.
__nsan_set_value_unknown(reinterpret_cast<u8 *>(Dst) + N, 1); // Terminator.
}
static void nsanWCopyZeroTerminated(wchar_t *Dst, const wchar_t *Src, uptr N) {
__nsan_copy_values(toU8Ptr(Dst), toU8Ptr(Src), sizeof(wchar_t) * N);
__nsan_set_value_unknown(toU8Ptr(Dst + N), sizeof(wchar_t));
}
INTERCEPTOR(char *, strdup, const char *S) {
char *Res = REAL(strdup)(S);
if (Res) {
nsanCopyZeroTerminated(Res, S, internal_strlen(S));
}
return Res;
}
INTERCEPTOR(wchar_t *, wcsdup, const wchar_t *S) {
wchar_t *Res = REAL(wcsdup)(S);
if (Res) {
nsanWCopyZeroTerminated(Res, S, wcslen(S));
}
return Res;
}
INTERCEPTOR(char *, strndup, const char *S, uptr Size) {
char *Res = REAL(strndup)(S, Size);
if (Res) {
nsanCopyZeroTerminated(Res, S, min(internal_strlen(S), Size));
}
return Res;
}
INTERCEPTOR(char *, strcpy, char *Dst, const char *Src) {
char *Res = REAL(strcpy)(Dst, Src);
nsanCopyZeroTerminated(Dst, Src, internal_strlen(Src));
return Res;
}
INTERCEPTOR(wchar_t *, wcscpy, wchar_t *Dst, const wchar_t *Src) {
wchar_t *Res = REAL(wcscpy)(Dst, Src);
nsanWCopyZeroTerminated(Dst, Src, wcslen(Src));
return Res;
}
INTERCEPTOR(char *, strncpy, char *Dst, const char *Src, uptr Size) {
char *Res = REAL(strncpy)(Dst, Src, Size);
nsanCopyZeroTerminated(Dst, Src, min(Size, internal_strlen(Src)));
return Res;
}
INTERCEPTOR(char *, strcat, char *Dst, const char *Src) {
const auto DstLenBeforeCat = internal_strlen(Dst);
char *Res = REAL(strcat)(Dst, Src);
nsanCopyZeroTerminated(Dst + DstLenBeforeCat, Src, internal_strlen(Src));
return Res;
}
INTERCEPTOR(wchar_t *, wcscat, wchar_t *Dst, const wchar_t *Src) {
const auto DstLenBeforeCat = wcslen(Dst);
wchar_t *Res = REAL(wcscat)(Dst, Src);
nsanWCopyZeroTerminated(Dst + DstLenBeforeCat, Src, wcslen(Src));
return Res;
}
INTERCEPTOR(char *, strncat, char *Dst, const char *Src, uptr Size) {
const auto DstLen = internal_strlen(Dst);
char *Res = REAL(strncat)(Dst, Src, Size);
nsanCopyZeroTerminated(Dst + DstLen, Src, min(Size, internal_strlen(Src)));
return Res;
}
INTERCEPTOR(char *, stpcpy, char *Dst, const char *Src) {
char *Res = REAL(stpcpy)(Dst, Src);
nsanCopyZeroTerminated(Dst, Src, internal_strlen(Src));
return Res;
}
INTERCEPTOR(wchar_t *, wcpcpy, wchar_t *Dst, const wchar_t *Src) {
wchar_t *Res = REAL(wcpcpy)(Dst, Src);
nsanWCopyZeroTerminated(Dst, Src, wcslen(Src));
return Res;
}
INTERCEPTOR(uptr, strxfrm, char *Dst, const char *Src, uptr Size) {
// This is overly conservative, but this function should very rarely be used.
__nsan_set_value_unknown(reinterpret_cast<u8 *>(Dst), internal_strlen(Dst));
const uptr Res = REAL(strxfrm)(Dst, Src, Size);
return Res;
}
namespace __nsan {
void initializeInterceptors() {
static bool Initialized = false;
CHECK(!Initialized);
// Instruct libc malloc to consume less memory.
#if SANITIZER_LINUX
mallopt(1, 0); // M_MXFAST
mallopt(-3, 32 * 1024); // M_MMAP_THRESHOLD
#endif
INTERCEPT_FUNCTION(malloc);
INTERCEPT_FUNCTION(calloc);
INTERCEPT_FUNCTION(free);
INTERCEPT_FUNCTION(realloc);
INTERCEPT_FUNCTION(valloc);
INTERCEPT_FUNCTION(memalign);
INTERCEPT_FUNCTION(__libc_memalign);
INTERCEPT_FUNCTION(pvalloc);
INTERCEPT_FUNCTION(aligned_alloc);
INTERCEPT_FUNCTION(posix_memalign);
INTERCEPT_FUNCTION(memset);
INTERCEPT_FUNCTION(wmemset);
INTERCEPT_FUNCTION(memmove);
INTERCEPT_FUNCTION(wmemmove);
INTERCEPT_FUNCTION(memcpy);
INTERCEPT_FUNCTION(wmemcpy);
INTERCEPT_FUNCTION(strdup);
INTERCEPT_FUNCTION(wcsdup);
INTERCEPT_FUNCTION(strndup);
INTERCEPT_FUNCTION(stpcpy);
INTERCEPT_FUNCTION(wcpcpy);
INTERCEPT_FUNCTION(strcpy);
INTERCEPT_FUNCTION(wcscpy);
INTERCEPT_FUNCTION(strncpy);
INTERCEPT_FUNCTION(strcat);
INTERCEPT_FUNCTION(wcscat);
INTERCEPT_FUNCTION(strncat);
INTERCEPT_FUNCTION(strxfrm);
INTERCEPT_FUNCTION(strfry);
INTERCEPT_FUNCTION(strsep);
INTERCEPT_FUNCTION(strtok);
Initialized = 1;
}
} // end namespace __nsan

View File

@@ -0,0 +1,356 @@
//===-- nsan_interceptors.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
//
//===----------------------------------------------------------------------===//
//
// Interceptors for standard library functions.
//
// A note about `printf`: Make sure none of the interceptor code calls any
// part of the nsan framework that can call `printf`, since this could create
// a loop (`printf` itself uses the libc). printf-free functions are documented
// as such in nsan.h.
//
//===----------------------------------------------------------------------===//
#include "interception/interception.h"
#include "nsan/nsan.h"
#include "sanitizer_common/sanitizer_common.h"
#include <wchar.h>
#if SANITIZER_LINUX
extern "C" int mallopt(int param, int value);
#endif
using namespace __sanitizer;
using __nsan::nsan_init_is_running;
using __nsan::nsan_initialized;
constexpr uptr kEarlyAllocBufSize = 16384;
static uptr allocated_bytes;
static char early_alloc_buf[kEarlyAllocBufSize];
static bool isInEarlyAllocBuf(const void *ptr) {
return ((uptr)ptr >= (uptr)early_alloc_buf &&
((uptr)ptr - (uptr)early_alloc_buf) < sizeof(early_alloc_buf));
}
template <typename T> T min(T a, T b) { return a < b ? a : b; }
// Handle allocation requests early (before all interceptors are setup). dlsym,
// for example, calls calloc.
static void *HandleEarlyAlloc(uptr size) {
void *Mem = (void *)&early_alloc_buf[allocated_bytes];
allocated_bytes += size;
CHECK_LT(allocated_bytes, kEarlyAllocBufSize);
return Mem;
}
INTERCEPTOR(void *, memset, void *dst, int v, uptr size) {
// NOTE: This guard is needed because nsan's initialization code might call
// memset.
if (!nsan_initialized && REAL(memset) == nullptr)
return internal_memset(dst, v, size);
void *res = REAL(memset)(dst, v, size);
__nsan_set_value_unknown(static_cast<u8 *>(dst), size);
return res;
}
INTERCEPTOR(wchar_t *, wmemset, wchar_t *dst, wchar_t v, uptr size) {
wchar_t *res = REAL(wmemset)(dst, v, size);
__nsan_set_value_unknown((u8 *)dst, sizeof(wchar_t) * size);
return res;
}
INTERCEPTOR(void *, memmove, void *dst, const void *src, uptr size) {
// NOTE: This guard is needed because nsan's initialization code might call
// memmove.
if (!nsan_initialized && REAL(memmove) == nullptr)
return internal_memmove(dst, src, size);
void *res = REAL(memmove)(dst, src, size);
__nsan_copy_values(static_cast<u8 *>(dst), static_cast<const u8 *>(src),
size);
return res;
}
INTERCEPTOR(wchar_t *, wmemmove, wchar_t *dst, const wchar_t *src, uptr size) {
wchar_t *res = REAL(wmemmove)(dst, src, size);
__nsan_copy_values((u8 *)dst, (const u8 *)src, sizeof(wchar_t) * size);
return res;
}
INTERCEPTOR(void *, memcpy, void *dst, const void *src, uptr size) {
// NOTE: This guard is needed because nsan's initialization code might call
// memcpy.
if (!nsan_initialized && REAL(memcpy) == nullptr) {
// memmove is used here because on some platforms this will also
// intercept the memmove implementation.
return internal_memmove(dst, src, size);
}
void *res = REAL(memcpy)(dst, src, size);
__nsan_copy_values(static_cast<u8 *>(dst), static_cast<const u8 *>(src),
size);
return res;
}
INTERCEPTOR(wchar_t *, wmemcpy, wchar_t *dst, const wchar_t *src, uptr size) {
wchar_t *res = REAL(wmemcpy)(dst, src, size);
__nsan_copy_values((u8 *)dst, (const u8 *)src, sizeof(wchar_t) * size);
return res;
}
INTERCEPTOR(void *, malloc, uptr size) {
// NOTE: This guard is needed because nsan's initialization code might call
// malloc.
if (nsan_init_is_running && REAL(malloc) == nullptr)
return HandleEarlyAlloc(size);
void *res = REAL(malloc)(size);
if (res)
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
return res;
}
INTERCEPTOR(void *, realloc, void *ptr, uptr size) {
void *res = REAL(realloc)(ptr, size);
// FIXME: We might want to copy the types from the original allocation
// (although that would require that we know its size).
if (res)
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
return res;
}
INTERCEPTOR(void *, calloc, uptr Nmemb, uptr size) {
// NOTE: This guard is needed because nsan's initialization code might call
// calloc.
if (nsan_init_is_running && REAL(calloc) == nullptr) {
// Note: EarlyAllocBuf is initialized with zeros.
return HandleEarlyAlloc(Nmemb * size);
}
void *res = REAL(calloc)(Nmemb, size);
if (res)
__nsan_set_value_unknown(static_cast<u8 *>(res), Nmemb * size);
return res;
}
INTERCEPTOR(void, free, void *P) {
// There are only a few early allocation requests, so we simply skip the free.
if (isInEarlyAllocBuf(P))
return;
REAL(free)(P);
}
INTERCEPTOR(void *, valloc, uptr size) {
void *const res = REAL(valloc)(size);
if (res)
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
return res;
}
INTERCEPTOR(void *, memalign, uptr align, uptr size) {
void *const res = REAL(memalign)(align, size);
if (res)
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
return res;
}
INTERCEPTOR(void *, __libc_memalign, uptr align, uptr size) {
void *const res = REAL(__libc_memalign)(align, size);
if (res)
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
return res;
}
INTERCEPTOR(void *, pvalloc, uptr size) {
void *const res = REAL(pvalloc)(size);
if (res)
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
return res;
}
INTERCEPTOR(void *, aligned_alloc, uptr align, uptr size) {
void *const res = REAL(aligned_alloc)(align, size);
if (res)
__nsan_set_value_unknown(static_cast<u8 *>(res), size);
return res;
}
INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr size) {
int res = REAL(posix_memalign)(memptr, align, size);
if (res == 0 && *memptr)
__nsan_set_value_unknown(static_cast<u8 *>(*memptr), size);
return res;
}
INTERCEPTOR(char *, strfry, char *s) {
const auto Len = internal_strlen(s);
char *res = REAL(strfry)(s);
if (res)
__nsan_set_value_unknown(reinterpret_cast<u8 *>(s), Len);
return res;
}
INTERCEPTOR(char *, strsep, char **Stringp, const char *delim) {
char *OrigStringp = REAL(strsep)(Stringp, delim);
if (Stringp != nullptr) {
// The previous character has been overwritten with a '\0' char.
__nsan_set_value_unknown(reinterpret_cast<u8 *>(*Stringp) - 1, 1);
}
return OrigStringp;
}
INTERCEPTOR(char *, strtok, char *str, const char *delim) {
// This is overly conservative, but the probability that modern code is using
// strtok on double data is essentially zero anyway.
if (str)
__nsan_set_value_unknown(reinterpret_cast<u8 *>(str), internal_strlen(str));
return REAL(strtok)(str, delim);
}
static void nsanCopyZeroTerminated(char *dst, const char *src, uptr n) {
__nsan_copy_values(reinterpret_cast<u8 *>(dst),
reinterpret_cast<const u8 *>(src), n); // Data.
__nsan_set_value_unknown(reinterpret_cast<u8 *>(dst) + n, 1); // Terminator.
}
static void nsanWCopyZeroTerminated(wchar_t *dst, const wchar_t *src, uptr n) {
__nsan_copy_values((u8 *)dst, (const u8 *)(src), sizeof(wchar_t) * n);
__nsan_set_value_unknown((u8 *)(dst + n), sizeof(wchar_t));
}
INTERCEPTOR(char *, strdup, const char *S) {
char *res = REAL(strdup)(S);
if (res) {
nsanCopyZeroTerminated(res, S, internal_strlen(S));
}
return res;
}
INTERCEPTOR(wchar_t *, wcsdup, const wchar_t *S) {
wchar_t *res = REAL(wcsdup)(S);
if (res) {
nsanWCopyZeroTerminated(res, S, wcslen(S));
}
return res;
}
INTERCEPTOR(char *, strndup, const char *S, uptr size) {
char *res = REAL(strndup)(S, size);
if (res) {
nsanCopyZeroTerminated(res, S, min(internal_strlen(S), size));
}
return res;
}
INTERCEPTOR(char *, strcpy, char *dst, const char *src) {
char *res = REAL(strcpy)(dst, src);
nsanCopyZeroTerminated(dst, src, internal_strlen(src));
return res;
}
INTERCEPTOR(wchar_t *, wcscpy, wchar_t *dst, const wchar_t *src) {
wchar_t *res = REAL(wcscpy)(dst, src);
nsanWCopyZeroTerminated(dst, src, wcslen(src));
return res;
}
INTERCEPTOR(char *, strncpy, char *dst, const char *src, uptr size) {
char *res = REAL(strncpy)(dst, src, size);
nsanCopyZeroTerminated(dst, src, min(size, internal_strlen(src)));
return res;
}
INTERCEPTOR(char *, strcat, char *dst, const char *src) {
const auto DstLenBeforeCat = internal_strlen(dst);
char *res = REAL(strcat)(dst, src);
nsanCopyZeroTerminated(dst + DstLenBeforeCat, src, internal_strlen(src));
return res;
}
INTERCEPTOR(wchar_t *, wcscat, wchar_t *dst, const wchar_t *src) {
const auto DstLenBeforeCat = wcslen(dst);
wchar_t *res = REAL(wcscat)(dst, src);
nsanWCopyZeroTerminated(dst + DstLenBeforeCat, src, wcslen(src));
return res;
}
INTERCEPTOR(char *, strncat, char *dst, const char *src, uptr size) {
const auto DstLen = internal_strlen(dst);
char *res = REAL(strncat)(dst, src, size);
nsanCopyZeroTerminated(dst + DstLen, src, min(size, internal_strlen(src)));
return res;
}
INTERCEPTOR(char *, stpcpy, char *dst, const char *src) {
char *res = REAL(stpcpy)(dst, src);
nsanCopyZeroTerminated(dst, src, internal_strlen(src));
return res;
}
INTERCEPTOR(wchar_t *, wcpcpy, wchar_t *dst, const wchar_t *src) {
wchar_t *res = REAL(wcpcpy)(dst, src);
nsanWCopyZeroTerminated(dst, src, wcslen(src));
return res;
}
INTERCEPTOR(uptr, strxfrm, char *dst, const char *src, uptr size) {
// This is overly conservative, but this function should very rarely be used.
__nsan_set_value_unknown(reinterpret_cast<u8 *>(dst), internal_strlen(dst));
const uptr res = REAL(strxfrm)(dst, src, size);
return res;
}
void __nsan::InitializeInterceptors() {
static bool initialized = false;
CHECK(!initialized);
// Instruct libc malloc to consume less memory.
#if SANITIZER_LINUX
mallopt(1, 0); // M_MXFAST
mallopt(-3, 32 * 1024); // M_MMAP_THRESHOLD
#endif
INTERCEPT_FUNCTION(malloc);
INTERCEPT_FUNCTION(calloc);
INTERCEPT_FUNCTION(free);
INTERCEPT_FUNCTION(realloc);
INTERCEPT_FUNCTION(valloc);
INTERCEPT_FUNCTION(memalign);
INTERCEPT_FUNCTION(__libc_memalign);
INTERCEPT_FUNCTION(pvalloc);
INTERCEPT_FUNCTION(aligned_alloc);
INTERCEPT_FUNCTION(posix_memalign);
INTERCEPT_FUNCTION(memset);
INTERCEPT_FUNCTION(wmemset);
INTERCEPT_FUNCTION(memmove);
INTERCEPT_FUNCTION(wmemmove);
INTERCEPT_FUNCTION(memcpy);
INTERCEPT_FUNCTION(wmemcpy);
INTERCEPT_FUNCTION(strdup);
INTERCEPT_FUNCTION(wcsdup);
INTERCEPT_FUNCTION(strndup);
INTERCEPT_FUNCTION(stpcpy);
INTERCEPT_FUNCTION(wcpcpy);
INTERCEPT_FUNCTION(strcpy);
INTERCEPT_FUNCTION(wcscpy);
INTERCEPT_FUNCTION(strncpy);
INTERCEPT_FUNCTION(strcat);
INTERCEPT_FUNCTION(wcscat);
INTERCEPT_FUNCTION(strncat);
INTERCEPT_FUNCTION(strxfrm);
INTERCEPT_FUNCTION(strfry);
INTERCEPT_FUNCTION(strsep);
INTERCEPT_FUNCTION(strtok);
initialized = 1;
}

View File

@@ -1,158 +0,0 @@
//===-- nsan_stats.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
//
//===----------------------------------------------------------------------===//
//
// This file is a part of NumericalStabilitySanitizer.
//
// NumericalStabilitySanitizer statistics.
//===----------------------------------------------------------------------===//
#include "nsan/nsan_stats.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
#include <assert.h>
#include <stdio.h>
namespace __nsan {
using namespace __sanitizer;
Stats::Stats() {
CheckAndWarnings.Initialize(0);
TrackedLoads.Initialize(0);
}
Stats::~Stats() { Printf("deleting nsan stats\n"); }
static uptr key(CheckTypeT CheckType, u32 StackId) {
return static_cast<uptr>(CheckType) +
StackId * static_cast<uptr>(CheckTypeT::kMaxCheckType);
}
template <typename MapT, typename VectorT, typename Fn>
void UpdateEntry(CheckTypeT CheckTy, uptr PC, uptr BP, MapT *Map,
VectorT *Vector, Mutex *Mutex, Fn F) {
BufferedStackTrace Stack;
Stack.Unwind(PC, BP, nullptr, false);
u32 StackId = StackDepotPut(Stack);
typename MapT::Handle Handle(Map, key(CheckTy, StackId));
Lock L(Mutex);
if (Handle.created()) {
typename VectorT::value_type Entry;
Entry.StackId = StackId;
Entry.CheckTy = CheckTy;
F(Entry);
Vector->push_back(Entry);
} else {
auto &Entry = (*Vector)[*Handle];
F(Entry);
}
}
void Stats::addCheck(CheckTypeT CheckTy, uptr PC, uptr BP, double RelErr) {
UpdateEntry(CheckTy, PC, BP, &CheckAndWarningsMap, &CheckAndWarnings,
&CheckAndWarningsMutex, [RelErr](CheckAndWarningsValue &Entry) {
++Entry.NumChecks;
if (RelErr > Entry.MaxRelativeError) {
Entry.MaxRelativeError = RelErr;
}
});
}
void Stats::addWarning(CheckTypeT CheckTy, uptr PC, uptr BP, double RelErr) {
UpdateEntry(CheckTy, PC, BP, &CheckAndWarningsMap, &CheckAndWarnings,
&CheckAndWarningsMutex, [RelErr](CheckAndWarningsValue &Entry) {
++Entry.NumWarnings;
if (RelErr > Entry.MaxRelativeError) {
Entry.MaxRelativeError = RelErr;
}
});
}
void Stats::addInvalidLoadTrackingEvent(uptr PC, uptr BP) {
UpdateEntry(CheckTypeT::kLoad, PC, BP, &LoadTrackingMap, &TrackedLoads,
&TrackedLoadsMutex,
[](LoadTrackingValue &Entry) { ++Entry.NumInvalid; });
}
void Stats::addUnknownLoadTrackingEvent(uptr PC, uptr BP) {
UpdateEntry(CheckTypeT::kLoad, PC, BP, &LoadTrackingMap, &TrackedLoads,
&TrackedLoadsMutex,
[](LoadTrackingValue &Entry) { ++Entry.NumUnknown; });
}
static const char *CheckTypeDisplay(CheckTypeT CheckType) {
switch (CheckType) {
case CheckTypeT::kUnknown:
return "unknown";
case CheckTypeT::kRet:
return "return";
case CheckTypeT::kArg:
return "argument";
case CheckTypeT::kLoad:
return "load";
case CheckTypeT::kStore:
return "store";
case CheckTypeT::kInsert:
return "vector insert";
case CheckTypeT::kUser:
return "user-initiated";
case CheckTypeT::kFcmp:
return "fcmp";
case CheckTypeT::kMaxCheckType:
return "[max]";
}
assert(false && "unknown CheckType case");
return "";
}
void Stats::print() const {
{
Lock L(&CheckAndWarningsMutex);
for (const auto &Entry : CheckAndWarnings) {
Printf("warned %llu times out of %llu %s checks ", Entry.NumWarnings,
Entry.NumChecks, CheckTypeDisplay(Entry.CheckTy));
if (Entry.NumWarnings > 0) {
char RelErrBuf[64];
snprintf(RelErrBuf, sizeof(RelErrBuf) - 1, "%f",
Entry.MaxRelativeError * 100.0);
Printf("(max relative error: %s%%) ", RelErrBuf);
}
Printf("at:\n");
StackDepotGet(Entry.StackId).Print();
}
}
{
Lock L(&TrackedLoadsMutex);
u64 TotalInvalidLoadTracking = 0;
u64 TotalUnknownLoadTracking = 0;
for (const auto &Entry : TrackedLoads) {
TotalInvalidLoadTracking += Entry.NumInvalid;
TotalUnknownLoadTracking += Entry.NumUnknown;
Printf("invalid/unknown type for %llu/%llu loads at:\n", Entry.NumInvalid,
Entry.NumUnknown);
StackDepotGet(Entry.StackId).Print();
}
Printf(
"There were %llu/%llu floating-point loads where the shadow type was "
"invalid/unknown.\n",
TotalInvalidLoadTracking, TotalUnknownLoadTracking);
}
}
ALIGNED(64) static char StatsPlaceholder[sizeof(Stats)];
Stats *nsan_stats = nullptr;
void initializeStats() { nsan_stats = new (StatsPlaceholder) Stats(); }
} // namespace __nsan

View File

@@ -0,0 +1,157 @@
//===-- nsan_stats.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
//
//===----------------------------------------------------------------------===//
//
// This file is a part of NumericalStabilitySanitizer.
//
// NumericalStabilitySanitizer statistics.
//===----------------------------------------------------------------------===//
#include "nsan/nsan_stats.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
#include <assert.h>
#include <stdio.h>
using namespace __sanitizer;
using namespace __nsan;
Stats::Stats() {
check_and_warnings.Initialize(0);
TrackedLoads.Initialize(0);
}
Stats::~Stats() { Printf("deleting nsan stats\n"); }
static uptr Key(CheckTypeT CheckType, u32 StackId) {
return static_cast<uptr>(CheckType) +
StackId * static_cast<uptr>(CheckTypeT::kMaxCheckType);
}
template <typename MapT, typename VectorT, typename Fn>
static void UpdateEntry(CheckTypeT check_ty, uptr pc, uptr bp, MapT *map,
VectorT *vector, Mutex *mutex, Fn F) {
BufferedStackTrace Stack;
Stack.Unwind(pc, bp, nullptr, false);
u32 stack_id = StackDepotPut(Stack);
typename MapT::Handle Handle(map, Key(check_ty, stack_id));
Lock L(mutex);
if (Handle.created()) {
typename VectorT::value_type entry;
entry.stack_id = stack_id;
entry.check_ty = check_ty;
F(entry);
vector->push_back(entry);
} else {
auto &entry = (*vector)[*Handle];
F(entry);
}
}
void Stats::AddCheck(CheckTypeT check_ty, uptr pc, uptr bp, double rel_err) {
UpdateEntry(check_ty, pc, bp, &CheckAndWarningsMap, &check_and_warnings,
&check_and_warning_mutex,
[rel_err](CheckAndWarningsValue &entry) {
++entry.num_checks;
if (rel_err > entry.max_relative_err) {
entry.max_relative_err = rel_err;
}
});
}
void Stats::AddWarning(CheckTypeT check_ty, uptr pc, uptr bp, double rel_err) {
UpdateEntry(check_ty, pc, bp, &CheckAndWarningsMap, &check_and_warnings,
&check_and_warning_mutex,
[rel_err](CheckAndWarningsValue &entry) {
++entry.num_warnings;
if (rel_err > entry.max_relative_err) {
entry.max_relative_err = rel_err;
}
});
}
void Stats::AddInvalidLoadTrackingEvent(uptr pc, uptr bp) {
UpdateEntry(CheckTypeT::kLoad, pc, bp, &LoadTrackingMap, &TrackedLoads,
&TrackedLoadsMutex,
[](LoadTrackingValue &entry) { ++entry.num_invalid; });
}
void Stats::AddUnknownLoadTrackingEvent(uptr pc, uptr bp) {
UpdateEntry(CheckTypeT::kLoad, pc, bp, &LoadTrackingMap, &TrackedLoads,
&TrackedLoadsMutex,
[](LoadTrackingValue &entry) { ++entry.num_unknown; });
}
static const char *CheckTypeDisplay(CheckTypeT CheckType) {
switch (CheckType) {
case CheckTypeT::kUnknown:
return "unknown";
case CheckTypeT::kRet:
return "return";
case CheckTypeT::kArg:
return "argument";
case CheckTypeT::kLoad:
return "load";
case CheckTypeT::kStore:
return "store";
case CheckTypeT::kInsert:
return "vector insert";
case CheckTypeT::kUser:
return "user-initiated";
case CheckTypeT::kFcmp:
return "fcmp";
case CheckTypeT::kMaxCheckType:
return "[max]";
}
assert(false && "unknown CheckType case");
return "";
}
void Stats::Print() const {
{
Lock L(&check_and_warning_mutex);
for (const auto &entry : check_and_warnings) {
Printf("warned %llu times out of %llu %s checks ", entry.num_warnings,
entry.num_checks, CheckTypeDisplay(entry.check_ty));
if (entry.num_warnings > 0) {
char RelErrBuf[64];
snprintf(RelErrBuf, sizeof(RelErrBuf) - 1, "%f",
entry.max_relative_err * 100.0);
Printf("(max relative error: %s%%) ", RelErrBuf);
}
Printf("at:\n");
StackDepotGet(entry.stack_id).Print();
}
}
{
Lock L(&TrackedLoadsMutex);
u64 TotalInvalidLoadTracking = 0;
u64 TotalUnknownLoadTracking = 0;
for (const auto &entry : TrackedLoads) {
TotalInvalidLoadTracking += entry.num_invalid;
TotalUnknownLoadTracking += entry.num_unknown;
Printf("invalid/unknown type for %llu/%llu loads at:\n",
entry.num_invalid, entry.num_unknown);
StackDepotGet(entry.stack_id).Print();
}
Printf(
"There were %llu/%llu floating-point loads where the shadow type was "
"invalid/unknown.\n",
TotalInvalidLoadTracking, TotalUnknownLoadTracking);
}
}
alignas(64) static char stats_placeholder[sizeof(Stats)];
Stats *__nsan::nsan_stats = nullptr;
void __nsan::InitializeStats() { nsan_stats = new (stats_placeholder) Stats(); }

View File

@@ -40,52 +40,53 @@ public:
~Stats();
// Signal that we checked the instruction at the given address.
void addCheck(CheckTypeT CheckType, __sanitizer::uptr PC,
__sanitizer::uptr BP, double RelErr);
void AddCheck(CheckTypeT check_ty, __sanitizer::uptr pc, __sanitizer::uptr bp,
double rel_err);
// Signal that we warned for the instruction at the given address.
void addWarning(CheckTypeT CheckType, __sanitizer::uptr PC,
__sanitizer::uptr BP, double RelErr);
void AddWarning(CheckTypeT check_ty, __sanitizer::uptr pc,
__sanitizer::uptr bp, double rel_err);
// Signal that we detected a floating-point load where the shadow type was
// invalid.
void addInvalidLoadTrackingEvent(__sanitizer::uptr PC, __sanitizer::uptr BP);
void AddInvalidLoadTrackingEvent(__sanitizer::uptr pc, __sanitizer::uptr bp);
// Signal that we detected a floating-point load where the shadow type was
// unknown but the value was nonzero.
void addUnknownLoadTrackingEvent(__sanitizer::uptr PC, __sanitizer::uptr BP);
void AddUnknownLoadTrackingEvent(__sanitizer::uptr pc, __sanitizer::uptr bp);
void print() const;
void Print() const;
private:
using IndexMap = __sanitizer::AddrHashMap<__sanitizer::uptr, 11>;
struct CheckAndWarningsValue {
CheckTypeT CheckTy;
__sanitizer::u32 StackId = 0;
__sanitizer::u64 NumChecks = 0;
__sanitizer::u64 NumWarnings = 0;
CheckTypeT check_ty;
__sanitizer::u32 stack_id = 0;
__sanitizer::u64 num_checks = 0;
__sanitizer::u64 num_warnings = 0;
// This is a bitcasted double. Doubles have the nice idea to be ordered as
// ints.
double MaxRelativeError = 0;
double max_relative_err = 0;
};
// Maps key(CheckType, StackId) to indices in CheckAndWarnings.
// Map Key(check_ty, StackId) to indices in CheckAndWarnings.
IndexMap CheckAndWarningsMap;
__sanitizer::InternalMmapVectorNoCtor<CheckAndWarningsValue> CheckAndWarnings;
mutable __sanitizer::Mutex CheckAndWarningsMutex;
__sanitizer::InternalMmapVectorNoCtor<CheckAndWarningsValue>
check_and_warnings;
mutable __sanitizer::Mutex check_and_warning_mutex;
struct LoadTrackingValue {
CheckTypeT CheckTy;
__sanitizer::u32 StackId = 0;
__sanitizer::u64 NumInvalid = 0;
__sanitizer::u64 NumUnknown = 0;
CheckTypeT check_ty;
__sanitizer::u32 stack_id = 0;
__sanitizer::u64 num_invalid = 0;
__sanitizer::u64 num_unknown = 0;
};
// Maps key(CheckTypeT::kLoad, StackId) to indices in TrackedLoads.
// Map Key(CheckTypeT::kLoad, StackId) to indices in TrackedLoads.
IndexMap LoadTrackingMap;
__sanitizer::InternalMmapVectorNoCtor<LoadTrackingValue> TrackedLoads;
mutable __sanitizer::Mutex TrackedLoadsMutex;
};
extern Stats *nsan_stats;
void initializeStats();
void InitializeStats();
} // namespace __nsan

View File

@@ -1,77 +0,0 @@
//===-- nsan_suppressions.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
//
//===----------------------------------------------------------------------===//
#include "nsan_suppressions.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
#include "nsan_flags.h"
// Can be overriden in frontend.
SANITIZER_WEAK_DEFAULT_IMPL
const char *__nsan_default_suppressions() { return 0; }
namespace __nsan {
const char *const kSuppressionFcmp = "fcmp";
const char *const kSuppressionConsistency = "consistency";
using namespace __sanitizer;
ALIGNED(64) static char SuppressionPlaceholder[sizeof(SuppressionContext)];
static SuppressionContext *SuppressionCtx = nullptr;
// The order should match the enum CheckKind.
static const char *kSuppressionTypes[] = {kSuppressionFcmp,
kSuppressionConsistency};
void InitializeSuppressions() {
CHECK_EQ(nullptr, SuppressionCtx);
SuppressionCtx = new (SuppressionPlaceholder)
SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
SuppressionCtx->ParseFromFile(flags().suppressions);
SuppressionCtx->Parse(__nsan_default_suppressions());
}
static Suppression *GetSuppressionForAddr(uptr Addr, const char *SupprType) {
Suppression *S = nullptr;
// Suppress by module name.
SuppressionContext *Suppressions = SuppressionCtx;
if (const char *ModuleName =
Symbolizer::GetOrInit()->GetModuleNameForPc(Addr)) {
if (Suppressions->Match(ModuleName, SupprType, &S))
return S;
}
// Suppress by file or function name.
SymbolizedStack *Frames = Symbolizer::GetOrInit()->SymbolizePC(Addr);
for (SymbolizedStack *Cur = Frames; Cur; Cur = Cur->next) {
if (Suppressions->Match(Cur->info.function, SupprType, &S) ||
Suppressions->Match(Cur->info.file, SupprType, &S)) {
break;
}
}
Frames->ClearAll();
return S;
}
Suppression *GetSuppressionForStack(const StackTrace *Stack, CheckKind K) {
for (uptr I = 0, E = Stack->size; I < E; I++) {
Suppression *S = GetSuppressionForAddr(
StackTrace::GetPreviousInstructionPc(Stack->trace[I]),
kSuppressionTypes[static_cast<int>(K)]);
if (S)
return S;
}
return nullptr;
}
} // end namespace __nsan

View File

@@ -0,0 +1,73 @@
//===-- nsan_suppressions.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
//
//===----------------------------------------------------------------------===//
#include "nsan_suppressions.h"
#include "nsan_flags.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
using namespace __sanitizer;
using namespace __nsan;
SANITIZER_INTERFACE_WEAK_DEF(const char *, __nsan_default_suppressions, void) {
return 0;
}
const char kSuppressionFcmp[] = "fcmp";
const char kSuppressionConsistency[] = "consistency";
alignas(64) static char suppression_placeholder[sizeof(SuppressionContext)];
static SuppressionContext *suppression_ctx;
// The order should match the enum CheckKind.
static const char *kSuppressionTypes[] = {kSuppressionFcmp,
kSuppressionConsistency};
void __nsan::InitializeSuppressions() {
CHECK_EQ(nullptr, suppression_ctx);
suppression_ctx = new (suppression_placeholder)
SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
suppression_ctx->ParseFromFile(flags().suppressions);
suppression_ctx->Parse(__nsan_default_suppressions());
}
static Suppression *GetSuppressionForAddr(uptr addr, const char *suppr_type) {
Suppression *s = nullptr;
// Suppress by module name.
SuppressionContext *suppressions = suppression_ctx;
if (const char *moduleName =
Symbolizer::GetOrInit()->GetModuleNameForPc(addr)) {
if (suppressions->Match(moduleName, suppr_type, &s))
return s;
}
// Suppress by file or function name.
SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr);
for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
if (suppressions->Match(cur->info.function, suppr_type, &s) ||
suppressions->Match(cur->info.file, suppr_type, &s)) {
break;
}
}
frames->ClearAll();
return s;
}
Suppression *__nsan::GetSuppressionForStack(const StackTrace *stack,
CheckKind k) {
for (uptr i = 0, e = stack->size; i < e; i++) {
Suppression *s = GetSuppressionForAddr(
StackTrace::GetPreviousInstructionPc(stack->trace[i]),
kSuppressionTypes[static_cast<int>(k)]);
if (s)
return s;
}
return nullptr;
}

View File

@@ -14,41 +14,41 @@ namespace __nsan {
template <typename FT, auto next> void TestFT() {
// Basic local tests anchored at 0.0.
ASSERT_EQ(getULPDiff<FT>(0.0, 0.0), 0);
ASSERT_EQ(getULPDiff<FT>(-0.0, 0.0), 0);
ASSERT_EQ(getULPDiff<FT>(next(-0.0, -1.0), 0.0), 1);
ASSERT_EQ(getULPDiff<FT>(next(0.0, 1.0), -0.0), 1);
ASSERT_EQ(getULPDiff<FT>(next(-0.0, -1.0), next(0.0, 1.0)), 2);
ASSERT_EQ(GetULPDiff<FT>(0.0, 0.0), 0);
ASSERT_EQ(GetULPDiff<FT>(-0.0, 0.0), 0);
ASSERT_EQ(GetULPDiff<FT>(next(-0.0, -1.0), 0.0), 1);
ASSERT_EQ(GetULPDiff<FT>(next(0.0, 1.0), -0.0), 1);
ASSERT_EQ(GetULPDiff<FT>(next(-0.0, -1.0), next(0.0, 1.0)), 2);
// Basic local tests anchored at 2.0.
ASSERT_EQ(getULPDiff<FT>(next(2.0, 1.0), 2.0), 1);
ASSERT_EQ(getULPDiff<FT>(next(2.0, 3.0), 2.0), 1);
ASSERT_EQ(getULPDiff<FT>(next(2.0, 1.0), next(2.0, 3.0)), 2);
ASSERT_EQ(GetULPDiff<FT>(next(2.0, 1.0), 2.0), 1);
ASSERT_EQ(GetULPDiff<FT>(next(2.0, 3.0), 2.0), 1);
ASSERT_EQ(GetULPDiff<FT>(next(2.0, 1.0), next(2.0, 3.0)), 2);
ASSERT_NE(getULPDiff<FT>(-0.01, 0.01), kMaxULPDiff);
ASSERT_NE(GetULPDiff<FT>(-0.01, 0.01), kMaxULPDiff);
// Basic local tests anchored at a random number.
const FT X = 4863.5123;
const FT To = 2 * X;
FT Y = X;
ASSERT_EQ(getULPDiff<FT>(X, Y), 0);
ASSERT_EQ(getULPDiff<FT>(-X, -Y), 0);
ASSERT_EQ(GetULPDiff<FT>(X, Y), 0);
ASSERT_EQ(GetULPDiff<FT>(-X, -Y), 0);
Y = next(Y, To);
ASSERT_EQ(getULPDiff<FT>(X, Y), 1);
ASSERT_EQ(getULPDiff<FT>(-X, -Y), 1);
ASSERT_EQ(GetULPDiff<FT>(X, Y), 1);
ASSERT_EQ(GetULPDiff<FT>(-X, -Y), 1);
Y = next(Y, To);
ASSERT_EQ(getULPDiff<FT>(X, Y), 2);
ASSERT_EQ(getULPDiff<FT>(-X, -Y), 2);
ASSERT_EQ(GetULPDiff<FT>(X, Y), 2);
ASSERT_EQ(GetULPDiff<FT>(-X, -Y), 2);
Y = next(Y, To);
ASSERT_EQ(getULPDiff<FT>(X, Y), 3);
ASSERT_EQ(getULPDiff<FT>(-X, -Y), 3);
ASSERT_EQ(GetULPDiff<FT>(X, Y), 3);
ASSERT_EQ(GetULPDiff<FT>(-X, -Y), 3);
// Values with larger differences.
static constexpr const __sanitizer::u64 MantissaSize =
__sanitizer::u64{1} << FTInfo<FT>::kMantissaBits;
ASSERT_EQ(getULPDiff<FT>(1.0, next(2.0, 1.0)), MantissaSize - 1);
ASSERT_EQ(getULPDiff<FT>(1.0, 2.0), MantissaSize);
ASSERT_EQ(getULPDiff<FT>(1.0, next(2.0, 3.0)), MantissaSize + 1);
ASSERT_EQ(getULPDiff<FT>(1.0, 3.0), (3 * MantissaSize) / 2);
ASSERT_EQ(GetULPDiff<FT>(1.0, next(2.0, 1.0)), MantissaSize - 1);
ASSERT_EQ(GetULPDiff<FT>(1.0, 2.0), MantissaSize);
ASSERT_EQ(GetULPDiff<FT>(1.0, next(2.0, 3.0)), MantissaSize + 1);
ASSERT_EQ(GetULPDiff<FT>(1.0, 3.0), (3 * MantissaSize) / 2);
}
TEST(NSanTest, Float) { TestFT<float, nextafterf>(); }
@@ -59,9 +59,9 @@ TEST(NSanTest, Double) {
TEST(NSanTest, Float128) {
// Very basic tests. FIXME: improve when we have nextafter<__float128>.
ASSERT_EQ(getULPDiff<__float128>(0.0, 0.0), 0);
ASSERT_EQ(getULPDiff<__float128>(-0.0, 0.0), 0);
ASSERT_NE(getULPDiff<__float128>(-0.01, 0.01), kMaxULPDiff);
ASSERT_EQ(GetULPDiff<__float128>(0.0, 0.0), 0);
ASSERT_EQ(GetULPDiff<__float128>(-0.0, 0.0), 0);
ASSERT_NE(GetULPDiff<__float128>(-0.01, 0.01), kMaxULPDiff);
}
} // end namespace __nsan