[compiler-rt] [CompilerRT] Add numerical sanitizer (PR #94322)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 4 01:32:35 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-compiler-rt-sanitizer
Author: Alexander Shaposhnikov (alexander-shaposhnikov)
<details>
<summary>Changes</summary>
This PR contains the compiler-rt changes that were split out from https://github.com/llvm/llvm-project/pull/85916.
Follow-up patch with numerical tests will be sent once the initial changes (instrumentation, clang, runtime) land.
Test plan:
1. cd build/runtimes/runtimes-bins && ninja check-nsan
2. ninja check-all
---
Patch is 90.73 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/94322.diff
23 Files Affected:
- (modified) compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake (+1)
- (modified) compiler-rt/cmake/config-ix.cmake (+12-1)
- (added) compiler-rt/include/sanitizer/nsan_interface.h (+75)
- (added) compiler-rt/lib/nsan/CMakeLists.txt (+61)
- (added) compiler-rt/lib/nsan/nsan.cc (+828)
- (added) compiler-rt/lib/nsan/nsan.h (+224)
- (added) compiler-rt/lib/nsan/nsan.syms.extra (+2)
- (added) compiler-rt/lib/nsan/nsan_flags.cc (+78)
- (added) compiler-rt/lib/nsan/nsan_flags.h (+35)
- (added) compiler-rt/lib/nsan/nsan_flags.inc (+49)
- (added) compiler-rt/lib/nsan/nsan_interceptors.cc (+363)
- (added) compiler-rt/lib/nsan/nsan_platform.h (+135)
- (added) compiler-rt/lib/nsan/nsan_stats.cc (+158)
- (added) compiler-rt/lib/nsan/nsan_stats.h (+92)
- (added) compiler-rt/lib/nsan/nsan_suppressions.cc (+76)
- (added) compiler-rt/lib/nsan/nsan_suppressions.h (+31)
- (added) compiler-rt/lib/nsan/tests/CMakeLists.txt (+54)
- (added) compiler-rt/lib/nsan/tests/NSanUnitTest.cpp (+67)
- (added) compiler-rt/lib/nsan/tests/nsan_unit_test_main.cpp (+18)
- (added) compiler-rt/test/nsan/CMakeLists.txt (+20)
- (added) compiler-rt/test/nsan/Unit/lit.site.cfg.py.in (+10)
- (added) compiler-rt/test/nsan/lit.cfg.py (+1)
- (added) compiler-rt/test/nsan/lit.site.cfg.py.in (+14)
``````````diff
diff --git a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
index 2fe06273a814c..a914b62cd5c5f 100644
--- a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
+++ b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
@@ -61,6 +61,7 @@ else()
endif()
set(ALL_MSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${PPC64} ${S390X}
${LOONGARCH64})
+set(ALL_NSAN_SUPPORTED_ARCH ${X86} ${X86_64})
set(ALL_HWASAN_SUPPORTED_ARCH ${X86_64} ${ARM64} ${RISCV64})
set(ALL_MEMPROF_SUPPORTED_ARCH ${X86_64})
set(ALL_PROFILE_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${PPC32} ${PPC64}
diff --git a/compiler-rt/cmake/config-ix.cmake b/compiler-rt/cmake/config-ix.cmake
index ba740af9e1d60..85609a903896e 100644
--- a/compiler-rt/cmake/config-ix.cmake
+++ b/compiler-rt/cmake/config-ix.cmake
@@ -623,6 +623,9 @@ if(APPLE)
list_intersect(MSAN_SUPPORTED_ARCH
ALL_MSAN_SUPPORTED_ARCH
SANITIZER_COMMON_SUPPORTED_ARCH)
+ list_intersect(NSAN_SUPPORTED_ARCH
+ ALL_NSAN_SUPPORTED_ARCH
+ SANITIZER_COMMON_SUPPORTED_ARCH)
list_intersect(HWASAN_SUPPORTED_ARCH
ALL_HWASAN_SUPPORTED_ARCH
SANITIZER_COMMON_SUPPORTED_ARCH)
@@ -692,6 +695,7 @@ else()
filter_available_targets(SHADOWCALLSTACK_SUPPORTED_ARCH
${ALL_SHADOWCALLSTACK_SUPPORTED_ARCH})
filter_available_targets(GWP_ASAN_SUPPORTED_ARCH ${ALL_GWP_ASAN_SUPPORTED_ARCH})
+ filter_available_targets(NSAN_SUPPORTED_ARCH ${ALL_NSAN_SUPPORTED_ARCH})
filter_available_targets(ORC_SUPPORTED_ARCH ${ALL_ORC_SUPPORTED_ARCH})
endif()
@@ -726,7 +730,7 @@ if(COMPILER_RT_SUPPORTED_ARCH)
endif()
message(STATUS "Compiler-RT supported architectures: ${COMPILER_RT_SUPPORTED_ARCH}")
-set(ALL_SANITIZERS asan;dfsan;msan;hwasan;tsan;safestack;cfi;scudo_standalone;ubsan_minimal;gwp_asan;asan_abi)
+set(ALL_SANITIZERS asan;dfsan;msan;hwasan;tsan;safestack;cfi;scudo_standalone;ubsan_minimal;gwp_asan;nsan;asan_abi)
set(COMPILER_RT_SANITIZERS_TO_BUILD all CACHE STRING
"sanitizers to build if supported on the target (all;${ALL_SANITIZERS})")
list_replace(COMPILER_RT_SANITIZERS_TO_BUILD all "${ALL_SANITIZERS}")
@@ -911,4 +915,11 @@ if (GWP_ASAN_SUPPORTED_ARCH AND
else()
set(COMPILER_RT_HAS_GWP_ASAN FALSE)
endif()
+
+if (COMPILER_RT_HAS_SANITIZER_COMMON AND NSAN_SUPPORTED_ARCH AND
+ OS_NAME MATCHES "Linux")
+ set(COMPILER_RT_HAS_NSAN TRUE)
+else()
+ set(COMPILER_RT_HAS_NSAN FALSE)
+endif()
pythonize_bool(COMPILER_RT_HAS_GWP_ASAN)
diff --git a/compiler-rt/include/sanitizer/nsan_interface.h b/compiler-rt/include/sanitizer/nsan_interface.h
new file mode 100644
index 0000000000000..057ca0473bb3c
--- /dev/null
+++ b/compiler-rt/include/sanitizer/nsan_interface.h
@@ -0,0 +1,75 @@
+//===-- sanitizer/nsan_interface.h ------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Public interface for nsan.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_NSAN_INTERFACE_H
+#define SANITIZER_NSAN_INTERFACE_H
+
+#include <sanitizer/common_interface_defs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// User-provided default option settings.
+///
+/// You can provide your own implementation of this function to return a string
+/// containing NSan runtime options (for example,
+/// <c>verbosity=1:halt_on_error=0</c>).
+///
+/// \returns Default options string.
+const char *__nsan_default_options(void);
+
+// Dumps nsan shadow data for a block of `size_bytes` bytes of application
+// memory at location `addr`.
+//
+// Each line contains application address, shadow types, then values.
+// Unknown types are shown as `__`, while known values are shown as
+// `f`, `d`, `l` for float, double, and long double respectively. Position is
+// shown as a single hex digit. The shadow value itself appears on the line that
+// contains the first byte of the value.
+// FIXME: Show both shadow and application value.
+//
+// Example: `__nsan_dump_shadow_mem(addr, 32, 8, 0)` might print:
+//
+// 0x0add7359: __ f0 f1 f2 f3 __ __ __ (42.000)
+// 0x0add7361: __ d1 d2 d3 d4 d5 d6 d7
+// 0x0add7369: d8 f0 f1 f2 f3 __ __ f2 (-1.000) (12.5)
+// 0x0add7371: f3 __ __ __ __ __ __ __
+//
+// This means that there is:
+// - a shadow double for the float at address 0x0add7360, with value 42;
+// - a shadow float128 for the double at address 0x0add7362, with value -1;
+// - a shadow double for the float at address 0x0add736a, with value 12.5;
+// There was also a shadow double for the float at address 0x0add736e, but bytes
+// f0 and f1 were overwritten by one or several stores, so that the shadow value
+// is no longer valid.
+// The argument `reserved` can be any value. Its true value is provided by the
+// instrumentation.
+void __nsan_dump_shadow_mem(const char *addr, size_t size_bytes,
+ size_t bytes_per_line, size_t reserved);
+
+// Explicitly dumps a value.
+// FIXME: vector versions ?
+void __nsan_dump_float(float value);
+void __nsan_dump_double(double value);
+void __nsan_dump_longdouble(long double value);
+
+// Explicitly checks a value.
+// FIXME: vector versions ?
+void __nsan_check_float(float value);
+void __nsan_check_double(double value);
+void __nsan_check_longdouble(long double value);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // SANITIZER_NSAN_INTERFACE_H
diff --git a/compiler-rt/lib/nsan/CMakeLists.txt b/compiler-rt/lib/nsan/CMakeLists.txt
new file mode 100644
index 0000000000000..00b16473bff0e
--- /dev/null
+++ b/compiler-rt/lib/nsan/CMakeLists.txt
@@ -0,0 +1,61 @@
+add_compiler_rt_component(nsan)
+
+include_directories(..)
+
+set(NSAN_SOURCES
+ nsan.cc
+ nsan_flags.cc
+ nsan_interceptors.cc
+ nsan_stats.cc
+ nsan_suppressions.cc
+)
+
+set(NSAN_HEADERS
+ nsan.h
+ nsan_flags.h
+ nsan_flags.inc
+ nsan_platform.h
+ nsan_stats.h
+ nsan_suppressions.h
+)
+
+append_list_if(COMPILER_RT_HAS_FPIC_FLAG -fPIC NSAN_CFLAGS)
+
+set(NSAN_DYNAMIC_LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS})
+
+set(NSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
+#-fno-rtti -fno-exceptions
+# -nostdinc++ -pthread -fno-omit-frame-pointer)
+
+# Remove -stdlib= which is unused when passing -nostdinc++.
+# string(REGEX REPLACE "-stdlib=[a-zA-Z+]*" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
+
+if (COMPILER_RT_HAS_NSAN)
+ foreach(arch ${NSAN_SUPPORTED_ARCH})
+ add_compiler_rt_runtime(
+ clang_rt.nsan
+ STATIC
+ ARCHS ${arch}
+ SOURCES ${NSAN_SOURCES}
+ $<TARGET_OBJECTS:RTInterception.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommonCoverage.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommonSymbolizer.${arch}>
+ $<TARGET_OBJECTS:RTUbsan.${arch}>
+ ADDITIONAL_HEADERS ${NSAN_HEADERS}
+ CFLAGS ${NSAN_CFLAGS}
+ PARENT_TARGET nsan
+ )
+ endforeach()
+
+ add_compiler_rt_object_libraries(RTNsan
+ ARCHS ${NSAN_SUPPORTED_ARCH}
+ SOURCES ${NSAN_SOURCES}
+ ADDITIONAL_HEADERS ${NSAN_HEADERS}
+ CFLAGS ${NSAN_CFLAGS})
+endif()
+
+if(COMPILER_RT_INCLUDE_TESTS)
+ add_subdirectory(tests)
+endif()
diff --git a/compiler-rt/lib/nsan/nsan.cc b/compiler-rt/lib/nsan/nsan.cc
new file mode 100644
index 0000000000000..29351ca111a3f
--- /dev/null
+++ b/compiler-rt/lib/nsan/nsan.cc
@@ -0,0 +1,828 @@
+//===-- 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;
+
+static constexpr const 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 char *daddr, const char *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 char *addr, uptr size) {
+ internal_memset((void *)getShadowTypeAddrFor(addr), 0, size);
+}
+
+namespace __nsan {
+
+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> __float128 readShadowInternal(const char *Ptr) {
+ ShadowFT Shadow;
+ __builtin_memcpy(&Shadow, Ptr, sizeof(Shadow));
+ return Shadow;
+}
+
+__float128 readShadow(const char *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;
+ }
+}
+
+class Decorator : public __sanitizer::SanitizerCommonDecorator {
+public:
+ Decorator() : SanitizerCommonDecorator() {}
+ const char *Warning() { return Red(); }
+ const char *Name() { return Green(); }
+ const char *End() { return Default(); }
+};
+
+namespace {
+
+// 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
+
+} // end namespace __nsan
+
+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 char *getShadowPtrForStore(char *StoreAddr, uptr N) {
+ unsigned char *ShadowType = getShadowTypeAddrFor(StoreAddr);
+ for (uptr I = 0; I < N; ++I) {
+ __builtin_memcpy(ShadowType + I * sizeof(FT), FTInfo<FT>::kTypePattern,
+ sizeof(FTInfo<FT>::kTypePattern));
+ }
+ return getShadowAddrFor(StoreAddr);
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE char *
+__nsan_get_shadow_ptr_for_float_store(char *store_addr, uptr n) {
+ return getShadowPtrForStore<float>(store_addr, n);
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE char *
+__nsan_get_shadow_ptr_for_double_store(char *store_addr, uptr n) {
+ return getShadowPtrForStore<double>(store_addr, n);
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE char *
+__nsan_get_shadow_ptr_for_longdouble_store(char *store_addr, uptr n) {
+ return getShadowPtrForStore<long double>(store_addr, n);
+}
+
+template <typename FT>
+static bool isValidShadowType(const unsigned char *ShadowType) {
+ return __builtin_memcmp(ShadowType, 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 unsigned char *ShadowType) {
+ return isZero<sizeof(FTInfo<FT>::kTypePattern)>(ShadowType);
+}
+
+// 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 char *getShadowPtrForLoad(const char *LoadAddr, uptr N) {
+ const unsigned char *const ShadowType = getShadowTypeAddrFor(LoadAddr);
+ for (uptr I = 0; I < N; ++I) {
+ if (!isValidShadowType<FT>(ShadowType + 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))) {
+ // 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)) {
+ GET_CALLER_PC_BP;
+ nsan_stats->addUnknownLoadTrackingEvent(pc, bp);
+ }
+ } else {
+ GET_CALLER_PC_BP;
+ nsan_stats->addInvalidLoadTrackingEvent(pc, bp);
+ }
+ }
+ return nullptr;
+ }
+ }
+ return getShadowAddrFor(LoadAddr);
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE const char *
+__nsan_get_shadow_ptr_for_float_load(const char *load_addr, uptr n) {
+ return getShadowPtrForLoad<float>(load_addr, n);
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE const char *
+__nsan_get_shadow_ptr_for_double_load(const char *load_addr, uptr n) {
+ return getShadowPtrForLoad<double>(load_addr, n);
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE const char *
+__nsan_get_shadow_ptr_for_longdouble_load(const char *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 char *
+__nsan_internal_get_raw_shadow_ptr(const char *addr) {
+ return getShadowAddrFor(const_cast<char *>(addr));
+}
+
+// Returns the raw shadow type pointer. The returned pointer should be
+// considered opaque.
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE char *
+__nsan_internal_get_raw_shadow_type_ptr(const char *addr) {
+ return reinterpret_cast<char *>(
+ getShadowTypeAddrFor(const_cast<char *>(addr)));
+}
+
+static ValueType getValueType(unsigned char c) {
+ return static_cast<ValueType>(c & 0x3);
+}
+
+static int getValuePos(unsigned char 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 unsigned char *ShadowType) {
+ const int Pos = getValuePos(*ShadowType);
+ // Check that all bytes from the start of the value are ordered.
+ for (uptr I = 0; I < sizeof(FT); ++I) {
+ const unsigned char T = *(ShadowType - 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 char *addr, size_t size_bytes,
+ size_t bytes_per_line, size_t shadow_value_type_ids) {
+ const unsigned char *const ShadowType = getShadowTypeAddrFor(addr);
+ const char *const Shadow = getShadowAddrFor(addr);
+
+ constexpr const int kMaxNumDecodedValues = 16;
+ __float128 DecodedValues[kMaxNumDecodedValues];
+ int NumDecodedValues...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/94322
More information about the llvm-commits
mailing list