@@ -1,4 +1,4 @@
-c425db2eb558c26377edc04e062c0c1f999b2770
+61a6439f35b6de28ff4aff4450d6fca970292fd5
The first line of this file holds the git revision number of the
last merge done from the master library sources.
@@ -46,7 +46,6 @@ asan_files = \
asan_suppressions.cpp \
asan_thread.cpp \
asan_win.cpp \
- asan_win_dll_thunk.cpp \
asan_win_dynamic_runtime_thunk.cpp \
asan_interceptors_vfork.S
@@ -160,8 +160,7 @@ am__objects_1 = asan_activation.lo asan_allocator.lo asan_debugging.lo \
asan_posix.lo asan_premap_shadow.lo asan_report.lo asan_rtl.lo \
asan_shadow_setup.lo asan_stack.lo asan_stats.lo \
asan_suppressions.lo asan_thread.lo asan_win.lo \
- asan_win_dll_thunk.lo asan_win_dynamic_runtime_thunk.lo \
- asan_interceptors_vfork.lo
+ asan_win_dynamic_runtime_thunk.lo asan_interceptors_vfork.lo
am_libasan_la_OBJECTS = $(am__objects_1)
libasan_la_OBJECTS = $(am_libasan_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
@@ -457,7 +456,6 @@ asan_files = \
asan_suppressions.cpp \
asan_thread.cpp \
asan_win.cpp \
- asan_win_dll_thunk.cpp \
asan_win_dynamic_runtime_thunk.cpp \
asan_interceptors_vfork.S
@@ -619,7 +617,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_suppressions.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_thread.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dll_thunk.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dynamic_runtime_thunk.Plo@am__quote@
.S.o:
@@ -717,7 +717,15 @@ struct Allocator {
return;
}
- RunFreeHooks(ptr);
+ if (RunFreeHooks(ptr)) {
+ // Someone used __sanitizer_ignore_free_hook() and decided that they
+ // didn't want the memory to __sanitizer_ignore_free_hook freed right now.
+ // When they call free() on this pointer again at a later time, we should
+ // ignore the alloc-type mismatch and allow them to deallocate the pointer
+ // through free(), rather than the initial alloc type.
+ m->alloc_type = FROM_MALLOC;
+ return;
+ }
// Must mark the chunk as quarantined before any changes to its metadata.
// Do not quarantine given chunk if we failed to set CHUNK_QUARANTINE flag.
@@ -182,42 +182,44 @@ static_assert(SizeClassMap::kNumClassesRounded <= 32,
"allocator size and SizeClassMap tunings that allows us to "
"reliably run all bringup tests in a sanitized environment.");
-# else
+# else // SANITIZER_RISCV64
// These are the default allocator tunings for non-RISCV environments where the
// VMA is usually 48 bits and we have lots of space.
const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
typedef DefaultSizeClassMap SizeClassMap;
-# endif
-# elif defined(__powerpc64__)
+# endif // SANITIZER_RISCV64
+# else // SANITIZER_FUCHSIA
+
+# if SANITIZER_APPLE
+const uptr kAllocatorSpace = 0x600000000000ULL;
+# else // SANITIZER_APPLE
const uptr kAllocatorSpace = ~(uptr)0;
+# endif // SANITIZER_APPLE
+
+# if defined(__powerpc64__)
const uptr kAllocatorSize = 0x20000000000ULL; // 2T.
typedef DefaultSizeClassMap SizeClassMap;
-# elif defined(__aarch64__) && SANITIZER_ANDROID
+# elif defined(__aarch64__) && SANITIZER_ANDROID
// Android needs to support 39, 42 and 48 bit VMA.
-const uptr kAllocatorSpace = ~(uptr)0;
const uptr kAllocatorSize = 0x2000000000ULL; // 128G.
typedef VeryCompactSizeClassMap SizeClassMap;
-# elif SANITIZER_RISCV64
-const uptr kAllocatorSpace = ~(uptr)0;
+# elif SANITIZER_RISCV64
const uptr kAllocatorSize = 0x2000000000ULL; // 128G.
typedef VeryDenseSizeClassMap SizeClassMap;
-# elif defined(__sparc__)
-const uptr kAllocatorSpace = ~(uptr)0;
+# elif defined(__sparc__)
const uptr kAllocatorSize = 0x20000000000ULL; // 2T.
typedef DefaultSizeClassMap SizeClassMap;
-# elif SANITIZER_WINDOWS
-const uptr kAllocatorSpace = ~(uptr)0;
+# elif SANITIZER_WINDOWS
const uptr kAllocatorSize = 0x8000000000ULL; // 500G
typedef DefaultSizeClassMap SizeClassMap;
-# elif SANITIZER_APPLE
-const uptr kAllocatorSpace = 0x600000000000ULL;
+# elif SANITIZER_APPLE
const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
typedef DefaultSizeClassMap SizeClassMap;
-# else
-const uptr kAllocatorSpace = 0x500000000000ULL;
+# else
const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
typedef DefaultSizeClassMap SizeClassMap;
-# endif
+# endif // defined(__powerpc64__) etc.
+# endif // SANITIZER_FUCHSIA
template <typename AddressSpaceViewTy>
struct AP64 { // Allocator64 parameters. Deliberately using a short name.
static const uptr kSpaceBeg = kAllocatorSpace;
@@ -232,7 +234,7 @@ struct AP64 { // Allocator64 parameters. Deliberately using a short name.
template <typename AddressSpaceView>
using PrimaryAllocatorASVT = SizeClassAllocator64<AP64<AddressSpaceView>>;
using PrimaryAllocator = PrimaryAllocatorASVT<LocalAddressSpaceView>;
-#else // Fallback to SizeClassAllocator32.
+#else // SANITIZER_CAN_USE_ALLOCATOR64. Fallback to SizeClassAllocator32.
typedef CompactSizeClassMap SizeClassMap;
template <typename AddressSpaceViewTy>
struct AP32 {
@@ -20,24 +20,20 @@
namespace __asan {
AsanThreadIdAndName::AsanThreadIdAndName(AsanThreadContext *t) {
- Init(t->tid, t->name);
-}
-
-AsanThreadIdAndName::AsanThreadIdAndName(u32 tid) {
- if (tid == kInvalidTid) {
- Init(tid, "");
- } else {
- asanThreadRegistry().CheckLocked();
- AsanThreadContext *t = GetThreadContextByTidLocked(tid);
- Init(tid, t->name);
+ if (!t) {
+ internal_snprintf(name, sizeof(name), "T-1");
+ return;
}
+ int len = internal_snprintf(name, sizeof(name), "T%llu", t->unique_id);
+ CHECK(((unsigned int)len) < sizeof(name));
+ if (internal_strlen(t->name))
+ internal_snprintf(&name[len], sizeof(name) - len, " (%s)", t->name);
}
-void AsanThreadIdAndName::Init(u32 tid, const char *tname) {
- int len = internal_snprintf(name, sizeof(name), "T%d", tid);
- CHECK(((unsigned int)len) < sizeof(name));
- if (tname[0] != '\0')
- internal_snprintf(&name[len], sizeof(name) - len, " (%s)", tname);
+AsanThreadIdAndName::AsanThreadIdAndName(u32 tid)
+ : AsanThreadIdAndName(
+ tid == kInvalidTid ? nullptr : GetThreadContextByTidLocked(tid)) {
+ asanThreadRegistry().CheckLocked();
}
void DescribeThread(AsanThreadContext *context) {
@@ -48,9 +44,20 @@ void DescribeThread(AsanThreadContext *context) {
return;
}
context->announced = true;
+
+ AsanThreadContext *parent_context =
+ context->parent_tid == kInvalidTid
+ ? nullptr
+ : GetThreadContextByTidLocked(context->parent_tid);
+
+ // `context->parent_tid` may point to reused slot. Check `unique_id` which
+ // is always smaller for the parent, always greater for a new user.
+ if (context->unique_id <= parent_context->unique_id)
+ parent_context = nullptr;
+
InternalScopedString str;
str.AppendF("Thread %s", AsanThreadIdAndName(context).c_str());
- if (context->parent_tid == kInvalidTid) {
+ if (!parent_context) {
str.Append(" created by unknown thread\n");
Printf("%s", str.data());
return;
@@ -60,11 +67,8 @@ void DescribeThread(AsanThreadContext *context) {
Printf("%s", str.data());
StackDepotGet(context->stack_id).Print();
// Recursively described parent thread if needed.
- if (flags()->print_full_thread_history) {
- AsanThreadContext *parent_context =
- GetThreadContextByTidLocked(context->parent_tid);
+ if (flags()->print_full_thread_history)
DescribeThread(parent_context);
- }
}
// Shadow descriptions
@@ -245,11 +249,11 @@ static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr,
InternalScopedString str;
str.AppendF(" [%zd, %zd)", var.beg, var_end);
// Render variable name.
- str.AppendF(" '");
+ str.Append(" '");
for (uptr i = 0; i < var.name_len; ++i) {
str.AppendF("%c", var.name_pos[i]);
}
- str.AppendF("'");
+ str.Append("'");
if (var.line > 0) {
str.AppendF(" (line %zd)", var.line);
}
@@ -260,7 +264,7 @@ static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr,
str.AppendF("%s <== Memory access at offset %zd %s this variable%s\n",
d.Location(), addr, pos_descr, d.Default());
} else {
- str.AppendF("\n");
+ str.Append("\n");
}
Printf("%s", str.data());
}
@@ -292,7 +296,7 @@ static void DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
str.AppendF(" global variable '%s' defined in '",
MaybeDemangleGlobalName(g.name));
PrintGlobalLocation(&str, g, /*print_module_name=*/false);
- str.AppendF("' (0x%zx) of size %zu\n", g.beg, g.size);
+ str.AppendF("' (%p) of size %zu\n", (void *)g.beg, g.size);
str.Append(d.Default());
PrintGlobalNameIfASCII(&str, g);
Printf("%s", str.data());
@@ -35,8 +35,6 @@ class AsanThreadIdAndName {
const char *c_str() const { return &name[0]; }
private:
- void Init(u32 tid, const char *tname);
-
char name[128];
};
@@ -327,9 +327,6 @@ void ErrorBadParamsToAnnotateContiguousContainer::Print() {
" old_mid : %p\n"
" new_mid : %p\n",
(void *)beg, (void *)end, (void *)old_mid, (void *)new_mid);
- uptr granularity = ASAN_SHADOW_GRANULARITY;
- if (!IsAligned(beg, granularity))
- Report("ERROR: beg is not aligned by %zu\n", granularity);
stack->Print();
ReportErrorSummary(scariness.GetDescription(), stack);
}
@@ -347,9 +344,20 @@ void ErrorBadParamsToAnnotateDoubleEndedContiguousContainer::Print() {
(void *)storage_beg, (void *)storage_end, (void *)old_container_beg,
(void *)old_container_end, (void *)new_container_beg,
(void *)new_container_end);
- uptr granularity = ASAN_SHADOW_GRANULARITY;
- if (!IsAligned(storage_beg, granularity))
- Report("ERROR: storage_beg is not aligned by %zu\n", granularity);
+ stack->Print();
+ ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorBadParamsToCopyContiguousContainerAnnotations::Print() {
+ Report(
+ "ERROR: AddressSanitizer: bad parameters to "
+ "__sanitizer_copy_contiguous_container_annotations:\n"
+ " src_storage_beg : %p\n"
+ " src_storage_end : %p\n"
+ " dst_storage_beg : %p\n"
+ " new_storage_end : %p\n",
+ (void *)old_storage_beg, (void *)old_storage_end, (void *)new_storage_beg,
+ (void *)new_storage_end);
stack->Print();
ReportErrorSummary(scariness.GetDescription(), stack);
}
@@ -353,6 +353,24 @@ struct ErrorBadParamsToAnnotateDoubleEndedContiguousContainer : ErrorBase {
void Print();
};
+struct ErrorBadParamsToCopyContiguousContainerAnnotations : ErrorBase {
+ const BufferedStackTrace *stack;
+ uptr old_storage_beg, old_storage_end, new_storage_beg, new_storage_end;
+
+ ErrorBadParamsToCopyContiguousContainerAnnotations() = default; // (*)
+ ErrorBadParamsToCopyContiguousContainerAnnotations(
+ u32 tid, BufferedStackTrace *stack_, uptr old_storage_beg_,
+ uptr old_storage_end_, uptr new_storage_beg_, uptr new_storage_end_)
+ : ErrorBase(tid, 10,
+ "bad-__sanitizer_annotate_double_ended_contiguous_container"),
+ stack(stack_),
+ old_storage_beg(old_storage_beg_),
+ old_storage_end(old_storage_end_),
+ new_storage_beg(new_storage_beg_),
+ new_storage_end(new_storage_end_) {}
+ void Print();
+};
+
struct ErrorODRViolation : ErrorBase {
__asan_global global1, global2;
u32 stack_id1, stack_id2;
@@ -421,6 +439,7 @@ struct ErrorGeneric : ErrorBase {
macro(StringFunctionSizeOverflow) \
macro(BadParamsToAnnotateContiguousContainer) \
macro(BadParamsToAnnotateDoubleEndedContiguousContainer) \
+ macro(BadParamsToCopyContiguousContainerAnnotations) \
macro(ODRViolation) \
macro(InvalidPointerPair) \
macro(Generic)
@@ -11,14 +11,16 @@
// ASan flag parsing logic.
//===----------------------------------------------------------------------===//
-#include "asan_activation.h"
#include "asan_flags.h"
+
+#include "asan_activation.h"
#include "asan_interface_internal.h"
#include "asan_stack.h"
#include "lsan/lsan_common.h"
#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_flag_parser.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_win_interception.h"
#include "ubsan/ubsan_flags.h"
#include "ubsan/ubsan_platform.h"
@@ -47,7 +49,21 @@ static void RegisterAsanFlags(FlagParser *parser, Flags *f) {
#undef ASAN_FLAG
}
-void InitializeFlags() {
+static void DisplayHelpMessages(FlagParser *parser) {
+ // TODO(eugenis): dump all flags at verbosity>=2?
+ if (Verbosity()) {
+ ReportUnrecognizedFlags();
+ }
+
+ if (common_flags()->help) {
+ parser->PrintFlagDescriptions();
+ }
+}
+
+static void InitializeDefaultFlags() {
+ Flags *f = flags();
+ FlagParser asan_parser;
+
// Set the default values and prepare for parsing ASan and common flags.
SetCommonFlagsDefaults();
{
@@ -60,10 +76,8 @@ void InitializeFlags() {
cf.exitcode = 1;
OverrideCommonFlags(cf);
}
- Flags *f = flags();
f->SetDefaults();
- FlagParser asan_parser;
RegisterAsanFlags(&asan_parser, f);
RegisterCommonFlags(&asan_parser);
@@ -126,13 +140,12 @@ void InitializeFlags() {
InitializeCommonFlags();
- // TODO(eugenis): dump all flags at verbosity>=2?
- if (Verbosity()) ReportUnrecognizedFlags();
+ // TODO(samsonov): print all of the flags (ASan, LSan, common).
+ DisplayHelpMessages(&asan_parser);
+}
- if (common_flags()->help) {
- // TODO(samsonov): print all of the flags (ASan, LSan, common).
- asan_parser.PrintFlagDescriptions();
- }
+static void ProcessFlags() {
+ Flags *f = flags();
// Flag validation:
if (!CAN_SANITIZE_LEAKS && common_flags()->detect_leaks) {
@@ -199,6 +212,67 @@ void InitializeFlags() {
}
}
+void InitializeFlags() {
+ InitializeDefaultFlags();
+ ProcessFlags();
+
+#if SANITIZER_WINDOWS
+ // On Windows, weak symbols are emulated by having the user program
+ // register which weak functions are defined.
+ // The ASAN DLL will initialize flags prior to user module initialization,
+ // so __asan_default_options will not point to the user definition yet.
+ // We still want to ensure we capture when options are passed via
+ // __asan_default_options, so we add a callback to be run
+ // when it is registered with the runtime.
+
+ // There is theoretically time between the initial ProcessFlags and
+ // registering the weak callback where a weak function could be added and we
+ // would miss it, but in practice, InitializeFlags will always happen under
+ // the loader lock (if built as a DLL) and so will any calls to
+ // __sanitizer_register_weak_function.
+ AddRegisterWeakFunctionCallback(
+ reinterpret_cast<uptr>(__asan_default_options), []() {
+ FlagParser asan_parser;
+
+ RegisterAsanFlags(&asan_parser, flags());
+ RegisterCommonFlags(&asan_parser);
+ asan_parser.ParseString(__asan_default_options());
+
+ DisplayHelpMessages(&asan_parser);
+ ProcessFlags();
+ });
+
+# if CAN_SANITIZE_UB
+ AddRegisterWeakFunctionCallback(
+ reinterpret_cast<uptr>(__ubsan_default_options), []() {
+ FlagParser ubsan_parser;
+
+ __ubsan::RegisterUbsanFlags(&ubsan_parser, __ubsan::flags());
+ RegisterCommonFlags(&ubsan_parser);
+ ubsan_parser.ParseString(__ubsan_default_options());
+
+ // To match normal behavior, do not print UBSan help.
+ ProcessFlags();
+ });
+# endif
+
+# if CAN_SANITIZE_LEAKS
+ AddRegisterWeakFunctionCallback(
+ reinterpret_cast<uptr>(__lsan_default_options), []() {
+ FlagParser lsan_parser;
+
+ __lsan::RegisterLsanFlags(&lsan_parser, __lsan::flags());
+ RegisterCommonFlags(&lsan_parser);
+ lsan_parser.ParseString(__lsan_default_options());
+
+ // To match normal behavior, do not print LSan help.
+ ProcessFlags();
+ });
+# endif
+
+#endif
+}
+
} // namespace __asan
SANITIZER_INTERFACE_WEAK_DEF(const char*, __asan_default_options, void) {
@@ -57,8 +57,6 @@ void AsanCheckDynamicRTPrereqs() {}
void AsanCheckIncompatibleRT() {}
void InitializeAsanInterceptors() {}
-void *AsanDoesNotSupportStaticLinkage() { return nullptr; }
-
void InitializePlatformExceptionHandlers() {}
void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
UNIMPLEMENTED();
@@ -123,8 +121,7 @@ static AsanThread *CreateAsanThread(StackTrace *stack, u32 parent_tid,
// In lieu of AsanThread::Create.
AsanThread *thread = (AsanThread *)MmapOrDie(AsanThreadMmapSize(), __func__);
- AsanThreadContext::CreateThreadContextArgs args = {thread, stack};
- u32 tid = asanThreadRegistry().CreateThread(0, detached, parent_tid, &args);
+ u32 tid = asanThreadRegistry().CreateThread(0, detached, parent_tid, thread);
asanThreadRegistry().SetThreadName(tid, name);
return thread;
@@ -240,6 +237,8 @@ void FlushUnneededASanShadowMemory(uptr p, uptr size) {
// So this doesn't install any atexit hook like on other platforms.
void InstallAtExitCheckLeaks() {}
+void InstallAtForkHandler() {}
+
} // namespace __asan
namespace __lsan {
@@ -21,31 +21,32 @@
#include "asan_suppressions.h"
#include "asan_thread.h"
#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_dense_map.h"
+#include "sanitizer_common/sanitizer_list.h"
#include "sanitizer_common/sanitizer_mutex.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
+#include "sanitizer_common/sanitizer_thread_safety.h"
namespace __asan {
typedef __asan_global Global;
-struct ListOfGlobals {
- const Global *g;
- ListOfGlobals *next;
+struct GlobalListNode {
+ const Global *g = nullptr;
+ GlobalListNode *next = nullptr;
};
+typedef IntrusiveList<GlobalListNode> ListOfGlobals;
static Mutex mu_for_globals;
-static ListOfGlobals *list_of_all_globals;
+static ListOfGlobals list_of_all_globals SANITIZER_GUARDED_BY(mu_for_globals);
-static const int kDynamicInitGlobalsInitialCapacity = 512;
struct DynInitGlobal {
- Global g;
- bool initialized;
+ Global g = {};
+ bool initialized = false;
+ DynInitGlobal *next = nullptr;
};
-typedef InternalMmapVector<DynInitGlobal> VectorOfGlobals;
-// Lazy-initialized and never deleted.
-static VectorOfGlobals *dynamic_init_globals;
// We want to remember where a certain range of globals was registered.
struct GlobalRegistrationSite {
@@ -55,6 +56,39 @@ struct GlobalRegistrationSite {
typedef InternalMmapVector<GlobalRegistrationSite> GlobalRegistrationSiteVector;
static GlobalRegistrationSiteVector *global_registration_site_vector;
+static ListOfGlobals &GlobalsByIndicator(uptr odr_indicator)
+ SANITIZER_REQUIRES(mu_for_globals) {
+ using MapOfGlobals = DenseMap<uptr, ListOfGlobals>;
+
+ static MapOfGlobals *globals_by_indicator = nullptr;
+ if (!globals_by_indicator) {
+ alignas(
+ alignof(MapOfGlobals)) static char placeholder[sizeof(MapOfGlobals)];
+ globals_by_indicator = new (placeholder) MapOfGlobals();
+ }
+
+ return (*globals_by_indicator)[odr_indicator];
+}
+
+static const char *current_dynamic_init_module_name
+ SANITIZER_GUARDED_BY(mu_for_globals) = nullptr;
+
+using DynInitGlobalsByModule =
+ DenseMap<const char *, IntrusiveList<DynInitGlobal>>;
+
+// TODO: Add a NoDestroy helper, this patter is very common in sanitizers.
+static DynInitGlobalsByModule &DynInitGlobals()
+ SANITIZER_REQUIRES(mu_for_globals) {
+ static DynInitGlobalsByModule *globals_by_module = nullptr;
+ if (!globals_by_module) {
+ alignas(alignof(DynInitGlobalsByModule)) static char
+ placeholder[sizeof(DynInitGlobalsByModule)];
+ globals_by_module = new (placeholder) DynInitGlobalsByModule();
+ }
+
+ return *globals_by_module;
+}
+
ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) {
FastPoisonShadow(g->beg, g->size_with_redzone, value);
}
@@ -73,6 +107,35 @@ ALWAYS_INLINE void PoisonRedZones(const Global &g) {
const uptr kMinimalDistanceFromAnotherGlobal = 64;
+static void AddGlobalToList(ListOfGlobals &list, const Global *g) {
+ list.push_front(new (GetGlobalLowLevelAllocator()) GlobalListNode{g});
+}
+
+static void UnpoisonDynamicGlobals(IntrusiveList<DynInitGlobal> &dyn_globals,
+ bool mark_initialized) {
+ for (auto &dyn_g : dyn_globals) {
+ const Global *g = &dyn_g.g;
+ if (dyn_g.initialized)
+ continue;
+ // Unpoison the whole global.
+ PoisonShadowForGlobal(g, 0);
+ // Poison redzones back.
+ PoisonRedZones(*g);
+ if (mark_initialized)
+ dyn_g.initialized = true;
+ }
+}
+
+static void PoisonDynamicGlobals(
+ const IntrusiveList<DynInitGlobal> &dyn_globals) {
+ for (auto &dyn_g : dyn_globals) {
+ const Global *g = &dyn_g.g;
+ if (dyn_g.initialized)
+ continue;
+ PoisonShadowForGlobal(g, kAsanInitializationOrderMagic);
+ }
+}
+
static bool IsAddressNearGlobal(uptr addr, const __asan_global &g) {
if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false;
if (addr >= g.beg + g.size_with_redzone) return false;
@@ -114,8 +177,8 @@ int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites,
if (!flags()->report_globals) return 0;
Lock lock(&mu_for_globals);
int res = 0;
- for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
- const Global &g = *l->g;
+ for (const auto &l : list_of_all_globals) {
+ const Global &g = *l.g;
if (flags()->report_globals >= 2)
ReportGlobal(g, "Search");
if (IsAddressNearGlobal(addr, g)) {
@@ -138,23 +201,47 @@ enum GlobalSymbolState {
// Check ODR violation for given global G via special ODR indicator. We use
// this method in case compiler instruments global variables through their
// local aliases.
-static void CheckODRViolationViaIndicator(const Global *g) {
+static void CheckODRViolationViaIndicator(const Global *g)
+ SANITIZER_REQUIRES(mu_for_globals) {
// Instrumentation requests to skip ODR check.
if (g->odr_indicator == UINTPTR_MAX)
return;
+
+ ListOfGlobals &relevant_globals = GlobalsByIndicator(g->odr_indicator);
+
u8 *odr_indicator = reinterpret_cast<u8 *>(g->odr_indicator);
- if (*odr_indicator == UNREGISTERED) {
+ if (*odr_indicator == REGISTERED) {
+ // If *odr_indicator is REGISTERED, some module have already registered
+ // externally visible symbol with the same name. This is an ODR violation.
+ for (const auto &l : relevant_globals) {
+ if ((flags()->detect_odr_violation >= 2 || g->size != l.g->size) &&
+ !IsODRViolationSuppressed(g->name))
+ ReportODRViolation(g, FindRegistrationSite(g), l.g,
+ FindRegistrationSite(l.g));
+ }
+ } else { // UNREGISTERED
*odr_indicator = REGISTERED;
- return;
}
- // If *odr_indicator is DEFINED, some module have already registered
- // externally visible symbol with the same name. This is an ODR violation.
- for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
- if (g->odr_indicator == l->g->odr_indicator &&
- (flags()->detect_odr_violation >= 2 || g->size != l->g->size) &&
- !IsODRViolationSuppressed(g->name))
- ReportODRViolation(g, FindRegistrationSite(g),
- l->g, FindRegistrationSite(l->g));
+
+ AddGlobalToList(relevant_globals, g);
+}
+
+// Check ODR violation for given global G by checking if it's already poisoned.
+// We use this method in case compiler doesn't use private aliases for global
+// variables.
+static void CheckODRViolationViaPoisoning(const Global *g)
+ SANITIZER_REQUIRES(mu_for_globals) {
+ if (__asan_region_is_poisoned(g->beg, g->size_with_redzone)) {
+ // This check may not be enough: if the first global is much larger
+ // the entire redzone of the second global may be within the first global.
+ for (const auto &l : list_of_all_globals) {
+ if (g->beg == l.g->beg &&
+ (flags()->detect_odr_violation >= 2 || g->size != l.g->size) &&
+ !IsODRViolationSuppressed(g->name)) {
+ ReportODRViolation(g, FindRegistrationSite(g), l.g,
+ FindRegistrationSite(l.g));
+ }
+ }
}
}
@@ -181,7 +268,7 @@ static inline bool UseODRIndicator(const Global *g) {
// Register a global variable.
// This function may be called more than once for every global
// so we store the globals in a map.
-static void RegisterGlobal(const Global *g) {
+static void RegisterGlobal(const Global *g) SANITIZER_REQUIRES(mu_for_globals) {
CHECK(AsanInited());
if (flags()->report_globals >= 2)
ReportGlobal(*g, "Added");
@@ -203,24 +290,22 @@ static void RegisterGlobal(const Global *g) {
// where two globals with the same name are defined in different modules.
if (UseODRIndicator(g))
CheckODRViolationViaIndicator(g);
+ else
+ CheckODRViolationViaPoisoning(g);
}
if (CanPoisonMemory())
PoisonRedZones(*g);
- ListOfGlobals *l = new (GetGlobalLowLevelAllocator()) ListOfGlobals;
- l->g = g;
- l->next = list_of_all_globals;
- list_of_all_globals = l;
+
+ AddGlobalToList(list_of_all_globals, g);
+
if (g->has_dynamic_init) {
- if (!dynamic_init_globals) {
- dynamic_init_globals = new (GetGlobalLowLevelAllocator()) VectorOfGlobals;
- dynamic_init_globals->reserve(kDynamicInitGlobalsInitialCapacity);
- }
- DynInitGlobal dyn_global = { *g, false };
- dynamic_init_globals->push_back(dyn_global);
+ DynInitGlobals()[g->module_name].push_back(
+ new (GetGlobalLowLevelAllocator()) DynInitGlobal{*g, false});
}
}
-static void UnregisterGlobal(const Global *g) {
+static void UnregisterGlobal(const Global *g)
+ SANITIZER_REQUIRES(mu_for_globals) {
CHECK(AsanInited());
if (flags()->report_globals >= 2)
ReportGlobal(*g, "Removed");
@@ -242,18 +327,14 @@ static void UnregisterGlobal(const Global *g) {
}
void StopInitOrderChecking() {
- Lock lock(&mu_for_globals);
- if (!flags()->check_initialization_order || !dynamic_init_globals)
+ if (!flags()->check_initialization_order)
return;
+ Lock lock(&mu_for_globals);
flags()->check_initialization_order = false;
- for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) {
- DynInitGlobal &dyn_g = (*dynamic_init_globals)[i];
- const Global *g = &dyn_g.g;
- // Unpoison the whole global.
- PoisonShadowForGlobal(g, 0);
- // Poison redzones back.
- PoisonRedZones(*g);
- }
+ DynInitGlobals().forEach([&](auto &kv) {
+ UnpoisonDynamicGlobals(kv.second, /*mark_initialized=*/false);
+ return true;
+ });
}
static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; }
@@ -325,8 +406,8 @@ void __asan_unregister_image_globals(uptr *flag) {
}
void __asan_register_elf_globals(uptr *flag, void *start, void *stop) {
- if (*flag) return;
- if (!start) return;
+ if (*flag || start == stop)
+ return;
CHECK_EQ(0, ((uptr)stop - (uptr)start) % sizeof(__asan_global));
__asan_global *globals_start = (__asan_global*)start;
__asan_global *globals_stop = (__asan_global*)stop;
@@ -335,8 +416,8 @@ void __asan_register_elf_globals(uptr *flag, void *start, void *stop) {
}
void __asan_unregister_elf_globals(uptr *flag, void *start, void *stop) {
- if (!*flag) return;
- if (!start) return;
+ if (!*flag || start == stop)
+ return;
CHECK_EQ(0, ((uptr)stop - (uptr)start) % sizeof(__asan_global));
__asan_global *globals_start = (__asan_global*)start;
__asan_global *globals_stop = (__asan_global*)stop;
@@ -408,47 +489,94 @@ void __asan_unregister_globals(__asan_global *globals, uptr n) {
// poisons all global variables not defined in this TU, so that a dynamic
// initializer can only touch global variables in the same TU.
void __asan_before_dynamic_init(const char *module_name) {
- if (!flags()->check_initialization_order ||
- !CanPoisonMemory() ||
- !dynamic_init_globals)
+ if (!flags()->check_initialization_order || !CanPoisonMemory())
return;
bool strict_init_order = flags()->strict_init_order;
CHECK(module_name);
CHECK(AsanInited());
Lock lock(&mu_for_globals);
+ if (current_dynamic_init_module_name == module_name)
+ return;
if (flags()->report_globals >= 3)
Printf("DynInitPoison module: %s\n", module_name);
- for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) {
- DynInitGlobal &dyn_g = (*dynamic_init_globals)[i];
- const Global *g = &dyn_g.g;
- if (dyn_g.initialized)
- continue;
- if (g->module_name != module_name)
- PoisonShadowForGlobal(g, kAsanInitializationOrderMagic);
- else if (!strict_init_order)
- dyn_g.initialized = true;
+
+ if (current_dynamic_init_module_name == nullptr) {
+ // First call, poison all globals from other modules.
+ DynInitGlobals().forEach([&](auto &kv) {
+ if (kv.first != module_name) {
+ PoisonDynamicGlobals(kv.second);
+ } else {
+ UnpoisonDynamicGlobals(kv.second,
+ /*mark_initialized=*/!strict_init_order);
+ }
+ return true;
+ });
+ } else {
+ // Module changed.
+ PoisonDynamicGlobals(DynInitGlobals()[current_dynamic_init_module_name]);
+ UnpoisonDynamicGlobals(DynInitGlobals()[module_name],
+ /*mark_initialized=*/!strict_init_order);
}
+ current_dynamic_init_module_name = module_name;
}
+// Maybe SANITIZER_CAN_USE_PREINIT_ARRAY is to conservative for `.init_array`,
+// however we should not make mistake here. If `UnpoisonBeforeMain` was not
+// executed at all we will have false reports on globals.
+#if SANITIZER_CAN_USE_PREINIT_ARRAY
+// This optimization aims to reduce the overhead of `__asan_after_dynamic_init`
+// calls by leveraging incremental unpoisoning/poisoning in
+// `__asan_before_dynamic_init`. We expect most `__asan_after_dynamic_init
+// calls` to be no-ops. However, to ensure all globals are unpoisoned before the
+// `main`, we force `UnpoisonBeforeMain` to fully execute
+// `__asan_after_dynamic_init`.
+
+// With lld, `UnpoisonBeforeMain` runs after standard `.init_array`, making it
+// the final `__asan_after_dynamic_init` call for the static runtime. In
+// contrast, GNU ld executes it earlier, causing subsequent
+// `__asan_after_dynamic_init` calls to perform full unpoisoning, losing the
+// optimization.
+bool allow_after_dynamic_init SANITIZER_GUARDED_BY(mu_for_globals) = false;
+
+static void UnpoisonBeforeMain(void) {
+ {
+ Lock lock(&mu_for_globals);
+ if (allow_after_dynamic_init)
+ return;
+ allow_after_dynamic_init = true;
+ }
+ if (flags()->report_globals >= 3)
+ Printf("UnpoisonBeforeMain\n");
+ __asan_after_dynamic_init();
+}
+
+__attribute__((section(".init_array.65537"), used)) static void (
+ *asan_after_init_array)(void) = UnpoisonBeforeMain;
+#else
+// Incremental poisoning is disabled, unpoison globals immediately.
+static constexpr bool allow_after_dynamic_init = true;
+#endif // SANITIZER_CAN_USE_PREINIT_ARRAY
+
// This method runs immediately after dynamic initialization in each TU, when
// all dynamically initialized globals except for those defined in the current
// TU are poisoned. It simply unpoisons all dynamically initialized globals.
void __asan_after_dynamic_init() {
- if (!flags()->check_initialization_order ||
- !CanPoisonMemory() ||
- !dynamic_init_globals)
+ if (!flags()->check_initialization_order || !CanPoisonMemory())
return;
CHECK(AsanInited());
Lock lock(&mu_for_globals);
- // FIXME: Optionally report that we're unpoisoning globals from a module.
- for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) {
- DynInitGlobal &dyn_g = (*dynamic_init_globals)[i];
- const Global *g = &dyn_g.g;
- if (!dyn_g.initialized) {
- // Unpoison the whole global.
- PoisonShadowForGlobal(g, 0);
- // Poison redzones back.
- PoisonRedZones(*g);
- }
- }
+ if (!allow_after_dynamic_init)
+ return;
+ if (!current_dynamic_init_module_name)
+ return;
+
+ if (flags()->report_globals >= 3)
+ Printf("DynInitUnpoison\n");
+
+ DynInitGlobals().forEach([&](auto &kv) {
+ UnpoisonDynamicGlobals(kv.second, /*mark_initialized=*/false);
+ return true;
+ });
+
+ current_dynamic_init_module_name = nullptr;
}
@@ -17,10 +17,10 @@ namespace __asan {
#pragma section(".ASAN$GA", read, write)
#pragma section(".ASAN$GZ", read, write)
-extern "C" __declspec(allocate(".ASAN$GA"))
- ALIGNED(sizeof(__asan_global)) __asan_global __asan_globals_start = {};
-extern "C" __declspec(allocate(".ASAN$GZ"))
- ALIGNED(sizeof(__asan_global)) __asan_global __asan_globals_end = {};
+extern "C" alignas(sizeof(__asan_global))
+ __declspec(allocate(".ASAN$GA")) __asan_global __asan_globals_start = {};
+extern "C" alignas(sizeof(__asan_global))
+ __declspec(allocate(".ASAN$GZ")) __asan_global __asan_globals_end = {};
#pragma comment(linker, "/merge:.ASAN=.data")
static void call_on_globals(void (*hook)(__asan_global *, uptr)) {
@@ -28,7 +28,9 @@ static void call_on_globals(void (*hook)(__asan_global *, uptr)) {
__asan_global *end = &__asan_globals_end;
uptr bytediff = (uptr)end - (uptr)start;
if (bytediff % sizeof(__asan_global) != 0) {
-#if defined(SANITIZER_DLL_THUNK) || defined(SANITIZER_DYNAMIC_RUNTIME_THUNK)
+# if defined(SANITIZER_DLL_THUNK) || \
+ defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) || \
+ defined(SANITIZER_STATIC_RUNTIME_THUNK)
__debugbreak();
#else
CHECK("corrupt asan global array");
@@ -96,14 +96,16 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
ASAN_WRITE_RANGE(ctx, ptr, size)
#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
ASAN_READ_RANGE(ctx, ptr, size)
-# define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
- ASAN_INTERCEPTOR_ENTER(ctx, func); \
- do { \
- if (AsanInitIsRunning()) \
- return REAL(func)(__VA_ARGS__); \
- if (SANITIZER_APPLE && UNLIKELY(!AsanInited())) \
- return REAL(func)(__VA_ARGS__); \
- ENSURE_ASAN_INITED(); \
+# define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
+ ASAN_INTERCEPTOR_ENTER(ctx, func); \
+ do { \
+ if constexpr (SANITIZER_APPLE) { \
+ if (UNLIKELY(!AsanInited())) \
+ return REAL(func)(__VA_ARGS__); \
+ } else { \
+ if (!TryAsanInitFromRtl()) \
+ return REAL(func)(__VA_ARGS__); \
+ } \
} while (false)
#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \
do { \
@@ -194,10 +196,13 @@ static int munmap_interceptor(Munmap real_munmap, void *addr, SIZE_T length) {
__lsan::ScopedInterceptorDisabler disabler
#endif
-#define SIGNAL_INTERCEPTOR_ENTER() ENSURE_ASAN_INITED()
+# define SIGNAL_INTERCEPTOR_ENTER() \
+ do { \
+ AsanInitFromRtl(); \
+ } while (false)
-#include "sanitizer_common/sanitizer_common_interceptors.inc"
-#include "sanitizer_common/sanitizer_signal_interceptors.inc"
+# include "sanitizer_common/sanitizer_common_interceptors.inc"
+# include "sanitizer_common/sanitizer_signal_interceptors.inc"
// Syscall interceptors don't have contexts, we don't support suppressions
// for them.
@@ -328,7 +333,7 @@ INTERCEPTOR(int, pthread_timedjoin_np, void *thread, void **ret,
}
# endif
-DEFINE_REAL_PTHREAD_FUNCTIONS
+DEFINE_INTERNAL_PTHREAD_FUNCTIONS
#endif // ASAN_INTERCEPT_PTHREAD_CREATE
#if ASAN_INTERCEPT_SWAPCONTEXT
@@ -506,7 +511,7 @@ DEFINE_REAL(char*, index, const char *string, int c)
INTERCEPTOR(char *, strcat, char *to, const char *from) {
void *ctx;
ASAN_INTERCEPTOR_ENTER(ctx, strcat);
- ENSURE_ASAN_INITED();
+ AsanInitFromRtl();
if (flags()->replace_str) {
uptr from_length = internal_strlen(from);
ASAN_READ_RANGE(ctx, from, from_length + 1);
@@ -527,7 +532,7 @@ DEFINE_REAL(char*, index, const char *string, int c)
INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) {
void *ctx;
ASAN_INTERCEPTOR_ENTER(ctx, strncat);
- ENSURE_ASAN_INITED();
+ AsanInitFromRtl();
if (flags()->replace_str) {
uptr from_length = MaybeRealStrnlen(from, size);
uptr copy_length = Min(size, from_length + 1);
@@ -546,16 +551,16 @@ INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) {
INTERCEPTOR(char *, strcpy, char *to, const char *from) {
void *ctx;
ASAN_INTERCEPTOR_ENTER(ctx, strcpy);
-#if SANITIZER_APPLE
- if (UNLIKELY(!AsanInited()))
- return REAL(strcpy)(to, from);
-#endif
- // strcpy is called from malloc_default_purgeable_zone()
- // in __asan::ReplaceSystemAlloc() on Mac.
- if (AsanInitIsRunning()) {
- return REAL(strcpy)(to, from);
+ if constexpr (SANITIZER_APPLE) {
+ // strcpy is called from malloc_default_purgeable_zone()
+ // in __asan::ReplaceSystemAlloc() on Mac.
+ if (UNLIKELY(!AsanInited()))
+ return REAL(strcpy)(to, from);
+ } else {
+ if (!TryAsanInitFromRtl())
+ return REAL(strcpy)(to, from);
}
- ENSURE_ASAN_INITED();
+
if (flags()->replace_str) {
uptr from_size = internal_strlen(from) + 1;
CHECK_RANGES_OVERLAP("strcpy", to, from_size, from, from_size);
@@ -565,12 +570,22 @@ INTERCEPTOR(char *, strcpy, char *to, const char *from) {
return REAL(strcpy)(to, from);
}
+// Windows doesn't always define the strdup identifier,
+// and when it does it's a macro defined to either _strdup
+// or _strdup_dbg, _strdup_dbg ends up calling _strdup, so
+// we want to intercept that. push/pop_macro are used to avoid problems
+// if this file ends up including <string.h> in the future.
+# if SANITIZER_WINDOWS
+# pragma push_macro("strdup")
+# undef strdup
+# define strdup _strdup
+# endif
+
INTERCEPTOR(char*, strdup, const char *s) {
void *ctx;
ASAN_INTERCEPTOR_ENTER(ctx, strdup);
- if (UNLIKELY(!AsanInited()))
+ if (UNLIKELY(!TryAsanInitFromRtl()))
return internal_strdup(s);
- ENSURE_ASAN_INITED();
uptr length = internal_strlen(s);
if (flags()->replace_str) {
ASAN_READ_RANGE(ctx, s, length + 1);
@@ -583,13 +598,12 @@ INTERCEPTOR(char*, strdup, const char *s) {
return reinterpret_cast<char*>(new_mem);
}
-#if ASAN_INTERCEPT___STRDUP
+# if ASAN_INTERCEPT___STRDUP
INTERCEPTOR(char*, __strdup, const char *s) {
void *ctx;
ASAN_INTERCEPTOR_ENTER(ctx, strdup);
- if (UNLIKELY(!AsanInited()))
+ if (UNLIKELY(!TryAsanInitFromRtl()))
return internal_strdup(s);
- ENSURE_ASAN_INITED();
uptr length = internal_strlen(s);
if (flags()->replace_str) {
ASAN_READ_RANGE(ctx, s, length + 1);
@@ -606,7 +620,7 @@ INTERCEPTOR(char*, __strdup, const char *s) {
INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) {
void *ctx;
ASAN_INTERCEPTOR_ENTER(ctx, strncpy);
- ENSURE_ASAN_INITED();
+ AsanInitFromRtl();
if (flags()->replace_str) {
uptr from_size = Min(size, MaybeRealStrnlen(from, size) + 1);
CHECK_RANGES_OVERLAP("strncpy", to, from_size, from, from_size);
@@ -632,13 +646,38 @@ static ALWAYS_INLINE auto StrtolImpl(void *ctx, Fn real, const char *nptr,
INTERCEPTOR(ret_type, func, const char *nptr, char **endptr, int base) { \
void *ctx; \
ASAN_INTERCEPTOR_ENTER(ctx, func); \
- ENSURE_ASAN_INITED(); \
+ AsanInitFromRtl(); \
return StrtolImpl(ctx, REAL(func), nptr, endptr, base); \
}
-INTERCEPTOR_STRTO_BASE(long, strtol)
INTERCEPTOR_STRTO_BASE(long long, strtoll)
+# if SANITIZER_WINDOWS
+INTERCEPTOR(long, strtol, const char *nptr, char **endptr, int base) {
+ // REAL(strtol) may be ntdll!strtol, which doesn't set errno. Instead,
+ // call REAL(strtoll) and do the range check ourselves.
+ COMPILER_CHECK(sizeof(long) == sizeof(u32));
+
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strtol);
+ AsanInitFromRtl();
+
+ long long result = StrtolImpl(ctx, REAL(strtoll), nptr, endptr, base);
+
+ if (result > INT32_MAX) {
+ errno = errno_ERANGE;
+ return INT32_MAX;
+ }
+ if (result < INT32_MIN) {
+ errno = errno_ERANGE;
+ return INT32_MIN;
+ }
+ return (long)result;
+}
+# else
+INTERCEPTOR_STRTO_BASE(long, strtol)
+# endif
+
# if SANITIZER_GLIBC
INTERCEPTOR_STRTO_BASE(long, __isoc23_strtol)
INTERCEPTOR_STRTO_BASE(long long, __isoc23_strtoll)
@@ -647,11 +686,9 @@ INTERCEPTOR_STRTO_BASE(long long, __isoc23_strtoll)
INTERCEPTOR(int, atoi, const char *nptr) {
void *ctx;
ASAN_INTERCEPTOR_ENTER(ctx, atoi);
-#if SANITIZER_APPLE
- if (UNLIKELY(!AsanInited()))
+ if (SANITIZER_APPLE && UNLIKELY(!AsanInited()))
return REAL(atoi)(nptr);
-# endif
- ENSURE_ASAN_INITED();
+ AsanInitFromRtl();
if (!flags()->replace_str) {
return REAL(atoi)(nptr);
}
@@ -669,11 +706,9 @@ INTERCEPTOR(int, atoi, const char *nptr) {
INTERCEPTOR(long, atol, const char *nptr) {
void *ctx;
ASAN_INTERCEPTOR_ENTER(ctx, atol);
-#if SANITIZER_APPLE
- if (UNLIKELY(!AsanInited()))
+ if (SANITIZER_APPLE && UNLIKELY(!AsanInited()))
return REAL(atol)(nptr);
-# endif
- ENSURE_ASAN_INITED();
+ AsanInitFromRtl();
if (!flags()->replace_str) {
return REAL(atol)(nptr);
}
@@ -687,7 +722,7 @@ INTERCEPTOR(long, atol, const char *nptr) {
INTERCEPTOR(long long, atoll, const char *nptr) {
void *ctx;
ASAN_INTERCEPTOR_ENTER(ctx, atoll);
- ENSURE_ASAN_INITED();
+ AsanInitFromRtl();
if (!flags()->replace_str) {
return REAL(atoll)(nptr);
}
@@ -708,12 +743,10 @@ static void AtCxaAtexit(void *unused) {
#if ASAN_INTERCEPT___CXA_ATEXIT
INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg,
void *dso_handle) {
-#if SANITIZER_APPLE
- if (UNLIKELY(!AsanInited()))
+ if (SANITIZER_APPLE && UNLIKELY(!AsanInited()))
return REAL(__cxa_atexit)(func, arg, dso_handle);
-# endif
- ENSURE_ASAN_INITED();
-#if CAN_SANITIZE_LEAKS
+ AsanInitFromRtl();
+# if CAN_SANITIZE_LEAKS
__lsan::ScopedInterceptorDisabler disabler;
#endif
int res = REAL(__cxa_atexit)(func, arg, dso_handle);
@@ -724,8 +757,8 @@ INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg,
#if ASAN_INTERCEPT_ATEXIT
INTERCEPTOR(int, atexit, void (*func)()) {
- ENSURE_ASAN_INITED();
-#if CAN_SANITIZE_LEAKS
+ AsanInitFromRtl();
+# if CAN_SANITIZE_LEAKS
__lsan::ScopedInterceptorDisabler disabler;
#endif
// Avoid calling real atexit as it is unreachable on at least on Linux.
@@ -739,7 +772,7 @@ INTERCEPTOR(int, atexit, void (*func)()) {
extern "C" {
extern int _pthread_atfork(void (*prepare)(), void (*parent)(),
void (*child)());
-};
+}
INTERCEPTOR(int, pthread_atfork, void (*prepare)(), void (*parent)(),
void (*child)()) {
@@ -753,8 +786,8 @@ INTERCEPTOR(int, pthread_atfork, void (*prepare)(), void (*parent)(),
#endif
#if ASAN_INTERCEPT_VFORK
-DEFINE_REAL(int, vfork)
-DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork)
+DEFINE_REAL(int, vfork,)
+DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork,)
#endif
// ---------------------- InitializeAsanInterceptors ---------------- {{{1
@@ -773,7 +806,7 @@ void InitializeAsanInterceptors() {
ASAN_INTERCEPT_FUNC(strncat);
ASAN_INTERCEPT_FUNC(strncpy);
ASAN_INTERCEPT_FUNC(strdup);
-#if ASAN_INTERCEPT___STRDUP
+# if ASAN_INTERCEPT___STRDUP
ASAN_INTERCEPT_FUNC(__strdup);
#endif
#if ASAN_INTERCEPT_INDEX && ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX
@@ -869,6 +902,10 @@ void InitializeAsanInterceptors() {
VReport(1, "AddressSanitizer: libc interceptors initialized\n");
}
+# if SANITIZER_WINDOWS
+# pragma pop_macro("strdup")
+# endif
+
} // namespace __asan
#endif // !SANITIZER_FUCHSIA
@@ -24,14 +24,6 @@ namespace __asan {
void InitializeAsanInterceptors();
void InitializePlatformInterceptors();
-#define ENSURE_ASAN_INITED() \
- do { \
- CHECK(!AsanInitIsRunning()); \
- if (UNLIKELY(!AsanInited())) { \
- AsanInitFromRtl(); \
- } \
- } while (0)
-
} // namespace __asan
// There is no general interception at all on Fuchsia.
@@ -79,12 +71,7 @@ void InitializePlatformInterceptors();
#if ASAN_HAS_EXCEPTIONS && !SANITIZER_SOLARIS && !SANITIZER_NETBSD && \
(!SANITIZER_WINDOWS || (defined(__MINGW32__) && defined(__i386__)))
# define ASAN_INTERCEPT___CXA_THROW 1
-# if ! defined(ASAN_HAS_CXA_RETHROW_PRIMARY_EXCEPTION) \
- || ASAN_HAS_CXA_RETHROW_PRIMARY_EXCEPTION
-# define ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION 1
-# else
-# define ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION 0
-# endif
+# define ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION 1
# if defined(_GLIBCXX_SJLJ_EXCEPTIONS) || (SANITIZER_IOS && defined(__arm__))
# define ASAN_INTERCEPT__UNWIND_SJLJ_RAISEEXCEPTION 1
# else
@@ -60,6 +60,7 @@ class AsanThread;
using __sanitizer::StackTrace;
void AsanInitFromRtl();
+bool TryAsanInitFromRtl();
// asan_win.cpp
void InitializePlatformExceptionHandlers();
@@ -79,7 +80,6 @@ void ReplaceSystemMalloc();
// asan_linux.cpp / asan_mac.cpp / asan_win.cpp
uptr FindDynamicShadowStart();
-void *AsanDoesNotSupportStaticLinkage();
void AsanCheckDynamicRTPrereqs();
void AsanCheckIncompatibleRT();
@@ -125,13 +125,13 @@ void *AsanDlSymNext(const char *sym);
bool HandleDlopenInit();
void InstallAtExitCheckLeaks();
+void InstallAtForkHandler();
#define ASAN_ON_ERROR() \
if (&__asan_on_error) \
__asan_on_error()
bool AsanInited();
-bool AsanInitIsRunning(); // Used to avoid infinite recursion in __asan_init().
extern bool replace_intrin_cached;
extern void (*death_callback)(void);
// These magic values are written to shadow for better error
@@ -33,7 +33,6 @@
# include "asan_premap_shadow.h"
# include "asan_thread.h"
# include "sanitizer_common/sanitizer_flags.h"
-# include "sanitizer_common/sanitizer_freebsd.h"
# include "sanitizer_common/sanitizer_hash.h"
# include "sanitizer_common/sanitizer_libc.h"
# include "sanitizer_common/sanitizer_procmaps.h"
@@ -48,22 +47,12 @@
# if SANITIZER_ANDROID || SANITIZER_FREEBSD || SANITIZER_SOLARIS
# include <ucontext.h>
-extern "C" void *_DYNAMIC;
# elif SANITIZER_NETBSD
# include <link_elf.h>
# include <ucontext.h>
-extern Elf_Dyn _DYNAMIC;
# else
# include <link.h>
# include <sys/ucontext.h>
-extern ElfW(Dyn) _DYNAMIC[];
-# endif
-
-// x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in
-// 32-bit mode.
-# if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32) && \
- __FreeBSD_version <= 902001 // v9.2
-# define ucontext_t xucontext_t
# endif
typedef enum {
@@ -84,11 +73,6 @@ void InitializePlatformInterceptors() {}
void InitializePlatformExceptionHandlers() {}
bool IsSystemHeapAddress(uptr addr) { return false; }
-void *AsanDoesNotSupportStaticLinkage() {
- // This will fail to link with -static.
- return &_DYNAMIC;
-}
-
# if ASAN_PREMAP_SHADOW
uptr FindPremappedShadowStart(uptr shadow_size_bytes) {
uptr granularity = GetMmapGranularity();
@@ -109,7 +93,8 @@ uptr FindDynamicShadowStart() {
# endif
return MapDynamicShadow(shadow_size_bytes, ASAN_SHADOW_SCALE,
- /*min_shadow_base_alignment*/ 0, kHighMemEnd);
+ /*min_shadow_base_alignment*/ 0, kHighMemEnd,
+ GetMmapGranularity());
}
void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
@@ -148,6 +133,11 @@ static int FindFirstDSOCallback(struct dl_phdr_info *info, size_t size,
internal_strncmp(info->dlpi_name, "linux-", sizeof("linux-") - 1) == 0)
return 0;
# endif
+# if SANITIZER_FREEBSD
+ // Ignore vDSO.
+ if (internal_strcmp(info->dlpi_name, "[vdso]") == 0)
+ return 0;
+# endif
*name = info->dlpi_name;
return 1;
@@ -197,10 +187,7 @@ void AsanCheckIncompatibleRT() {
MemoryMappedSegment segment(filename, sizeof(filename));
while (proc_maps.Next(&segment)) {
if (IsDynamicRTName(segment.filename)) {
- Report(
- "Your application is linked against "
- "incompatible ASan runtimes.\n");
- Die();
+ ReportIncompatibleRT();
}
}
__asan_rt_version = ASAN_RT_VERSION_STATIC;
deleted file mode 100644
@@ -49,14 +49,10 @@ void InitializePlatformInterceptors() {}
void InitializePlatformExceptionHandlers() {}
bool IsSystemHeapAddress (uptr addr) { return false; }
-// No-op. Mac does not support static linkage anyway.
-void *AsanDoesNotSupportStaticLinkage() {
- return 0;
-}
-
uptr FindDynamicShadowStart() {
return MapDynamicShadow(MemToShadowSize(kHighMemEnd), ASAN_SHADOW_SCALE,
- /*min_shadow_base_alignment*/ 0, kHighMemEnd);
+ /*min_shadow_base_alignment*/ 0, kHighMemEnd,
+ GetMmapGranularity());
}
// No-op. Mac does not support static linkage anyway.
@@ -139,11 +135,11 @@ typedef void (*dispatch_mach_handler_function_t)(void *context,
dispatch_mach_reason reason,
dispatch_mach_msg_t message,
mach_error_t error);
-#if !defined(MISSING_BLOCKS_SUPPORT)
+# if !defined(MISSING_BLOCKS_SUPPORT)
typedef void (^dispatch_mach_handler_t)(dispatch_mach_reason reason,
dispatch_mach_msg_t message,
mach_error_t error);
-#endif
+# endif
// A wrapper for the ObjC blocks used to support libdispatch.
typedef struct {
@@ -25,13 +25,12 @@
# include "sanitizer_common/sanitizer_allocator_checks.h"
# include "sanitizer_common/sanitizer_allocator_dlsym.h"
# include "sanitizer_common/sanitizer_errno.h"
-# include "sanitizer_common/sanitizer_tls_get_addr.h"
// ---------------------- Replacement functions ---------------- {{{1
using namespace __asan;
struct DlsymAlloc : public DlSymAllocator<DlsymAlloc> {
- static bool UseImpl() { return AsanInitIsRunning(); }
+ static bool UseImpl() { return !TryAsanInitFromRtl(); }
static void OnAllocate(const void *ptr, uptr size) {
# if CAN_SANITIZE_LEAKS
// Suppress leaks from dlerror(). Previously dlsym hack on global array was
@@ -65,7 +64,6 @@ INTERCEPTOR(void, cfree, void *ptr) {
INTERCEPTOR(void*, malloc, uptr size) {
if (DlsymAlloc::Use())
return DlsymAlloc::Allocate(size);
- ENSURE_ASAN_INITED();
GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack);
}
@@ -73,7 +71,6 @@ INTERCEPTOR(void*, malloc, uptr size) {
INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
if (DlsymAlloc::Use())
return DlsymAlloc::Callocate(nmemb, size);
- ENSURE_ASAN_INITED();
GET_STACK_TRACE_MALLOC;
return asan_calloc(nmemb, size, &stack);
}
@@ -81,14 +78,13 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
INTERCEPTOR(void*, realloc, void *ptr, uptr size) {
if (DlsymAlloc::Use() || DlsymAlloc::PointerIsMine(ptr))
return DlsymAlloc::Realloc(ptr, size);
- ENSURE_ASAN_INITED();
GET_STACK_TRACE_MALLOC;
return asan_realloc(ptr, size, &stack);
}
#if SANITIZER_INTERCEPT_REALLOCARRAY
INTERCEPTOR(void*, reallocarray, void *ptr, uptr nmemb, uptr size) {
- ENSURE_ASAN_INITED();
+ AsanInitFromRtl();
GET_STACK_TRACE_MALLOC;
return asan_reallocarray(ptr, nmemb, size, &stack);
}
@@ -102,9 +98,7 @@ INTERCEPTOR(void*, memalign, uptr boundary, uptr size) {
INTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) {
GET_STACK_TRACE_MALLOC;
- void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC);
- DTLS_on_libc_memalign(res, size);
- return res;
+ return asan_memalign(boundary, size, &stack, FROM_MALLOC);
}
#endif // SANITIZER_INTERCEPT_MEMALIGN
@@ -188,11 +182,11 @@ struct MallocDebugL {
void* (*valloc)(uptr size);
};
-ALIGNED(32) const MallocDebugK asan_malloc_dispatch_k = {
+alignas(32) const MallocDebugK asan_malloc_dispatch_k = {
WRAP(malloc), WRAP(free), WRAP(calloc),
WRAP(realloc), WRAP(memalign), WRAP(malloc_usable_size)};
-ALIGNED(32) const MallocDebugL asan_malloc_dispatch_l = {
+alignas(32) const MallocDebugL asan_malloc_dispatch_l = {
WRAP(calloc), WRAP(free), WRAP(mallinfo),
WRAP(malloc), WRAP(malloc_usable_size), WRAP(memalign),
WRAP(posix_memalign), WRAP(pvalloc), WRAP(realloc),
@@ -22,7 +22,10 @@
using namespace __asan;
#define COMMON_MALLOC_ZONE_NAME "asan"
-#define COMMON_MALLOC_ENTER() ENSURE_ASAN_INITED()
+# define COMMON_MALLOC_ENTER() \
+ do { \
+ AsanInitFromRtl(); \
+ } while (false)
# define COMMON_MALLOC_SANITIZER_INITIALIZED AsanInited()
# define COMMON_MALLOC_FORCE_LOCK() asan_mz_force_lock()
# define COMMON_MALLOC_FORCE_UNLOCK() asan_mz_force_unlock()
@@ -58,97 +58,69 @@ using namespace __asan;
// MD: Memory allocation functions are defined in the CRT .dll,
// so we have to intercept them before they are called for the first time.
-#if ASAN_DYNAMIC
-# define ALLOCATION_FUNCTION_ATTRIBUTE
-#else
-# define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
-#endif
-
extern "C" {
-ALLOCATION_FUNCTION_ATTRIBUTE
-size_t _msize(void *ptr) {
+__declspec(noinline) size_t _msize(void *ptr) {
GET_CURRENT_PC_BP_SP;
(void)sp;
return asan_malloc_usable_size(ptr, pc, bp);
}
-ALLOCATION_FUNCTION_ATTRIBUTE
-size_t _msize_base(void *ptr) {
- return _msize(ptr);
-}
+__declspec(noinline) size_t _msize_base(void *ptr) { return _msize(ptr); }
-ALLOCATION_FUNCTION_ATTRIBUTE
-void free(void *ptr) {
+__declspec(noinline) void free(void *ptr) {
GET_STACK_TRACE_FREE;
return asan_free(ptr, &stack, FROM_MALLOC);
}
-ALLOCATION_FUNCTION_ATTRIBUTE
-void _free_dbg(void *ptr, int) {
- free(ptr);
-}
+__declspec(noinline) void _free_dbg(void *ptr, int) { free(ptr); }
-ALLOCATION_FUNCTION_ATTRIBUTE
-void _free_base(void *ptr) {
- free(ptr);
-}
+__declspec(noinline) void _free_base(void *ptr) { free(ptr); }
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *malloc(size_t size) {
+__declspec(noinline) void *malloc(size_t size) {
GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack);
}
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_malloc_base(size_t size) {
- return malloc(size);
-}
+__declspec(noinline) void *_malloc_base(size_t size) { return malloc(size); }
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_malloc_dbg(size_t size, int, const char *, int) {
+__declspec(noinline) void *_malloc_dbg(size_t size, int, const char *, int) {
return malloc(size);
}
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *calloc(size_t nmemb, size_t size) {
+__declspec(noinline) void *calloc(size_t nmemb, size_t size) {
GET_STACK_TRACE_MALLOC;
return asan_calloc(nmemb, size, &stack);
}
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_calloc_base(size_t nmemb, size_t size) {
+__declspec(noinline) void *_calloc_base(size_t nmemb, size_t size) {
return calloc(nmemb, size);
}
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) {
+__declspec(noinline) void *_calloc_dbg(size_t nmemb, size_t size, int,
+ const char *, int) {
return calloc(nmemb, size);
}
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
+__declspec(noinline) void *_calloc_impl(size_t nmemb, size_t size,
+ int *errno_tmp) {
return calloc(nmemb, size);
}
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *realloc(void *ptr, size_t size) {
+__declspec(noinline) void *realloc(void *ptr, size_t size) {
GET_STACK_TRACE_MALLOC;
return asan_realloc(ptr, size, &stack);
}
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_realloc_dbg(void *ptr, size_t size, int) {
+__declspec(noinline) void *_realloc_dbg(void *ptr, size_t size, int) {
UNREACHABLE("_realloc_dbg should not exist!");
return 0;
}
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_realloc_base(void *ptr, size_t size) {
+__declspec(noinline) void *_realloc_base(void *ptr, size_t size) {
return realloc(ptr, size);
}
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_recalloc(void *p, size_t n, size_t elem_size) {
+__declspec(noinline) void *_recalloc(void *p, size_t n, size_t elem_size) {
if (!p)
return calloc(n, elem_size);
const size_t size = n * elem_size;
@@ -166,23 +138,41 @@ void *_recalloc(void *p, size_t n, size_t elem_size) {
return new_alloc;
}
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_recalloc_base(void *p, size_t n, size_t elem_size) {
+__declspec(noinline) void *_recalloc_base(void *p, size_t n, size_t elem_size) {
return _recalloc(p, n, elem_size);
}
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_expand(void *memblock, size_t size) {
+__declspec(noinline) void *_expand(void *memblock, size_t size) {
// _expand is used in realloc-like functions to resize the buffer if possible.
// We don't want memory to stand still while resizing buffers, so return 0.
return 0;
}
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_expand_dbg(void *memblock, size_t size) {
+__declspec(noinline) void *_expand_dbg(void *memblock, size_t size) {
return _expand(memblock, size);
}
+__declspec(dllexport) size_t __cdecl __asan_msize(void *ptr) {
+ return _msize(ptr);
+}
+__declspec(dllexport) void __cdecl __asan_free(void *const ptr) { free(ptr); }
+__declspec(dllexport) void *__cdecl __asan_malloc(const size_t size) {
+ return malloc(size);
+}
+__declspec(dllexport) void *__cdecl __asan_calloc(const size_t nmemb,
+ const size_t size) {
+ return calloc(nmemb, size);
+}
+__declspec(dllexport) void *__cdecl __asan_realloc(void *const ptr,
+ const size_t size) {
+ return realloc(ptr, size);
+}
+__declspec(dllexport) void *__cdecl __asan_recalloc(void *const ptr,
+ const size_t nmemb,
+ const size_t size) {
+ return _recalloc(ptr, nmemb, size);
+}
+
// TODO(timurrrr): Might want to add support for _aligned_* allocation
// functions to detect a bit more bugs. Those functions seem to wrap malloc().
@@ -487,7 +477,6 @@ static void TryToOverrideFunction(const char *fname, uptr new_func) {
}
void ReplaceSystemMalloc() {
-#if defined(ASAN_DYNAMIC)
TryToOverrideFunction("free", (uptr)free);
TryToOverrideFunction("_free_base", (uptr)free);
TryToOverrideFunction("malloc", (uptr)malloc);
@@ -543,8 +532,6 @@ void ReplaceSystemMalloc() {
// allocation API will be directed to ASan's heap. We don't currently
// intercept all calls to HeapAlloc. If we did, we would have to check on
// HeapFree whether the pointer came from ASan of from the system.
-
-#endif // defined(ASAN_DYNAMIC)
}
} // namespace __asan
new file mode 100644
@@ -0,0 +1,229 @@
+//===-- asan_malloc_win_thunk.cpp
+//-----------------------------------------------===//
+//
+// 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 AddressSanitizer, an address sanity checker.
+//
+// Windows-specific malloc interception.
+// This is included statically for projects statically linking
+// with the C Runtime (/MT, /MTd) in order to provide ASAN-aware
+// versions of the C allocation functions.
+//===----------------------------------------------------------------------===//
+
+#ifdef SANITIZER_STATIC_RUNTIME_THUNK
+# include "..\sanitizer_common\sanitizer_allocator_interface.h"
+// #include "asan_win_thunk_common.h"
+
+// Preserve stack traces with noinline.
+# define STATIC_MALLOC_INTERFACE __declspec(noinline)
+
+extern "C" {
+__declspec(dllimport) size_t __cdecl __asan_msize(void *ptr);
+__declspec(dllimport) void __cdecl __asan_free(void *const ptr);
+__declspec(dllimport) void *__cdecl __asan_malloc(const size_t size);
+__declspec(dllimport) void *__cdecl __asan_calloc(const size_t nmemb,
+ const size_t size);
+__declspec(dllimport) void *__cdecl __asan_realloc(void *const ptr,
+ const size_t size);
+__declspec(dllimport) void *__cdecl __asan_recalloc(void *const ptr,
+ const size_t nmemb,
+ const size_t size);
+
+// Avoid tailcall optimization to preserve stack frames.
+# pragma optimize("", off)
+
+// _msize
+STATIC_MALLOC_INTERFACE size_t _msize(void *ptr) { return __asan_msize(ptr); }
+
+STATIC_MALLOC_INTERFACE size_t _msize_base(void *ptr) {
+ return __asan_msize(ptr);
+}
+
+STATIC_MALLOC_INTERFACE size_t _msize_dbg(void *ptr) {
+ return __asan_msize(ptr);
+}
+
+// free
+STATIC_MALLOC_INTERFACE void free(void *const ptr) { return __asan_free(ptr); }
+
+STATIC_MALLOC_INTERFACE void _free_base(void *const ptr) {
+ return __asan_free(ptr);
+}
+
+STATIC_MALLOC_INTERFACE void _free_dbg(void *const ptr) {
+ return __asan_free(ptr);
+}
+
+// malloc
+STATIC_MALLOC_INTERFACE void *malloc(const size_t size) {
+ return __asan_malloc(size);
+}
+
+STATIC_MALLOC_INTERFACE void *_malloc_base(const size_t size) {
+ return __asan_malloc(size);
+}
+
+STATIC_MALLOC_INTERFACE void *_malloc_dbg(const size_t size) {
+ return __asan_malloc(size);
+}
+
+// calloc
+STATIC_MALLOC_INTERFACE void *calloc(const size_t nmemb, const size_t size) {
+ return __asan_calloc(nmemb, size);
+}
+
+STATIC_MALLOC_INTERFACE void *_calloc_base(const size_t nmemb,
+ const size_t size) {
+ return __asan_calloc(nmemb, size);
+}
+
+STATIC_MALLOC_INTERFACE void *_calloc_impl(const size_t nmemb,
+ const size_t size,
+ int *const errno_tmp) {
+ // Provided by legacy msvcrt.
+ (void)errno_tmp;
+
+ return __asan_calloc(nmemb, size);
+}
+
+STATIC_MALLOC_INTERFACE void *_calloc_dbg(const size_t nmemb, const size_t size,
+ int, const char *, int) {
+ return __asan_calloc(nmemb, size);
+}
+
+// realloc
+STATIC_MALLOC_INTERFACE void *realloc(void *const ptr, const size_t size) {
+ return __asan_realloc(ptr, size);
+}
+
+STATIC_MALLOC_INTERFACE void *_realloc_base(void *const ptr,
+ const size_t size) {
+ return __asan_realloc(ptr, size);
+}
+
+STATIC_MALLOC_INTERFACE void *_realloc_dbg(void *const ptr, const size_t size,
+ int, const char *, int) {
+ return __asan_realloc(ptr, size);
+}
+
+// recalloc
+STATIC_MALLOC_INTERFACE void *_recalloc(void *const ptr, const size_t nmemb,
+ const size_t size) {
+ return __asan_recalloc(ptr, nmemb, size);
+}
+
+STATIC_MALLOC_INTERFACE void *_recalloc_base(void *const ptr,
+ const size_t nmemb,
+ const size_t size) {
+ return __asan_recalloc(ptr, nmemb, size);
+}
+
+STATIC_MALLOC_INTERFACE void *_recalloc_dbg(void *const ptr, const size_t nmemb,
+ const size_t size, int,
+ const char *, int) {
+ return __asan_recalloc(ptr, nmemb, size);
+}
+
+// expand
+STATIC_MALLOC_INTERFACE void *_expand(void *, size_t) {
+ // _expand is used in realloc-like functions to resize the buffer if possible.
+ // We don't want memory to stand still while resizing buffers, so return 0.
+ return nullptr;
+}
+
+STATIC_MALLOC_INTERFACE void *_expand_dbg(void *, size_t, int, const char *,
+ int) {
+ return nullptr;
+}
+
+// We need to provide symbols for all the debug CRT functions if we decide to
+// provide any. Most of these functions make no sense under ASan and so we
+// make them no-ops.
+long _CrtSetBreakAlloc(long const) { return ~0; }
+
+void _CrtSetDbgBlockType(void *const, int const) { return; }
+
+typedef int(__cdecl *CRT_ALLOC_HOOK)(int, void *, size_t, int, long,
+ const unsigned char *, int);
+
+CRT_ALLOC_HOOK _CrtGetAllocHook() { return nullptr; }
+
+CRT_ALLOC_HOOK _CrtSetAllocHook(CRT_ALLOC_HOOK const hook) { return hook; }
+
+int _CrtCheckMemory() { return 1; }
+
+int _CrtSetDbgFlag(int const new_bits) { return new_bits; }
+
+typedef void (*CrtDoForAllClientObjectsCallback)(void *, void *);
+
+void _CrtDoForAllClientObjects(CrtDoForAllClientObjectsCallback const,
+ void *const) {
+ return;
+}
+
+int _CrtIsValidPointer(void const *const p, unsigned int const, int const) {
+ return p != nullptr;
+}
+
+int _CrtIsValidHeapPointer(void const *const block) {
+ if (!block) {
+ return 0;
+ }
+
+ return __sanitizer_get_ownership(block);
+}
+
+int _CrtIsMemoryBlock(void const *const, unsigned const, long *const,
+ char **const, int *const) {
+ return 0;
+}
+
+int _CrtReportBlockType(void const *const) { return -1; }
+
+typedef void(__cdecl *CRT_DUMP_CLIENT)(void *, size_t);
+
+CRT_DUMP_CLIENT _CrtGetDumpClient() { return nullptr; }
+
+CRT_DUMP_CLIENT _CrtSetDumpClient(CRT_DUMP_CLIENT new_client) {
+ return new_client;
+}
+
+void _CrtMemCheckpoint(void *const) { return; }
+
+int _CrtMemDifference(void *const, void const *const, void const *const) {
+ return 0;
+}
+
+void _CrtMemDumpAllObjectsSince(void const *const) { return; }
+
+int _CrtDumpMemoryLeaks() { return 0; }
+
+void _CrtMemDumpStatistics(void const *const) { return; }
+
+int _crtDbgFlag{0};
+long _crtBreakAlloc{-1};
+CRT_DUMP_CLIENT _pfnDumpClient{nullptr};
+
+int *__p__crtDbgFlag() { return &_crtDbgFlag; }
+
+long *__p__crtBreakAlloc() { return &_crtBreakAlloc; }
+
+// TODO: These were added upstream but conflict with definitions in ucrtbased.
+// int _CrtDbgReport(int, const char *, int, const char *, const char *, ...) {
+// ShowStatsAndAbort();
+// }
+//
+// int _CrtDbgReportW(int reportType, const wchar_t *, int, const wchar_t *,
+// const wchar_t *, ...) {
+// ShowStatsAndAbort();
+// }
+//
+// int _CrtSetReportMode(int, int) { return 0; }
+
+} // extern "C"
+#endif // SANITIZER_STATIC_RUNTIME_THUNK
@@ -72,7 +72,10 @@
// || `[0x2000000000, 0x23ffffffff]` || LowShadow ||
// || `[0x0000000000, 0x1fffffffff]` || LowMem ||
//
-// Default Linux/RISCV64 Sv39 mapping:
+// Default Linux/RISCV64 Sv39 mapping with SHADOW_OFFSET == 0xd55550000;
+// (the exact location of SHADOW_OFFSET may vary depending the dynamic probing
+// by FindDynamicShadowStart).
+//
// || `[0x1555550000, 0x3fffffffff]` || HighMem ||
// || `[0x0fffffa000, 0x1555555fff]` || HighShadow ||
// || `[0x0effffa000, 0x0fffff9fff]` || ShadowGap ||
@@ -186,11 +189,11 @@
# elif SANITIZER_FREEBSD && defined(__aarch64__)
# define ASAN_SHADOW_OFFSET_CONST 0x0000800000000000
# elif SANITIZER_RISCV64
-# define ASAN_SHADOW_OFFSET_CONST 0x0000000d55550000
+# define ASAN_SHADOW_OFFSET_DYNAMIC
# elif defined(__aarch64__)
# define ASAN_SHADOW_OFFSET_CONST 0x0000001000000000
# elif defined(__powerpc64__)
-# define ASAN_SHADOW_OFFSET_CONST 0x0000020000000000
+# define ASAN_SHADOW_OFFSET_CONST 0x0000100000000000
# elif defined(__s390x__)
# define ASAN_SHADOW_OFFSET_CONST 0x0010000000000000
# elif SANITIZER_FREEBSD
@@ -48,15 +48,6 @@ COMMENT_EXPORT("??_V@YAXPAX@Z") // operator delete[]
using namespace __asan;
-// FreeBSD prior v9.2 have wrong definition of 'size_t'.
-// http://svnweb.freebsd.org/base?view=revision&revision=232261
-#if SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32
-#include <sys/param.h>
-#if __FreeBSD_version <= 902001 // v9.2
-#define size_t unsigned
-#endif // __FreeBSD_version
-#endif // SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32
-
// This code has issues on OSX.
// See https://github.com/google/sanitizers/issues/131.
@@ -16,6 +16,7 @@
#include "asan_report.h"
#include "asan_stack.h"
#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_interface_internal.h"
#include "sanitizer_common/sanitizer_libc.h"
@@ -410,7 +411,7 @@ void __sanitizer_annotate_contiguous_container(const void *beg_p,
const void *new_mid_p) {
if (!flags()->detect_container_overflow)
return;
- VPrintf(2, "contiguous_container: %p %p %p %p\n", beg_p, end_p, old_mid_p,
+ VPrintf(3, "contiguous_container: %p %p %p %p\n", beg_p, end_p, old_mid_p,
new_mid_p);
uptr storage_beg = reinterpret_cast<uptr>(beg_p);
uptr storage_end = reinterpret_cast<uptr>(end_p);
@@ -479,7 +480,7 @@ void __sanitizer_annotate_double_ended_contiguous_container(
if (!flags()->detect_container_overflow)
return;
- VPrintf(2, "contiguous_container: %p %p %p %p %p %p\n", storage_beg_p,
+ VPrintf(3, "contiguous_container: %p %p %p %p %p %p\n", storage_beg_p,
storage_end_p, old_container_beg_p, old_container_end_p,
new_container_beg_p, new_container_end_p);
@@ -576,6 +577,185 @@ void __sanitizer_annotate_double_ended_contiguous_container(
}
}
+// Marks the specified number of bytes in a granule as accessible or
+// poisones the whole granule with kAsanContiguousContainerOOBMagic value.
+static void SetContainerGranule(uptr ptr, u8 n) {
+ constexpr uptr granularity = ASAN_SHADOW_GRANULARITY;
+ u8 s = (n == granularity) ? 0 : (n ? n : kAsanContiguousContainerOOBMagic);
+ *(u8 *)MemToShadow(ptr) = s;
+}
+
+// Performs a byte-by-byte copy of ASan annotations (shadow memory values).
+// Result may be different due to ASan limitations, but result cannot lead
+// to false positives (more memory than requested may get unpoisoned).
+static void SlowCopyContainerAnnotations(uptr src_beg, uptr src_end,
+ uptr dst_beg, uptr dst_end) {
+ constexpr uptr granularity = ASAN_SHADOW_GRANULARITY;
+ uptr dst_end_down = RoundDownTo(dst_end, granularity);
+ uptr src_ptr = src_beg;
+ uptr dst_ptr = dst_beg;
+
+ while (dst_ptr < dst_end) {
+ uptr granule_beg = RoundDownTo(dst_ptr, granularity);
+ uptr granule_end = granule_beg + granularity;
+ uptr unpoisoned_bytes = 0;
+
+ uptr end = Min(granule_end, dst_end);
+ for (; dst_ptr != end; ++dst_ptr, ++src_ptr)
+ if (!AddressIsPoisoned(src_ptr))
+ unpoisoned_bytes = dst_ptr - granule_beg + 1;
+
+ if (dst_ptr == dst_end && dst_end != dst_end_down &&
+ !AddressIsPoisoned(dst_end))
+ continue;
+
+ if (unpoisoned_bytes != 0 || granule_beg >= dst_beg)
+ SetContainerGranule(granule_beg, unpoisoned_bytes);
+ else if (!AddressIsPoisoned(dst_beg))
+ SetContainerGranule(granule_beg, dst_beg - granule_beg);
+ }
+}
+
+// Performs a byte-by-byte copy of ASan annotations (shadow memory values),
+// going through bytes in reversed order, but not reversing annotations.
+// Result may be different due to ASan limitations, but result cannot lead
+// to false positives (more memory than requested may get unpoisoned).
+static void SlowReversedCopyContainerAnnotations(uptr src_beg, uptr src_end,
+ uptr dst_beg, uptr dst_end) {
+ constexpr uptr granularity = ASAN_SHADOW_GRANULARITY;
+ uptr dst_end_down = RoundDownTo(dst_end, granularity);
+ uptr src_ptr = src_end;
+ uptr dst_ptr = dst_end;
+
+ while (dst_ptr > dst_beg) {
+ uptr granule_beg = RoundDownTo(dst_ptr - 1, granularity);
+ uptr unpoisoned_bytes = 0;
+
+ uptr end = Max(granule_beg, dst_beg);
+ for (; dst_ptr != end; --dst_ptr, --src_ptr)
+ if (unpoisoned_bytes == 0 && !AddressIsPoisoned(src_ptr - 1))
+ unpoisoned_bytes = dst_ptr - granule_beg;
+
+ if (dst_ptr >= dst_end_down && !AddressIsPoisoned(dst_end))
+ continue;
+
+ if (granule_beg == dst_ptr || unpoisoned_bytes != 0)
+ SetContainerGranule(granule_beg, unpoisoned_bytes);
+ else if (!AddressIsPoisoned(dst_beg))
+ SetContainerGranule(granule_beg, dst_beg - granule_beg);
+ }
+}
+
+// A helper function for __sanitizer_copy_contiguous_container_annotations,
+// has assumption about begin and end of the container.
+// Should not be used stand alone.
+static void CopyContainerFirstGranuleAnnotation(uptr src_beg, uptr dst_beg) {
+ constexpr uptr granularity = ASAN_SHADOW_GRANULARITY;
+ // First granule
+ uptr src_beg_down = RoundDownTo(src_beg, granularity);
+ uptr dst_beg_down = RoundDownTo(dst_beg, granularity);
+ if (dst_beg_down == dst_beg)
+ return;
+ if (!AddressIsPoisoned(src_beg))
+ *(u8 *)MemToShadow(dst_beg_down) = *(u8 *)MemToShadow(src_beg_down);
+ else if (!AddressIsPoisoned(dst_beg))
+ SetContainerGranule(dst_beg_down, dst_beg - dst_beg_down);
+}
+
+// A helper function for __sanitizer_copy_contiguous_container_annotations,
+// has assumption about begin and end of the container.
+// Should not be used stand alone.
+static void CopyContainerLastGranuleAnnotation(uptr src_end, uptr dst_end) {
+ constexpr uptr granularity = ASAN_SHADOW_GRANULARITY;
+ // Last granule
+ uptr src_end_down = RoundDownTo(src_end, granularity);
+ uptr dst_end_down = RoundDownTo(dst_end, granularity);
+ if (dst_end_down == dst_end || !AddressIsPoisoned(dst_end))
+ return;
+ if (AddressIsPoisoned(src_end))
+ *(u8 *)MemToShadow(dst_end_down) = *(u8 *)MemToShadow(src_end_down);
+ else
+ SetContainerGranule(dst_end_down, src_end - src_end_down);
+}
+
+// This function copies ASan memory annotations (poisoned/unpoisoned states)
+// from one buffer to another.
+// It's main purpose is to help with relocating trivially relocatable objects,
+// which memory may be poisoned, without calling copy constructor.
+// However, it does not move memory content itself, only annotations.
+// If the buffers aren't aligned (the distance between buffers isn't
+// granule-aligned)
+// // src_beg % granularity != dst_beg % granularity
+// the function handles this by going byte by byte, slowing down performance.
+// The old buffer annotations are not removed. If necessary,
+// user can unpoison old buffer with __asan_unpoison_memory_region.
+void __sanitizer_copy_contiguous_container_annotations(const void *src_beg_p,
+ const void *src_end_p,
+ const void *dst_beg_p,
+ const void *dst_end_p) {
+ if (!flags()->detect_container_overflow)
+ return;
+
+ VPrintf(3, "contiguous_container_src: %p %p\n", src_beg_p, src_end_p);
+ VPrintf(3, "contiguous_container_dst: %p %p\n", dst_beg_p, dst_end_p);
+
+ uptr src_beg = reinterpret_cast<uptr>(src_beg_p);
+ uptr src_end = reinterpret_cast<uptr>(src_end_p);
+ uptr dst_beg = reinterpret_cast<uptr>(dst_beg_p);
+ uptr dst_end = reinterpret_cast<uptr>(dst_end_p);
+
+ constexpr uptr granularity = ASAN_SHADOW_GRANULARITY;
+
+ if (src_beg > src_end || (dst_end - dst_beg) != (src_end - src_beg)) {
+ GET_STACK_TRACE_FATAL_HERE;
+ ReportBadParamsToCopyContiguousContainerAnnotations(
+ src_beg, src_end, dst_beg, dst_end, &stack);
+ }
+
+ if (src_beg == src_end || src_beg == dst_beg)
+ return;
+ // Due to support for overlapping buffers, we may have to copy elements
+ // in reversed order, when destination buffer starts in the middle of
+ // the source buffer (or shares first granule with it).
+ //
+ // When buffers are not granule-aligned (or distance between them,
+ // to be specific), annotatios have to be copied byte by byte.
+ //
+ // The only remaining edge cases involve edge granules,
+ // when the container starts or ends within a granule.
+ uptr src_beg_up = RoundUpTo(src_beg, granularity);
+ uptr src_end_up = RoundUpTo(src_end, granularity);
+ bool copy_in_reversed_order = src_beg < dst_beg && dst_beg <= src_end_up;
+ if (src_beg % granularity != dst_beg % granularity ||
+ RoundDownTo(dst_end - 1, granularity) <= dst_beg) {
+ if (copy_in_reversed_order)
+ SlowReversedCopyContainerAnnotations(src_beg, src_end, dst_beg, dst_end);
+ else
+ SlowCopyContainerAnnotations(src_beg, src_end, dst_beg, dst_end);
+ return;
+ }
+
+ // As buffers are granule-aligned, we can just copy annotations of granules
+ // from the middle.
+ uptr dst_beg_up = RoundUpTo(dst_beg, granularity);
+ uptr dst_end_down = RoundDownTo(dst_end, granularity);
+ if (copy_in_reversed_order)
+ CopyContainerLastGranuleAnnotation(src_end, dst_end);
+ else
+ CopyContainerFirstGranuleAnnotation(src_beg, dst_beg);
+
+ if (dst_beg_up < dst_end_down) {
+ internal_memmove((u8 *)MemToShadow(dst_beg_up),
+ (u8 *)MemToShadow(src_beg_up),
+ (dst_end_down - dst_beg_up) / granularity);
+ }
+
+ if (copy_in_reversed_order)
+ CopyContainerFirstGranuleAnnotation(src_beg, dst_beg);
+ else
+ CopyContainerLastGranuleAnnotation(src_end, dst_end);
+}
+
static const void *FindBadAddress(uptr begin, uptr end, bool poisoned) {
CHECK_LE(begin, end);
constexpr uptr kMaxRangeToCheck = 32;
@@ -59,10 +59,10 @@ bool PlatformUnpoisonStacks() {
// Since we're on the signal alternate stack, we cannot find the DEFAULT
// stack bottom using a local variable.
- uptr default_bottom, tls_addr, tls_size, stack_size;
- GetThreadStackAndTls(/*main=*/false, &default_bottom, &stack_size, &tls_addr,
- &tls_size);
- UnpoisonStack(default_bottom, default_bottom + stack_size, "default");
+ uptr stack_begin, stack_end, tls_begin, tls_end;
+ GetThreadStackAndTls(/*main=*/false, &stack_begin, &stack_end, &tls_begin,
+ &tls_end);
+ UnpoisonStack(stack_begin, stack_end, "default");
return true;
}
@@ -146,7 +146,44 @@ void PlatformTSDDtor(void *tsd) {
# endif
AsanThread::TSDDtor(tsd);
}
-#endif
+# endif
+
+static void BeforeFork() {
+ VReport(2, "BeforeFork tid: %llu\n", GetTid());
+ if (CAN_SANITIZE_LEAKS) {
+ __lsan::LockGlobal();
+ }
+ // `_lsan` functions defined regardless of `CAN_SANITIZE_LEAKS` and lock the
+ // stuff we need.
+ __lsan::LockThreads();
+ __lsan::LockAllocator();
+ StackDepotLockBeforeFork();
+}
+
+static void AfterFork(bool fork_child) {
+ StackDepotUnlockAfterFork(fork_child);
+ // `_lsan` functions defined regardless of `CAN_SANITIZE_LEAKS` and unlock
+ // the stuff we need.
+ __lsan::UnlockAllocator();
+ __lsan::UnlockThreads();
+ if (CAN_SANITIZE_LEAKS) {
+ __lsan::UnlockGlobal();
+ }
+ VReport(2, "AfterFork tid: %llu\n", GetTid());
+}
+
+void InstallAtForkHandler() {
+# if SANITIZER_SOLARIS || SANITIZER_NETBSD || SANITIZER_APPLE || \
+ (SANITIZER_LINUX && SANITIZER_SPARC)
+ // While other Linux targets use clone in internal_fork which doesn't
+ // trigger pthread_atfork handlers, Linux/sparc64 uses __fork, causing a
+ // hang.
+ return; // FIXME: Implement FutexWait.
+# endif
+ pthread_atfork(
+ &BeforeFork, []() { AfterFork(/* fork_child= */ false); },
+ []() { AfterFork(/* fork_child= */ true); });
+}
void InstallAtExitCheckLeaks() {
if (CAN_SANITIZE_LEAKS) {
@@ -15,10 +15,8 @@
using namespace __asan;
#if SANITIZER_CAN_USE_PREINIT_ARRAY
- // The symbol is called __local_asan_preinit, because it's not intended to be
- // exported.
- // This code linked into the main executable when -fsanitize=address is in
- // the link flags. It can only use exported interface functions.
- __attribute__((section(".preinit_array"), used))
- void (*__local_asan_preinit)(void) = __asan_init;
+// This section is linked into the main executable when -fsanitize=address is
+// specified to perform initialization at a very early stage.
+__attribute__((section(".preinit_array"), used)) static auto preinit =
+ __asan_init;
#endif
@@ -33,7 +33,8 @@ uptr PremapShadowSize() {
// PremapShadowSize() bytes on the right of it are mapped r/o.
uptr PremapShadow() {
return MapDynamicShadow(PremapShadowSize(), /*mmap_alignment_scale*/ 3,
- /*min_shadow_base_alignment*/ 0, kHighMemEnd);
+ /*min_shadow_base_alignment*/ 0, kHighMemEnd,
+ GetMmapGranularity());
}
bool PremapShadowFailed() {
@@ -24,6 +24,7 @@
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_interface_internal.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_report_decorator.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
@@ -32,8 +33,11 @@ namespace __asan {
// -------------------- User-specified callbacks ----------------- {{{1
static void (*error_report_callback)(const char*);
-static char *error_message_buffer = nullptr;
-static uptr error_message_buffer_pos = 0;
+using ErrorMessageBuffer = InternalMmapVectorNoCtor<char, true>;
+alignas(
+ alignof(ErrorMessageBuffer)) static char error_message_buffer_placeholder
+ [sizeof(ErrorMessageBuffer)];
+static ErrorMessageBuffer *error_message_buffer = nullptr;
static Mutex error_message_buf_mutex;
static const unsigned kAsanBuggyPcPoolSize = 25;
static __sanitizer::atomic_uintptr_t AsanBuggyPcPool[kAsanBuggyPcPoolSize];
@@ -42,17 +46,14 @@ void AppendToErrorMessageBuffer(const char *buffer) {
Lock l(&error_message_buf_mutex);
if (!error_message_buffer) {
error_message_buffer =
- (char*)MmapOrDieQuietly(kErrorMessageBufferSize, __func__);
- error_message_buffer_pos = 0;
+ new (error_message_buffer_placeholder) ErrorMessageBuffer();
+ error_message_buffer->Initialize(kErrorMessageBufferSize);
}
- uptr length = internal_strlen(buffer);
- RAW_CHECK(kErrorMessageBufferSize >= error_message_buffer_pos);
- uptr remaining = kErrorMessageBufferSize - error_message_buffer_pos;
- internal_strncpy(error_message_buffer + error_message_buffer_pos,
- buffer, remaining);
- error_message_buffer[kErrorMessageBufferSize - 1] = '\0';
- // FIXME: reallocate the buffer instead of truncating the message.
- error_message_buffer_pos += Min(remaining, length);
+ uptr error_message_buffer_len = error_message_buffer->size();
+ uptr buffer_len = internal_strlen(buffer);
+ error_message_buffer->resize(error_message_buffer_len + buffer_len);
+ internal_memcpy(error_message_buffer->data() + error_message_buffer_len,
+ buffer, buffer_len);
}
// ---------------------- Helper functions ----------------------- {{{1
@@ -158,14 +159,14 @@ class ScopedInErrorReport {
// Copy the message buffer so that we could start logging without holding a
// lock that gets acquired during printing.
- InternalMmapVector<char> buffer_copy(kErrorMessageBufferSize);
+ InternalScopedString buffer_copy;
{
Lock l(&error_message_buf_mutex);
- internal_memcpy(buffer_copy.data(),
- error_message_buffer, kErrorMessageBufferSize);
+ error_message_buffer->push_back('\0');
+ buffer_copy.Append(error_message_buffer->data());
// Clear error_message_buffer so that if we find other errors
// we don't re-log this error.
- error_message_buffer_pos = 0;
+ error_message_buffer->clear();
}
LogFullErrorReport(buffer_copy.data());
@@ -366,6 +367,16 @@ void ReportBadParamsToAnnotateDoubleEndedContiguousContainer(
in_report.ReportError(error);
}
+void ReportBadParamsToCopyContiguousContainerAnnotations(
+ uptr old_storage_beg, uptr old_storage_end, uptr new_storage_beg,
+ uptr new_storage_end, BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report;
+ ErrorBadParamsToCopyContiguousContainerAnnotations error(
+ GetCurrentTidOrInvalid(), stack, old_storage_beg, old_storage_end,
+ new_storage_beg, new_storage_end);
+ in_report.ReportError(error);
+}
+
void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
const __asan_global *g2, u32 stack_id2) {
ScopedInErrorReport in_report;
@@ -88,6 +88,9 @@ void ReportBadParamsToAnnotateDoubleEndedContiguousContainer(
uptr storage_beg, uptr storage_end, uptr old_container_beg,
uptr old_container_end, uptr new_container_beg, uptr new_container_end,
BufferedStackTrace *stack);
+void ReportBadParamsToCopyContiguousContainerAnnotations(
+ uptr old_storage_beg, uptr old_storage_end, uptr new_storage_beg,
+ uptr new_storage_end, BufferedStackTrace *stack);
void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
const __asan_global *g2, u32 stack_id2);
@@ -71,16 +71,16 @@ static void CheckUnwind() {
}
// -------------------------- Globals --------------------- {{{1
-static int asan_inited = 0;
-static int asan_init_is_running = 0;
+static StaticSpinMutex asan_inited_mutex;
+static atomic_uint8_t asan_inited = {0};
-void SetAsanInited(u32 val) { asan_inited = val; }
-
-void SetAsanInitIsRunning(u32 val) { asan_init_is_running = val; }
-
-bool AsanInited() { return asan_inited == 1; }
+static void SetAsanInited() {
+ atomic_store(&asan_inited, 1, memory_order_release);
+}
-bool AsanInitIsRunning() { return asan_init_is_running == 1; }
+bool AsanInited() {
+ return atomic_load(&asan_inited, memory_order_acquire) == 1;
+}
bool replace_intrin_cached;
@@ -382,7 +382,7 @@ void PrintAddressSpaceLayout() {
Printf("SHADOW_SCALE: %d\n", (int)ASAN_SHADOW_SCALE);
Printf("SHADOW_GRANULARITY: %d\n", (int)ASAN_SHADOW_GRANULARITY);
- Printf("SHADOW_OFFSET: 0x%zx\n", (uptr)ASAN_SHADOW_OFFSET);
+ Printf("SHADOW_OFFSET: %p\n", (void *)ASAN_SHADOW_OFFSET);
CHECK(ASAN_SHADOW_SCALE >= 3 && ASAN_SHADOW_SCALE <= 7);
if (kMidMemBeg)
CHECK(kMidShadowBeg > kLowShadowEnd &&
@@ -390,12 +390,10 @@ void PrintAddressSpaceLayout() {
kHighShadowBeg > kMidMemEnd);
}
-static void AsanInitInternal() {
+static bool AsanInitInternal() {
if (LIKELY(AsanInited()))
- return;
+ return true;
SanitizerToolName = "AddressSanitizer";
- CHECK(!AsanInitIsRunning() && "ASan init calls itself!");
- SetAsanInitIsRunning(1);
CacheBinaryName();
@@ -408,11 +406,12 @@ static void AsanInitInternal() {
// Stop performing init at this point if we are being loaded via
// dlopen() and the platform supports it.
if (SANITIZER_SUPPORTS_INIT_FOR_DLOPEN && UNLIKELY(HandleDlopenInit())) {
- SetAsanInitIsRunning(0);
VReport(1, "AddressSanitizer init is being performed for dlopen().\n");
- return;
+ return false;
}
+ // Make sure we are not statically linked.
+ __interception::DoesNotSupportStaticLinking();
AsanCheckIncompatibleRT();
AsanCheckDynamicRTPrereqs();
AvoidCVE_2016_2143();
@@ -424,9 +423,6 @@ static void AsanInitInternal() {
InitializeHighMemEnd();
- // Make sure we are not statically linked.
- AsanDoesNotSupportStaticLinkage();
-
// Install tool-specific callbacks in sanitizer_common.
AddDieCallback(AsanDie);
SetCheckUnwindCallback(CheckUnwind);
@@ -470,8 +466,7 @@ static void AsanInitInternal() {
// On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
// should be set to 1 prior to initializing the threads.
replace_intrin_cached = flags()->replace_intrin;
- SetAsanInited(1);
- SetAsanInitIsRunning(0);
+ SetAsanInited();
if (flags()->atexit)
Atexit(asan_atexit);
@@ -483,9 +478,6 @@ static void AsanInitInternal() {
if (flags()->start_deactivated)
AsanDeactivate();
- // interceptors
- InitTlsSize();
-
// Create main thread.
AsanThread *main_thread = CreateMainThread();
CHECK_EQ(0, main_thread->tid());
@@ -497,6 +489,8 @@ static void AsanInitInternal() {
InstallAtExitCheckLeaks();
}
+ InstallAtForkHandler();
+
#if CAN_SANITIZE_UB
__ubsan::InitAsPlugin();
#endif
@@ -515,14 +509,29 @@ static void AsanInitInternal() {
VReport(1, "AddressSanitizer Init done\n");
WaitForDebugger(flags()->sleep_after_init, "after init");
+
+ return true;
}
// Initialize as requested from some part of ASan runtime library (interceptors,
// allocator, etc).
void AsanInitFromRtl() {
+ if (LIKELY(AsanInited()))
+ return;
+ SpinMutexLock lock(&asan_inited_mutex);
AsanInitInternal();
}
+bool TryAsanInitFromRtl() {
+ if (LIKELY(AsanInited()))
+ return true;
+ if (!asan_inited_mutex.TryLock())
+ return false;
+ bool result = AsanInitInternal();
+ asan_inited_mutex.Unlock();
+ return result;
+}
+
#if ASAN_DYNAMIC
// Initialize runtime in case it's LD_PRELOAD-ed into unsanitized executable
// (and thus normal initializers from .preinit_array or modules haven't run).
@@ -568,10 +577,8 @@ static void UnpoisonDefaultStack() {
} else {
CHECK(!SANITIZER_FUCHSIA);
// If we haven't seen this thread, try asking the OS for stack bounds.
- uptr tls_addr, tls_size, stack_size;
- GetThreadStackAndTls(/*main=*/false, &bottom, &stack_size, &tls_addr,
- &tls_size);
- top = bottom + stack_size;
+ uptr tls_begin, tls_end;
+ GetThreadStackAndTls(/*main=*/false, &bottom, &top, &tls_begin, &tls_end);
}
UnpoisonStack(bottom, top, "default");
@@ -593,7 +600,7 @@ static void UnpoisonFakeStack() {
using namespace __asan;
void NOINLINE __asan_handle_no_return() {
- if (AsanInitIsRunning())
+ if (UNLIKELY(!AsanInited()))
return;
if (!PlatformUnpoisonStacks())
@@ -623,7 +630,7 @@ void NOINLINE __asan_set_death_callback(void (*callback)(void)) {
// We use this call as a trigger to wake up ASan from deactivated state.
void __asan_init() {
AsanActivate();
- AsanInitInternal();
+ AsanInitFromRtl();
}
void __asan_version_mismatch_check() {
@@ -27,7 +27,12 @@ FNAME(reg, op, s, i): ;\
#define ASAN_MEMORY_ACCESS_INITIAL_CHECK_ADD(reg, op, s) \
mov %##reg,%r10 ;\
shr $0x3,%r10 ;\
+ .if ASAN_SHADOW_OFFSET_CONST < 0x80000000 ;\
movsbl ASAN_SHADOW_OFFSET_CONST(%r10),%r10d ;\
+ .else ;\
+ movabsq $ASAN_SHADOW_OFFSET_CONST,%r11 ;\
+ movsbl (%r10,%r11),%r10d ;\
+ .endif ;\
test %r10d,%r10d ;\
jne CLABEL(reg, op, s, add) ;\
RLABEL(reg, op, s, add): ;\
@@ -84,7 +89,12 @@ ENDF
#define ASAN_MEMORY_ACCESS_CHECK_ADD(reg, op, s, c) \
mov %##reg,%r10 ;\
shr $0x3,%r10 ;\
+ .if ASAN_SHADOW_OFFSET_CONST < 0x80000000 ;\
##c $0x0,ASAN_SHADOW_OFFSET_CONST(%r10) ;\
+ .else ;\
+ movabsq $ASAN_SHADOW_OFFSET_CONST,%r11 ;\
+ ##c $0x0,(%r10,%r11) ;\
+ .endif ;\
jne FLABEL(reg, op, s, add) ;\
retq ;\
@@ -20,7 +20,7 @@
namespace __asan {
-ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
+alignas(64) static char suppression_placeholder[sizeof(SuppressionContext)];
static SuppressionContext *suppression_ctx = nullptr;
static const char kInterceptorName[] = "interceptor_name";
static const char kInterceptorViaFunction[] = "interceptor_via_fun";
@@ -39,8 +39,7 @@ void InitializeSuppressions() {
suppression_ctx = new (suppression_placeholder)
SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
suppression_ctx->ParseFromFile(flags()->suppressions);
- if (&__asan_default_suppressions)
- suppression_ctx->Parse(__asan_default_suppressions());
+ suppression_ctx->Parse(__asan_default_suppressions());
}
bool IsInterceptorSuppressed(const char *interceptor_name) {
@@ -81,9 +80,10 @@ bool IsStackTraceSuppressed(const StackTrace *stack) {
}
if (suppression_ctx->HasSuppressionType(kInterceptorViaFunction)) {
- SymbolizedStack *frames = symbolizer->SymbolizePC(addr);
+ SymbolizedStackHolder symbolized_stack(symbolizer->SymbolizePC(addr));
+ const SymbolizedStack *frames = symbolized_stack.get();
CHECK(frames);
- for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
+ for (const SymbolizedStack *cur = frames; cur; cur = cur->next) {
const char *function_name = cur->info.function;
if (!function_name) {
continue;
@@ -91,11 +91,9 @@ bool IsStackTraceSuppressed(const StackTrace *stack) {
// Match "interceptor_via_fun" suppressions.
if (suppression_ctx->Match(function_name, kInterceptorViaFunction,
&s)) {
- frames->ClearAll();
return true;
}
}
- frames->ClearAll();
}
}
return false;
@@ -21,6 +21,7 @@
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_thread_history.h"
#include "sanitizer_common/sanitizer_tls_get_addr.h"
namespace __asan {
@@ -28,10 +29,7 @@ namespace __asan {
// AsanThreadContext implementation.
void AsanThreadContext::OnCreated(void *arg) {
- CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs *>(arg);
- if (args->stack)
- stack_id = StackDepotPut(*args->stack);
- thread = args->thread;
+ thread = static_cast<AsanThread *>(arg);
thread->set_context(this);
}
@@ -44,10 +42,15 @@ static ThreadRegistry *asan_thread_registry;
static ThreadArgRetval *thread_data;
static Mutex mu_for_thread_context;
+// TODO(leonardchan@): It should be possible to make LowLevelAllocator
+// threadsafe and consolidate this one into the GlobalLoweLevelAllocator.
+// We should be able to do something similar to what's in
+// sanitizer_stack_store.cpp.
+static LowLevelAllocator allocator_for_thread_context;
static ThreadContextBase *GetAsanThreadContext(u32 tid) {
Lock lock(&mu_for_thread_context);
- return new (GetGlobalLowLevelAllocator()) AsanThreadContext(tid);
+ return new (allocator_for_thread_context) AsanThreadContext(tid);
}
static void InitThreads() {
@@ -62,10 +65,10 @@ static void InitThreads() {
// thread before all TSD destructors will be called for it.
// MIPS requires aligned address
- static ALIGNED(alignof(
- ThreadRegistry)) char thread_registry_placeholder[sizeof(ThreadRegistry)];
- static ALIGNED(alignof(
- ThreadArgRetval)) char thread_data_placeholder[sizeof(ThreadArgRetval)];
+ alignas(alignof(ThreadRegistry)) static char
+ thread_registry_placeholder[sizeof(ThreadRegistry)];
+ alignas(alignof(ThreadArgRetval)) static char
+ thread_data_placeholder[sizeof(ThreadArgRetval)];
asan_thread_registry =
new (thread_registry_placeholder) ThreadRegistry(GetAsanThreadContext);
@@ -101,8 +104,8 @@ AsanThread *AsanThread::Create(const void *start_data, uptr data_size,
CHECK_LE(data_size, availible_size);
internal_memcpy(thread->start_data_, start_data, data_size);
}
- AsanThreadContext::CreateThreadContextArgs args = {thread, stack};
- asanThreadRegistry().CreateThread(0, detached, parent_tid, &args);
+ asanThreadRegistry().CreateThread(0, detached, parent_tid,
+ stack ? StackDepotPut(*stack) : 0, thread);
return thread;
}
@@ -301,13 +304,10 @@ AsanThread *CreateMainThread() {
// OS-specific implementations that need more information passed through.
void AsanThread::SetThreadStackAndTls(const InitOptions *options) {
DCHECK_EQ(options, nullptr);
- uptr tls_size = 0;
- uptr stack_size = 0;
- GetThreadStackAndTls(tid() == kMainTid, &stack_bottom_, &stack_size,
- &tls_begin_, &tls_size);
- stack_top_ = RoundDownTo(stack_bottom_ + stack_size, ASAN_SHADOW_GRANULARITY);
+ GetThreadStackAndTls(tid() == kMainTid, &stack_bottom_, &stack_top_,
+ &tls_begin_, &tls_end_);
+ stack_top_ = RoundDownTo(stack_top_, ASAN_SHADOW_GRANULARITY);
stack_bottom_ = RoundDownTo(stack_bottom_, ASAN_SHADOW_GRANULARITY);
- tls_end_ = tls_begin_ + tls_size;
dtls_ = DTLS_Get();
if (stack_top_ != stack_bottom_) {
@@ -556,6 +556,12 @@ void GetRunningThreadsLocked(InternalMmapVector<tid_t> *threads) {
threads);
}
+void PrintThreads() {
+ InternalScopedString out;
+ PrintThreadHistory(__asan::asanThreadRegistry(), out);
+ Report("%s\n", out.data());
+}
+
} // namespace __lsan
// ---------------------- Interface ---------------- {{{1
@@ -36,21 +36,16 @@ class AsanThread;
class AsanThreadContext final : public ThreadContextBase {
public:
explicit AsanThreadContext(int tid)
- : ThreadContextBase(tid), announced(false),
- destructor_iterations(GetPthreadDestructorIterations()), stack_id(0),
+ : ThreadContextBase(tid),
+ announced(false),
+ destructor_iterations(GetPthreadDestructorIterations()),
thread(nullptr) {}
bool announced;
u8 destructor_iterations;
- u32 stack_id;
AsanThread *thread;
void OnCreated(void *arg) override;
void OnFinished() override;
-
- struct CreateThreadContextArgs {
- AsanThread *thread;
- StackTrace *stack;
- };
};
// AsanThreadContext objects are never freed, so we need many of them.
@@ -203,6 +203,8 @@ void InitializePlatformInterceptors() {
void InstallAtExitCheckLeaks() {}
+void InstallAtForkHandler() {}
+
void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
UNIMPLEMENTED();
}
@@ -264,16 +266,10 @@ void PlatformTSDDtor(void *tsd) { AsanThread::TSDDtor(tsd); }
// }}}
// ---------------------- Various stuff ---------------- {{{
-void *AsanDoesNotSupportStaticLinkage() {
-#if defined(_DEBUG)
-#error Please build the runtime with a non-debug CRT: /MD or /MT
-#endif
- return 0;
-}
-
uptr FindDynamicShadowStart() {
return MapDynamicShadow(MemToShadowSize(kHighMemEnd), ASAN_SHADOW_SCALE,
- /*min_shadow_base_alignment*/ 0, kHighMemEnd);
+ /*min_shadow_base_alignment*/ 0, kHighMemEnd,
+ GetMmapGranularity());
}
void AsanCheckDynamicRTPrereqs() {}
new file mode 100644
@@ -0,0 +1,112 @@
+//===-- asan_win_common_runtime_thunk.cpp --------------------------- -----===//
+//
+// 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 AddressSanitizer, an address sanity checker.
+//
+// This file defines things that need to be present in the application modules
+// to interact with the ASan DLL runtime correctly and can't be implemented
+// using the default "import library" generated when linking the DLL.
+//
+// This includes:
+// - Cloning shadow memory dynamic address from ASAN DLL
+// - Creating weak aliases to default implementation imported from asan dll
+// - Forwarding the detect_stack_use_after_return runtime option
+// - installing a custom SEH handler
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) || \
+ defined(SANITIZER_STATIC_RUNTIME_THUNK)
+# define SANITIZER_IMPORT_INTERFACE 1
+# define WIN32_LEAN_AND_MEAN
+# include "asan_win_common_runtime_thunk.h"
+
+# include <windows.h>
+
+# include "sanitizer_common/sanitizer_win_defs.h"
+# include "sanitizer_common/sanitizer_win_thunk_interception.h"
+
+// Define weak alias for all weak functions imported from asan dll.
+# define INTERFACE_FUNCTION(Name)
+# define INTERFACE_WEAK_FUNCTION(Name) REGISTER_WEAK_FUNCTION(Name)
+# include "asan_interface.inc"
+
+////////////////////////////////////////////////////////////////////////////////
+// Define a copy of __asan_option_detect_stack_use_after_return that should be
+// used when linking an MD runtime with a set of object files on Windows.
+//
+// The ASan MD runtime dllexports '__asan_option_detect_stack_use_after_return',
+// so normally we would just dllimport it. Unfortunately, the dllimport
+// attribute adds __imp_ prefix to the symbol name of a variable.
+// Since in general we don't know if a given TU is going to be used
+// with a MT or MD runtime and we don't want to use ugly __imp_ names on Windows
+// just to work around this issue, let's clone the variable that is constant
+// after initialization anyways.
+
+extern "C" {
+__declspec(dllimport) int __asan_should_detect_stack_use_after_return();
+int __asan_option_detect_stack_use_after_return;
+
+__declspec(dllimport) void *__asan_get_shadow_memory_dynamic_address();
+void *__asan_shadow_memory_dynamic_address;
+
+static void __asan_initialize_cloned_variables() {
+ __asan_option_detect_stack_use_after_return =
+ __asan_should_detect_stack_use_after_return();
+ __asan_shadow_memory_dynamic_address =
+ __asan_get_shadow_memory_dynamic_address();
+}
+}
+
+static int asan_thunk_init() {
+ __asan_initialize_cloned_variables();
+
+# ifdef SANITIZER_STATIC_RUNTIME_THUNK
+ __asan_initialize_static_thunk();
+# endif
+
+ return 0;
+}
+
+static void WINAPI asan_thread_init(void *mod, unsigned long reason,
+ void *reserved) {
+ if (reason == DLL_PROCESS_ATTACH) {
+ asan_thunk_init();
+ }
+}
+
+// Our cloned variables must be initialized before C/C++ constructors. If TLS
+// is used, our .CRT$XLAB initializer will run first. If not, our .CRT$XIB
+// initializer is needed as a backup.
+extern "C" __declspec(allocate(".CRT$XIB")) int (*__asan_thunk_init)() =
+ asan_thunk_init;
+WIN_FORCE_LINK(__asan_thunk_init)
+
+extern "C" __declspec(allocate(".CRT$XLAB")) void(WINAPI *__asan_tls_init)(
+ void *, unsigned long, void *) = asan_thread_init;
+WIN_FORCE_LINK(__asan_tls_init)
+
+////////////////////////////////////////////////////////////////////////////////
+// ASan SEH handling.
+// We need to set the ASan-specific SEH handler at the end of CRT initialization
+// of each module (see also asan_win.cpp).
+extern "C" {
+__declspec(dllimport) int __asan_set_seh_filter();
+static int SetSEHFilter() { return __asan_set_seh_filter(); }
+
+// Unfortunately, putting a pointer to __asan_set_seh_filter into
+// __asan_intercept_seh gets optimized out, so we have to use an extra function.
+extern "C" __declspec(allocate(".CRT$XCAB")) int (*__asan_seh_interceptor)() =
+ SetSEHFilter;
+WIN_FORCE_LINK(__asan_seh_interceptor)
+}
+
+WIN_FORCE_LINK(__asan_dso_reg_hook)
+
+#endif // defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) ||
+ // defined(SANITIZER_STATIC_RUNTIME_THUNK)
new file mode 100644
@@ -0,0 +1,38 @@
+//===-- asan_win_common_runtime_thunk.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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// This file defines things that need to be present in the application modules
+// to interact with the ASan DLL runtime correctly and can't be implemented
+// using the default "import library" generated when linking the DLL.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(SANITIZER_STATIC_RUNTIME_THUNK) || \
+ defined(SANITIZER_DYNAMIC_RUNTIME_THUNK)
+# include "sanitizer_common/sanitizer_win_defs.h"
+
+# pragma section(".CRT$XIB", long, \
+ read) // C initializer (during C init before dyninit)
+# pragma section(".CRT$XID", long, \
+ read) // First C initializer after CRT initializers
+# pragma section(".CRT$XCAB", long, \
+ read) // First C++ initializer after startup initializers
+
+# pragma section(".CRT$XTW", long, read) // First ASAN globals terminator
+# pragma section(".CRT$XTY", long, read) // Last ASAN globals terminator
+
+# pragma section(".CRT$XLAB", long, read) // First TLS initializer
+
+# ifdef SANITIZER_STATIC_RUNTIME_THUNK
+extern "C" void __asan_initialize_static_thunk();
+# endif
+
+#endif // defined(SANITIZER_STATIC_RUNTIME_THUNK) ||
+ // defined(SANITIZER_DYNAMIC_RUNTIME_THUNK)
\ No newline at end of file
deleted file mode 100644
@@ -1,165 +0,0 @@
-//===-- asan_win_dll_thunk.cpp --------------------------------------------===//
-//
-// 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 AddressSanitizer, an address sanity checker.
-//
-// This file defines a family of thunks that should be statically linked into
-// the DLLs that have ASan instrumentation in order to delegate the calls to the
-// shared runtime that lives in the main binary.
-// See https://github.com/google/sanitizers/issues/209 for the details.
-//===----------------------------------------------------------------------===//
-
-#ifdef SANITIZER_DLL_THUNK
-#include "asan_init_version.h"
-#include "interception/interception.h"
-#include "sanitizer_common/sanitizer_win_defs.h"
-#include "sanitizer_common/sanitizer_win_dll_thunk.h"
-#include "sanitizer_common/sanitizer_platform_interceptors.h"
-
-// ASan own interface functions.
-#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
-#include "asan_interface.inc"
-
-// Memory allocation functions.
-INTERCEPT_WRAP_V_W(free)
-INTERCEPT_WRAP_V_W(_free_base)
-INTERCEPT_WRAP_V_WW(_free_dbg)
-
-INTERCEPT_WRAP_W_W(malloc)
-INTERCEPT_WRAP_W_W(_malloc_base)
-INTERCEPT_WRAP_W_WWWW(_malloc_dbg)
-
-INTERCEPT_WRAP_W_WW(calloc)
-INTERCEPT_WRAP_W_WW(_calloc_base)
-INTERCEPT_WRAP_W_WWWWW(_calloc_dbg)
-INTERCEPT_WRAP_W_WWW(_calloc_impl)
-
-INTERCEPT_WRAP_W_WW(realloc)
-INTERCEPT_WRAP_W_WW(_realloc_base)
-INTERCEPT_WRAP_W_WWW(_realloc_dbg)
-INTERCEPT_WRAP_W_WWW(_recalloc)
-INTERCEPT_WRAP_W_WWW(_recalloc_base)
-
-INTERCEPT_WRAP_W_W(_msize)
-INTERCEPT_WRAP_W_W(_msize_base)
-INTERCEPT_WRAP_W_W(_expand)
-INTERCEPT_WRAP_W_W(_expand_dbg)
-
-// TODO(timurrrr): Might want to add support for _aligned_* allocation
-// functions to detect a bit more bugs. Those functions seem to wrap malloc().
-
-// TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cpp)
-
-# if defined(_MSC_VER) && !defined(__clang__)
-// Disable warnings such as: 'void memchr(void)': incorrect number of arguments
-// for intrinsic function, expected '3' arguments.
-# pragma warning(push)
-# pragma warning(disable : 4392)
-# endif
-
-INTERCEPT_LIBRARY_FUNCTION(atoi);
-INTERCEPT_LIBRARY_FUNCTION(atol);
-INTERCEPT_LIBRARY_FUNCTION(atoll);
-INTERCEPT_LIBRARY_FUNCTION(frexp);
-INTERCEPT_LIBRARY_FUNCTION(longjmp);
-#if SANITIZER_INTERCEPT_MEMCHR
-INTERCEPT_LIBRARY_FUNCTION(memchr);
-#endif
-INTERCEPT_LIBRARY_FUNCTION(memcmp);
-INTERCEPT_LIBRARY_FUNCTION(memcpy);
-INTERCEPT_LIBRARY_FUNCTION(memmove);
-INTERCEPT_LIBRARY_FUNCTION(memset);
-INTERCEPT_LIBRARY_FUNCTION(strcat);
-INTERCEPT_LIBRARY_FUNCTION(strchr);
-INTERCEPT_LIBRARY_FUNCTION(strcmp);
-INTERCEPT_LIBRARY_FUNCTION(strcpy);
-INTERCEPT_LIBRARY_FUNCTION(strcspn);
-INTERCEPT_LIBRARY_FUNCTION(strdup);
-INTERCEPT_LIBRARY_FUNCTION(strlen);
-INTERCEPT_LIBRARY_FUNCTION(strncat);
-INTERCEPT_LIBRARY_FUNCTION(strncmp);
-INTERCEPT_LIBRARY_FUNCTION(strncpy);
-INTERCEPT_LIBRARY_FUNCTION(strnlen);
-INTERCEPT_LIBRARY_FUNCTION(strpbrk);
-INTERCEPT_LIBRARY_FUNCTION(strrchr);
-INTERCEPT_LIBRARY_FUNCTION(strspn);
-INTERCEPT_LIBRARY_FUNCTION(strstr);
-INTERCEPT_LIBRARY_FUNCTION(strtok);
-INTERCEPT_LIBRARY_FUNCTION(strtol);
-INTERCEPT_LIBRARY_FUNCTION(strtoll);
-INTERCEPT_LIBRARY_FUNCTION(wcslen);
-INTERCEPT_LIBRARY_FUNCTION(wcsnlen);
-
-# if defined(_MSC_VER) && !defined(__clang__)
-# pragma warning(pop)
-# endif
-
-#ifdef _WIN64
-INTERCEPT_LIBRARY_FUNCTION(__C_specific_handler);
-#else
-INTERCEPT_LIBRARY_FUNCTION(_except_handler3);
-// _except_handler4 checks -GS cookie which is different for each module, so we
-// can't use INTERCEPT_LIBRARY_FUNCTION(_except_handler4).
-INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
- __asan_handle_no_return();
- return REAL(_except_handler4)(a, b, c, d);
-}
-#endif
-
-// Windows specific functions not included in asan_interface.inc.
-INTERCEPT_WRAP_W_V(__asan_should_detect_stack_use_after_return)
-INTERCEPT_WRAP_W_V(__asan_get_shadow_memory_dynamic_address)
-INTERCEPT_WRAP_W_W(__asan_unhandled_exception_filter)
-
-using namespace __sanitizer;
-
-extern "C" {
-int __asan_option_detect_stack_use_after_return;
-uptr __asan_shadow_memory_dynamic_address;
-} // extern "C"
-
-static int asan_dll_thunk_init() {
- typedef void (*fntype)();
- static fntype fn = 0;
- // asan_dll_thunk_init is expected to be called by only one thread.
- if (fn) return 0;
-
- // Ensure all interception was executed.
- __dll_thunk_init();
-
- fn = (fntype) dllThunkGetRealAddrOrDie("__asan_init");
- fn();
- __asan_option_detect_stack_use_after_return =
- (__asan_should_detect_stack_use_after_return() != 0);
- __asan_shadow_memory_dynamic_address =
- (uptr)__asan_get_shadow_memory_dynamic_address();
-
-#ifndef _WIN64
- INTERCEPT_FUNCTION(_except_handler4);
-#endif
- // In DLLs, the callbacks are expected to return 0,
- // otherwise CRT initialization fails.
- return 0;
-}
-
-#pragma section(".CRT$XIB", long, read)
-__declspec(allocate(".CRT$XIB")) int (*__asan_preinit)() = asan_dll_thunk_init;
-
-static void WINAPI asan_thread_init(void *mod, unsigned long reason,
- void *reserved) {
- if (reason == /*DLL_PROCESS_ATTACH=*/1) asan_dll_thunk_init();
-}
-
-#pragma section(".CRT$XLAB", long, read)
-__declspec(allocate(".CRT$XLAB")) void (WINAPI *__asan_tls_init)(void *,
- unsigned long, void *) = asan_thread_init;
-
-WIN_FORCE_LINK(__asan_dso_reg_hook)
-
-#endif // SANITIZER_DLL_THUNK
@@ -8,76 +8,17 @@
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
-// This file defines things that need to be present in the application modules
-// to interact with the ASan DLL runtime correctly and can't be implemented
-// using the default "import library" generated when linking the DLL RTL.
-//
-// This includes:
-// - creating weak aliases to default implementation imported from asan dll.
-// - forwarding the detect_stack_use_after_return runtime option
-// - working around deficiencies of the MD runtime
-// - installing a custom SEH handler
+// This file defines things that need to be present for application modules
+// that are dynamic linked with the C Runtime.
//
//===----------------------------------------------------------------------===//
#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK
-#define SANITIZER_IMPORT_INTERFACE 1
-#include "sanitizer_common/sanitizer_win_defs.h"
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-// Define weak alias for all weak functions imported from asan dll.
-#define INTERFACE_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name)
-#include "asan_interface.inc"
-
-// First, declare CRT sections we'll be using in this file
-#pragma section(".CRT$XIB", long, read)
-#pragma section(".CRT$XID", long, read)
-#pragma section(".CRT$XCAB", long, read)
-#pragma section(".CRT$XTW", long, read)
-#pragma section(".CRT$XTY", long, read)
-#pragma section(".CRT$XLAB", long, read)
-
-////////////////////////////////////////////////////////////////////////////////
-// Define a copy of __asan_option_detect_stack_use_after_return that should be
-// used when linking an MD runtime with a set of object files on Windows.
-//
-// The ASan MD runtime dllexports '__asan_option_detect_stack_use_after_return',
-// so normally we would just dllimport it. Unfortunately, the dllimport
-// attribute adds __imp_ prefix to the symbol name of a variable.
-// Since in general we don't know if a given TU is going to be used
-// with a MT or MD runtime and we don't want to use ugly __imp_ names on Windows
-// just to work around this issue, let's clone the variable that is constant
-// after initialization anyways.
-extern "C" {
-__declspec(dllimport) int __asan_should_detect_stack_use_after_return();
-int __asan_option_detect_stack_use_after_return;
-
-__declspec(dllimport) void* __asan_get_shadow_memory_dynamic_address();
-void* __asan_shadow_memory_dynamic_address;
-}
-
-static int InitializeClonedVariables() {
- __asan_option_detect_stack_use_after_return =
- __asan_should_detect_stack_use_after_return();
- __asan_shadow_memory_dynamic_address =
- __asan_get_shadow_memory_dynamic_address();
- return 0;
-}
-
-static void NTAPI asan_thread_init(void *mod, unsigned long reason,
- void *reserved) {
- if (reason == DLL_PROCESS_ATTACH) InitializeClonedVariables();
-}
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
-// Our cloned variables must be initialized before C/C++ constructors. If TLS
-// is used, our .CRT$XLAB initializer will run first. If not, our .CRT$XIB
-// initializer is needed as a backup.
-__declspec(allocate(".CRT$XIB")) int (*__asan_initialize_cloned_variables)() =
- InitializeClonedVariables;
-__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)(void *,
- unsigned long, void *) = asan_thread_init;
+# include "asan_win_common_runtime_thunk.h"
+# include "sanitizer_common/sanitizer_win_defs.h"
////////////////////////////////////////////////////////////////////////////////
// For some reason, the MD CRT doesn't call the C/C++ terminators during on DLL
@@ -88,43 +29,26 @@ __declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)(void *,
// using atexit() that calls a small subset of C terminators
// where LLVM global_dtors is placed. Fingers crossed, no other C terminators
// are there.
-extern "C" int __cdecl atexit(void (__cdecl *f)(void));
+extern "C" int __cdecl atexit(void(__cdecl *f)(void));
extern "C" void __cdecl _initterm(void *a, void *b);
namespace {
-__declspec(allocate(".CRT$XTW")) void* before_global_dtors = 0;
-__declspec(allocate(".CRT$XTY")) void* after_global_dtors = 0;
+__declspec(allocate(".CRT$XTW")) void *before_global_dtors = 0;
+__declspec(allocate(".CRT$XTY")) void *after_global_dtors = 0;
void UnregisterGlobals() {
_initterm(&before_global_dtors, &after_global_dtors);
}
-int ScheduleUnregisterGlobals() {
- return atexit(UnregisterGlobals);
-}
+int ScheduleUnregisterGlobals() { return atexit(UnregisterGlobals); }
} // namespace
// We need to call 'atexit(UnregisterGlobals);' as early as possible, but after
// atexit() is initialized (.CRT$XIC). As this is executed before C++
// initializers (think ctors for globals), UnregisterGlobals gets executed after
// dtors for C++ globals.
-__declspec(allocate(".CRT$XID"))
-int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals;
-
-////////////////////////////////////////////////////////////////////////////////
-// ASan SEH handling.
-// We need to set the ASan-specific SEH handler at the end of CRT initialization
-// of each module (see also asan_win.cpp).
-extern "C" {
-__declspec(dllimport) int __asan_set_seh_filter();
-static int SetSEHFilter() { return __asan_set_seh_filter(); }
-
-// Unfortunately, putting a pointer to __asan_set_seh_filter into
-// __asan_intercept_seh gets optimized out, so we have to use an extra function.
-__declspec(allocate(".CRT$XCAB")) int (*__asan_seh_interceptor)() =
- SetSEHFilter;
-}
-
-WIN_FORCE_LINK(__asan_dso_reg_hook)
+extern "C" __declspec(allocate(".CRT$XID")) int (
+ *__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals;
+WIN_FORCE_LINK(__asan_schedule_unregister_globals)
-#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK
+#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK
new file mode 100644
@@ -0,0 +1,113 @@
+//===-- asan_win_static_runtime_thunk.cpp ---------------------------------===//
+//
+// 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 AddressSanitizer, an address sanity checker.
+//
+// This file defines a family of thunks that should be statically linked into
+// modules that are statically linked with the C Runtime in order to delegate
+// the calls to the ASAN runtime DLL.
+// See https://github.com/google/sanitizers/issues/209 for the details.
+//===----------------------------------------------------------------------===//
+
+#ifdef SANITIZER_STATIC_RUNTIME_THUNK
+# include "asan_init_version.h"
+# include "asan_interface_internal.h"
+# include "asan_win_common_runtime_thunk.h"
+# include "sanitizer_common/sanitizer_platform_interceptors.h"
+# include "sanitizer_common/sanitizer_win_defs.h"
+# include "sanitizer_common/sanitizer_win_thunk_interception.h"
+
+# if defined(_MSC_VER) && !defined(__clang__)
+// Disable warnings such as: 'void memchr(void)': incorrect number of arguments
+// for intrinsic function, expected '3' arguments.
+# pragma warning(push)
+# pragma warning(disable : 4392)
+# endif
+
+# define INTERCEPT_LIBRARY_FUNCTION_ASAN(X) \
+ INTERCEPT_LIBRARY_FUNCTION(X, "__asan_wrap_" #X)
+
+INTERCEPT_LIBRARY_FUNCTION_ASAN(atoi);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(atol);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(atoll);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(frexp);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(longjmp);
+# if SANITIZER_INTERCEPT_MEMCHR
+INTERCEPT_LIBRARY_FUNCTION_ASAN(memchr);
+# endif
+INTERCEPT_LIBRARY_FUNCTION_ASAN(memcmp);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(memcpy);
+# ifndef _WIN64
+// memmove and memcpy share an implementation on amd64
+INTERCEPT_LIBRARY_FUNCTION_ASAN(memmove);
+# endif
+INTERCEPT_LIBRARY_FUNCTION_ASAN(memset);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(strcat);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(strchr);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(strcmp);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(strcpy);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(strcspn);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(_strdup);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(strlen);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(strncat);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(strncmp);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(strncpy);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(strnlen);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(strpbrk);
+// INTERCEPT_LIBRARY_FUNCTION_ASAN(strrchr);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(strspn);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(strstr);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(strtok);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(wcslen);
+INTERCEPT_LIBRARY_FUNCTION_ASAN(wcsnlen);
+
+// Note: Don't intercept strtol(l). They are supposed to set errno for out-of-
+// range values, but since the ASan runtime is linked against the dynamic CRT,
+// its errno is different from the one in the current module.
+
+# if defined(_MSC_VER) && !defined(__clang__)
+# pragma warning(pop)
+# endif
+
+# ifdef _WIN64
+INTERCEPT_LIBRARY_FUNCTION_ASAN(__C_specific_handler);
+# else
+extern "C" void abort();
+INTERCEPT_LIBRARY_FUNCTION_ASAN(_except_handler3);
+// _except_handler4 checks -GS cookie which is different for each module, so we
+// can't use INTERCEPT_LIBRARY_FUNCTION_ASAN(_except_handler4), need to apply
+// manually
+extern "C" int _except_handler4(void *, void *, void *, void *);
+static int (*real_except_handler4)(void *, void *, void *,
+ void *) = &_except_handler4;
+static int intercept_except_handler4(void *a, void *b, void *c, void *d) {
+ __asan_handle_no_return();
+ return real_except_handler4(a, b, c, d);
+}
+# endif
+
+// Windows specific functions not included in asan_interface.inc.
+// INTERCEPT_WRAP_W_V(__asan_should_detect_stack_use_after_return)
+// INTERCEPT_WRAP_W_V(__asan_get_shadow_memory_dynamic_address)
+// INTERCEPT_WRAP_W_W(__asan_unhandled_exception_filter)
+
+extern "C" void __asan_initialize_static_thunk() {
+# ifndef _WIN64
+ if (real_except_handler4 == &_except_handler4) {
+ // Single threaded, no need for synchronization.
+ if (!__sanitizer_override_function_by_addr(
+ reinterpret_cast<__sanitizer::uptr>(&intercept_except_handler4),
+ reinterpret_cast<__sanitizer::uptr>(&_except_handler4),
+ reinterpret_cast<__sanitizer::uptr*>(&real_except_handler4))) {
+ abort();
+ }
+ }
+# endif
+}
+
+#endif // SANITIZER_DLL_THUNK
deleted file mode 100644
@@ -1,22 +0,0 @@
-//===-- asan_win_weak_interception.cpp ------------------------------------===//
-//
-// 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 module should be included in Address Sanitizer when it is implemented as
-// a shared library on Windows (dll), in order to delegate the calls of weak
-// functions to the implementation in the main executable when a strong
-// definition is provided.
-//===----------------------------------------------------------------------===//
-#ifdef SANITIZER_DYNAMIC
-#include "sanitizer_common/sanitizer_win_weak_interception.h"
-#include "asan_interface_internal.h"
-// Check if strong definitions for weak functions are present in the main
-// executable. If that is the case, override dll functions to point to strong
-// implementations.
-#define INTERFACE_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
-#include "asan_interface.inc"
-#endif // SANITIZER_DYNAMIC
@@ -260,9 +260,10 @@
.globl name SEPARATOR \
SYMBOL_IS_FUNC(name) SEPARATOR \
DECLARE_SYMBOL_VISIBILITY_UNMANGLED(name) SEPARATOR \
- CFI_START SEPARATOR \
DECLARE_FUNC_ENCODING \
- name: SEPARATOR BTI_C
+ name: \
+ SEPARATOR CFI_START \
+ SEPARATOR BTI_C
#define DEFINE_COMPILERRT_FUNCTION_ALIAS(name, target) \
.globl SYMBOL_NAME(name) SEPARATOR \
@@ -357,8 +357,6 @@ __attribute__((constructor(0))) void __hwasan_init() {
hwasan_init_is_running = 1;
SanitizerToolName = "HWAddressSanitizer";
- InitTlsSize();
-
CacheBinaryName();
InitializeFlags();
@@ -367,6 +365,8 @@ __attribute__((constructor(0))) void __hwasan_init() {
__sanitizer_set_report_path(common_flags()->log_path);
+ InitializePlatformEarly();
+
AndroidTestTlsSlot();
DisableCoreDumperIfNecessary();
@@ -678,6 +678,8 @@ uptr __hwasan_tag_pointer(uptr p, u8 tag) {
return AddTagToPointer(p, tag);
}
+u8 __hwasan_get_tag_from_pointer(uptr p) { return GetTagFromPointer(p); }
+
void __hwasan_handle_longjmp(const void *sp_dst) {
uptr dst = (uptr)sp_dst;
// HWASan does not support tagged SP.
@@ -690,7 +692,7 @@ void __hwasan_handle_longjmp(const void *sp_dst) {
"WARNING: HWASan is ignoring requested __hwasan_handle_longjmp: "
"stack top: %p; target %p; distance: %p (%zd)\n"
"False positive error reports may follow\n",
- (void *)sp, (void *)dst, dst - sp);
+ (void *)sp, (void *)dst, dst - sp, dst - sp);
return;
}
TagMemory(sp, dst - sp, 0);
@@ -104,9 +104,9 @@ static inline void *UntagPtr(const void *tagged_ptr) {
}
static inline uptr AddTagToPointer(uptr p, tag_t tag) {
- return InTaggableRegion(p)
- ? ((p & ~kAddressTagMask) | ((uptr)tag << kAddressTagShift))
- : p;
+ return InTaggableRegion(p) ? ((p & ~kAddressTagMask) |
+ ((uptr)(tag & kTagMask) << kAddressTagShift))
+ : p;
}
namespace __hwasan {
@@ -139,14 +139,14 @@ void hwasan_free(void *ptr, StackTrace *stack);
void InstallAtExitHandler();
#define GET_MALLOC_STACK_TRACE \
- BufferedStackTrace stack; \
+ UNINITIALIZED BufferedStackTrace stack; \
if (hwasan_inited) \
stack.Unwind(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \
nullptr, common_flags()->fast_unwind_on_malloc, \
common_flags()->malloc_context_size)
#define GET_FATAL_STACK_TRACE_PC_BP(pc, bp) \
- BufferedStackTrace stack; \
+ UNINITIALIZED BufferedStackTrace stack; \
if (hwasan_inited) \
stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal)
@@ -17,7 +17,6 @@
#include "sanitizer_common/sanitizer_allocator_dlsym.h"
#include "sanitizer_common/sanitizer_allocator_interface.h"
#include "sanitizer_common/sanitizer_mallinfo.h"
-#include "sanitizer_common/sanitizer_tls_get_addr.h"
using namespace __hwasan;
@@ -62,10 +61,7 @@ void *__sanitizer_aligned_alloc(uptr alignment, uptr size) {
SANITIZER_INTERFACE_ATTRIBUTE
void *__sanitizer___libc_memalign(uptr alignment, uptr size) {
GET_MALLOC_STACK_TRACE;
- void *ptr = hwasan_memalign(alignment, size, &stack);
- if (ptr)
- DTLS_on_libc_memalign(ptr, size);
- return ptr;
+ return hwasan_memalign(alignment, size, &stack);
}
SANITIZER_INTERFACE_ATTRIBUTE
@@ -184,7 +180,7 @@ INTERCEPTOR_ALIAS(void *, malloc, SIZE_T size);
INTERCEPTOR_ALIAS(void *, memalign, SIZE_T alignment, SIZE_T size);
INTERCEPTOR_ALIAS(void *, pvalloc, SIZE_T size);
INTERCEPTOR_ALIAS(void, cfree, void *ptr);
-INTERCEPTOR_ALIAS(__sanitizer_struct_mallinfo, mallinfo);
+INTERCEPTOR_ALIAS(__sanitizer_struct_mallinfo, mallinfo,);
INTERCEPTOR_ALIAS(int, mallopt, int cmd, int value);
INTERCEPTOR_ALIAS(void, malloc_stats, void);
# endif
@@ -44,7 +44,7 @@ enum {
// Initialized in HwasanAllocatorInit, an never changed.
-static ALIGNED(16) u8 tail_magic[kShadowAlignment - 1];
+alignas(16) static u8 tail_magic[kShadowAlignment - 1];
static uptr max_malloc_size;
bool HwasanChunkView::IsAllocated() const {
@@ -289,6 +289,9 @@ static void HwasanDeallocate(StackTrace *stack, void *tagged_ptr) {
CHECK(tagged_ptr);
void *untagged_ptr = UntagPtr(tagged_ptr);
+ if (RunFreeHooks(tagged_ptr))
+ return;
+
if (CheckInvalidFree(stack, untagged_ptr, tagged_ptr))
return;
@@ -302,8 +305,6 @@ static void HwasanDeallocate(StackTrace *stack, void *tagged_ptr) {
return;
}
- RunFreeHooks(tagged_ptr);
-
uptr orig_size = meta->GetRequestedSize();
u32 free_context_id = StackDepotPut(*stack);
u32 alloc_context_id = meta->GetAllocStackId();
@@ -140,7 +140,6 @@ __attribute__((always_inline, nodebug)) static inline uptr ShortTagSize(
__attribute__((always_inline, nodebug)) static inline bool
PossiblyShortTagMatches(tag_t mem_tag, uptr ptr, uptr sz) {
- DCHECK(IsAligned(ptr, kShadowAlignment));
tag_t ptr_tag = GetTagFromPointer(ptr);
if (ptr_tag == mem_tag)
return true;
@@ -36,15 +36,20 @@ decltype(__hwasan_shadow)* __hwasan_premap_shadow();
namespace __hwasan {
+// We cannot call anything in libc here (see comment above), so we need to
+// assume the biggest allowed page size.
+// Android max page size is defined as 16k here:
+// https://android.googlesource.com/platform/bionic/+/main/libc/platform/bionic/page.h#41
+static constexpr uptr kMaxGranularity = 16384;
+
// Conservative upper limit.
static uptr PremapShadowSize() {
- return RoundUpTo(GetMaxVirtualAddress() >> kShadowScale,
- GetMmapGranularity());
+ return RoundUpTo(GetMaxVirtualAddress() >> kShadowScale, kMaxGranularity);
}
static uptr PremapShadow() {
return MapDynamicShadow(PremapShadowSize(), kShadowScale,
- kShadowBaseAlignment, kHighMemEnd);
+ kShadowBaseAlignment, kHighMemEnd, kMaxGranularity);
}
static bool IsPremapShadowAvailable() {
@@ -56,7 +61,7 @@ static bool IsPremapShadowAvailable() {
}
static uptr FindPremappedShadowStart(uptr shadow_size_bytes) {
- const uptr granularity = GetMmapGranularity();
+ const uptr granularity = kMaxGranularity;
const uptr shadow_start = reinterpret_cast<uptr>(&__hwasan_shadow);
const uptr premap_shadow_size = PremapShadowSize();
const uptr shadow_size = RoundUpTo(shadow_size_bytes, granularity);
@@ -109,7 +114,7 @@ uptr FindDynamicShadowStart(uptr shadow_size_bytes) {
if (IsPremapShadowAvailable())
return FindPremappedShadowStart(shadow_size_bytes);
return MapDynamicShadow(shadow_size_bytes, kShadowScale, kShadowBaseAlignment,
- kHighMemEnd);
+ kHighMemEnd, kMaxGranularity);
}
} // namespace __hwasan
@@ -135,7 +140,7 @@ uptr FindDynamicShadowStart(uptr shadow_size_bytes) {
RingBufferSize());
# endif
return MapDynamicShadow(shadow_size_bytes, kShadowScale, kShadowBaseAlignment,
- kHighMemEnd);
+ kHighMemEnd, GetMmapGranularity());
}
} // namespace __hwasan
@@ -84,3 +84,10 @@ HWASAN_FLAG(bool, malloc_bisect_dump, false,
// are untagged before the call.
HWASAN_FLAG(bool, fail_without_syscall_abi, true,
"Exit if fail to request relaxed syscall ABI.")
+
+HWASAN_FLAG(
+ uptr, fixed_shadow_base, -1,
+ "If not -1, HWASan will attempt to allocate the shadow at this address, "
+ "instead of choosing one dynamically."
+ "Tip: this can be combined with the compiler option, "
+ "-hwasan-mapping-offset, to optimize the instrumentation.")
@@ -334,10 +334,10 @@ INTERCEPTOR(int, pthread_timedjoin_np, void *thread, void **ret,
}
# endif
-DEFINE_REAL_PTHREAD_FUNCTIONS
+DEFINE_INTERNAL_PTHREAD_FUNCTIONS
-DEFINE_REAL(int, vfork)
-DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork)
+DEFINE_REAL(int, vfork,)
+DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork,)
// Get and/or change the set of blocked signals.
extern "C" int sigprocmask(int __how, const __hw_sigset_t *__restrict __set,
@@ -520,6 +520,7 @@ void InitializeInterceptors() {
CHECK_EQ(inited, 0);
# if HWASAN_WITH_INTERCEPTORS
+ __interception::DoesNotSupportStaticLinking();
InitializeCommonInterceptors();
(void)(read_iovec);
@@ -160,6 +160,9 @@ void __hwasan_tag_memory(uptr p, u8 tag, uptr sz);
SANITIZER_INTERFACE_ATTRIBUTE
uptr __hwasan_tag_pointer(uptr p, u8 tag);
+SANITIZER_INTERFACE_ATTRIBUTE
+u8 __hwasan_get_tag_from_pointer(uptr p);
+
SANITIZER_INTERFACE_ATTRIBUTE
void __hwasan_tag_mismatch(uptr addr, u8 ts);
@@ -106,8 +106,22 @@ static uptr GetHighMemEnd() {
}
static void InitializeShadowBaseAddress(uptr shadow_size_bytes) {
- __hwasan_shadow_memory_dynamic_address =
- FindDynamicShadowStart(shadow_size_bytes);
+ // FIXME: Android should init flags before shadow.
+ if (!SANITIZER_ANDROID && flags()->fixed_shadow_base != (uptr)-1) {
+ __hwasan_shadow_memory_dynamic_address = flags()->fixed_shadow_base;
+ uptr beg = __hwasan_shadow_memory_dynamic_address;
+ uptr end = beg + shadow_size_bytes;
+ if (!MemoryRangeIsAvailable(beg, end)) {
+ Report(
+ "FATAL: HWAddressSanitizer: Shadow range %p-%p is not available.\n",
+ (void *)beg, (void *)end);
+ DumpProcessMap();
+ CHECK(MemoryRangeIsAvailable(beg, end));
+ }
+ } else {
+ __hwasan_shadow_memory_dynamic_address =
+ FindDynamicShadowStart(shadow_size_bytes);
+ }
}
static void MaybeDieIfNoTaggingAbi(const char *message) {
@@ -246,9 +260,6 @@ bool InitShadow() {
CHECK_GT(kLowShadowEnd, kLowShadowStart);
CHECK_GT(kLowShadowStart, kLowMemEnd);
- if (Verbosity())
- PrintAddressSpaceLayout();
-
// Reserve shadow memory.
ReserveShadowMemoryRange(kLowShadowStart, kLowShadowEnd, "low shadow");
ReserveShadowMemoryRange(kHighShadowStart, kHighShadowEnd, "high shadow");
@@ -262,6 +273,9 @@ bool InitShadow() {
if (kHighShadowEnd + 1 < kHighMemStart)
ProtectGap(kHighShadowEnd + 1, kHighMemStart - kHighShadowEnd - 1);
+ if (Verbosity())
+ PrintAddressSpaceLayout();
+
return true;
}
@@ -294,25 +308,6 @@ void InstallAtExitHandler() { atexit(HwasanAtExit); }
// ---------------------- TSD ---------------- {{{1
-extern "C" void __hwasan_thread_enter() {
- hwasanThreadList().CreateCurrentThread()->EnsureRandomStateInited();
-}
-
-extern "C" void __hwasan_thread_exit() {
- Thread *t = GetCurrentThread();
- // Make sure that signal handler can not see a stale current thread pointer.
- atomic_signal_fence(memory_order_seq_cst);
- if (t) {
- // Block async signals on the thread as the handler can be instrumented.
- // After this point instrumented code can't access essential data from TLS
- // and will crash.
- // Bionic already calls __hwasan_thread_exit with blocked signals.
- if (SANITIZER_GLIBC)
- BlockSignals();
- hwasanThreadList().ReleaseThread(t);
- }
-}
-
# if HWASAN_WITH_INTERCEPTORS
static pthread_key_t tsd_key;
static bool tsd_key_inited = false;
@@ -504,12 +499,8 @@ void HwasanOnDeadlySignal(int signo, void *info, void *context) {
}
void Thread::InitStackAndTls(const InitState *) {
- uptr tls_size;
- uptr stack_size;
- GetThreadStackAndTls(IsMainThread(), &stack_bottom_, &stack_size, &tls_begin_,
- &tls_size);
- stack_top_ = stack_bottom_ + stack_size;
- tls_end_ = tls_begin_ + tls_size;
+ GetThreadStackAndTls(IsMainThread(), &stack_bottom_, &stack_top_, &tls_begin_,
+ &tls_end_);
}
uptr TagMemoryAligned(uptr p, uptr size, tag_t tag) {
@@ -536,16 +527,34 @@ uptr TagMemoryAligned(uptr p, uptr size, tag_t tag) {
return AddTagToPointer(p, tag);
}
+static void BeforeFork() {
+ VReport(2, "BeforeFork tid: %llu\n", GetTid());
+ if (CAN_SANITIZE_LEAKS) {
+ __lsan::LockGlobal();
+ }
+ // `_lsan` functions defined regardless of `CAN_SANITIZE_LEAKS` and lock the
+ // stuff we need.
+ __lsan::LockThreads();
+ __lsan::LockAllocator();
+ StackDepotLockBeforeFork();
+}
+
+static void AfterFork(bool fork_child) {
+ StackDepotUnlockAfterFork(fork_child);
+ // `_lsan` functions defined regardless of `CAN_SANITIZE_LEAKS` and unlock
+ // the stuff we need.
+ __lsan::UnlockAllocator();
+ __lsan::UnlockThreads();
+ if (CAN_SANITIZE_LEAKS) {
+ __lsan::UnlockGlobal();
+ }
+ VReport(2, "AfterFork tid: %llu\n", GetTid());
+}
+
void HwasanInstallAtForkHandler() {
- auto before = []() {
- HwasanAllocatorLock();
- StackDepotLockAll();
- };
- auto after = []() {
- StackDepotUnlockAll();
- HwasanAllocatorUnlock();
- };
- pthread_atfork(before, after, after);
+ pthread_atfork(
+ &BeforeFork, []() { AfterFork(/* fork_child= */ false); },
+ []() { AfterFork(/* fork_child= */ true); });
}
void InstallAtExitCheckLeaks() {
@@ -561,4 +570,25 @@ void InstallAtExitCheckLeaks() {
} // namespace __hwasan
+using namespace __hwasan;
+
+extern "C" void __hwasan_thread_enter() {
+ hwasanThreadList().CreateCurrentThread()->EnsureRandomStateInited();
+}
+
+extern "C" void __hwasan_thread_exit() {
+ Thread *t = GetCurrentThread();
+ // Make sure that signal handler can not see a stale current thread pointer.
+ atomic_signal_fence(memory_order_seq_cst);
+ if (t) {
+ // Block async signals on the thread as the handler can be instrumented.
+ // After this point instrumented code can't access essential data from TLS
+ // and will crash.
+ // Bionic already calls __hwasan_thread_exit with blocked signals.
+ if (SANITIZER_GLIBC)
+ BlockSignals();
+ hwasanThreadList().ReleaseThread(t);
+ }
+}
+
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
@@ -14,10 +14,8 @@
#include "sanitizer_common/sanitizer_internal_defs.h"
#if SANITIZER_CAN_USE_PREINIT_ARRAY
-// The symbol is called __local_hwasan_preinit, because it's not intended to
-// be exported.
-// This code linked into the main executable when -fsanitize=hwaddress is in
-// the link flags. It can only use exported interface functions.
-__attribute__((section(".preinit_array"), used)) static void (
- *__local_hwasan_preinit)(void) = __hwasan_init;
+// This section is linked into the main executable when -fsanitize=hwaddress is
+// specified to perform initialization at a very early stage.
+__attribute__((section(".preinit_array"), used)) static auto preinit =
+ __hwasan_init;
#endif
@@ -27,6 +27,7 @@
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_mutex.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_report_decorator.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_stacktrace_printer.h"
@@ -40,7 +41,7 @@ class ScopedReport {
public:
explicit ScopedReport(bool fatal) : fatal(fatal) {
Lock lock(&error_message_lock_);
- error_message_ptr_ = fatal ? &error_message_ : nullptr;
+ error_message_ptr_ = &error_message_;
++hwasan_report_count;
}
@@ -205,6 +206,7 @@ static void PrintStackAllocations(const StackAllocationsRingBuffer *sa,
tag_t addr_tag, uptr untagged_addr) {
uptr frames = Min((uptr)flags()->stack_history_size, sa->size());
bool found_local = false;
+ InternalScopedString location;
for (uptr i = 0; i < frames; i++) {
const uptr *record_addr = &(*sa)[i];
uptr record = *record_addr;
@@ -212,35 +214,104 @@ static void PrintStackAllocations(const StackAllocationsRingBuffer *sa,
break;
tag_t base_tag =
reinterpret_cast<uptr>(record_addr) >> kRecordAddrBaseTagShift;
- uptr fp = (record >> kRecordFPShift) << kRecordFPLShift;
+ const uptr fp = (record >> kRecordFPShift) << kRecordFPLShift;
+ CHECK_LT(fp, kRecordFPModulus);
uptr pc_mask = (1ULL << kRecordFPShift) - 1;
uptr pc = record & pc_mask;
FrameInfo frame;
- if (Symbolizer::GetOrInit()->SymbolizeFrame(pc, &frame)) {
- for (LocalInfo &local : frame.locals) {
- if (!local.has_frame_offset || !local.has_size || !local.has_tag_offset)
- continue;
- tag_t obj_tag = base_tag ^ local.tag_offset;
- if (obj_tag != addr_tag)
- continue;
- // Calculate the offset from the object address to the faulting
- // address. Because we only store bits 4-19 of FP (bits 0-3 are
- // guaranteed to be zero), the calculation is performed mod 2^20 and may
- // harmlessly underflow if the address mod 2^20 is below the object
- // address.
- uptr obj_offset =
- (untagged_addr - fp - local.frame_offset) & (kRecordFPModulus - 1);
- if (obj_offset >= local.size)
- continue;
- if (!found_local) {
- Printf("Potentially referenced stack objects:\n");
- found_local = true;
+ if (!Symbolizer::GetOrInit()->SymbolizeFrame(pc, &frame))
+ continue;
+ for (LocalInfo &local : frame.locals) {
+ if (!local.has_frame_offset || !local.has_size || !local.has_tag_offset)
+ continue;
+ if (!(local.name && internal_strlen(local.name)) &&
+ !(local.function_name && internal_strlen(local.function_name)) &&
+ !(local.decl_file && internal_strlen(local.decl_file)))
+ continue;
+ tag_t obj_tag = base_tag ^ local.tag_offset;
+ if (obj_tag != addr_tag)
+ continue;
+
+ // We only store bits 4-19 of FP (bits 0-3 are guaranteed to be zero).
+ // So we know only `FP % kRecordFPModulus`, and we can only calculate
+ // `local_beg % kRecordFPModulus`.
+ // Out of all possible `local_beg` we will only consider 2 candidates
+ // nearest to the `untagged_addr`.
+ uptr local_beg_mod = (fp + local.frame_offset) % kRecordFPModulus;
+ // Pick `local_beg` in the same 1 MiB block as `untagged_addr`.
+ uptr local_beg =
+ RoundDownTo(untagged_addr, kRecordFPModulus) + local_beg_mod;
+ // Pick the largest `local_beg <= untagged_addr`. It's either the current
+ // one or the one before.
+ if (local_beg > untagged_addr)
+ local_beg -= kRecordFPModulus;
+
+ uptr offset = -1ull;
+ const char *whence;
+ const char *cause = nullptr;
+ uptr best_beg;
+
+ // Try two 1 MiB blocks options and pick nearest one.
+ for (uptr i = 0; i < 2; ++i, local_beg += kRecordFPModulus) {
+ uptr local_end = local_beg + local.size;
+ if (local_beg > local_end)
+ continue; // This is a wraparound.
+ if (local_beg <= untagged_addr && untagged_addr < local_end) {
+ offset = untagged_addr - local_beg;
+ whence = "inside";
+ cause = "use-after-scope";
+ best_beg = local_beg;
+ break; // This is as close at it can be.
+ }
+
+ if (untagged_addr >= local_end) {
+ uptr new_offset = untagged_addr - local_end;
+ if (new_offset < offset) {
+ offset = new_offset;
+ whence = "after";
+ cause = "stack-buffer-overflow";
+ best_beg = local_beg;
+ }
+ } else {
+ uptr new_offset = local_beg - untagged_addr;
+ if (new_offset < offset) {
+ offset = new_offset;
+ whence = "before";
+ cause = "stack-buffer-overflow";
+ best_beg = local_beg;
+ }
}
- Printf(" %s in %s %s:%d\n", local.name, local.function_name,
- local.decl_file, local.decl_line);
}
- frame.Clear();
+
+ // To fail the `untagged_addr` must be near nullptr, which is impossible
+ // with Linux user space memory layout.
+ if (!cause)
+ continue;
+
+ if (!found_local) {
+ Printf("\nPotentially referenced stack objects:\n");
+ found_local = true;
+ }
+
+ Decorator d;
+ Printf("%s", d.Error());
+ Printf("Cause: %s\n", cause);
+ Printf("%s", d.Default());
+ Printf("%s", d.Location());
+ StackTracePrinter::GetOrInit()->RenderSourceLocation(
+ &location, local.decl_file, local.decl_line, /* column= */ 0,
+ common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
+ Printf(
+ "%p is located %zd bytes %s a %zd-byte local variable %s "
+ "[%p,%p) "
+ "in %s %s\n",
+ untagged_addr, offset, whence, local.size, local.name, best_beg,
+ best_beg + local.size, local.function_name, location.data());
+ location.clear();
+ Printf("%s\n", d.Default());
}
+ frame.Clear();
}
if (found_local)
@@ -257,14 +328,16 @@ static void PrintStackAllocations(const StackAllocationsRingBuffer *sa,
break;
uptr pc_mask = (1ULL << 48) - 1;
uptr pc = record & pc_mask;
- frame_desc.AppendF(" record_addr:0x%zx record:0x%zx",
- reinterpret_cast<uptr>(record_addr), record);
- if (SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc)) {
+ frame_desc.AppendF(" record_addr:%p record:0x%zx",
+ reinterpret_cast<const void *>(record_addr), record);
+ SymbolizedStackHolder symbolized_stack(
+ Symbolizer::GetOrInit()->SymbolizePC(pc));
+ const SymbolizedStack *frame = symbolized_stack.get();
+ if (frame) {
StackTracePrinter::GetOrInit()->RenderFrame(
&frame_desc, " %F %L", 0, frame->info.address, &frame->info,
common_flags()->symbolize_vs_style,
common_flags()->strip_path_prefix);
- frame->ClearAll();
}
Printf("%s\n", frame_desc.data());
frame_desc.clear();
@@ -353,7 +426,7 @@ static void PrintTagInfoAroundAddr(uptr addr, uptr num_rows,
print_tag(s, row + i);
s.Append(row + i == addr ? "]" : " ");
}
- s.AppendF("\n");
+ s.Append("\n");
}
}
@@ -363,7 +436,7 @@ static void PrintTagsAroundAddr(uptr addr, GetTag get_tag,
InternalScopedString s;
addr = MemToShadow(addr);
s.AppendF(
- "Memory tags around the buggy address (one tag corresponds to %zd "
+ "\nMemory tags around the buggy address (one tag corresponds to %zd "
"bytes):\n",
kShadowAlignment);
PrintTagInfoAroundAddr(addr, kShadowLines, s,
@@ -383,10 +456,10 @@ static void PrintTagsAroundAddr(uptr addr, GetTag get_tag,
tag_t short_tag = get_short_tag(tag_addr);
s.AppendF("%02x", short_tag);
} else {
- s.AppendF("..");
+ s.Append("..");
}
});
- s.AppendF(
+ s.Append(
"See "
"https://clang.llvm.org/docs/"
"HardwareAssistedAddressSanitizerDesign.html#short-granules for a "
@@ -745,8 +818,6 @@ void BaseReport::PrintAddressDescription() const {
// Check stack first. If the address is on the stack of a live thread, we
// know it cannot be a heap / global overflow.
for (const auto &sa : allocations.stack) {
- // TODO(fmayer): figure out how to distinguish use-after-return and
- // stack-buffer-overflow.
Printf("%s", d.Error());
Printf("\nCause: stack tag-mismatch\n");
Printf("%s", d.Location());
@@ -803,8 +874,10 @@ void BaseReport::PrintAddressDescription() const {
}
// Print the remaining threads, as an extra information, 1 line per thread.
- if (flags()->print_live_threads_info)
+ if (flags()->print_live_threads_info) {
+ Printf("\n");
hwasanThreadList().VisitAllLiveThreads([&](Thread *t) { t->Announce(); });
+ }
if (!num_descriptions_printed)
// We exhausted our possibilities. Bail out.
@@ -912,16 +985,16 @@ TailOverwrittenReport::~TailOverwrittenReport() {
InternalScopedString s;
u8 *tail = tail_copy;
- s.AppendF("Tail contains: ");
- for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.AppendF(".. ");
+ s.Append("Tail contains: ");
+ for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.Append(".. ");
for (uptr i = 0; i < tail_size; i++) s.AppendF("%02x ", tail[i]);
- s.AppendF("\n");
- s.AppendF("Expected: ");
- for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.AppendF(".. ");
+ s.Append("\n");
+ s.Append("Expected: ");
+ for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.Append(".. ");
for (uptr i = 0; i < tail_size; i++) s.AppendF("%02x ", actual_expected[i]);
- s.AppendF("\n");
- s.AppendF(" ");
- for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.AppendF(" ");
+ s.Append("\n");
+ s.Append(" ");
+ for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.Append(" ");
for (uptr i = 0; i < tail_size; i++)
s.AppendF("%s ", actual_expected[i] != tail[i] ? "^^" : " ");
@@ -1020,7 +1093,7 @@ void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
// See the frame breakdown defined in __hwasan_tag_mismatch (from
// hwasan_tag_mismatch_{aarch64,riscv64}.S).
void ReportRegisters(const uptr *frame, uptr pc) {
- Printf("Registers where the failure occurred (pc %p):\n", pc);
+ Printf("\nRegisters where the failure occurred (pc %p):\n", pc);
// We explicitly print a single line (4 registers/line) each iteration to
// reduce the amount of logcat error messages printed. Each Printf() will
@@ -68,6 +68,7 @@ void Thread::Init(uptr stack_buffer_start, uptr stack_buffer_size,
}
Print("Creating : ");
}
+ ClearShadowForThreadStackAndTLS();
}
void Thread::InitStackRingBuffer(uptr stack_buffer_start,
@@ -217,6 +218,11 @@ void GetAdditionalThreadContextPtrsLocked(InternalMmapVector<uptr> *ptrs) {
__hwasan::hwasanThreadArgRetval().GetAllPtrsLocked(ptrs);
}
-void GetRunningThreadsLocked(InternalMmapVector<tid_t> *threads) {}
+void GetRunningThreadsLocked(InternalMmapVector<tid_t> *threads) {
+ // TODO: implement.
+}
+void PrintThreads() {
+ // TODO: implement.
+}
} // namespace __lsan
@@ -1,5 +1,6 @@
#include "hwasan_thread_list.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_thread_arg_retval.h"
namespace __hwasan {
@@ -13,15 +14,15 @@ ThreadArgRetval &hwasanThreadArgRetval() { return *thread_data; }
void InitThreadList(uptr storage, uptr size) {
CHECK_EQ(hwasan_thread_list, nullptr);
- static ALIGNED(alignof(
- HwasanThreadList)) char thread_list_placeholder[sizeof(HwasanThreadList)];
+ alignas(alignof(HwasanThreadList)) static char
+ thread_list_placeholder[sizeof(HwasanThreadList)];
hwasan_thread_list =
new (thread_list_placeholder) HwasanThreadList(storage, size);
CHECK_EQ(thread_data, nullptr);
- static ALIGNED(alignof(
- ThreadArgRetval)) char thread_data_placeholder[sizeof(ThreadArgRetval)];
+ alignas(alignof(ThreadArgRetval)) static char
+ thread_data_placeholder[sizeof(ThreadArgRetval)];
thread_data = new (thread_data_placeholder) ThreadArgRetval();
}
@@ -18,7 +18,7 @@
// * Start of the shadow memory region is aligned to 2**kShadowBaseAlignment.
// * All stack ring buffers are located within (2**kShadowBaseAlignment)
// sized region below and adjacent to the shadow region.
-// * Each ring buffer has a size of (2**N)*4096 where N is in [0, 8), and is
+// * Each ring buffer has a size of (2**N)*4096 where N is in [0, 7), and is
// aligned to twice its size. The value of N can be different for each buffer.
//
// These constrains guarantee that, given an address A of any element of the
@@ -47,7 +47,6 @@
#include "hwasan_allocator.h"
#include "hwasan_flags.h"
#include "hwasan_thread.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_thread_arg_retval.h"
namespace __hwasan {
@@ -56,7 +55,10 @@ static uptr RingBufferSize() {
uptr desired_bytes = flags()->stack_history_size * sizeof(uptr);
// FIXME: increase the limit to 8 once this bug is fixed:
// https://bugs.llvm.org/show_bug.cgi?id=39030
- for (int shift = 1; shift < 7; ++shift) {
+ // Note that we *cannot* do that on Android, as the runtime will indefinitely
+ // have to support code that is compiled with ashr, which only works with
+ // shifts up to 6.
+ for (int shift = 0; shift < 7; ++shift) {
uptr size = 4096 * (1ULL << shift);
if (size >= desired_bytes)
return size;
@@ -62,13 +62,20 @@ size_t SANITIZER_CDECL __sanitizer_get_free_bytes(void);
size_t SANITIZER_CDECL __sanitizer_get_unmapped_bytes(void);
/* Malloc hooks that may be optionally provided by user.
- __sanitizer_malloc_hook(ptr, size) is called immediately after
- allocation of "size" bytes, which returned "ptr".
- __sanitizer_free_hook(ptr) is called immediately before
- deallocation of "ptr". */
+ - __sanitizer_malloc_hook(ptr, size) is called immediately after allocation
+ of "size" bytes, which returned "ptr".
+ - __sanitizer_free_hook(ptr) is called immediately before deallocation of
+ "ptr".
+ - __sanitizer_ignore_free_hook(ptr) is called immediately before deallocation
+ of "ptr", and if it returns a non-zero value, the deallocation of "ptr"
+ will not take place. This allows software to make free a no-op until it
+ calls free() again in the same pointer at a later time. Hint: read this as
+ "ignore the free" rather than "ignore the hook".
+*/
void SANITIZER_CDECL __sanitizer_malloc_hook(const volatile void *ptr,
size_t size);
void SANITIZER_CDECL __sanitizer_free_hook(const volatile void *ptr);
+int SANITIZER_CDECL __sanitizer_ignore_free_hook(const volatile void *ptr);
/* Installs a pair of hooks for malloc/free.
Several (currently, 5) hook pairs may be installed, they are executed
@@ -193,6 +193,43 @@ void SANITIZER_CDECL __sanitizer_annotate_double_ended_contiguous_container(
const void *old_container_beg, const void *old_container_end,
const void *new_container_beg, const void *new_container_end);
+/// Copies memory annotations from a source storage region to a destination
+/// storage region. After the operation, the destination region has the same
+/// memory annotations as the source region, as long as sanitizer limitations
+/// allow it (more bytes may be unpoisoned than in the source region, resulting
+/// in more false negatives, but never false positives). If the source and
+/// destination regions overlap, only the minimal required changes are made to
+/// preserve the correct annotations. Old storage bytes that are not in the new
+/// storage should have the same annotations, as long as sanitizer limitations
+/// allow it.
+///
+/// This function is primarily designed to be used when moving trivially
+/// relocatable objects that may have poisoned memory, making direct copying
+/// problematic under sanitizer. However, this function does not move memory
+/// content itself, only annotations.
+///
+/// A contiguous container is a container that keeps all of its elements in a
+/// contiguous region of memory. The container owns the region of memory
+/// <c>[src_begin, src_end)</c> and <c>[dst_begin, dst_end)</c>. The memory
+/// within these regions may be alternately poisoned and non-poisoned, with
+/// possibly smaller poisoned and unpoisoned regions.
+///
+/// If this function fully poisons a granule, it is marked as "container
+/// overflow".
+///
+/// Argument requirements: The destination container must have the same size as
+/// the source container, which is inferred from the beginning and end of the
+/// source region. Addresses may be granule-unaligned, but this may affect
+/// performance.
+///
+/// \param src_begin Begin of the source container region.
+/// \param src_end End of the source container region.
+/// \param dst_begin Begin of the destination container region.
+/// \param dst_end End of the destination container region.
+void SANITIZER_CDECL __sanitizer_copy_contiguous_container_annotations(
+ const void *src_begin, const void *src_end, const void *dst_begin,
+ const void *dst_end);
+
/// Returns true if the contiguous container <c>[beg, end)</c> is properly
/// poisoned.
///
@@ -293,7 +330,7 @@ void SANITIZER_CDECL __sanitizer_symbolize_global(void *data_ptr,
#define __sanitizer_return_address() \
__builtin_extract_return_addr(__builtin_return_address(0))
#else
-void *SANITIZER_CDECL _ReturnAddress(void);
+void *_ReturnAddress(void);
#pragma intrinsic(_ReturnAddress)
#define __sanitizer_return_address() _ReturnAddress()
#endif
@@ -44,6 +44,10 @@ void SANITIZER_CDECL __hwasan_tag_memory(const volatile void *p,
void *SANITIZER_CDECL __hwasan_tag_pointer(const volatile void *p,
unsigned char tag);
+/// Get tag from the pointer.
+unsigned char SANITIZER_CDECL
+__hwasan_get_tag_from_pointer(const volatile void *p);
+
// Set memory tag from the current SP address to the given address to zero.
// This is meant to annotate longjmp and other non-local jumps.
// This function needs to know the (almost) exact destination frame address;
@@ -1856,6 +1856,15 @@
__sanitizer_syscall_pre_impl_sigaltstack((long)ss, (long)oss)
#define __sanitizer_syscall_post_sigaltstack(res, ss, oss) \
__sanitizer_syscall_post_impl_sigaltstack(res, (long)ss, (long)oss)
+#define __sanitizer_syscall_pre_futex(uaddr, futex_op, val, timeout, uaddr2, \
+ val3) \
+ __sanitizer_syscall_pre_impl_futex((long)uaddr, (long)futex_op, (long)val, \
+ (long)timeout, (long)uaddr2, (long)val3)
+#define __sanitizer_syscall_post_futex(res, uaddr, futex_op, val, timeout, \
+ uaddr2, val3) \
+ __sanitizer_syscall_post_impl_futex(res, (long)uaddr, (long)futex_op, \
+ (long)val, (long)timeout, (long)uaddr2, \
+ (long)val3)
// And now a few syscalls we don't handle yet.
#define __sanitizer_syscall_pre_afs_syscall(...)
@@ -1875,7 +1884,6 @@
#define __sanitizer_syscall_pre_fchown32(...)
#define __sanitizer_syscall_pre_ftime(...)
#define __sanitizer_syscall_pre_ftruncate64(...)
-#define __sanitizer_syscall_pre_futex(...)
#define __sanitizer_syscall_pre_getegid32(...)
#define __sanitizer_syscall_pre_geteuid32(...)
#define __sanitizer_syscall_pre_getgid32(...)
@@ -1954,7 +1962,6 @@
#define __sanitizer_syscall_post_fchown32(res, ...)
#define __sanitizer_syscall_post_ftime(res, ...)
#define __sanitizer_syscall_post_ftruncate64(res, ...)
-#define __sanitizer_syscall_post_futex(res, ...)
#define __sanitizer_syscall_post_getegid32(res, ...)
#define __sanitizer_syscall_post_geteuid32(res, ...)
#define __sanitizer_syscall_post_getgid32(res, ...)
@@ -3093,6 +3100,11 @@ void __sanitizer_syscall_post_impl_rt_sigaction(long res, long signum, long act,
long oldact, long sz);
void __sanitizer_syscall_pre_impl_sigaltstack(long ss, long oss);
void __sanitizer_syscall_post_impl_sigaltstack(long res, long ss, long oss);
+void __sanitizer_syscall_pre_impl_futex(long uaddr, long futex_op, long val,
+ long timeout, long uaddr2, long val3);
+void __sanitizer_syscall_post_impl_futex(long res, long uaddr, long futex_op,
+ long val, long timeout, long uaddr2,
+ long val3);
#ifdef __cplusplus
} // extern "C"
#endif
@@ -59,6 +59,12 @@ const char *SANITIZER_CDECL __memprof_default_options(void);
/// \returns 0 on success.
int SANITIZER_CDECL __memprof_profile_dump(void);
+/// Closes the existing file descriptor, if it is valid and not stdout or
+/// stderr, and resets the internal state such that the profile filename is
+/// reopened on the next profile dump attempt. This can be used to enable
+/// multiple rounds of profiling on the same binary.
+void SANITIZER_CDECL __memprof_profile_reset(void);
+
#ifdef __cplusplus
} // extern "C"
#endif
new file mode 100644
@@ -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
new file mode 100644
@@ -0,0 +1,75 @@
+//===-- sanitizer/rtsan_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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of RealtimeSanitizer.
+//
+// Public interface header.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_RTSAN_INTERFACE_H
+#define SANITIZER_RTSAN_INTERFACE_H
+
+#include <sanitizer/common_interface_defs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+// Disable all RTSan error reporting.
+// Must be paired with a call to `__rtsan_enable`
+void SANITIZER_CDECL __rtsan_disable(void);
+
+// Re-enable all RTSan error reporting.
+// Must follow a call to `__rtsan_disable`.
+void SANITIZER_CDECL __rtsan_enable(void);
+
+#ifdef __cplusplus
+} // extern "C"
+
+namespace __rtsan {
+#if defined(__has_feature) && __has_feature(realtime_sanitizer)
+
+class ScopedDisabler {
+public:
+ ScopedDisabler() { __rtsan_disable(); }
+ ~ScopedDisabler() { __rtsan_enable(); }
+
+#if __cplusplus >= 201103L
+ ScopedDisabler(const ScopedDisabler &) = delete;
+ ScopedDisabler &operator=(const ScopedDisabler &) = delete;
+ ScopedDisabler(ScopedDisabler &&) = delete;
+ ScopedDisabler &operator=(ScopedDisabler &&) = delete;
+#else
+private:
+ ScopedDisabler(const ScopedDisabler &);
+ ScopedDisabler &operator=(const ScopedDisabler &);
+#endif // __cplusplus >= 201103L
+};
+
+#else
+
+class ScopedDisabler {
+public:
+ ScopedDisabler() {}
+#if __cplusplus >= 201103L
+ ScopedDisabler(const ScopedDisabler &) = delete;
+ ScopedDisabler &operator=(const ScopedDisabler &) = delete;
+ ScopedDisabler(ScopedDisabler &&) = delete;
+ ScopedDisabler &operator=(ScopedDisabler &&) = delete;
+#else
+private:
+ ScopedDisabler(const ScopedDisabler &);
+ ScopedDisabler &operator=(const ScopedDisabler &);
+#endif // __cplusplus >= 201103L
+};
+
+#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
+} // namespace __rtsan
+#endif // __cplusplus
+
+#endif // SANITIZER_RTSAN_INTERFACE_H
@@ -13,6 +13,8 @@
#ifndef SANITIZER_UBSAN_INTERFACE_H
#define SANITIZER_UBSAN_INTERFACE_H
+#include <sanitizer/common_interface_defs.h>
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -25,8 +25,19 @@
// These typedefs should be used only in the interceptor definitions to replace
// the standard system types (e.g. SSIZE_T instead of ssize_t)
-typedef __sanitizer::uptr SIZE_T;
-typedef __sanitizer::sptr SSIZE_T;
+// On Windows the system headers (basetsd.h) provide a conflicting definition
+// of SIZE_T/SSIZE_T that do not match the real size_t/ssize_t for 32-bit
+// systems (using long instead of the expected int). Work around the typedef
+// redefinition by #defining SIZE_T instead of using a typedef.
+// TODO: We should be using __sanitizer::usize (and a new ssize) instead of
+// these new macros as long as we ensure they match the real system definitions.
+#if SANITIZER_WINDOWS
+// Ensure that (S)SIZE_T were already defined as we are about to override them.
+# include <basetsd.h>
+#endif
+
+#define SIZE_T __sanitizer::usize
+#define SSIZE_T __sanitizer::sptr
typedef __sanitizer::sptr PTRDIFF_T;
typedef __sanitizer::s64 INTMAX_T;
typedef __sanitizer::u64 UINTMAX_T;
@@ -338,16 +349,20 @@ const interpose_substitution substitution_##func_name[] \
#endif
// ISO C++ forbids casting between pointer-to-function and pointer-to-object,
-// so we use casting via an integral type __interception::uptr,
-// assuming that system is POSIX-compliant. Using other hacks seem
-// challenging, as we don't even pass function type to
-// INTERCEPT_FUNCTION macro, only its name.
+// so we use casts via uintptr_t (the local __sanitizer::uptr equivalent).
namespace __interception {
-#if defined(_WIN64)
-typedef unsigned long long uptr;
+
+#if defined(__ELF__) && !SANITIZER_FUCHSIA
+// The use of interceptors makes many sanitizers unusable for static linking.
+// Define a function, if called, will cause a linker error (undefined _DYNAMIC).
+// However, -static-pie (which is not common) cannot be detected at link time.
+extern uptr kDynamic[] asm("_DYNAMIC");
+inline void DoesNotSupportStaticLinking() {
+ [[maybe_unused]] volatile auto x = &kDynamic;
+}
#else
-typedef unsigned long uptr;
-#endif // _WIN64
+inline void DoesNotSupportStaticLinking() {}
+#endif
} // namespace __interception
#define INCLUDED_FROM_INTERCEPTION_LIB
@@ -28,12 +28,14 @@ bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real,
uptr func, uptr trampoline);
} // namespace __interception
-#define INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) \
- ::__interception::InterceptFunction( \
- #func, \
- (::__interception::uptr *)&REAL(func), \
- (::__interception::uptr)&(func), \
- (::__interception::uptr)&TRAMPOLINE(func))
+// Cast func to type of REAL(func) before casting to uptr in case it is an
+// overloaded function, which is the case for some glibc functions when
+// _FORTIFY_SOURCE is used. This disambiguates which overload to use.
+#define INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) \
+ ::__interception::InterceptFunction( \
+ #func, (::__interception::uptr *)&REAL(func), \
+ (::__interception::uptr)(decltype(REAL(func)))&(func), \
+ (::__interception::uptr) &TRAMPOLINE(func))
// dlvsym is a GNU extension supported by some other platforms.
#if SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD
@@ -41,7 +43,7 @@ bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real,
::__interception::InterceptFunction( \
#func, symver, \
(::__interception::uptr *)&REAL(func), \
- (::__interception::uptr)&(func), \
+ (::__interception::uptr)(decltype(REAL(func)))&(func), \
(::__interception::uptr)&TRAMPOLINE(func))
#else
#define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \
@@ -12,28 +12,35 @@
//===----------------------------------------------------------------------===//
#include "interception.h"
+#include "sanitizer_common/sanitizer_type_traits.h"
-#if SANITIZER_LINUX || SANITIZER_APPLE
-
-#include <sys/types.h>
+#if __has_include(<sys/types.h>)
+# include <sys/types.h>
+#endif
#include <stddef.h>
#include <stdint.h>
-COMPILER_CHECK(sizeof(::SIZE_T) == sizeof(size_t));
-COMPILER_CHECK(sizeof(::SSIZE_T) == sizeof(ssize_t));
-COMPILER_CHECK(sizeof(::PTRDIFF_T) == sizeof(ptrdiff_t));
+COMPILER_CHECK((__sanitizer::is_same<__sanitizer::uptr, ::uintptr_t>::value));
+COMPILER_CHECK((__sanitizer::is_same<__sanitizer::sptr, ::intptr_t>::value));
+COMPILER_CHECK((__sanitizer::is_same<__sanitizer::usize, ::size_t>::value));
+COMPILER_CHECK((__sanitizer::is_same<::PTRDIFF_T, ::ptrdiff_t>::value));
+COMPILER_CHECK((__sanitizer::is_same<::SIZE_T, ::size_t>::value));
+#if !SANITIZER_WINDOWS
+// No ssize_t on Windows.
+COMPILER_CHECK((__sanitizer::is_same<::SSIZE_T, ::ssize_t>::value));
+#endif
+// TODO: These are not actually the same type on Linux (long vs long long)
COMPILER_CHECK(sizeof(::INTMAX_T) == sizeof(intmax_t));
+COMPILER_CHECK(sizeof(::UINTMAX_T) == sizeof(uintmax_t));
-# if SANITIZER_GLIBC || SANITIZER_ANDROID
+#if SANITIZER_GLIBC || SANITIZER_ANDROID
COMPILER_CHECK(sizeof(::OFF64_T) == sizeof(off64_t));
-# endif
+#endif
// The following are the cases when pread (and friends) is used instead of
// pread64. In those cases we need OFF_T to match off_t. We don't care about the
// rest (they depend on _FILE_OFFSET_BITS setting when building an application).
-# if SANITIZER_ANDROID || !defined _FILE_OFFSET_BITS || \
- _FILE_OFFSET_BITS != 64
+#if !SANITIZER_WINDOWS && (SANITIZER_ANDROID || !defined _FILE_OFFSET_BITS || \
+ _FILE_OFFSET_BITS != 64)
COMPILER_CHECK(sizeof(::OFF_T) == sizeof(off_t));
-# endif
-
#endif
@@ -27,7 +27,7 @@
//
// 1) Detour
//
-// The Detour hooking technique is assuming the presence of an header with
+// The Detour hooking technique is assuming the presence of a header with
// padding and an overridable 2-bytes nop instruction (mov edi, edi). The
// nop instruction can safely be replaced by a 2-bytes jump without any need
// to save the instruction. A jump to the target is encoded in the function
@@ -47,7 +47,7 @@
//
// func: jmp <label> --> func: jmp <hook>
//
-// On an 64-bit architecture, a trampoline is inserted.
+// On a 64-bit architecture, a trampoline is inserted.
//
// func: jmp <label> --> func: jmp <tramp>
// [...]
@@ -60,7 +60,7 @@
//
// 3) HotPatch
//
-// The HotPatch hooking is assuming the presence of an header with padding
+// The HotPatch hooking is assuming the presence of a header with padding
// and a first instruction with at least 2-bytes.
//
// The reason to enforce the 2-bytes limitation is to provide the minimal
@@ -80,7 +80,7 @@
// real: <instr>
// jmp <body>
//
-// On an 64-bit architecture:
+// On a 64-bit architecture:
//
// head: 6 x nop head: jmp QWORD [addr1]
// func: <instr> --> func: jmp short <head>
@@ -110,7 +110,7 @@
// <instr>
// jmp <body>
//
-// On an 64-bit architecture:
+// On a 64-bit architecture:
//
// func: <instr> --> func: jmp QWORD [addr1]
// <instr>
@@ -130,6 +130,7 @@
#include "sanitizer_common/sanitizer_platform.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
+#include <psapi.h>
namespace __interception {
@@ -339,7 +340,7 @@ struct TrampolineMemoryRegion {
uptr max_size;
};
-UNUSED static const uptr kTrampolineScanLimitRange = 1 << 31; // 2 gig
+UNUSED static const uptr kTrampolineScanLimitRange = 1ull << 31; // 2 gig
static const int kMaxTrampolineRegion = 1024;
static TrampolineMemoryRegion TrampolineRegions[kMaxTrampolineRegion];
@@ -385,7 +386,30 @@ void TestOnlyReleaseTrampolineRegions() {
}
}
-static uptr AllocateMemoryForTrampoline(uptr image_address, size_t size) {
+static uptr AllocateMemoryForTrampoline(uptr func_address, size_t size) {
+ uptr image_address = func_address;
+
+#if SANITIZER_WINDOWS64
+ // Allocate memory after the module (DLL or EXE file), but within 2GB
+ // of the start of the module so that any address within the module can be
+ // referenced with PC-relative operands.
+ // This allows us to not just jump to the trampoline with a PC-relative
+ // offset, but to relocate any instructions that we copy to the trampoline
+ // which have references to the original module. If we can't find the base
+ // address of the module (e.g. if func_address is in mmap'ed memory), just
+ // use func_address as is.
+ HMODULE module;
+ if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ (LPCWSTR)func_address, &module)) {
+ MODULEINFO module_info;
+ if (::GetModuleInformation(::GetCurrentProcess(), module,
+ &module_info, sizeof(module_info))) {
+ image_address = (uptr)module_info.lpBaseOfDll;
+ }
+ }
+#endif
+
// Find a region within 2G with enough space to allocate |size| bytes.
TrampolineMemoryRegion *region = nullptr;
for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) {
@@ -431,7 +455,8 @@ static uptr AllocateMemoryForTrampoline(uptr image_address, size_t size) {
// The following prologues cannot be patched because of the short jump
// jumping to the patching region.
-#if SANITIZER_WINDOWS64
+// Short jump patterns below are only for x86_64.
+# if SANITIZER_WINDOWS_x64
// ntdll!wcslen in Win11
// 488bc1 mov rax,rcx
// 0fb710 movzx edx,word ptr [rax]
@@ -462,7 +487,7 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
return 4;
#endif
-#if SANITIZER_WINDOWS64
+# if SANITIZER_WINDOWS_x64
if (memcmp((u8*)address, kPrologueWithShortJump1,
sizeof(kPrologueWithShortJump1)) == 0 ||
memcmp((u8*)address, kPrologueWithShortJump2,
@@ -478,6 +503,8 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
switch (*(u8*)address) {
case 0x90: // 90 : nop
+ case 0xC3: // C3 : ret (for small/empty function interception
+ case 0xCC: // CC : int 3 i.e. registering weak functions)
return 1;
case 0x50: // push eax / rax
@@ -494,6 +521,11 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
case 0x6A: // 6A XX = push XX
return 2;
+ // This instruction can be encoded with a 16-bit immediate but that is
+ // incredibly unlikely.
+ case 0x68: // 68 XX XX XX XX : push imm32
+ return 5;
+
case 0xb8: // b8 XX XX XX XX : mov eax, XX XX XX XX
case 0xB9: // b9 XX XX XX XX : mov ecx, XX XX XX XX
return 5;
@@ -501,7 +533,6 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
// Cannot overwrite control-instruction. Return 0 to indicate failure.
case 0xE9: // E9 XX XX XX XX : jmp <label>
case 0xE8: // E8 XX XX XX XX : call <func>
- case 0xC3: // C3 : ret
case 0xEB: // EB XX : jmp XX (short jump)
case 0x70: // 7Y YY : jy XX (short conditional jump)
case 0x71:
@@ -532,6 +563,9 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
case 0xC033: // 33 C0 : xor eax, eax
case 0xC933: // 33 C9 : xor ecx, ecx
case 0xD233: // 33 D2 : xor edx, edx
+ case 0xDB84: // 84 DB : test bl,bl
+ case 0xC984: // 84 C9 : test cl,cl
+ case 0xD284: // 84 D2 : test dl,dl
return 2;
// Cannot overwrite control-instruction. Return 0 to indicate failure.
@@ -540,15 +574,38 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
}
switch (0x00FFFFFF & *(u32*)address) {
+ case 0xF8E483: // 83 E4 F8 : and esp, 0xFFFFFFF8
+ case 0x64EC83: // 83 EC 64 : sub esp, 64h
+ return 3;
case 0x24A48D: // 8D A4 24 XX XX XX XX : lea esp, [esp + XX XX XX XX]
return 7;
}
-#if SANITIZER_WINDOWS64
+ switch (0x000000FF & *(u32 *)address) {
+ case 0xc2: // C2 XX XX : ret XX (needed for registering weak functions)
+ return 3;
+ }
+
+# if SANITIZER_WINDOWS_x64
switch (*(u8*)address) {
case 0xA1: // A1 XX XX XX XX XX XX XX XX :
// movabs eax, dword ptr ds:[XXXXXXXX]
return 9;
+ case 0xF2:
+ switch (*(u32 *)(address + 1)) {
+ case 0x2444110f: // f2 0f 11 44 24 XX movsd QWORD PTR
+ // [rsp + XX], xmm0
+ case 0x244c110f: // f2 0f 11 4c 24 XX movsd QWORD PTR
+ // [rsp + XX], xmm1
+ case 0x2454110f: // f2 0f 11 54 24 XX movsd QWORD PTR
+ // [rsp + XX], xmm2
+ case 0x245c110f: // f2 0f 11 5c 24 XX movsd QWORD PTR
+ // [rsp + XX], xmm3
+ case 0x2464110f: // f2 0f 11 64 24 XX movsd QWORD PTR
+ // [rsp + XX], xmm4
+ return 6;
+ }
+ break;
case 0x83:
const u8 next_byte = *(u8*)(address + 1);
@@ -577,51 +634,126 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
case 0x018a: // mov al, byte ptr [rcx]
return 2;
+ case 0x058A: // 8A 05 XX XX XX XX : mov al, byte ptr [XX XX XX XX]
+ case 0x7E80: // 80 7E YY XX cmp BYTE PTR [rsi+YY], XX
+ case 0x7D80: // 80 7D YY XX cmp BYTE PTR [rbp+YY], XX
+ case 0x7A80: // 80 7A YY XX cmp BYTE PTR [rdx+YY], XX
+ case 0x7880: // 80 78 YY XX cmp BYTE PTR [rax+YY], XX
+ case 0x7B80: // 80 7B YY XX cmp BYTE PTR [rbx+YY], XX
+ case 0x7980: // 80 79 YY XX cmp BYTE ptr [rcx+YY], XX
+ return 4;
+
case 0x058B: // 8B 05 XX XX XX XX : mov eax, dword ptr [XX XX XX XX]
if (rel_offset)
*rel_offset = 2;
return 6;
+
+ case 0x7E81: // 81 7E YY XX XX XX XX cmp DWORD PTR [rsi+YY], XX XX XX XX
+ case 0x7D81: // 81 7D YY XX XX XX XX cmp DWORD PTR [rbp+YY], XX XX XX XX
+ case 0x7A81: // 81 7A YY XX XX XX XX cmp DWORD PTR [rdx+YY], XX XX XX XX
+ case 0x7881: // 81 78 YY XX XX XX XX cmp DWORD PTR [rax+YY], XX XX XX XX
+ case 0x7B81: // 81 7B YY XX XX XX XX cmp DWORD PTR [rbx+YY], XX XX XX XX
+ case 0x7981: // 81 79 YY XX XX XX XX cmp dword ptr [rcx+YY], XX XX XX XX
+ return 7;
}
switch (0x00FFFFFF & *(u32*)address) {
- case 0xe58948: // 48 8b c4 : mov rbp, rsp
- case 0xc18b48: // 48 8b c1 : mov rax, rcx
- case 0xc48b48: // 48 8b c4 : mov rax, rsp
- case 0xd9f748: // 48 f7 d9 : neg rcx
- case 0xd12b48: // 48 2b d1 : sub rdx, rcx
case 0x07c1f6: // f6 c1 07 : test cl, 0x7
- case 0xc98548: // 48 85 C9 : test rcx, rcx
- case 0xd28548: // 48 85 d2 : test rdx, rdx
+ case 0x10b70f: // 0f b7 10 : movzx edx, WORD PTR [rax]
+ case 0xc00b4d: // 4d 0b c0 : or r8, r8
+ case 0xc03345: // 45 33 c0 : xor r8d, r8d
+ case 0xc08548: // 48 85 c0 : test rax, rax
case 0xc0854d: // 4d 85 c0 : test r8, r8
+ case 0xc08b41: // 41 8b c0 : mov eax, r8d
+ case 0xc0ff48: // 48 ff c0 : inc rax
+ case 0xc0ff49: // 49 ff c0 : inc r8
+ case 0xc18b41: // 41 8b c1 : mov eax, r9d
+ case 0xc18b48: // 48 8b c1 : mov rax, rcx
+ case 0xc18b4c: // 4c 8b c1 : mov r8, rcx
+ case 0xc1ff48: // 48 ff c1 : inc rcx
+ case 0xc1ff49: // 49 ff c1 : inc r9
+ case 0xc28b41: // 41 8b c2 : mov eax, r10d
case 0xc2b60f: // 0f b6 c2 : movzx eax, dl
- case 0xc03345: // 45 33 c0 : xor r8d, r8d
+ case 0xc2ff48: // 48 ff c2 : inc rdx
+ case 0xc2ff49: // 49 ff c2 : inc r10
+ case 0xc38b41: // 41 8b c3 : mov eax, r11d
+ case 0xc3ff48: // 48 ff c3 : inc rbx
+ case 0xc3ff49: // 49 ff c3 : inc r11
+ case 0xc48b41: // 41 8b c4 : mov eax, r12d
+ case 0xc48b48: // 48 8b c4 : mov rax, rsp
+ case 0xc4ff49: // 49 ff c4 : inc r12
+ case 0xc5ff49: // 49 ff c5 : inc r13
+ case 0xc6ff48: // 48 ff c6 : inc rsi
+ case 0xc6ff49: // 49 ff c6 : inc r14
+ case 0xc7ff48: // 48 ff c7 : inc rdi
+ case 0xc7ff49: // 49 ff c7 : inc r15
case 0xc93345: // 45 33 c9 : xor r9d, r9d
- case 0xdb3345: // 45 33 DB : xor r11d, r11d
- case 0xd98b4c: // 4c 8b d9 : mov r11, rcx
- case 0xd28b4c: // 4c 8b d2 : mov r10, rdx
- case 0xc98b4c: // 4C 8B C9 : mov r9, rcx
- case 0xc18b4c: // 4C 8B C1 : mov r8, rcx
- case 0xd2b60f: // 0f b6 d2 : movzx edx, dl
+ case 0xc98548: // 48 85 c9 : test rcx, rcx
+ case 0xc9854d: // 4d 85 c9 : test r9, r9
+ case 0xc98b4c: // 4c 8b c9 : mov r9, rcx
case 0xca2b48: // 48 2b ca : sub rcx, rdx
- case 0x10b70f: // 0f b7 10 : movzx edx, WORD PTR [rax]
- case 0xc00b4d: // 3d 0b c0 : or r8, r8
- case 0xc08b41: // 41 8b c0 : mov eax, r8d
+ case 0xca3b48: // 48 3b ca : cmp rcx, rdx
+ case 0xd12b48: // 48 2b d1 : sub rdx, rcx
case 0xd18b48: // 48 8b d1 : mov rdx, rcx
- case 0xdc8b4c: // 4c 8b dc : mov r11, rsp
case 0xd18b4c: // 4c 8b d1 : mov r10, rcx
- case 0xE0E483: // 83 E4 E0 : and esp, 0xFFFFFFE0
+ case 0xd28548: // 48 85 d2 : test rdx, rdx
+ case 0xd2854d: // 4d 85 d2 : test r10, r10
+ case 0xd28b4c: // 4c 8b d2 : mov r10, rdx
+ case 0xd2b60f: // 0f b6 d2 : movzx edx, dl
+ case 0xd98b4c: // 4c 8b d9 : mov r11, rcx
+ case 0xd9f748: // 48 f7 d9 : neg rcx
+ case 0xdb3345: // 45 33 db : xor r11d, r11d
+ case 0xdb8548: // 48 85 db : test rbx, rbx
+ case 0xdb854d: // 4d 85 db : test r11, r11
+ case 0xdc8b4c: // 4c 8b dc : mov r11, rsp
+ case 0xe0e483: // 83 e4 e0 : and esp, 0xFFFFFFE0
+ case 0xe48548: // 48 85 e4 : test rsp, rsp
+ case 0xe4854d: // 4d 85 e4 : test r12, r12
+ case 0xe58948: // 48 89 e5 : mov rbp, rsp
+ case 0xed8548: // 48 85 ed : test rbp, rbp
+ case 0xed854d: // 4d 85 ed : test r13, r13
+ case 0xf6854d: // 4d 85 f6 : test r14, r14
+ case 0xff854d: // 4d 85 ff : test r15, r15
return 3;
+ case 0x245489: // 89 54 24 XX : mov DWORD PTR[rsp + XX], edx
+ case 0x428d44: // 44 8d 42 XX : lea r8d , [rdx + XX]
+ case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx
case 0xec8348: // 48 83 ec XX : sub rsp, XX
case 0xf88349: // 49 83 f8 XX : cmp r8, XX
- case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx
return 4;
+ case 0x246483: // 83 64 24 XX YY : and DWORD PTR [rsp+XX], YY
+ return 5;
+
+ case 0x788166: // 66 81 78 XX YY YY cmp WORD PTR [rax+XX], YY YY
+ case 0x798166: // 66 81 79 XX YY YY cmp WORD PTR [rcx+XX], YY YY
+ case 0x7a8166: // 66 81 7a XX YY YY cmp WORD PTR [rdx+XX], YY YY
+ case 0x7b8166: // 66 81 7b XX YY YY cmp WORD PTR [rbx+XX], YY YY
+ case 0x7e8166: // 66 81 7e XX YY YY cmp WORD PTR [rsi+XX], YY YY
+ case 0x7f8166: // 66 81 7f XX YY YY cmp WORD PTR [rdi+XX], YY YY
+ return 6;
+
case 0xec8148: // 48 81 EC XX XX XX XX : sub rsp, XXXXXXXX
return 7;
+ // clang-format off
+ case 0x788141: // 41 81 78 XX YY YY YY YY : cmp DWORD PTR [r8+YY], XX XX XX XX
+ case 0x798141: // 41 81 79 XX YY YY YY YY : cmp DWORD PTR [r9+YY], XX XX XX XX
+ case 0x7a8141: // 41 81 7a XX YY YY YY YY : cmp DWORD PTR [r10+YY], XX XX XX XX
+ case 0x7b8141: // 41 81 7b XX YY YY YY YY : cmp DWORD PTR [r11+YY], XX XX XX XX
+ case 0x7c8141: // 41 81 7c XX YY YY YY YY : cmp DWORD PTR [r12+YY], XX XX XX XX
+ case 0x7d8141: // 41 81 7d XX YY YY YY YY : cmp DWORD PTR [r13+YY], XX XX XX XX
+ case 0x7e8141: // 41 81 7e XX YY YY YY YY : cmp DWORD PTR [r14+YY], XX XX XX XX
+ case 0x7f8141: // 41 81 7f YY XX XX XX XX : cmp DWORD PTR [r15+YY], XX XX XX XX
+ case 0x247c81: // 81 7c 24 YY XX XX XX XX : cmp DWORD PTR [rsp+YY], XX XX XX XX
+ return 8;
+ // clang-format on
+
case 0x058b48: // 48 8b 05 XX XX XX XX :
// mov rax, QWORD PTR [rip + XXXXXXXX]
+ case 0x058d48: // 48 8d 05 XX XX XX XX :
+ // lea rax, QWORD PTR [rip + XXXXXXXX]
case 0x25ff48: // 48 ff 25 XX XX XX XX :
// rex.W jmp QWORD PTR [rip + XXXXXXXX]
case 0x158D4C: // 4c 8d 15 XX XX XX XX : lea r10, [rip + XX]
@@ -636,6 +768,8 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
}
switch (*(u32*)(address)) {
+ case 0x1ab60f44: // 44 0f b6 1a : movzx r11d, BYTE PTR [rdx]
+ return 4;
case 0x24448b48: // 48 8b 44 24 XX : mov rax, QWORD ptr [rsp + XX]
case 0x246c8948: // 48 89 6C 24 XX : mov QWORD ptr [rsp + XX], rbp
case 0x245c8948: // 48 89 5c 24 XX : mov QWORD PTR [rsp + XX], rbx
@@ -645,8 +779,11 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
case 0x24548948: // 48 89 54 24 XX : mov QWORD PTR [rsp + XX], rdx
case 0x244c894c: // 4c 89 4c 24 XX : mov QWORD PTR [rsp + XX], r9
case 0x2444894c: // 4c 89 44 24 XX : mov QWORD PTR [rsp + XX], r8
+ case 0x244c8944: // 44 89 4c 24 XX mov DWORD PTR [rsp + XX], r9d
+ case 0x24448944: // 44 89 44 24 XX mov DWORD PTR [rsp + XX], r8d
+ case 0x246c8d48: // 48 8d 6c 24 XX : lea rbp, [rsp + XX]
return 5;
- case 0x24648348: // 48 83 64 24 XX : and QWORD PTR [rsp + XX], YY
+ case 0x24648348: // 48 83 64 24 XX YY : and QWORD PTR [rsp + XX], YY
return 6;
}
@@ -660,6 +797,7 @@ static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
case 0x458B: // 8B 45 XX : mov eax, dword ptr [ebp + XX]
case 0x5D8B: // 8B 5D XX : mov ebx, dword ptr [ebp + XX]
case 0x7D8B: // 8B 7D XX : mov edi, dword ptr [ebp + XX]
+ case 0x758B: // 8B 75 XX : mov esi, dword ptr [ebp + XX]
case 0xEC83: // 83 EC XX : sub esp, XX
case 0x75FF: // FF 75 XX : push dword ptr [ebp + XX]
return 3;
@@ -943,19 +1081,26 @@ bool OverrideFunction(
static void **InterestingDLLsAvailable() {
static const char *InterestingDLLs[] = {
- "kernel32.dll",
- "msvcr100.dll", // VS2010
- "msvcr110.dll", // VS2012
- "msvcr120.dll", // VS2013
- "vcruntime140.dll", // VS2015
- "ucrtbase.dll", // Universal CRT
-#if (defined(__MINGW32__) && defined(__i386__))
- "libc++.dll", // libc++
- "libunwind.dll", // libunwind
-#endif
- // NTDLL should go last as it exports some functions that we should
- // override in the CRT [presumably only used internally].
- "ntdll.dll", NULL};
+ "kernel32.dll",
+ "msvcr100d.dll", // VS2010
+ "msvcr110d.dll", // VS2012
+ "msvcr120d.dll", // VS2013
+ "vcruntime140d.dll", // VS2015
+ "ucrtbased.dll", // Universal CRT
+ "msvcr100.dll", // VS2010
+ "msvcr110.dll", // VS2012
+ "msvcr120.dll", // VS2013
+ "vcruntime140.dll", // VS2015
+ "ucrtbase.dll", // Universal CRT
+# if (defined(__MINGW32__) && defined(__i386__))
+ "libc++.dll", // libc++
+ "libunwind.dll", // libunwind
+# endif
+ // NTDLL should go last as it exports some functions that we should
+ // override in the CRT [presumably only used internally].
+ "ntdll.dll",
+ NULL
+ };
static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 };
if (!result[0]) {
for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) {
@@ -1126,4 +1271,4 @@ bool OverrideImportedFunction(const char *module_to_patch,
} // namespace __interception
-#endif // SANITIZER_APPLE
+#endif // SANITIZER_WINDOWS
@@ -92,15 +92,16 @@ extern "C" void __lsan_init() {
CacheBinaryName();
AvoidCVE_2016_2143();
InitializeFlags();
+ InitializePlatformEarly();
InitCommonLsan();
InitializeAllocator();
ReplaceSystemMalloc();
- InitTlsSize();
InitializeInterceptors();
InitializeThreads();
InstallDeadlySignalHandlers(LsanOnDeadlySignal);
InitializeMainThread();
InstallAtExitCheckLeaks();
+ InstallAtForkHandler();
InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
@@ -40,6 +40,7 @@ void InitializeInterceptors();
void ReplaceSystemMalloc();
void LsanOnDeadlySignal(int signo, void *siginfo, void *context);
void InstallAtExitCheckLeaks();
+void InstallAtForkHandler();
#define ENSURE_LSAN_INITED \
do { \
@@ -31,7 +31,7 @@ static const uptr kMaxAllowedMallocSize = 1ULL << 30;
#elif defined(__mips64) || defined(__aarch64__)
static const uptr kMaxAllowedMallocSize = 4ULL << 30;
#else
-static const uptr kMaxAllowedMallocSize = 8ULL << 30;
+static const uptr kMaxAllowedMallocSize = 1ULL << 40;
#endif
static Allocator allocator;
@@ -42,6 +42,9 @@ namespace __lsan {
// also to protect the global list of root regions.
static Mutex global_mutex;
+void LockGlobal() SANITIZER_ACQUIRE(global_mutex) { global_mutex.Lock(); }
+void UnlockGlobal() SANITIZER_RELEASE(global_mutex) { global_mutex.Unlock(); }
+
Flags lsan_flags;
void DisableCounterUnderflow() {
@@ -105,7 +108,7 @@ class LeakSuppressionContext {
void PrintMatchedSuppressions();
};
-ALIGNED(64) static char suppression_placeholder[sizeof(LeakSuppressionContext)];
+alignas(64) static char suppression_placeholder[sizeof(LeakSuppressionContext)];
static LeakSuppressionContext *suppression_ctx = nullptr;
static const char kSuppressionLeak[] = "leak";
static const char *kSuppressionTypes[] = {kSuppressionLeak};
@@ -152,14 +155,15 @@ Suppression *LeakSuppressionContext::GetSuppressionForAddr(uptr addr) {
return s;
// Suppress by file or function name.
- SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr);
- for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
+ SymbolizedStackHolder symbolized_stack(
+ Symbolizer::GetOrInit()->SymbolizePC(addr));
+ const SymbolizedStack *frames = symbolized_stack.get();
+ for (const SymbolizedStack *cur = frames; cur; cur = cur->next) {
if (context.Match(cur->info.function, kSuppressionLeak, &s) ||
context.Match(cur->info.file, kSuppressionLeak, &s)) {
break;
}
}
- frames->ClearAll();
return s;
}
@@ -284,23 +288,54 @@ static inline bool MaybeUserPointer(uptr p) {
# endif
}
+namespace {
+struct DirectMemoryAccessor {
+ void Init(uptr begin, uptr end) {};
+ void *LoadPtr(uptr p) const { return *reinterpret_cast<void **>(p); }
+};
+
+struct CopyMemoryAccessor {
+ void Init(uptr begin, uptr end) {
+ this->begin = begin;
+ buffer.clear();
+ buffer.resize(end - begin);
+ MemCpyAccessible(buffer.data(), reinterpret_cast<void *>(begin),
+ buffer.size());
+ };
+
+ void *LoadPtr(uptr p) const {
+ uptr offset = p - begin;
+ CHECK_LE(offset + sizeof(void *), reinterpret_cast<uptr>(buffer.size()));
+ return *reinterpret_cast<void **>(offset +
+ reinterpret_cast<uptr>(buffer.data()));
+ }
+
+ private:
+ uptr begin;
+ InternalMmapVector<char> buffer;
+};
+} // namespace
+
// Scans the memory range, looking for byte patterns that point into allocator
// chunks. Marks those chunks with |tag| and adds them to |frontier|.
// There are two usage modes for this function: finding reachable chunks
// (|tag| = kReachable) and finding indirectly leaked chunks
// (|tag| = kIndirectlyLeaked). In the second case, there's no flood fill,
// so |frontier| = 0.
-void ScanRangeForPointers(uptr begin, uptr end, Frontier *frontier,
- const char *region_type, ChunkTag tag) {
+template <class Accessor>
+void ScanForPointers(uptr begin, uptr end, Frontier *frontier,
+ const char *region_type, ChunkTag tag,
+ Accessor &accessor) {
CHECK(tag == kReachable || tag == kIndirectlyLeaked);
const uptr alignment = flags()->pointer_alignment();
LOG_POINTERS("Scanning %s range %p-%p.\n", region_type, (void *)begin,
(void *)end);
+ accessor.Init(begin, end);
uptr pp = begin;
if (pp % alignment)
pp = pp + alignment - pp % alignment;
for (; pp + sizeof(void *) <= end; pp += alignment) {
- void *p = *reinterpret_cast<void **>(pp);
+ void *p = accessor.LoadPtr(pp);
# if SANITIZER_APPLE
p = TransformPointer(p);
# endif
@@ -335,6 +370,12 @@ void ScanRangeForPointers(uptr begin, uptr end, Frontier *frontier,
}
}
+void ScanRangeForPointers(uptr begin, uptr end, Frontier *frontier,
+ const char *region_type, ChunkTag tag) {
+ DirectMemoryAccessor accessor;
+ ScanForPointers(begin, end, frontier, region_type, tag, accessor);
+}
+
// Scans a global range for pointers
void ScanGlobalRange(uptr begin, uptr end, Frontier *frontier) {
uptr allocator_begin = 0, allocator_end = 0;
@@ -352,14 +393,21 @@ void ScanGlobalRange(uptr begin, uptr end, Frontier *frontier) {
}
}
-void ScanExtraStackRanges(const InternalMmapVector<Range> &ranges,
- Frontier *frontier) {
+template <class Accessor>
+void ScanRanges(const InternalMmapVector<Range> &ranges, Frontier *frontier,
+ const char *region_type, Accessor &accessor) {
for (uptr i = 0; i < ranges.size(); i++) {
- ScanRangeForPointers(ranges[i].begin, ranges[i].end, frontier, "FAKE STACK",
- kReachable);
+ ScanForPointers(ranges[i].begin, ranges[i].end, frontier, region_type,
+ kReachable, accessor);
}
}
+void ScanExtraStackRanges(const InternalMmapVector<Range> &ranges,
+ Frontier *frontier) {
+ DirectMemoryAccessor accessor;
+ ScanRanges(ranges, frontier, "FAKE STACK", accessor);
+}
+
# if SANITIZER_FUCHSIA
// Fuchsia handles all threads together with its own callback.
@@ -395,26 +443,129 @@ static void ProcessThreadRegistry(Frontier *frontier) {
}
// Scans thread data (stacks and TLS) for heap pointers.
+template <class Accessor>
+static void ProcessThread(tid_t os_id, uptr sp,
+ const InternalMmapVector<uptr> ®isters,
+ InternalMmapVector<Range> &extra_ranges,
+ Frontier *frontier, Accessor &accessor) {
+ // `extra_ranges` is outside of the function and the loop to reused mapped
+ // memory.
+ CHECK(extra_ranges.empty());
+ LOG_THREADS("Processing thread %llu.\n", os_id);
+ uptr stack_begin, stack_end, tls_begin, tls_end, cache_begin, cache_end;
+ DTLS *dtls;
+ bool thread_found =
+ GetThreadRangesLocked(os_id, &stack_begin, &stack_end, &tls_begin,
+ &tls_end, &cache_begin, &cache_end, &dtls);
+ if (!thread_found) {
+ // If a thread can't be found in the thread registry, it's probably in the
+ // process of destruction. Log this event and move on.
+ LOG_THREADS("Thread %llu not found in registry.\n", os_id);
+ return;
+ }
+
+ if (!sp)
+ sp = stack_begin;
+
+ if (flags()->use_registers) {
+ uptr registers_begin = reinterpret_cast<uptr>(registers.data());
+ uptr registers_end =
+ reinterpret_cast<uptr>(registers.data() + registers.size());
+ ScanForPointers(registers_begin, registers_end, frontier, "REGISTERS",
+ kReachable, accessor);
+ }
+
+ if (flags()->use_stacks) {
+ LOG_THREADS("Stack at %p-%p (SP = %p).\n", (void *)stack_begin,
+ (void *)stack_end, (void *)sp);
+ if (sp < stack_begin || sp >= stack_end) {
+ // SP is outside the recorded stack range (e.g. the thread is running a
+ // signal handler on alternate stack, or swapcontext was used).
+ // Again, consider the entire stack range to be reachable.
+ LOG_THREADS("WARNING: stack pointer not in stack range.\n");
+ uptr page_size = GetPageSizeCached();
+ int skipped = 0;
+ while (stack_begin < stack_end &&
+ !IsAccessibleMemoryRange(stack_begin, 1)) {
+ skipped++;
+ stack_begin += page_size;
+ }
+ LOG_THREADS("Skipped %d guard page(s) to obtain stack %p-%p.\n", skipped,
+ (void *)stack_begin, (void *)stack_end);
+ } else {
+ // Shrink the stack range to ignore out-of-scope values.
+ stack_begin = sp;
+ }
+ ScanForPointers(stack_begin, stack_end, frontier, "STACK", kReachable,
+ accessor);
+ GetThreadExtraStackRangesLocked(os_id, &extra_ranges);
+ ScanRanges(extra_ranges, frontier, "FAKE STACK", accessor);
+ }
+
+ if (flags()->use_tls) {
+ if (tls_begin) {
+ LOG_THREADS("TLS at %p-%p.\n", (void *)tls_begin, (void *)tls_end);
+ // If the tls and cache ranges don't overlap, scan full tls range,
+ // otherwise, only scan the non-overlapping portions
+ if (cache_begin == cache_end || tls_end < cache_begin ||
+ tls_begin > cache_end) {
+ ScanForPointers(tls_begin, tls_end, frontier, "TLS", kReachable,
+ accessor);
+ } else {
+ if (tls_begin < cache_begin)
+ ScanForPointers(tls_begin, cache_begin, frontier, "TLS", kReachable,
+ accessor);
+ if (tls_end > cache_end)
+ ScanForPointers(cache_end, tls_end, frontier, "TLS", kReachable,
+ accessor);
+ }
+ }
+# if SANITIZER_ANDROID
+ extra_ranges.clear();
+ auto *cb = +[](void *dtls_begin, void *dtls_end, uptr /*dso_idd*/,
+ void *arg) -> void {
+ reinterpret_cast<InternalMmapVector<Range> *>(arg)->push_back(
+ {reinterpret_cast<uptr>(dtls_begin),
+ reinterpret_cast<uptr>(dtls_end)});
+ };
+ ScanRanges(extra_ranges, frontier, "DTLS", accessor);
+ // FIXME: There might be a race-condition here (and in Bionic) if the
+ // thread is suspended in the middle of updating its DTLS. IOWs, we
+ // could scan already freed memory. (probably fine for now)
+ __libc_iterate_dynamic_tls(os_id, cb, frontier);
+# else
+ if (dtls && !DTLSInDestruction(dtls)) {
+ ForEachDVT(dtls, [&](const DTLS::DTV &dtv, int id) {
+ uptr dtls_beg = dtv.beg;
+ uptr dtls_end = dtls_beg + dtv.size;
+ if (dtls_beg < dtls_end) {
+ LOG_THREADS("DTLS %d at %p-%p.\n", id, (void *)dtls_beg,
+ (void *)dtls_end);
+ ScanForPointers(dtls_beg, dtls_end, frontier, "DTLS", kReachable,
+ accessor);
+ }
+ });
+ } else {
+ // We are handling a thread with DTLS under destruction. Log about
+ // this and continue.
+ LOG_THREADS("Thread %llu has DTLS under destruction.\n", os_id);
+ }
+# endif
+ }
+}
+
static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
Frontier *frontier, tid_t caller_tid,
uptr caller_sp) {
+ InternalMmapVector<tid_t> done_threads;
InternalMmapVector<uptr> registers;
InternalMmapVector<Range> extra_ranges;
for (uptr i = 0; i < suspended_threads.ThreadCount(); i++) {
- tid_t os_id = static_cast<tid_t>(suspended_threads.GetThreadID(i));
- LOG_THREADS("Processing thread %llu.\n", os_id);
- uptr stack_begin, stack_end, tls_begin, tls_end, cache_begin, cache_end;
- DTLS *dtls;
- bool thread_found =
- GetThreadRangesLocked(os_id, &stack_begin, &stack_end, &tls_begin,
- &tls_end, &cache_begin, &cache_end, &dtls);
- if (!thread_found) {
- // If a thread can't be found in the thread registry, it's probably in the
- // process of destruction. Log this event and move on.
- LOG_THREADS("Thread %llu not found in registry.\n", os_id);
- continue;
- }
- uptr sp;
+ registers.clear();
+ extra_ranges.clear();
+
+ const tid_t os_id = suspended_threads.GetThreadID(i);
+ uptr sp = 0;
PtraceRegistersStatus have_registers =
suspended_threads.GetRegistersAndSP(i, ®isters, &sp);
if (have_registers != REGISTERS_AVAILABLE) {
@@ -423,96 +574,32 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
// GetRegistersAndSP failed with ESRCH.
if (have_registers == REGISTERS_UNAVAILABLE_FATAL)
continue;
- sp = stack_begin;
+ sp = 0;
}
- if (suspended_threads.GetThreadID(i) == caller_tid) {
+
+ if (os_id == caller_tid)
sp = caller_sp;
- }
- if (flags()->use_registers && have_registers) {
- uptr registers_begin = reinterpret_cast<uptr>(registers.data());
- uptr registers_end =
- reinterpret_cast<uptr>(registers.data() + registers.size());
- ScanRangeForPointers(registers_begin, registers_end, frontier,
- "REGISTERS", kReachable);
- }
+ DirectMemoryAccessor accessor;
+ ProcessThread(os_id, sp, registers, extra_ranges, frontier, accessor);
+ if (flags()->use_detached)
+ done_threads.push_back(os_id);
+ }
- if (flags()->use_stacks) {
- LOG_THREADS("Stack at %p-%p (SP = %p).\n", (void *)stack_begin,
- (void *)stack_end, (void *)sp);
- if (sp < stack_begin || sp >= stack_end) {
- // SP is outside the recorded stack range (e.g. the thread is running a
- // signal handler on alternate stack, or swapcontext was used).
- // Again, consider the entire stack range to be reachable.
- LOG_THREADS("WARNING: stack pointer not in stack range.\n");
- uptr page_size = GetPageSizeCached();
- int skipped = 0;
- while (stack_begin < stack_end &&
- !IsAccessibleMemoryRange(stack_begin, 1)) {
- skipped++;
- stack_begin += page_size;
- }
- LOG_THREADS("Skipped %d guard page(s) to obtain stack %p-%p.\n",
- skipped, (void *)stack_begin, (void *)stack_end);
- } else {
- // Shrink the stack range to ignore out-of-scope values.
- stack_begin = sp;
- }
- ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK",
- kReachable);
+ if (flags()->use_detached) {
+ CopyMemoryAccessor accessor;
+ InternalMmapVector<tid_t> known_threads;
+ GetRunningThreadsLocked(&known_threads);
+ Sort(done_threads.data(), done_threads.size());
+ for (tid_t os_id : known_threads) {
+ registers.clear();
extra_ranges.clear();
- GetThreadExtraStackRangesLocked(os_id, &extra_ranges);
- ScanExtraStackRanges(extra_ranges, frontier);
- }
- if (flags()->use_tls) {
- if (tls_begin) {
- LOG_THREADS("TLS at %p-%p.\n", (void *)tls_begin, (void *)tls_end);
- // If the tls and cache ranges don't overlap, scan full tls range,
- // otherwise, only scan the non-overlapping portions
- if (cache_begin == cache_end || tls_end < cache_begin ||
- tls_begin > cache_end) {
- ScanRangeForPointers(tls_begin, tls_end, frontier, "TLS", kReachable);
- } else {
- if (tls_begin < cache_begin)
- ScanRangeForPointers(tls_begin, cache_begin, frontier, "TLS",
- kReachable);
- if (tls_end > cache_end)
- ScanRangeForPointers(cache_end, tls_end, frontier, "TLS",
- kReachable);
- }
- }
-# if SANITIZER_ANDROID
- auto *cb = +[](void *dtls_begin, void *dtls_end, uptr /*dso_idd*/,
- void *arg) -> void {
- ScanRangeForPointers(reinterpret_cast<uptr>(dtls_begin),
- reinterpret_cast<uptr>(dtls_end),
- reinterpret_cast<Frontier *>(arg), "DTLS",
- kReachable);
- };
-
- // FIXME: There might be a race-condition here (and in Bionic) if the
- // thread is suspended in the middle of updating its DTLS. IOWs, we
- // could scan already freed memory. (probably fine for now)
- __libc_iterate_dynamic_tls(os_id, cb, frontier);
-# else
- if (dtls && !DTLSInDestruction(dtls)) {
- ForEachDVT(dtls, [&](const DTLS::DTV &dtv, int id) {
- uptr dtls_beg = dtv.beg;
- uptr dtls_end = dtls_beg + dtv.size;
- if (dtls_beg < dtls_end) {
- LOG_THREADS("DTLS %d at %p-%p.\n", id, (void *)dtls_beg,
- (void *)dtls_end);
- ScanRangeForPointers(dtls_beg, dtls_end, frontier, "DTLS",
- kReachable);
- }
- });
- } else {
- // We are handling a thread with DTLS under destruction. Log about
- // this and continue.
- LOG_THREADS("Thread %llu has DTLS under destruction.\n", os_id);
+ uptr i = InternalLowerBound(done_threads, os_id);
+ if (i >= done_threads.size() || done_threads[i] != os_id) {
+ uptr sp = (os_id == caller_tid) ? caller_sp : 0;
+ ProcessThread(os_id, sp, registers, extra_ranges, frontier, accessor);
}
-# endif
}
}
@@ -694,11 +781,13 @@ void LeakSuppressionContext::PrintMatchedSuppressions() {
// Fuchsia provides a libc interface that guarantees all threads are
// covered, and SuspendedThreadList is never really used.
-static void ReportUnsuspendedThreads(const SuspendedThreadsList &) {}
+static bool ReportUnsuspendedThreads(const SuspendedThreadsList &) {
+ return true;
+}
# else // !SANITIZER_FUCHSIA
-static void ReportUnsuspendedThreads(
+static bool ReportUnsuspendedThreads(
const SuspendedThreadsList &suspended_threads) {
InternalMmapVector<tid_t> threads(suspended_threads.ThreadCount());
for (uptr i = 0; i < suspended_threads.ThreadCount(); ++i)
@@ -706,16 +795,20 @@ static void ReportUnsuspendedThreads(
Sort(threads.data(), threads.size());
- InternalMmapVector<tid_t> unsuspended;
- GetRunningThreadsLocked(&unsuspended);
+ InternalMmapVector<tid_t> known_threads;
+ GetRunningThreadsLocked(&known_threads);
- for (auto os_id : unsuspended) {
+ bool succeded = true;
+ for (auto os_id : known_threads) {
uptr i = InternalLowerBound(threads, os_id);
- if (i >= threads.size() || threads[i] != os_id)
+ if (i >= threads.size() || threads[i] != os_id) {
+ succeded = false;
Report(
"Running thread %zu was not suspended. False leaks are possible.\n",
os_id);
+ }
}
+ return succeded;
}
# endif // !SANITIZER_FUCHSIA
@@ -725,7 +818,18 @@ static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads,
CheckForLeaksParam *param = reinterpret_cast<CheckForLeaksParam *>(arg);
CHECK(param);
CHECK(!param->success);
- ReportUnsuspendedThreads(suspended_threads);
+ if (!ReportUnsuspendedThreads(suspended_threads)) {
+ switch (flags()->thread_suspend_fail) {
+ case 0:
+ param->success = true;
+ return;
+ case 1:
+ break;
+ case 2:
+ // Will crash on return.
+ return;
+ }
+ }
ClassifyAllChunks(suspended_threads, ¶m->frontier, param->caller_tid,
param->caller_sp);
ForEachChunk(CollectLeaksCb, ¶m->leaks);
@@ -750,19 +854,20 @@ static bool PrintResults(LeakReport &report) {
}
if (common_flags()->print_suppressions)
GetSuppressionContext()->PrintMatchedSuppressions();
- if (unsuppressed_count > 0) {
+ if (unsuppressed_count)
report.PrintSummary();
- return true;
- }
- return false;
+ if ((unsuppressed_count && common_flags()->verbosity >= 2) ||
+ flags()->log_threads)
+ PrintThreads();
+ return unsuppressed_count;
}
-static bool CheckForLeaks() {
+static bool CheckForLeaksOnce() {
if (&__lsan_is_turned_off && __lsan_is_turned_off()) {
- VReport(1, "LeakSanitizer is disabled");
+ VReport(1, "LeakSanitizer is disabled\n");
return false;
}
- VReport(1, "LeakSanitizer: checking for leaks");
+ VReport(1, "LeakSanitizer: checking for leaks\n");
// Inside LockStuffAndStopTheWorld we can't run symbolizer, so we can't match
// suppressions. However if a stack id was previously suppressed, it should be
// suppressed in future checks as well.
@@ -809,6 +914,12 @@ static bool CheckForLeaks() {
}
}
+static bool CheckForLeaks() {
+ int leaking_tries = 0;
+ for (int i = 0; i < flags()->tries; ++i) leaking_tries += CheckForLeaksOnce();
+ return leaking_tries == flags()->tries;
+}
+
static bool has_reported_leaks = false;
bool HasReportedLeaks() { return has_reported_leaks; }
@@ -111,6 +111,7 @@ void GetThreadExtraStackRangesLocked(tid_t os_id,
InternalMmapVector<Range> *ranges);
void GetAdditionalThreadContextPtrsLocked(InternalMmapVector<uptr> *ptrs);
void GetRunningThreadsLocked(InternalMmapVector<tid_t> *threads);
+void PrintThreads();
//// --------------------------------------------------------------------------
//// Allocator prototypes.
@@ -120,6 +121,10 @@ void GetRunningThreadsLocked(InternalMmapVector<tid_t> *threads);
void LockAllocator();
void UnlockAllocator();
+// Lock/unlock global mutext.
+void LockGlobal();
+void UnlockGlobal();
+
// Returns the address range occupied by the global allocator object.
void GetAllocatorGlobalRange(uptr *begin, uptr *end);
// If p points into a chunk that has been allocated to the user, returns its
@@ -28,7 +28,7 @@ namespace __lsan {
static const char kLinkerName[] = "ld";
-static char linker_placeholder[sizeof(LoadedModule)] ALIGNED(64);
+alignas(64) static char linker_placeholder[sizeof(LoadedModule)];
static LoadedModule *linker = nullptr;
static bool IsLinker(const LoadedModule& module) {
@@ -41,6 +41,13 @@ LSAN_FLAG(bool, use_ld_allocations, true,
LSAN_FLAG(bool, use_unaligned, false, "Consider unaligned pointers valid.")
LSAN_FLAG(bool, use_poisoned, false,
"Consider pointers found in poisoned memory to be valid.")
+LSAN_FLAG(bool, use_detached, false,
+ "Scan threads even if attaching to them failed.")
LSAN_FLAG(bool, log_pointers, false, "Debug logging")
LSAN_FLAG(bool, log_threads, false, "Debug logging")
+LSAN_FLAG(int, tries, 1, "Debug option to repeat leak checking multiple times")
LSAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
+LSAN_FLAG(int, thread_suspend_fail, 1,
+ "Behaviour if thread suspendion all thread (0 - "
+ "abandon leak checking, 1 - continue with leak checking (reported "
+ "leaks can be false), 2 - crash (for debugging LSAN)).")
\ No newline at end of file
@@ -80,6 +80,7 @@ void GetAllThreadAllocatorCachesLocked(InternalMmapVector<uptr> *caches) {
// On Fuchsia, leak detection is done by a special hook after atexit hooks.
// So this doesn't install any atexit hook like on other platforms.
void InstallAtExitCheckLeaks() {}
+void InstallAtForkHandler() {}
// ASan defines this to check its `halt_on_error` flag.
bool UseExitcodeOnLeak() { return true; }
@@ -26,7 +26,6 @@
#if SANITIZER_POSIX
#include "sanitizer_common/sanitizer_posix.h"
#endif
-#include "sanitizer_common/sanitizer_tls_get_addr.h"
#include "lsan.h"
#include "lsan_allocator.h"
#include "lsan_common.h"
@@ -77,6 +76,8 @@ INTERCEPTOR(void*, malloc, uptr size) {
}
INTERCEPTOR(void, free, void *p) {
+ if (UNLIKELY(!p))
+ return;
if (DlsymAlloc::PointerIsMine(p))
return DlsymAlloc::Free(p);
ENSURE_LSAN_INITED;
@@ -133,9 +134,7 @@ INTERCEPTOR(void*, memalign, uptr alignment, uptr size) {
INTERCEPTOR(void *, __libc_memalign, uptr alignment, uptr size) {
ENSURE_LSAN_INITED;
GET_STACK_TRACE_MALLOC;
- void *res = lsan_memalign(alignment, size, stack);
- DTLS_on_libc_memalign(res, size);
- return res;
+ return lsan_memalign(alignment, size, stack);
}
#define LSAN_MAYBE_INTERCEPT___LIBC_MEMALIGN INTERCEPT_FUNCTION(__libc_memalign)
#else
@@ -389,7 +388,7 @@ INTERCEPTOR(int, atexit, void (*f)()) {
extern "C" {
extern int _pthread_atfork(void (*prepare)(), void (*parent)(),
void (*child)());
-};
+}
INTERCEPTOR(int, pthread_atfork, void (*prepare)(), void (*parent)(),
void (*child)()) {
@@ -525,7 +524,7 @@ INTERCEPTOR(int, pthread_timedjoin_np, void *thread, void **ret,
# define LSAN_MAYBE_INTERCEPT_TIMEDJOIN
# endif // SANITIZER_INTERCEPT_TIMEDJOIN
-DEFINE_REAL_PTHREAD_FUNCTIONS
+DEFINE_INTERNAL_PTHREAD_FUNCTIONS
INTERCEPTOR(void, _exit, int status) {
if (status == 0 && HasReportedLeaks()) status = common_flags()->exitcode;
@@ -543,6 +542,7 @@ namespace __lsan {
void InitializeInterceptors() {
// Fuchsia doesn't use interceptors that require any setup.
#if !SANITIZER_FUCHSIA
+ __interception::DoesNotSupportStaticLinking();
InitializeSignalInterceptors();
INTERCEPT_FUNCTION(malloc);
@@ -14,11 +14,13 @@
#include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_POSIX
-#include "lsan.h"
-#include "lsan_allocator.h"
-#include "lsan_thread.h"
-#include "sanitizer_common/sanitizer_stacktrace.h"
-#include "sanitizer_common/sanitizer_tls_get_addr.h"
+# include <pthread.h>
+
+# include "lsan.h"
+# include "lsan_allocator.h"
+# include "lsan_thread.h"
+# include "sanitizer_common/sanitizer_stacktrace.h"
+# include "sanitizer_common/sanitizer_tls_get_addr.h"
namespace __lsan {
@@ -48,12 +50,8 @@ void ThreadContext::OnStarted(void *arg) {
void ThreadStart(u32 tid, tid_t os_id, ThreadType thread_type) {
OnStartedArgs args;
- uptr stack_size = 0;
- uptr tls_size = 0;
- GetThreadStackAndTls(tid == kMainTid, &args.stack_begin, &stack_size,
- &args.tls_begin, &tls_size);
- args.stack_end = args.stack_begin + stack_size;
- args.tls_end = args.tls_begin + tls_size;
+ GetThreadStackAndTls(tid == kMainTid, &args.stack_begin, &args.stack_end,
+ &args.tls_begin, &args.tls_end);
GetAllocatorCacheRange(&args.cache_begin, &args.cache_end);
args.dtls = DTLS_Get();
ThreadContextLsanBase::ThreadStart(tid, os_id, thread_type, &args);
@@ -98,6 +96,31 @@ void InstallAtExitCheckLeaks() {
Atexit(DoLeakCheck);
}
+static void BeforeFork() {
+ VReport(2, "BeforeFork tid: %llu\n", GetTid());
+ LockGlobal();
+ LockThreads();
+ LockAllocator();
+ StackDepotLockBeforeFork();
+}
+
+static void AfterFork(bool fork_child) {
+ StackDepotUnlockAfterFork(fork_child);
+ UnlockAllocator();
+ UnlockThreads();
+ UnlockGlobal();
+ VReport(2, "AfterFork tid: %llu\n", GetTid());
+}
+
+void InstallAtForkHandler() {
+# if SANITIZER_SOLARIS || SANITIZER_NETBSD || SANITIZER_APPLE
+ return; // FIXME: Implement FutexWait.
+# endif
+ pthread_atfork(
+ &BeforeFork, []() { AfterFork(/* fork_child= */ false); },
+ []() { AfterFork(/* fork_child= */ true); });
+}
+
} // namespace __lsan
#endif // SANITIZER_POSIX
@@ -14,8 +14,8 @@
#include "lsan.h"
#if SANITIZER_CAN_USE_PREINIT_ARRAY
- // We force __lsan_init to be called before anyone else by placing it into
- // .preinit_array section.
- __attribute__((section(".preinit_array"), used))
- void (*__local_lsan_preinit)(void) = __lsan_init;
+// This section is linked into the main executable when -fsanitize=leak is
+// specified to perform initialization at a very early stage.
+__attribute__((section(".preinit_array"), used)) static auto preinit =
+ __lsan_init;
#endif
@@ -18,6 +18,7 @@
#include "lsan_common.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_thread_history.h"
#include "sanitizer_common/sanitizer_thread_registry.h"
#include "sanitizer_common/sanitizer_tls_get_addr.h"
@@ -35,12 +36,12 @@ static ThreadContextBase *CreateThreadContext(u32 tid) {
}
void InitializeThreads() {
- static ALIGNED(alignof(
- ThreadRegistry)) char thread_registry_placeholder[sizeof(ThreadRegistry)];
+ alignas(alignof(ThreadRegistry)) static char
+ thread_registry_placeholder[sizeof(ThreadRegistry)];
thread_registry =
new (thread_registry_placeholder) ThreadRegistry(CreateThreadContext);
- static ALIGNED(alignof(ThreadArgRetval)) char
+ alignas(alignof(ThreadArgRetval)) static char
thread_arg_retval_placeholder[sizeof(ThreadArgRetval)];
thread_arg_retval = new (thread_arg_retval_placeholder) ThreadArgRetval();
}
@@ -109,6 +110,12 @@ void GetRunningThreadsLocked(InternalMmapVector<tid_t> *threads) {
threads);
}
+void PrintThreads() {
+ InternalScopedString out;
+ PrintThreadHistory(*thread_registry, out);
+ Report("%s\n", out.data());
+}
+
void GetAdditionalThreadContextPtrsLocked(InternalMmapVector<uptr> *ptrs) {
GetThreadArgRetval().GetAllPtrsLocked(ptrs);
}
@@ -47,7 +47,6 @@ sanitizer_common_files = \
sanitizer_netbsd.cpp \
sanitizer_platform_limits_freebsd.cpp \
sanitizer_platform_limits_linux.cpp \
- sanitizer_platform_limits_openbsd.cpp \
sanitizer_platform_limits_posix.cpp \
sanitizer_platform_limits_solaris.cpp \
sanitizer_posix.cpp \
@@ -74,15 +73,18 @@ sanitizer_common_files = \
sanitizer_symbolizer.cpp \
sanitizer_symbolizer_libbacktrace.cpp \
sanitizer_symbolizer_libcdep.cpp \
+ sanitizer_symbolizer_markup.cpp \
sanitizer_symbolizer_posix_libcdep.cpp \
sanitizer_symbolizer_win.cpp \
sanitizer_termination.cpp \
sanitizer_thread_arg_retval.cpp \
+ sanitizer_thread_history.cpp \
sanitizer_thread_registry.cpp \
sanitizer_tls_get_addr.cpp \
sanitizer_unwind_linux_libcdep.cpp \
sanitizer_unwind_win.cpp \
- sanitizer_win.cpp
+ sanitizer_win.cpp \
+ sanitizer_win_interception.cpp
libsanitizer_common_la_SOURCES = $(sanitizer_common_files)
@@ -133,7 +133,6 @@ am__objects_1 = sancov_flags.lo sanitizer_allocator.lo \
sanitizer_mac.lo sanitizer_mac_libcdep.lo sanitizer_mutex.lo \
sanitizer_netbsd.lo sanitizer_platform_limits_freebsd.lo \
sanitizer_platform_limits_linux.lo \
- sanitizer_platform_limits_openbsd.lo \
sanitizer_platform_limits_posix.lo \
sanitizer_platform_limits_solaris.lo sanitizer_posix.lo \
sanitizer_posix_libcdep.lo sanitizer_printf.lo \
@@ -148,12 +147,13 @@ am__objects_1 = sancov_flags.lo sanitizer_allocator.lo \
sanitizer_stoptheworld_linux_libcdep.lo \
sanitizer_stoptheworld_mac.lo sanitizer_suppressions.lo \
sanitizer_symbolizer.lo sanitizer_symbolizer_libbacktrace.lo \
- sanitizer_symbolizer_libcdep.lo \
+ sanitizer_symbolizer_libcdep.lo sanitizer_symbolizer_markup.lo \
sanitizer_symbolizer_posix_libcdep.lo \
sanitizer_symbolizer_win.lo sanitizer_termination.lo \
- sanitizer_thread_arg_retval.lo sanitizer_thread_registry.lo \
- sanitizer_tls_get_addr.lo sanitizer_unwind_linux_libcdep.lo \
- sanitizer_unwind_win.lo sanitizer_win.lo
+ sanitizer_thread_arg_retval.lo sanitizer_thread_history.lo \
+ sanitizer_thread_registry.lo sanitizer_tls_get_addr.lo \
+ sanitizer_unwind_linux_libcdep.lo sanitizer_unwind_win.lo \
+ sanitizer_win.lo sanitizer_win_interception.lo
am_libsanitizer_common_la_OBJECTS = $(am__objects_1)
libsanitizer_common_la_OBJECTS = $(am_libsanitizer_common_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
@@ -409,7 +409,6 @@ sanitizer_common_files = \
sanitizer_netbsd.cpp \
sanitizer_platform_limits_freebsd.cpp \
sanitizer_platform_limits_linux.cpp \
- sanitizer_platform_limits_openbsd.cpp \
sanitizer_platform_limits_posix.cpp \
sanitizer_platform_limits_solaris.cpp \
sanitizer_posix.cpp \
@@ -436,15 +435,18 @@ sanitizer_common_files = \
sanitizer_symbolizer.cpp \
sanitizer_symbolizer_libbacktrace.cpp \
sanitizer_symbolizer_libcdep.cpp \
+ sanitizer_symbolizer_markup.cpp \
sanitizer_symbolizer_posix_libcdep.cpp \
sanitizer_symbolizer_win.cpp \
sanitizer_termination.cpp \
sanitizer_thread_arg_retval.cpp \
+ sanitizer_thread_history.cpp \
sanitizer_thread_registry.cpp \
sanitizer_tls_get_addr.cpp \
sanitizer_unwind_linux_libcdep.cpp \
sanitizer_unwind_win.cpp \
- sanitizer_win.cpp
+ sanitizer_win.cpp \
+ sanitizer_win_interception.cpp
libsanitizer_common_la_SOURCES = $(sanitizer_common_files)
libsanitizer_common_la_LIBADD = $(SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS)
@@ -568,7 +570,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_netbsd.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_freebsd.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_linux.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_openbsd.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_posix.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_solaris.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_posix.Plo@am__quote@
@@ -594,16 +595,19 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_libbacktrace.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_mac.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_markup.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_posix_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_report.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_win.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_termination.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_thread_arg_retval.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_thread_history.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_thread_registry.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_tls_get_addr.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_unwind_linux_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_unwind_win.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_win.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_win_interception.Plo@am__quote@
.cpp.o:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -37,10 +37,6 @@ static void RegisterSancovFlags(FlagParser *parser, SancovFlags *f) {
#undef SANCOV_FLAG
}
-static const char *MaybeCallSancovDefaultOptions() {
- return (&__sancov_default_options) ? __sancov_default_options() : "";
-}
-
void InitializeSancovFlags() {
SancovFlags *f = sancov_flags();
f->SetDefaults();
@@ -48,7 +44,7 @@ void InitializeSancovFlags() {
FlagParser parser;
RegisterSancovFlags(&parser, f);
- parser.ParseString(MaybeCallSancovDefaultOptions());
+ parser.ParseString(__sancov_default_options());
parser.ParseStringFromEnv("SANCOV_OPTIONS");
ReportUnrecognizedFlags();
@@ -25,7 +25,7 @@ namespace __sanitizer {
const char *PrimaryAllocatorName = "SizeClassAllocator";
const char *SecondaryAllocatorName = "LargeMmapAllocator";
-static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)];
+alignas(64) static char internal_alloc_placeholder[sizeof(InternalAllocator)];
static atomic_uint8_t internal_allocator_initialized;
static StaticSpinMutex internal_alloc_init_mu;
@@ -59,7 +59,7 @@ static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache,
static void *RawInternalRealloc(void *ptr, uptr size,
InternalAllocatorCache *cache) {
- uptr alignment = 8;
+ constexpr usize alignment = Max<usize>(8, sizeof(void *));
if (cache == 0) {
SpinMutexLock l(&internal_allocator_cache_mu);
return internal_allocator()->Reallocate(&internal_allocator_cache, ptr,
@@ -137,7 +137,8 @@ void InternalAllocatorUnlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
}
// LowLevelAllocator
-constexpr uptr kLowLevelAllocatorDefaultAlignment = 8;
+constexpr usize kLowLevelAllocatorDefaultAlignment =
+ Max<usize>(8, sizeof(void *));
constexpr uptr kMinNumPagesRounded = 16;
constexpr uptr kMinRoundedSize = 65536;
static uptr low_level_alloc_min_alignment = kLowLevelAllocatorDefaultAlignment;
@@ -15,6 +15,8 @@
#define SANITIZER_ALLOCATOR_DLSYM_H
#include "sanitizer_allocator_internal.h"
+#include "sanitizer_common/sanitizer_allocator_checks.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
namespace __sanitizer {
@@ -31,15 +33,15 @@ struct DlSymAllocator {
UNLIKELY(internal_allocator()->FromPrimary(ptr));
}
- static void *Allocate(uptr size_in_bytes) {
- void *ptr = InternalAlloc(size_in_bytes, nullptr, kWordSize);
+ static void *Allocate(uptr size_in_bytes, uptr align = kWordSize) {
+ void *ptr = InternalAlloc(size_in_bytes, nullptr, align);
CHECK(internal_allocator()->FromPrimary(ptr));
Details::OnAllocate(ptr,
internal_allocator()->GetActuallyAllocatedSize(ptr));
return ptr;
}
- static void *Callocate(SIZE_T nmemb, SIZE_T size) {
+ static void *Callocate(usize nmemb, usize size) {
void *ptr = InternalCalloc(nmemb, size);
CHECK(internal_allocator()->FromPrimary(ptr));
Details::OnAllocate(ptr,
@@ -70,6 +72,11 @@ struct DlSymAllocator {
return new_ptr;
}
+ static void *ReallocArray(void *ptr, uptr count, uptr size) {
+ CHECK(!CheckForCallocOverflow(count, size));
+ return Realloc(ptr, count * size);
+ }
+
static void OnAllocate(const void *ptr, uptr size) {}
static void OnFree(const void *ptr, uptr size) {}
};
@@ -40,6 +40,8 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
void __sanitizer_malloc_hook(void *ptr, uptr size);
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
void __sanitizer_free_hook(void *ptr);
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE int
+__sanitizer_ignore_free_hook(void *ptr);
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void
__sanitizer_purge_allocator();
@@ -278,7 +278,7 @@ class SizeClassAllocator32 {
static const uptr kRegionSize = 1 << kRegionSizeLog;
static const uptr kNumPossibleRegions = kSpaceSize / kRegionSize;
- struct ALIGNED(SANITIZER_CACHE_LINE_SIZE) SizeClassInfo {
+ struct alignas(SANITIZER_CACHE_LINE_SIZE) SizeClassInfo {
StaticSpinMutex mutex;
IntrusiveList<TransferBatch> free_list;
u32 rand_state;
@@ -316,13 +316,13 @@ class SizeClassAllocator64 {
Printf(
"%s %02zd (%6zd): mapped: %6zdK allocs: %7zd frees: %7zd inuse: %6zd "
"num_freed_chunks %7zd avail: %6zd rss: %6zdK releases: %6zd "
- "last released: %6lldK region: 0x%zx\n",
+ "last released: %6lldK region: %p\n",
region->exhausted ? "F" : " ", class_id, ClassIdToSize(class_id),
region->mapped_user >> 10, region->stats.n_allocated,
region->stats.n_freed, in_use, region->num_freed_chunks, avail_chunks,
rss >> 10, region->rtoi.num_releases,
region->rtoi.last_released_bytes >> 10,
- SpaceBeg() + kRegionSize * class_id);
+ (void *)(SpaceBeg() + kRegionSize * class_id));
}
void PrintStats() {
@@ -639,13 +639,14 @@ class SizeClassAllocator64 {
static_assert(kRegionSize >= SizeClassMap::kMaxSize,
"Region size exceed largest size");
// kRegionSize must be <= 2^36, see CompactPtrT.
- COMPILER_CHECK((kRegionSize) <= (1ULL << (SANITIZER_WORDSIZE / 2 + 4)));
+ COMPILER_CHECK((kRegionSize) <=
+ (1ULL << (sizeof(CompactPtrT) * 8 + kCompactPtrScale)));
// Call mmap for user memory with at least this size.
- static const uptr kUserMapSize = 1 << 16;
+ static const uptr kUserMapSize = 1 << 18;
// Call mmap for metadata memory with at least this size.
static const uptr kMetaMapSize = 1 << 16;
// Call mmap for free array memory with at least this size.
- static const uptr kFreeArrayMapSize = 1 << 16;
+ static const uptr kFreeArrayMapSize = 1 << 18;
atomic_sint32_t release_to_os_interval_ms_;
@@ -666,7 +667,7 @@ class SizeClassAllocator64 {
u64 last_released_bytes;
};
- struct ALIGNED(SANITIZER_CACHE_LINE_SIZE) RegionInfo {
+ struct alignas(SANITIZER_CACHE_LINE_SIZE) RegionInfo {
Mutex mutex;
uptr num_freed_chunks; // Number of elements in the freearray.
uptr mapped_free_array; // Bytes mapped for freearray.
@@ -18,12 +18,24 @@
namespace __sanitizer {
enum memory_order {
+// If the __atomic atomic builtins are supported (Clang/GCC), use the
+// compiler provided macro values so that we can map the atomic operations
+// to __atomic_* directly.
+#ifdef __ATOMIC_SEQ_CST
+ memory_order_relaxed = __ATOMIC_RELAXED,
+ memory_order_consume = __ATOMIC_CONSUME,
+ memory_order_acquire = __ATOMIC_ACQUIRE,
+ memory_order_release = __ATOMIC_RELEASE,
+ memory_order_acq_rel = __ATOMIC_ACQ_REL,
+ memory_order_seq_cst = __ATOMIC_SEQ_CST
+#else
memory_order_relaxed = 1 << 0,
memory_order_consume = 1 << 1,
memory_order_acquire = 1 << 2,
memory_order_release = 1 << 3,
memory_order_acq_rel = 1 << 4,
memory_order_seq_cst = 1 << 5
+#endif
};
struct atomic_uint8_t {
@@ -49,7 +61,7 @@ struct atomic_uint32_t {
struct atomic_uint64_t {
typedef u64 Type;
// On 32-bit platforms u64 is not necessary aligned on 8 bytes.
- volatile ALIGNED(8) Type val_dont_use;
+ alignas(8) volatile Type val_dont_use;
};
struct atomic_uintptr_t {
@@ -14,60 +14,63 @@
#ifndef SANITIZER_ATOMIC_CLANG_H
#define SANITIZER_ATOMIC_CLANG_H
-#if defined(__i386__) || defined(__x86_64__)
-# include "sanitizer_atomic_clang_x86.h"
-#else
-# include "sanitizer_atomic_clang_other.h"
-#endif
-
namespace __sanitizer {
-// We would like to just use compiler builtin atomic operations
-// for loads and stores, but they are mostly broken in clang:
-// - they lead to vastly inefficient code generation
-// (http://llvm.org/bugs/show_bug.cgi?id=17281)
-// - 64-bit atomic operations are not implemented on x86_32
-// (http://llvm.org/bugs/show_bug.cgi?id=15034)
-// - they are not implemented on ARM
-// error: undefined reference to '__atomic_load_4'
+// We use the compiler builtin atomic operations for loads and stores, which
+// generates correct code for all architectures, but may require libatomic
+// on platforms where e.g. 64-bit atomics are not supported natively.
// See http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
// for mappings of the memory model to different processors.
-inline void atomic_signal_fence(memory_order) {
+inline void atomic_signal_fence(memory_order mo) { __atomic_signal_fence(mo); }
+
+inline void atomic_thread_fence(memory_order mo) { __atomic_thread_fence(mo); }
+
+inline void proc_yield(int cnt) {
+ __asm__ __volatile__("" ::: "memory");
+#if defined(__i386__) || defined(__x86_64__)
+ for (int i = 0; i < cnt; i++) __asm__ __volatile__("pause");
__asm__ __volatile__("" ::: "memory");
+#endif
}
-inline void atomic_thread_fence(memory_order) {
- __sync_synchronize();
+template <typename T>
+inline typename T::Type atomic_load(const volatile T *a, memory_order mo) {
+ DCHECK(mo == memory_order_relaxed || mo == memory_order_consume ||
+ mo == memory_order_acquire || mo == memory_order_seq_cst);
+ DCHECK(!((uptr)a % sizeof(*a)));
+ return __atomic_load_n(&a->val_dont_use, mo);
}
-template<typename T>
-inline typename T::Type atomic_fetch_add(volatile T *a,
- typename T::Type v, memory_order mo) {
- (void)mo;
+template <typename T>
+inline void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
+ DCHECK(mo == memory_order_relaxed || mo == memory_order_release ||
+ mo == memory_order_seq_cst);
DCHECK(!((uptr)a % sizeof(*a)));
- return __sync_fetch_and_add(&a->val_dont_use, v);
+ __atomic_store_n(&a->val_dont_use, v, mo);
}
-template<typename T>
-inline typename T::Type atomic_fetch_sub(volatile T *a,
- typename T::Type v, memory_order mo) {
+template <typename T>
+inline typename T::Type atomic_fetch_add(volatile T *a, typename T::Type v,
+ memory_order mo) {
+ DCHECK(!((uptr)a % sizeof(*a)));
+ return __atomic_fetch_add(&a->val_dont_use, v, mo);
+}
+
+template <typename T>
+inline typename T::Type atomic_fetch_sub(volatile T *a, typename T::Type v,
+ memory_order mo) {
(void)mo;
DCHECK(!((uptr)a % sizeof(*a)));
- return __sync_fetch_and_add(&a->val_dont_use, -v);
+ return __atomic_fetch_sub(&a->val_dont_use, v, mo);
}
-template<typename T>
-inline typename T::Type atomic_exchange(volatile T *a,
- typename T::Type v, memory_order mo) {
+template <typename T>
+inline typename T::Type atomic_exchange(volatile T *a, typename T::Type v,
+ memory_order mo) {
DCHECK(!((uptr)a % sizeof(*a)));
- if (mo & (memory_order_release | memory_order_acq_rel | memory_order_seq_cst))
- __sync_synchronize();
- v = __sync_lock_test_and_set(&a->val_dont_use, v);
- if (mo == memory_order_seq_cst)
- __sync_synchronize();
- return v;
+ return __atomic_exchange_n(&a->val_dont_use, v, mo);
}
template <typename T>
@@ -82,9 +85,8 @@ inline bool atomic_compare_exchange_strong(volatile T *a, typename T::Type *cmp,
__ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
}
-template<typename T>
-inline bool atomic_compare_exchange_weak(volatile T *a,
- typename T::Type *cmp,
+template <typename T>
+inline bool atomic_compare_exchange_weak(volatile T *a, typename T::Type *cmp,
typename T::Type xchg,
memory_order mo) {
return atomic_compare_exchange_strong(a, cmp, xchg, mo);
@@ -92,13 +94,6 @@ inline bool atomic_compare_exchange_weak(volatile T *a,
} // namespace __sanitizer
-// This include provides explicit template instantiations for atomic_uint64_t
-// on MIPS32, which does not directly support 8 byte atomics. It has to
-// proceed the template definitions above.
-#if defined(_MIPS_SIM) && defined(_ABIO32) && _MIPS_SIM == _ABIO32
-# include "sanitizer_atomic_clang_mips.h"
-#endif
-
#undef ATOMIC_ORDER
#endif // SANITIZER_ATOMIC_CLANG_H
deleted file mode 100644
@@ -1,117 +0,0 @@
-//===-- sanitizer_atomic_clang_mips.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
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
-// Not intended for direct inclusion. Include sanitizer_atomic.h.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef SANITIZER_ATOMIC_CLANG_MIPS_H
-#define SANITIZER_ATOMIC_CLANG_MIPS_H
-
-namespace __sanitizer {
-
-// MIPS32 does not support atomics > 4 bytes. To address this lack of
-// functionality, the sanitizer library provides helper methods which use an
-// internal spin lock mechanism to emulate atomic operations when the size is
-// 8 bytes.
-static void __spin_lock(volatile int *lock) {
- while (__sync_lock_test_and_set(lock, 1))
- while (*lock) {
- }
-}
-
-static void __spin_unlock(volatile int *lock) { __sync_lock_release(lock); }
-
-// Make sure the lock is on its own cache line to prevent false sharing.
-// Put it inside a struct that is aligned and padded to the typical MIPS
-// cacheline which is 32 bytes.
-static struct {
- int lock;
- char pad[32 - sizeof(int)];
-} __attribute__((aligned(32))) lock = {0, {0}};
-
-template <>
-inline atomic_uint64_t::Type atomic_fetch_add(volatile atomic_uint64_t *ptr,
- atomic_uint64_t::Type val,
- memory_order mo) {
- DCHECK(mo &
- (memory_order_relaxed | memory_order_release | memory_order_seq_cst));
- DCHECK(!((uptr)ptr % sizeof(*ptr)));
-
- atomic_uint64_t::Type ret;
-
- __spin_lock(&lock.lock);
- ret = *(const_cast<atomic_uint64_t::Type volatile *>(&ptr->val_dont_use));
- ptr->val_dont_use = ret + val;
- __spin_unlock(&lock.lock);
-
- return ret;
-}
-
-template <>
-inline atomic_uint64_t::Type atomic_fetch_sub(volatile atomic_uint64_t *ptr,
- atomic_uint64_t::Type val,
- memory_order mo) {
- return atomic_fetch_add(ptr, -val, mo);
-}
-
-template <>
-inline bool atomic_compare_exchange_strong(volatile atomic_uint64_t *ptr,
- atomic_uint64_t::Type *cmp,
- atomic_uint64_t::Type xchg,
- memory_order mo) {
- DCHECK(mo &
- (memory_order_relaxed | memory_order_release | memory_order_seq_cst));
- DCHECK(!((uptr)ptr % sizeof(*ptr)));
-
- typedef atomic_uint64_t::Type Type;
- Type cmpv = *cmp;
- Type prev;
- bool ret = false;
-
- __spin_lock(&lock.lock);
- prev = *(const_cast<Type volatile *>(&ptr->val_dont_use));
- if (prev == cmpv) {
- ret = true;
- ptr->val_dont_use = xchg;
- }
- __spin_unlock(&lock.lock);
-
- return ret;
-}
-
-template <>
-inline atomic_uint64_t::Type atomic_load(const volatile atomic_uint64_t *ptr,
- memory_order mo) {
- DCHECK(mo &
- (memory_order_relaxed | memory_order_release | memory_order_seq_cst));
- DCHECK(!((uptr)ptr % sizeof(*ptr)));
-
- atomic_uint64_t::Type zero = 0;
- volatile atomic_uint64_t *Newptr =
- const_cast<volatile atomic_uint64_t *>(ptr);
- return atomic_fetch_add(Newptr, zero, mo);
-}
-
-template <>
-inline void atomic_store(volatile atomic_uint64_t *ptr, atomic_uint64_t::Type v,
- memory_order mo) {
- DCHECK(mo &
- (memory_order_relaxed | memory_order_release | memory_order_seq_cst));
- DCHECK(!((uptr)ptr % sizeof(*ptr)));
-
- __spin_lock(&lock.lock);
- ptr->val_dont_use = v;
- __spin_unlock(&lock.lock);
-}
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_ATOMIC_CLANG_MIPS_H
-
deleted file mode 100644
@@ -1,85 +0,0 @@
-//===-- sanitizer_atomic_clang_other.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
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
-// Not intended for direct inclusion. Include sanitizer_atomic.h.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef SANITIZER_ATOMIC_CLANG_OTHER_H
-#define SANITIZER_ATOMIC_CLANG_OTHER_H
-
-namespace __sanitizer {
-
-
-inline void proc_yield(int cnt) {
- __asm__ __volatile__("" ::: "memory");
-}
-
-template<typename T>
-inline typename T::Type atomic_load(
- const volatile T *a, memory_order mo) {
- DCHECK(mo & (memory_order_relaxed | memory_order_consume
- | memory_order_acquire | memory_order_seq_cst));
- DCHECK(!((uptr)a % sizeof(*a)));
- typename T::Type v;
-
- if (sizeof(*a) < 8 || sizeof(void*) == 8) {
- // Assume that aligned loads are atomic.
- if (mo == memory_order_relaxed) {
- v = a->val_dont_use;
- } else if (mo == memory_order_consume) {
- // Assume that processor respects data dependencies
- // (and that compiler won't break them).
- __asm__ __volatile__("" ::: "memory");
- v = a->val_dont_use;
- __asm__ __volatile__("" ::: "memory");
- } else if (mo == memory_order_acquire) {
- __asm__ __volatile__("" ::: "memory");
- v = a->val_dont_use;
- __sync_synchronize();
- } else { // seq_cst
- // E.g. on POWER we need a hw fence even before the store.
- __sync_synchronize();
- v = a->val_dont_use;
- __sync_synchronize();
- }
- } else {
- __atomic_load(const_cast<typename T::Type volatile *>(&a->val_dont_use), &v,
- __ATOMIC_SEQ_CST);
- }
- return v;
-}
-
-template<typename T>
-inline void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
- DCHECK(mo & (memory_order_relaxed | memory_order_release
- | memory_order_seq_cst));
- DCHECK(!((uptr)a % sizeof(*a)));
-
- if (sizeof(*a) < 8 || sizeof(void*) == 8) {
- // Assume that aligned loads are atomic.
- if (mo == memory_order_relaxed) {
- a->val_dont_use = v;
- } else if (mo == memory_order_release) {
- __sync_synchronize();
- a->val_dont_use = v;
- __asm__ __volatile__("" ::: "memory");
- } else { // seq_cst
- __sync_synchronize();
- a->val_dont_use = v;
- __sync_synchronize();
- }
- } else {
- __atomic_store(&a->val_dont_use, &v, __ATOMIC_SEQ_CST);
- }
-}
-
-} // namespace __sanitizer
-
-#endif // #ifndef SANITIZER_ATOMIC_CLANG_OTHER_H
deleted file mode 100644
@@ -1,113 +0,0 @@
-//===-- sanitizer_atomic_clang_x86.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
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
-// Not intended for direct inclusion. Include sanitizer_atomic.h.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef SANITIZER_ATOMIC_CLANG_X86_H
-#define SANITIZER_ATOMIC_CLANG_X86_H
-
-namespace __sanitizer {
-
-inline void proc_yield(int cnt) {
- __asm__ __volatile__("" ::: "memory");
- for (int i = 0; i < cnt; i++)
- __asm__ __volatile__("pause");
- __asm__ __volatile__("" ::: "memory");
-}
-
-template<typename T>
-inline typename T::Type atomic_load(
- const volatile T *a, memory_order mo) {
- DCHECK(mo & (memory_order_relaxed | memory_order_consume
- | memory_order_acquire | memory_order_seq_cst));
- DCHECK(!((uptr)a % sizeof(*a)));
- typename T::Type v;
-
- if (sizeof(*a) < 8 || sizeof(void*) == 8) {
- // Assume that aligned loads are atomic.
- if (mo == memory_order_relaxed) {
- v = a->val_dont_use;
- } else if (mo == memory_order_consume) {
- // Assume that processor respects data dependencies
- // (and that compiler won't break them).
- __asm__ __volatile__("" ::: "memory");
- v = a->val_dont_use;
- __asm__ __volatile__("" ::: "memory");
- } else if (mo == memory_order_acquire) {
- __asm__ __volatile__("" ::: "memory");
- v = a->val_dont_use;
- // On x86 loads are implicitly acquire.
- __asm__ __volatile__("" ::: "memory");
- } else { // seq_cst
- // On x86 plain MOV is enough for seq_cst store.
- __asm__ __volatile__("" ::: "memory");
- v = a->val_dont_use;
- __asm__ __volatile__("" ::: "memory");
- }
- } else {
- // 64-bit load on 32-bit platform.
- __asm__ __volatile__(
- "movq %1, %%mm0;" // Use mmx reg for 64-bit atomic moves
- "movq %%mm0, %0;" // (ptr could be read-only)
- "emms;" // Empty mmx state/Reset FP regs
- : "=m" (v)
- : "m" (a->val_dont_use)
- : // mark the mmx registers as clobbered
-#ifdef __MMX__
- "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
-#endif // #ifdef __MMX__
- "memory");
- }
- return v;
-}
-
-template<typename T>
-inline void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
- DCHECK(mo & (memory_order_relaxed | memory_order_release
- | memory_order_seq_cst));
- DCHECK(!((uptr)a % sizeof(*a)));
-
- if (sizeof(*a) < 8 || sizeof(void*) == 8) {
- // Assume that aligned loads are atomic.
- if (mo == memory_order_relaxed) {
- a->val_dont_use = v;
- } else if (mo == memory_order_release) {
- // On x86 stores are implicitly release.
- __asm__ __volatile__("" ::: "memory");
- a->val_dont_use = v;
- __asm__ __volatile__("" ::: "memory");
- } else { // seq_cst
- // On x86 stores are implicitly release.
- __asm__ __volatile__("" ::: "memory");
- a->val_dont_use = v;
- __sync_synchronize();
- }
- } else {
- // 64-bit store on 32-bit platform.
- __asm__ __volatile__(
- "movq %1, %%mm0;" // Use mmx reg for 64-bit atomic moves
- "movq %%mm0, %0;"
- "emms;" // Empty mmx state/Reset FP regs
- : "=m" (a->val_dont_use)
- : "m" (v)
- : // mark the mmx registers as clobbered
-#ifdef __MMX__
- "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
-#endif // #ifdef __MMX__
- "memory");
- if (mo == memory_order_seq_cst)
- __sync_synchronize();
- }
-}
-
-} // namespace __sanitizer
-
-#endif // #ifndef SANITIZER_ATOMIC_CLANG_X86_H
@@ -70,8 +70,8 @@ inline void proc_yield(int cnt) {
template<typename T>
inline typename T::Type atomic_load(
const volatile T *a, memory_order mo) {
- DCHECK(mo & (memory_order_relaxed | memory_order_consume
- | memory_order_acquire | memory_order_seq_cst));
+ DCHECK(mo == memory_order_relaxed || mo == memory_order_consume ||
+ mo == memory_order_acquire || mo == memory_order_seq_cst);
DCHECK(!((uptr)a % sizeof(*a)));
typename T::Type v;
// FIXME(dvyukov): 64-bit load is not atomic on 32-bits.
@@ -87,8 +87,8 @@ inline typename T::Type atomic_load(
template<typename T>
inline void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
- DCHECK(mo & (memory_order_relaxed | memory_order_release
- | memory_order_seq_cst));
+ DCHECK(mo == memory_order_relaxed || mo == memory_order_release ||
+ mo == memory_order_seq_cst);
DCHECK(!((uptr)a % sizeof(*a)));
// FIXME(dvyukov): 64-bit store is not atomic on 32-bits.
if (mo == memory_order_relaxed) {
@@ -321,23 +321,23 @@ class TwoLevelBitVector {
};
private:
- void check(uptr idx) const { CHECK_LE(idx, size()); }
+ void check(uptr idx) const { CHECK_LT(idx, size()); }
uptr idx0(uptr idx) const {
uptr res = idx / (BV::kSize * BV::kSize);
- CHECK_LE(res, kLevel1Size);
+ CHECK_LT(res, kLevel1Size);
return res;
}
uptr idx1(uptr idx) const {
uptr res = (idx / BV::kSize) % BV::kSize;
- CHECK_LE(res, BV::kSize);
+ CHECK_LT(res, BV::kSize);
return res;
}
uptr idx2(uptr idx) const {
uptr res = idx % BV::kSize;
- CHECK_LE(res, BV::kSize);
+ CHECK_LT(res, BV::kSize);
return res;
}
@@ -139,9 +139,11 @@ u32 ChainedOriginDepot::Get(u32 id, u32 *other) {
return desc.here_id;
}
-void ChainedOriginDepot::LockAll() { depot.LockAll(); }
+void ChainedOriginDepot::LockBeforeFork() { depot.LockBeforeFork(); }
-void ChainedOriginDepot::UnlockAll() { depot.UnlockAll(); }
+void ChainedOriginDepot::UnlockAfterFork(bool fork_child) {
+ depot.UnlockAfterFork(fork_child);
+}
void ChainedOriginDepot::TestOnlyUnmap() { depot.TestOnlyUnmap(); }
@@ -32,8 +32,8 @@ class ChainedOriginDepot {
// Retrieves the stored StackDepot ID for the given origin ID.
u32 Get(u32 id, u32 *other);
- void LockAll();
- void UnlockAll();
+ void LockBeforeFork();
+ void UnlockAfterFork(bool fork_child);
void TestOnlyUnmap();
private:
@@ -347,7 +347,13 @@ void RunMallocHooks(void *ptr, uptr size) {
}
}
-void RunFreeHooks(void *ptr) {
+// Returns '1' if the call to free() should be ignored (based on
+// __sanitizer_ignore_free_hook), or '0' otherwise.
+int RunFreeHooks(void *ptr) {
+ if (__sanitizer_ignore_free_hook(ptr)) {
+ return 1;
+ }
+
__sanitizer_free_hook(ptr);
for (int i = 0; i < kMaxMallocFreeHooks; i++) {
auto hook = MFHooks[i].free_hook;
@@ -355,6 +361,8 @@ void RunFreeHooks(void *ptr) {
break;
hook(ptr);
}
+
+ return 0;
}
static int InstallMallocFreeHooks(void (*malloc_hook)(const void *, uptr),
@@ -419,4 +427,9 @@ SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_free_hook, void *ptr) {
(void)ptr;
}
+SANITIZER_INTERFACE_WEAK_DEF(int, __sanitizer_ignore_free_hook, void *ptr) {
+ (void)ptr;
+ return 0;
+}
+
} // extern "C"
@@ -32,6 +32,7 @@ struct AddressInfo;
struct BufferedStackTrace;
struct SignalContext;
struct StackTrace;
+struct SymbolizedStack;
// Constants.
const uptr kWordSize = SANITIZER_WORDSIZE / 8;
@@ -59,14 +60,10 @@ inline int Verbosity() {
return atomic_load(¤t_verbosity, memory_order_relaxed);
}
-#if SANITIZER_ANDROID
-inline uptr GetPageSize() {
-// Android post-M sysconf(_SC_PAGESIZE) crashes if called from .preinit_array.
- return 4096;
-}
-inline uptr GetPageSizeCached() {
- return 4096;
-}
+#if SANITIZER_ANDROID && !defined(__aarch64__)
+// 32-bit Android only has 4k pages.
+inline uptr GetPageSize() { return 4096; }
+inline uptr GetPageSizeCached() { return 4096; }
#else
uptr GetPageSize();
extern uptr PageSizeCached;
@@ -76,6 +73,7 @@ inline uptr GetPageSizeCached() {
return PageSizeCached;
}
#endif
+
uptr GetMmapGranularity();
uptr GetMaxVirtualAddress();
uptr GetMaxUserVirtualAddress();
@@ -85,15 +83,16 @@ int TgKill(pid_t pid, tid_t tid, int sig);
uptr GetThreadSelf();
void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
uptr *stack_bottom);
-void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
- uptr *tls_addr, uptr *tls_size);
+void GetThreadStackAndTls(bool main, uptr *stk_begin, uptr *stk_end,
+ uptr *tls_begin, uptr *tls_end);
// Memory management
void *MmapOrDie(uptr size, const char *mem_type, bool raw_report = false);
+
inline void *MmapOrDieQuietly(uptr size, const char *mem_type) {
return MmapOrDie(size, mem_type, /*raw_report*/ true);
}
-void UnmapOrDie(void *addr, uptr size);
+void UnmapOrDie(void *addr, uptr size, bool raw_report = false);
// Behaves just like MmapOrDie, but tolerates out of memory condition, in that
// case returns nullptr.
void *MmapOrDieOnFatalError(uptr size, const char *mem_type);
@@ -138,7 +137,8 @@ void UnmapFromTo(uptr from, uptr to);
// shadow_size_bytes bytes on the right, which on linux is mapped no access.
// The high_mem_end may be updated if the original shadow size doesn't fit.
uptr MapDynamicShadow(uptr shadow_size_bytes, uptr shadow_scale,
- uptr min_shadow_base_alignment, uptr &high_mem_end);
+ uptr min_shadow_base_alignment, uptr &high_mem_end,
+ uptr granularity);
// Let S = max(shadow_size, num_aliases * alias_size, ring_buffer_size).
// Reserves 2*S bytes of address space to the right of the returned address and
@@ -177,7 +177,7 @@ bool DontDumpShadowMemory(uptr addr, uptr length);
// Check if the built VMA size matches the runtime one.
void CheckVMASize();
void RunMallocHooks(void *ptr, uptr size);
-void RunFreeHooks(void *ptr);
+int RunFreeHooks(void *ptr);
class ReservedAddressRange {
public:
@@ -239,13 +239,15 @@ void RemoveANSIEscapeSequencesFromString(char *buffer);
void Printf(const char *format, ...) FORMAT(1, 2);
void Report(const char *format, ...) FORMAT(1, 2);
void SetPrintfAndReportCallback(void (*callback)(const char *));
-#define VReport(level, ...) \
- do { \
- if ((uptr)Verbosity() >= (level)) Report(__VA_ARGS__); \
+#define VReport(level, ...) \
+ do { \
+ if (UNLIKELY((uptr)Verbosity() >= (level))) \
+ Report(__VA_ARGS__); \
} while (0)
-#define VPrintf(level, ...) \
- do { \
- if ((uptr)Verbosity() >= (level)) Printf(__VA_ARGS__); \
+#define VPrintf(level, ...) \
+ do { \
+ if (UNLIKELY((uptr)Verbosity() >= (level))) \
+ Printf(__VA_ARGS__); \
} while (0)
// Lock sanitizer error reporting and protects against nested errors.
@@ -266,7 +268,15 @@ class ScopedErrorReportLock {
extern uptr stoptheworld_tracer_pid;
extern uptr stoptheworld_tracer_ppid;
+// Returns true if the entire range can be read.
bool IsAccessibleMemoryRange(uptr beg, uptr size);
+// Attempts to copy `n` bytes from memory range starting at `src` to `dest`.
+// Returns true if the entire range can be read. Returns `false` if any part of
+// the source range cannot be read, in which case the contents of `dest` are
+// undefined.
+bool TryMemCpy(void *dest, const void *src, uptr n);
+// Copies accessible memory, and zero fill inaccessible.
+void MemCpyAccessible(void *dest, const void *src, uptr n);
// Error report formatting.
const char *StripPathPrefix(const char *filepath,
@@ -393,6 +403,8 @@ void ReportErrorSummary(const char *error_type, const AddressInfo &info,
// Same as above, but obtains AddressInfo by symbolizing top stack trace frame.
void ReportErrorSummary(const char *error_type, const StackTrace *trace,
const char *alt_tool_name = nullptr);
+// Skips frames which we consider internal and not usefull to the users.
+const SymbolizedStack *SkipInternalFrames(const SymbolizedStack *frames);
void ReportMmapWriteExec(int prot, int mflags);
@@ -507,7 +519,7 @@ inline int ToLower(int c) {
// A low-level vector based on mmap. May incur a significant memory overhead for
// small vectors.
// WARNING: The current implementation supports only POD types.
-template<typename T>
+template <typename T, bool raw_report = false>
class InternalMmapVectorNoCtor {
public:
using value_type = T;
@@ -517,7 +529,7 @@ class InternalMmapVectorNoCtor {
data_ = 0;
reserve(initial_capacity);
}
- void Destroy() { UnmapOrDie(data_, capacity_bytes_); }
+ void Destroy() { UnmapOrDie(data_, capacity_bytes_, raw_report); }
T &operator[](uptr i) {
CHECK_LT(i, size_);
return data_[i];
@@ -593,9 +605,10 @@ class InternalMmapVectorNoCtor {
CHECK_LE(size_, new_capacity);
uptr new_capacity_bytes =
RoundUpTo(new_capacity * sizeof(T), GetPageSizeCached());
- T *new_data = (T *)MmapOrDie(new_capacity_bytes, "InternalMmapVector");
+ T *new_data =
+ (T *)MmapOrDie(new_capacity_bytes, "InternalMmapVector", raw_report);
internal_memcpy(new_data, data_, size_ * sizeof(T));
- UnmapOrDie(data_, capacity_bytes_);
+ UnmapOrDie(data_, capacity_bytes_, raw_report);
data_ = new_data;
capacity_bytes_ = new_capacity_bytes;
}
@@ -1094,7 +1107,7 @@ inline u32 GetNumberOfCPUsCached() {
} // namespace __sanitizer
-inline void *operator new(__sanitizer::operator_new_size_type size,
+inline void *operator new(__sanitizer::usize size,
__sanitizer::LowLevelAllocator &alloc) {
return alloc.Allocate(size);
}
@@ -41,6 +41,7 @@
#include "sanitizer_errno.h"
#include "sanitizer_placement_new.h"
#include "sanitizer_platform_interceptors.h"
+#include "sanitizer_platform_limits_posix.h"
#include "sanitizer_symbolizer.h"
#include "sanitizer_tls_get_addr.h"
@@ -127,6 +128,39 @@ extern const short *_toupper_tab_;
extern const short *_tolower_tab_;
#endif
+#if SANITIZER_LINUX && SANITIZER_SPARC32
+// On 32-bit Linux/sparc64, double and long double are identical and glibc
+// uses a __nldbl_ (no long double) prefix for various stdio functions.
+# define __isoc23_fscanf __nldbl___isoc23_fscanf
+# define __isoc23_scanf __nldbl___isoc23_scanf
+# define __isoc23_sscanf __nldbl___isoc23_sscanf
+# define __isoc23_vfscanf __nldbl___isoc23_vfscanf
+# define __isoc23_vscanf __nldbl___isoc23_vscanf
+# define __isoc23_vsscanf __nldbl___isoc23_vsscanf
+# define __isoc99_fscanf __nldbl___isoc99_fscanf
+# define __isoc99_scanf __nldbl___isoc99_scanf
+# define __isoc99_sscanf __nldbl___isoc99_sscanf
+# define __isoc99_vfscanf __nldbl___isoc99_vfscanf
+# define __isoc99_vscanf __nldbl___isoc99_vscanf
+# define __isoc99_vsscanf __nldbl___isoc99_vsscanf
+# define asprintf __nldbl_asprintf
+# define fprintf __nldbl_fprintf
+# define fscanf __nldbl_fscanf
+# define printf __nldbl_printf
+# define scanf __nldbl_scanf
+# define snprintf __nldbl_snprintf
+# define sprintf __nldbl_sprintf
+# define sscanf __nldbl_sscanf
+# define vasprintf __nldbl_vasprintf
+# define vfprintf __nldbl_vfprintf
+# define vfscanf __nldbl_vfscanf
+# define vprintf __nldbl_vprintf
+# define vscanf __nldbl_vscanf
+# define vsnprintf __nldbl_vsnprintf
+# define vsprintf __nldbl_vsprintf
+# define vsscanf __nldbl_vsscanf
+#endif
+
#if SANITIZER_MUSL && \
(defined(__i386__) || defined(__arm__) || SANITIZER_MIPS32 || SANITIZER_PPC32)
// musl 1.2.0 on existing 32-bit architectures uses new symbol names for the
@@ -974,7 +1008,7 @@ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) {
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
- SSIZE_T res = REAL(read)(fd, ptr, count);
+ SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(read)(fd, ptr, count);
if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
return res;
@@ -1009,7 +1043,7 @@ INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) {
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
- SSIZE_T res = REAL(pread)(fd, ptr, count, offset);
+ SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(pread)(fd, ptr, count, offset);
if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
return res;
@@ -1027,7 +1061,7 @@ INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) {
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
- SSIZE_T res = REAL(pread64)(fd, ptr, count, offset);
+ SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(pread64)(fd, ptr, count, offset);
if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
return res;
@@ -1043,7 +1077,7 @@ INTERCEPTOR_WITH_SUFFIX(SSIZE_T, readv, int fd, __sanitizer_iovec *iov,
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, readv, fd, iov, iovcnt);
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
- SSIZE_T res = REAL(readv)(fd, iov, iovcnt);
+ SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(readv)(fd, iov, iovcnt);
if (res > 0) write_iovec(ctx, iov, iovcnt, res);
if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
return res;
@@ -1059,7 +1093,7 @@ INTERCEPTOR(SSIZE_T, preadv, int fd, __sanitizer_iovec *iov, int iovcnt,
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, preadv, fd, iov, iovcnt, offset);
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
- SSIZE_T res = REAL(preadv)(fd, iov, iovcnt, offset);
+ SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(preadv)(fd, iov, iovcnt, offset);
if (res > 0) write_iovec(ctx, iov, iovcnt, res);
if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
return res;
@@ -1075,7 +1109,8 @@ INTERCEPTOR(SSIZE_T, preadv64, int fd, __sanitizer_iovec *iov, int iovcnt,
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, preadv64, fd, iov, iovcnt, offset);
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
- SSIZE_T res = REAL(preadv64)(fd, iov, iovcnt, offset);
+ SSIZE_T res =
+ COMMON_INTERCEPTOR_BLOCK_REAL(preadv64)(fd, iov, iovcnt, offset);
if (res > 0) write_iovec(ctx, iov, iovcnt, res);
if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
return res;
@@ -1091,8 +1126,9 @@ INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) {
COMMON_INTERCEPTOR_ENTER(ctx, write, fd, ptr, count);
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
- SSIZE_T res = REAL(write)(fd, ptr, count);
- // FIXME: this check should be _before_ the call to REAL(write), not after
+ SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(write)(fd, ptr, count);
+ // FIXME: this check should be _before_ the call to
+ // COMMON_INTERCEPTOR_BLOCK_REAL(write), not after
if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res);
return res;
}
@@ -1121,7 +1157,7 @@ INTERCEPTOR(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count, OFF_T offset) {
COMMON_INTERCEPTOR_ENTER(ctx, pwrite, fd, ptr, count, offset);
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
- SSIZE_T res = REAL(pwrite)(fd, ptr, count, offset);
+ SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(pwrite)(fd, ptr, count, offset);
if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res);
return res;
}
@@ -1137,7 +1173,7 @@ INTERCEPTOR(SSIZE_T, pwrite64, int fd, void *ptr, OFF64_T count,
COMMON_INTERCEPTOR_ENTER(ctx, pwrite64, fd, ptr, count, offset);
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
- SSIZE_T res = REAL(pwrite64)(fd, ptr, count, offset);
+ SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(pwrite64)(fd, ptr, count, offset);
if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res);
return res;
}
@@ -1153,7 +1189,7 @@ INTERCEPTOR_WITH_SUFFIX(SSIZE_T, writev, int fd, __sanitizer_iovec *iov,
COMMON_INTERCEPTOR_ENTER(ctx, writev, fd, iov, iovcnt);
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
- SSIZE_T res = REAL(writev)(fd, iov, iovcnt);
+ SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(writev)(fd, iov, iovcnt);
if (res > 0) read_iovec(ctx, iov, iovcnt, res);
return res;
}
@@ -1169,7 +1205,7 @@ INTERCEPTOR(SSIZE_T, pwritev, int fd, __sanitizer_iovec *iov, int iovcnt,
COMMON_INTERCEPTOR_ENTER(ctx, pwritev, fd, iov, iovcnt, offset);
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
- SSIZE_T res = REAL(pwritev)(fd, iov, iovcnt, offset);
+ SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(pwritev)(fd, iov, iovcnt, offset);
if (res > 0) read_iovec(ctx, iov, iovcnt, res);
return res;
}
@@ -1185,7 +1221,8 @@ INTERCEPTOR(SSIZE_T, pwritev64, int fd, __sanitizer_iovec *iov, int iovcnt,
COMMON_INTERCEPTOR_ENTER(ctx, pwritev64, fd, iov, iovcnt, offset);
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
- SSIZE_T res = REAL(pwritev64)(fd, iov, iovcnt, offset);
+ SSIZE_T res =
+ COMMON_INTERCEPTOR_BLOCK_REAL(pwritev64)(fd, iov, iovcnt, offset);
if (res > 0) read_iovec(ctx, iov, iovcnt, res);
return res;
}
@@ -1248,9 +1285,16 @@ INTERCEPTOR(int, prctl, int option, unsigned long arg2, unsigned long arg3,
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, prctl, option, arg2, arg3, arg4, arg5);
static const int PR_SET_NAME = 15;
+ static const int PR_GET_NAME = 16;
static const int PR_SET_VMA = 0x53564d41;
static const int PR_SCHED_CORE = 62;
static const int PR_SCHED_CORE_GET = 0;
+ static const int PR_GET_PDEATHSIG = 2;
+
+# if !SANITIZER_ANDROID
+ static const int PR_SET_SECCOMP = 22;
+ static const int SECCOMP_MODE_FILTER = 2;
+# endif
if (option == PR_SET_VMA && arg2 == 0UL) {
char *name = (char *)arg5;
COMMON_INTERCEPTOR_READ_RANGE(ctx, name, internal_strlen(name) + 1);
@@ -1261,8 +1305,19 @@ INTERCEPTOR(int, prctl, int option, unsigned long arg2, unsigned long arg3,
internal_strncpy(buff, (char *)arg2, 15);
buff[15] = 0;
COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, buff);
- } else if (res != -1 && option == PR_SCHED_CORE && arg2 == PR_SCHED_CORE_GET) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (u64*)(arg5), sizeof(u64));
+ } else if (res == 0 && option == PR_GET_NAME) {
+ char *name = (char *)arg2;
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, name, internal_strlen(name) + 1);
+ } else if (res != -1 && option == PR_SCHED_CORE &&
+ arg2 == PR_SCHED_CORE_GET) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (u64 *)(arg5), sizeof(u64));
+ } else if (res != -1 && option == PR_GET_PDEATHSIG) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (u64 *)(arg2), sizeof(int));
+# if !SANITIZER_ANDROID
+ } else if (res != -1 && option == PR_SET_SECCOMP &&
+ arg2 == SECCOMP_MODE_FILTER) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (u64 *)(arg3), struct_sock_fprog_sz);
+# endif
}
return res;
}
@@ -2549,7 +2604,7 @@ INTERCEPTOR_WITH_SUFFIX(int, wait, int *status) {
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
- int res = REAL(wait)(status);
+ int res = COMMON_INTERCEPTOR_BLOCK_REAL(wait)(status);
if (res != -1 && status)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
return res;
@@ -2567,7 +2622,7 @@ INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, int id, void *infop,
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
- int res = REAL(waitid)(idtype, id, infop, options);
+ int res = COMMON_INTERCEPTOR_BLOCK_REAL(waitid)(idtype, id, infop, options);
if (res != -1 && infop)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, infop, siginfo_t_sz);
return res;
@@ -2578,7 +2633,7 @@ INTERCEPTOR_WITH_SUFFIX(int, waitpid, int pid, int *status, int options) {
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
- int res = REAL(waitpid)(pid, status, options);
+ int res = COMMON_INTERCEPTOR_BLOCK_REAL(waitpid)(pid, status, options);
if (res != -1 && status)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
return res;
@@ -2589,7 +2644,7 @@ INTERCEPTOR(int, wait3, int *status, int options, void *rusage) {
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
- int res = REAL(wait3)(status, options, rusage);
+ int res = COMMON_INTERCEPTOR_BLOCK_REAL(wait3)(status, options, rusage);
if (res != -1) {
if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz);
@@ -2603,7 +2658,8 @@ INTERCEPTOR(int, __wait4, int pid, int *status, int options, void *rusage) {
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
- int res = REAL(__wait4)(pid, status, options, rusage);
+ int res =
+ COMMON_INTERCEPTOR_BLOCK_REAL(__wait4)(pid, status, options, rusage);
if (res != -1) {
if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz);
@@ -2618,7 +2674,7 @@ INTERCEPTOR(int, wait4, int pid, int *status, int options, void *rusage) {
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
- int res = REAL(wait4)(pid, status, options, rusage);
+ int res = COMMON_INTERCEPTOR_BLOCK_REAL(wait4)(pid, status, options, rusage);
if (res != -1) {
if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status));
if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz);
@@ -2996,7 +3052,7 @@ INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) {
COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen));
addrlen0 = *addrlen;
}
- int fd2 = REAL(accept)(fd, addr, addrlen);
+ int fd2 = COMMON_INTERCEPTOR_BLOCK_REAL(accept)(fd, addr, addrlen);
if (fd2 >= 0) {
if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2);
if (addr && addrlen)
@@ -3021,7 +3077,7 @@ INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) {
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
- int fd2 = REAL(accept4)(fd, addr, addrlen, f);
+ int fd2 = COMMON_INTERCEPTOR_BLOCK_REAL(accept4)(fd, addr, addrlen, f);
if (fd2 >= 0) {
if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2);
if (addr && addrlen)
@@ -3045,7 +3101,7 @@ INTERCEPTOR(int, paccept, int fd, void *addr, unsigned *addrlen,
addrlen0 = *addrlen;
}
if (set) COMMON_INTERCEPTOR_READ_RANGE(ctx, set, sizeof(*set));
- int fd2 = REAL(paccept)(fd, addr, addrlen, set, f);
+ int fd2 = COMMON_INTERCEPTOR_BLOCK_REAL(paccept)(fd, addr, addrlen, set, f);
if (fd2 >= 0) {
if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2);
if (addr && addrlen)
@@ -3126,7 +3182,7 @@ INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct __sanitizer_msghdr *msg,
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
- SSIZE_T res = REAL(recvmsg)(fd, msg, flags);
+ SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(recvmsg)(fd, msg, flags);
if (res >= 0) {
if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
if (msg) {
@@ -3147,7 +3203,8 @@ INTERCEPTOR(int, recvmmsg, int fd, struct __sanitizer_mmsghdr *msgvec,
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, recvmmsg, fd, msgvec, vlen, flags, timeout);
if (timeout) COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout, struct_timespec_sz);
- int res = REAL(recvmmsg)(fd, msgvec, vlen, flags, timeout);
+ int res =
+ COMMON_INTERCEPTOR_BLOCK_REAL(recvmmsg)(fd, msgvec, vlen, flags, timeout);
if (res >= 0) {
if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
for (int i = 0; i < res; ++i) {
@@ -3225,7 +3282,7 @@ INTERCEPTOR(SSIZE_T, sendmsg, int fd, struct __sanitizer_msghdr *msg,
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
}
- SSIZE_T res = REAL(sendmsg)(fd, msg, flags);
+ SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(sendmsg)(fd, msg, flags);
if (common_flags()->intercept_send && res >= 0 && msg)
read_msghdr(ctx, msg, res);
return res;
@@ -3244,7 +3301,7 @@ INTERCEPTOR(int, sendmmsg, int fd, struct __sanitizer_mmsghdr *msgvec,
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
}
- int res = REAL(sendmmsg)(fd, msgvec, vlen, flags);
+ int res = COMMON_INTERCEPTOR_BLOCK_REAL(sendmmsg)(fd, msgvec, vlen, flags);
if (res >= 0 && msgvec) {
for (int i = 0; i < res; ++i) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &msgvec[i].msg_len,
@@ -3267,7 +3324,7 @@ INTERCEPTOR(int, msgsnd, int msqid, const void *msgp, SIZE_T msgsz,
COMMON_INTERCEPTOR_ENTER(ctx, msgsnd, msqid, msgp, msgsz, msgflg);
if (msgp)
COMMON_INTERCEPTOR_READ_RANGE(ctx, msgp, sizeof(long) + msgsz);
- int res = REAL(msgsnd)(msqid, msgp, msgsz, msgflg);
+ int res = COMMON_INTERCEPTOR_BLOCK_REAL(msgsnd)(msqid, msgp, msgsz, msgflg);
return res;
}
@@ -3275,7 +3332,8 @@ INTERCEPTOR(SSIZE_T, msgrcv, int msqid, void *msgp, SIZE_T msgsz,
long msgtyp, int msgflg) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, msgrcv, msqid, msgp, msgsz, msgtyp, msgflg);
- SSIZE_T len = REAL(msgrcv)(msqid, msgp, msgsz, msgtyp, msgflg);
+ SSIZE_T len =
+ COMMON_INTERCEPTOR_BLOCK_REAL(msgrcv)(msqid, msgp, msgsz, msgtyp, msgflg);
if (len != -1)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msgp, sizeof(long) + len);
return len;
@@ -3416,23 +3474,27 @@ INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) {
COMMON_INTERCEPTOR_ENTER(ctx, ptrace, request, pid, addr, data);
__sanitizer_iovec local_iovec;
- if (data) {
+ void *data_arg = ptrace_data_arg(request, addr, data);
+ if (data_arg) {
if (request == ptrace_setregs) {
- COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_regs_struct_sz);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data_arg, struct_user_regs_struct_sz);
} else if (request == ptrace_setfpregs) {
- COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpregs_struct_sz);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data_arg,
+ struct_user_fpregs_struct_sz);
} else if (request == ptrace_setfpxregs) {
- COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpxregs_struct_sz);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data_arg,
+ struct_user_fpxregs_struct_sz);
} else if (request == ptrace_setvfpregs) {
- COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_vfpregs_struct_sz);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data_arg,
+ struct_user_vfpregs_struct_sz);
} else if (request == ptrace_setsiginfo) {
- COMMON_INTERCEPTOR_READ_RANGE(ctx, data, siginfo_t_sz);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data_arg, siginfo_t_sz);
- // Some kernel might zero the iovec::iov_base in case of invalid
- // write access. In this case copy the invalid address for further
- // inspection.
+ // Some kernel might zero the iovec::iov_base in case of invalid
+ // write access. In this case copy the invalid address for further
+ // inspection.
} else if (request == ptrace_setregset || request == ptrace_getregset) {
- __sanitizer_iovec *iovec = (__sanitizer_iovec*)data;
+ __sanitizer_iovec *iovec = (__sanitizer_iovec *)data_arg;
COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec, sizeof(*iovec));
local_iovec = *iovec;
if (request == ptrace_setregset)
@@ -3445,23 +3507,26 @@ INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) {
// https://github.com/google/sanitizers/issues/321.
uptr res = REAL(ptrace)(request, pid, addr, data);
- if (!res && data) {
+ if (!res && data_arg) {
// Note that PEEK* requests assign different meaning to the return value.
// This function does not handle them (nor does it need to).
if (request == ptrace_getregs) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_regs_struct_sz);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg, struct_user_regs_struct_sz);
} else if (request == ptrace_getfpregs) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpregs_struct_sz);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg,
+ struct_user_fpregs_struct_sz);
} else if (request == ptrace_getfpxregs) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpxregs_struct_sz);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg,
+ struct_user_fpxregs_struct_sz);
} else if (request == ptrace_getvfpregs) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_vfpregs_struct_sz);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg,
+ struct_user_vfpregs_struct_sz);
} else if (request == ptrace_getsiginfo) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, siginfo_t_sz);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg, siginfo_t_sz);
} else if (request == ptrace_geteventmsg) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, sizeof(unsigned long));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data_arg, sizeof(unsigned long));
} else if (request == ptrace_getregset) {
- __sanitizer_iovec *iovec = (__sanitizer_iovec*)data;
+ __sanitizer_iovec *iovec = (__sanitizer_iovec *)data_arg;
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iovec, sizeof(*iovec));
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, local_iovec.iov_base,
local_iovec.iov_len);
@@ -6119,7 +6184,7 @@ INTERCEPTOR(int, flopen, const char *path, int flags, ...) {
if (path) {
COMMON_INTERCEPTOR_READ_RANGE(ctx, path, internal_strlen(path) + 1);
}
- return REAL(flopen)(path, flags, mode);
+ return COMMON_INTERCEPTOR_BLOCK_REAL(flopen)(path, flags, mode);
}
INTERCEPTOR(int, flopenat, int dirfd, const char *path, int flags, ...) {
@@ -6132,7 +6197,7 @@ INTERCEPTOR(int, flopenat, int dirfd, const char *path, int flags, ...) {
if (path) {
COMMON_INTERCEPTOR_READ_RANGE(ctx, path, internal_strlen(path) + 1);
}
- return REAL(flopenat)(dirfd, path, flags, mode);
+ return COMMON_INTERCEPTOR_BLOCK_REAL(flopenat)(dirfd, path, flags, mode);
}
#define INIT_FLOPEN \
@@ -6325,9 +6390,9 @@ INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
const char *SelfFName = DladdrSelfFName();
VPrintf(1, "dlopen interceptor: DladdrSelfFName: %p %s\n",
- (void *)SelfFName, SelfFName);
+ (const void *)SelfFName, SelfFName);
- if (internal_strcmp(SelfFName, filename) == 0) {
+ if (SelfFName && internal_strcmp(SelfFName, filename) == 0) {
// It's possible they copied the string from dladdr, so
// we do a string comparison rather than pointer comparison.
VPrintf(1, "dlopen interceptor: replacing %s because it matches %s\n",
@@ -6717,7 +6782,7 @@ INTERCEPTOR(SSIZE_T, recv, int fd, void *buf, SIZE_T len, int flags) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, recv, fd, buf, len, flags);
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
- SSIZE_T res = REAL(recv)(fd, buf, len, flags);
+ SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(recv)(fd, buf, len, flags);
if (res > 0) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, Min((SIZE_T)res, len));
}
@@ -6734,7 +6799,8 @@ INTERCEPTOR(SSIZE_T, recvfrom, int fd, void *buf, SIZE_T len, int flags,
SIZE_T srcaddr_sz;
if (srcaddr) srcaddr_sz = *addrlen;
(void)srcaddr_sz; // prevent "set but not used" warning
- SSIZE_T res = REAL(recvfrom)(fd, buf, len, flags, srcaddr, addrlen);
+ SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(recvfrom)(fd, buf, len, flags,
+ srcaddr, addrlen);
if (res > 0)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, Min((SIZE_T)res, len));
if (res >= 0 && srcaddr)
@@ -6757,7 +6823,7 @@ INTERCEPTOR(SSIZE_T, send, int fd, void *buf, SIZE_T len, int flags) {
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
}
- SSIZE_T res = REAL(send)(fd, buf, len, flags);
+ SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(send)(fd, buf, len, flags);
if (common_flags()->intercept_send && res > 0)
COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, Min((SIZE_T)res, len));
return res;
@@ -6772,7 +6838,8 @@ INTERCEPTOR(SSIZE_T, sendto, int fd, void *buf, SIZE_T len, int flags,
COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
}
// Can't check dstaddr as it may have uninitialized padding at the end.
- SSIZE_T res = REAL(sendto)(fd, buf, len, flags, dstaddr, addrlen);
+ SSIZE_T res = COMMON_INTERCEPTOR_BLOCK_REAL(sendto)(fd, buf, len, flags,
+ dstaddr, addrlen);
if (common_flags()->intercept_send && res > 0)
COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, Min((SIZE_T)res, len));
return res;
@@ -6785,25 +6852,25 @@ INTERCEPTOR(SSIZE_T, sendto, int fd, void *buf, SIZE_T len, int flags,
#endif
#if SANITIZER_INTERCEPT_EVENTFD_READ_WRITE
-INTERCEPTOR(int, eventfd_read, int fd, u64 *value) {
+INTERCEPTOR(int, eventfd_read, int fd, __sanitizer_eventfd_t *value) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, eventfd_read, fd, value);
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
- int res = REAL(eventfd_read)(fd, value);
+ int res = COMMON_INTERCEPTOR_BLOCK_REAL(eventfd_read)(fd, value);
if (res == 0) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, sizeof(*value));
if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
}
return res;
}
-INTERCEPTOR(int, eventfd_write, int fd, u64 value) {
+INTERCEPTOR(int, eventfd_write, int fd, __sanitizer_eventfd_t value) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, eventfd_write, fd, value);
if (fd >= 0) {
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
}
- int res = REAL(eventfd_write)(fd, value);
+ int res = COMMON_INTERCEPTOR_BLOCK_REAL(eventfd_write)(fd, value);
return res;
}
#define INIT_EVENTFD_READ_WRITE \
@@ -7426,7 +7493,8 @@ INTERCEPTOR(int, open_by_handle_at, int mount_fd, struct file_handle* handle,
COMMON_INTERCEPTOR_READ_RANGE(
ctx, &sanitizer_handle->f_handle, sanitizer_handle->handle_bytes);
- return REAL(open_by_handle_at)(mount_fd, handle, flags);
+ return COMMON_INTERCEPTOR_BLOCK_REAL(open_by_handle_at)(mount_fd, handle,
+ flags);
}
#define INIT_OPEN_BY_HANDLE_AT COMMON_INTERCEPT_FUNCTION(open_by_handle_at)
@@ -7641,9 +7709,9 @@ static void write_protoent(void *ctx, struct __sanitizer_protoent *p) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->p_aliases, pp_size * sizeof(char *));
}
-INTERCEPTOR(struct __sanitizer_protoent *, getprotoent) {
+INTERCEPTOR(struct __sanitizer_protoent *, getprotoent,) {
void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, getprotoent);
+ COMMON_INTERCEPTOR_ENTER(ctx, getprotoent,);
struct __sanitizer_protoent *p = REAL(getprotoent)();
if (p)
write_protoent(ctx, p);
@@ -7730,9 +7798,9 @@ INTERCEPTOR(int, getprotobynumber_r, int num,
#endif
#if SANITIZER_INTERCEPT_NETENT
-INTERCEPTOR(struct __sanitizer_netent *, getnetent) {
+INTERCEPTOR(struct __sanitizer_netent *, getnetent,) {
void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, getnetent);
+ COMMON_INTERCEPTOR_ENTER(ctx, getnetent,);
struct __sanitizer_netent *n = REAL(getnetent)();
if (n) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n));
@@ -8809,83 +8877,6 @@ INTERCEPTOR(char *, RMD160Data, u8 *data, SIZE_T len, char *buf) {
#define INIT_RMD160
#endif
-#if SANITIZER_INTERCEPT_MD5
-INTERCEPTOR(void, MD5Init, void *context) {
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, MD5Init, context);
- REAL(MD5Init)(context);
- if (context)
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD5_CTX_sz);
-}
-
-INTERCEPTOR(void, MD5Update, void *context, const unsigned char *data,
- unsigned int len) {
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, MD5Update, context, data, len);
- if (data && len > 0)
- COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
- if (context)
- COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD5_CTX_sz);
- REAL(MD5Update)(context, data, len);
- if (context)
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD5_CTX_sz);
-}
-
-INTERCEPTOR(void, MD5Final, unsigned char digest[16], void *context) {
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, MD5Final, digest, context);
- if (context)
- COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD5_CTX_sz);
- REAL(MD5Final)(digest, context);
- if (digest)
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, sizeof(unsigned char) * 16);
-}
-
-INTERCEPTOR(char *, MD5End, void *context, char *buf) {
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, MD5End, context, buf);
- if (context)
- COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD5_CTX_sz);
- char *ret = REAL(MD5End)(context, buf);
- if (ret)
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD5_return_length);
- return ret;
-}
-
-INTERCEPTOR(char *, MD5File, const char *filename, char *buf) {
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, MD5File, filename, buf);
- if (filename)
- COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, internal_strlen(filename) + 1);
- char *ret = REAL(MD5File)(filename, buf);
- if (ret)
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD5_return_length);
- return ret;
-}
-
-INTERCEPTOR(char *, MD5Data, const unsigned char *data, unsigned int len,
- char *buf) {
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, MD5Data, data, len, buf);
- if (data && len > 0)
- COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
- char *ret = REAL(MD5Data)(data, len, buf);
- if (ret)
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD5_return_length);
- return ret;
-}
-
-#define INIT_MD5 \
- COMMON_INTERCEPT_FUNCTION(MD5Init); \
- COMMON_INTERCEPT_FUNCTION(MD5Update); \
- COMMON_INTERCEPT_FUNCTION(MD5Final); \
- COMMON_INTERCEPT_FUNCTION(MD5End); \
- COMMON_INTERCEPT_FUNCTION(MD5File); \
- COMMON_INTERCEPT_FUNCTION(MD5Data)
-#else
-#define INIT_MD5
-#endif
-
#if SANITIZER_INTERCEPT_FSEEK
INTERCEPTOR(int, fseek, __sanitizer_FILE *stream, long int offset, int whence) {
void *ctx;
@@ -9016,107 +9007,6 @@ INTERCEPTOR(char *, MD2Data, const unsigned char *data, unsigned int len,
#define INIT_MD2
#endif
-#if SANITIZER_INTERCEPT_SHA2
-#define SHA2_INTERCEPTORS(LEN, SHA2_STATE_T) \
- INTERCEPTOR(void, SHA##LEN##_Init, void *context) { \
- void *ctx; \
- COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Init, context); \
- REAL(SHA##LEN##_Init)(context); \
- if (context) \
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
- } \
- INTERCEPTOR(void, SHA##LEN##_Update, void *context, \
- const u8 *data, SIZE_T len) { \
- void *ctx; \
- COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Update, context, data, len); \
- if (data && len > 0) \
- COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len); \
- if (context) \
- COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
- REAL(SHA##LEN##_Update)(context, data, len); \
- if (context) \
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
- } \
- INTERCEPTOR(void, SHA##LEN##_Final, u8 digest[LEN/8], \
- void *context) { \
- void *ctx; \
- CHECK_EQ(SHA##LEN##_digest_length, LEN/8); \
- COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Final, digest, context); \
- if (context) \
- COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
- REAL(SHA##LEN##_Final)(digest, context); \
- if (digest) \
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, \
- sizeof(digest[0]) * \
- SHA##LEN##_digest_length); \
- } \
- INTERCEPTOR(char *, SHA##LEN##_End, void *context, char *buf) { \
- void *ctx; \
- COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_End, context, buf); \
- if (context) \
- COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
- char *ret = REAL(SHA##LEN##_End)(context, buf); \
- if (ret) \
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \
- return ret; \
- } \
- INTERCEPTOR(char *, SHA##LEN##_File, const char *filename, char *buf) { \
- void *ctx; \
- COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_File, filename, buf); \
- if (filename) \
- COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, internal_strlen(filename) + 1);\
- char *ret = REAL(SHA##LEN##_File)(filename, buf); \
- if (ret) \
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \
- return ret; \
- } \
- INTERCEPTOR(char *, SHA##LEN##_FileChunk, const char *filename, char *buf, \
- OFF_T offset, OFF_T length) { \
- void *ctx; \
- COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_FileChunk, filename, buf, offset, \
- length); \
- if (filename) \
- COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, internal_strlen(filename) + 1);\
- char *ret = REAL(SHA##LEN##_FileChunk)(filename, buf, offset, length); \
- if (ret) \
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \
- return ret; \
- } \
- INTERCEPTOR(char *, SHA##LEN##_Data, u8 *data, SIZE_T len, char *buf) { \
- void *ctx; \
- COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Data, data, len, buf); \
- if (data && len > 0) \
- COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len); \
- char *ret = REAL(SHA##LEN##_Data)(data, len, buf); \
- if (ret) \
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \
- return ret; \
- }
-
-SHA2_INTERCEPTORS(224, u32)
-SHA2_INTERCEPTORS(256, u32)
-SHA2_INTERCEPTORS(384, u64)
-SHA2_INTERCEPTORS(512, u64)
-
-#define INIT_SHA2_INTECEPTORS(LEN) \
- COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Init); \
- COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Update); \
- COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Final); \
- COMMON_INTERCEPT_FUNCTION(SHA##LEN##_End); \
- COMMON_INTERCEPT_FUNCTION(SHA##LEN##_File); \
- COMMON_INTERCEPT_FUNCTION(SHA##LEN##_FileChunk); \
- COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Data)
-
-#define INIT_SHA2 \
- INIT_SHA2_INTECEPTORS(224); \
- INIT_SHA2_INTECEPTORS(256); \
- INIT_SHA2_INTECEPTORS(384); \
- INIT_SHA2_INTECEPTORS(512)
-#undef SHA2_INTERCEPTORS
-#else
-#define INIT_SHA2
-#endif
-
#if SANITIZER_INTERCEPT_VIS
INTERCEPTOR(char *, vis, char *dst, int c, int flag, int nextc) {
void *ctx;
@@ -9894,9 +9784,9 @@ INTERCEPTOR(char *, fdevname_r, int fd, char *buf, SIZE_T len) {
#endif
#if SANITIZER_INTERCEPT_GETUSERSHELL
-INTERCEPTOR(char *, getusershell) {
+INTERCEPTOR(char *, getusershell,) {
void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, getusershell);
+ COMMON_INTERCEPTOR_ENTER(ctx, getusershell,);
char *res = REAL(getusershell)();
if (res)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, internal_strlen(res) + 1);
@@ -9965,7 +9855,13 @@ INTERCEPTOR(void, sl_free, void *sl, int freeall) {
INTERCEPTOR(SSIZE_T, getrandom, void *buf, SIZE_T buflen, unsigned int flags) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getrandom, buf, buflen, flags);
- SSIZE_T n = REAL(getrandom)(buf, buflen, flags);
+ // If GRND_NONBLOCK is set in the flags, it is non blocking.
+ static const int grnd_nonblock = 1;
+ SSIZE_T n;
+ if ((flags & grnd_nonblock))
+ n = REAL(getrandom)(buf, buflen, flags);
+ else
+ n = COMMON_INTERCEPTOR_BLOCK_REAL(getrandom)(buf, buflen, flags);
if (n > 0) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, n);
}
@@ -10212,20 +10108,6 @@ INTERCEPTOR(int, __xuname, int size, void *utsname) {
#define INIT___XUNAME
#endif
-#if SANITIZER_INTERCEPT_HEXDUMP
-INTERCEPTOR(void, hexdump, const void *ptr, int length, const char *header, int flags) {
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, hexdump, ptr, length, header, flags);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, length);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, header, internal_strlen(header) + 1);
- REAL(hexdump)(ptr, length, header, flags);
-}
-
-#define INIT_HEXDUMP COMMON_INTERCEPT_FUNCTION(hexdump);
-#else
-#define INIT_HEXDUMP
-#endif
-
#if SANITIZER_INTERCEPT_ARGP_PARSE
INTERCEPTOR(int, argp_parse, const struct argp *argp, int argc, char **argv,
unsigned flags, int *arg_index, void *input) {
@@ -10258,6 +10140,55 @@ INTERCEPTOR(int, cpuset_getaffinity, int level, int which, __int64_t id, SIZE_T
#define INIT_CPUSET_GETAFFINITY
#endif
+#if SANITIZER_INTERCEPT_PREADV2
+INTERCEPTOR(SSIZE_T, preadv2, int fd, __sanitizer_iovec *iov, int iovcnt,
+ OFF_T offset, int flags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, preadv2, fd, iov, iovcnt, offset, flags);
+ COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+ SSIZE_T res = REAL(preadv2)(fd, iov, iovcnt, offset, flags);
+ if (res > 0) write_iovec(ctx, iov, iovcnt, res);
+ if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+ return res;
+}
+#define INIT_PREADV2 COMMON_INTERCEPT_FUNCTION(preadv2)
+#else
+#define INIT_PREADV2
+#endif
+
+#if SANITIZER_INTERCEPT_PWRITEV2
+INTERCEPTOR(SSIZE_T, pwritev2, int fd, __sanitizer_iovec *iov, int iovcnt,
+ OFF_T offset, int flags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pwritev2, fd, iov, iovcnt, offset, flags);
+ COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+ if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
+ SSIZE_T res = REAL(pwritev2)(fd, iov, iovcnt, offset, flags);
+ if (res > 0) read_iovec(ctx, iov, iovcnt, res);
+ return res;
+}
+#define INIT_PWRITEV2 COMMON_INTERCEPT_FUNCTION(pwritev2)
+#else
+#define INIT_PWRITEV2
+#endif
+
+#if SANITIZER_INTERCEPT_FREADLINK
+INTERCEPTOR(SSIZE_T, freadlink, int fd, char *buf, SIZE_T bufsiz) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, freadlink, fd, buf, bufsiz);
+ COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+ SSIZE_T res = REAL(freadlink)(fd, buf, bufsiz);
+ if (res > 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, res);
+ if (res >= 0 && fd > 0)
+ COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+ return res;
+}
+# define INIT_FREADLINK COMMON_INTERCEPT_FUNCTION(freadlink)
+#else
+# define INIT_FREADLINK
+#endif
+
#include "sanitizer_common_interceptors_netbsd_compat.inc"
namespace __sanitizer {
@@ -10550,10 +10481,8 @@ static void InitializeCommonInterceptors() {
INIT_SHA1;
INIT_MD4;
INIT_RMD160;
- INIT_MD5;
INIT_FSEEK;
INIT_MD2;
- INIT_SHA2;
INIT_VIS;
INIT_CDB;
INIT_GETFSENT;
@@ -10575,9 +10504,11 @@ static void InitializeCommonInterceptors() {
INIT_PROCCTL
INIT_UNAME;
INIT___XUNAME;
- INIT_HEXDUMP;
INIT_ARGP_PARSE;
INIT_CPUSET_GETAFFINITY;
+ INIT_PREADV2;
+ INIT_PWRITEV2;
+ INIT_FREADLINK;
INIT___PRINTF_CHK;
}
@@ -10,6 +10,7 @@
INTERFACE_FUNCTION(__sanitizer_acquire_crash_state)
INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container)
INTERFACE_FUNCTION(__sanitizer_annotate_double_ended_contiguous_container)
+INTERFACE_FUNCTION(__sanitizer_copy_contiguous_container_annotations)
INTERFACE_FUNCTION(__sanitizer_contiguous_container_find_bad_address)
INTERFACE_FUNCTION(
__sanitizer_double_ended_contiguous_container_find_bad_address)
@@ -22,6 +23,7 @@ INTERFACE_FUNCTION(__sanitizer_verify_double_ended_contiguous_container)
INTERFACE_WEAK_FUNCTION(__sanitizer_on_print)
INTERFACE_WEAK_FUNCTION(__sanitizer_report_error_summary)
INTERFACE_WEAK_FUNCTION(__sanitizer_sandbox_on_notify)
+INTERFACE_WEAK_FUNCTION(__sanitizer_get_dtls_size)
// Sanitizer weak hooks
INTERFACE_WEAK_FUNCTION(__sanitizer_weak_hook_memcmp)
INTERFACE_WEAK_FUNCTION(__sanitizer_weak_hook_strcmp)
@@ -46,7 +48,14 @@ INTERFACE_FUNCTION(__sanitizer_purge_allocator)
INTERFACE_FUNCTION(__sanitizer_print_memory_profile)
INTERFACE_WEAK_FUNCTION(__sanitizer_free_hook)
INTERFACE_WEAK_FUNCTION(__sanitizer_malloc_hook)
+INTERFACE_WEAK_FUNCTION(__sanitizer_ignore_free_hook)
// Memintrinsic functions.
INTERFACE_FUNCTION(__sanitizer_internal_memcpy)
INTERFACE_FUNCTION(__sanitizer_internal_memmove)
INTERFACE_FUNCTION(__sanitizer_internal_memset)
+
+#if SANITIZER_WINDOWS
+INTERFACE_FUNCTION(__sanitizer_override_function)
+INTERFACE_FUNCTION(__sanitizer_override_function_by_addr)
+INTERFACE_FUNCTION(__sanitizer_register_weak_function)
+#endif
@@ -87,8 +87,8 @@ void MaybeStartBackgroudThread() {
if (!common_flags()->hard_rss_limit_mb &&
!common_flags()->soft_rss_limit_mb &&
!common_flags()->heap_profile) return;
- if (!&real_pthread_create) {
- VPrintf(1, "%s: real_pthread_create undefined\n", SanitizerToolName);
+ if (!&internal_pthread_create) {
+ VPrintf(1, "%s: internal_pthread_create undefined\n", SanitizerToolName);
return; // Can't spawn the thread anyway.
}
@@ -169,9 +169,9 @@ void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name,
: !MmapFixedNoReserve(beg, size, name)) {
Report(
"ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. "
- "Perhaps you're using ulimit -v\n",
+ "Perhaps you're using ulimit -v or ulimit -d\n",
size);
- Abort();
+ Die();
}
if (madvise_shadow && common_flags()->use_madv_dontdump)
DontDumpShadowMemory(beg, size);
@@ -219,6 +219,32 @@ static void StopStackDepotBackgroundThread() {
static void StopStackDepotBackgroundThread() {}
#endif
+void MemCpyAccessible(void *dest, const void *src, uptr n) {
+ if (TryMemCpy(dest, src, n))
+ return;
+
+ const uptr page_size = GetPageSize();
+ uptr b = reinterpret_cast<uptr>(src);
+ uptr b_up = RoundUpTo(b, page_size);
+
+ uptr e = reinterpret_cast<uptr>(src) + n;
+ uptr e_down = RoundDownTo(e, page_size);
+
+ auto copy_or_zero = [dest, src](uptr beg, uptr end) {
+ const uptr udest = reinterpret_cast<uptr>(dest);
+ const uptr usrc = reinterpret_cast<uptr>(src);
+ void *d = reinterpret_cast<void *>(udest + (beg - usrc));
+ const uptr size = end - beg;
+ if (!TryMemCpy(d, reinterpret_cast<void *>(beg), size))
+ internal_memset(d, 0, size);
+ };
+
+ copy_or_zero(b, b_up);
+ for (uptr p = b_up; p < e_down; p += page_size)
+ copy_or_zero(p, p + page_size);
+ copy_or_zero(e_down, e);
+}
+
} // namespace __sanitizer
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_sandbox_on_notify,
@@ -20,13 +20,14 @@ namespace __sanitizer {
// The Windows implementations of these functions use the win32 API directly,
// bypassing libc.
#if !SANITIZER_WINDOWS
-#if SANITIZER_LINUX
+# if SANITIZER_LINUX
void LogMessageOnPrintf(const char *str) {}
-#endif
+void InitTlsSize() {}
+# endif
void WriteToSyslog(const char *buffer) {}
void Abort() { internal__exit(1); }
bool CreateDir(const char *pathname) { return false; }
-#endif // !SANITIZER_WINDOWS
+#endif // !SANITIZER_WINDOWS
#if !SANITIZER_WINDOWS && !SANITIZER_APPLE
void ListOfModules::init() {}
@@ -38,12 +38,17 @@
// Called before fork syscall.
// COMMON_SYSCALL_POST_FORK(long res)
// Called after fork syscall.
+// COMMON_SYSCALL_BLOCKING_START()
+// Called before blocking syscall.
+// COMMON_SYSCALL_BLOCKING_END()
+// Called after blocking syscall.
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
#if SANITIZER_LINUX
# include "sanitizer_libc.h"
+# include "sanitizer_platform_limits_posix.h"
# define PRE_SYSCALL(name) \
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_syscall_pre_impl_##name
@@ -85,6 +90,16 @@
{}
# endif
+# ifndef COMMON_SYSCALL_BLOCKING_START
+# define COMMON_SYSCALL_BLOCKING_START() \
+ {}
+# endif
+
+# ifndef COMMON_SYSCALL_BLOCKING_END
+# define COMMON_SYSCALL_BLOCKING_END() \
+ {}
+# endif
+
// FIXME: do some kind of PRE_READ for all syscall arguments (int(s) and such).
extern "C" {
@@ -2516,18 +2531,19 @@ PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) {
# if !SANITIZER_ANDROID && \
(defined(__i386) || defined(__x86_64) || defined(__mips64) || \
defined(__powerpc64__) || defined(__aarch64__) || defined(__s390__) || \
- defined(__loongarch__) || SANITIZER_RISCV64)
- if (data) {
+ defined(__loongarch__) || SANITIZER_RISCV64 || defined(__sparc__))
+ long data_arg = ptrace_data_arg(request, addr, data);
+ if (data_arg) {
if (request == ptrace_setregs) {
- PRE_READ((void *)data, struct_user_regs_struct_sz);
+ PRE_READ((void *)data_arg, struct_user_regs_struct_sz);
} else if (request == ptrace_setfpregs) {
- PRE_READ((void *)data, struct_user_fpregs_struct_sz);
+ PRE_READ((void *)data_arg, struct_user_fpregs_struct_sz);
} else if (request == ptrace_setfpxregs) {
- PRE_READ((void *)data, struct_user_fpxregs_struct_sz);
+ PRE_READ((void *)data_arg, struct_user_fpxregs_struct_sz);
} else if (request == ptrace_setsiginfo) {
- PRE_READ((void *)data, siginfo_t_sz);
+ PRE_READ((void *)data_arg, siginfo_t_sz);
} else if (request == ptrace_setregset) {
- __sanitizer_iovec *iov = (__sanitizer_iovec *)data;
+ __sanitizer_iovec *iov = (__sanitizer_iovec *)data_arg;
PRE_READ(iov->iov_base, iov->iov_len);
}
}
@@ -2538,25 +2554,26 @@ POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) {
# if !SANITIZER_ANDROID && \
(defined(__i386) || defined(__x86_64) || defined(__mips64) || \
defined(__powerpc64__) || defined(__aarch64__) || defined(__s390__) || \
- defined(__loongarch__) || SANITIZER_RISCV64)
- if (res >= 0 && data) {
+ defined(__loongarch__) || SANITIZER_RISCV64 || defined(__sparc__))
+ long data_arg = ptrace_data_arg(request, addr, data);
+ if (res >= 0 && data_arg) {
// Note that this is different from the interceptor in
// sanitizer_common_interceptors.inc.
// PEEK* requests return resulting values through data pointer.
if (request == ptrace_getregs) {
- POST_WRITE((void *)data, struct_user_regs_struct_sz);
+ POST_WRITE((void *)data_arg, struct_user_regs_struct_sz);
} else if (request == ptrace_getfpregs) {
- POST_WRITE((void *)data, struct_user_fpregs_struct_sz);
+ POST_WRITE((void *)data_arg, struct_user_fpregs_struct_sz);
} else if (request == ptrace_getfpxregs) {
- POST_WRITE((void *)data, struct_user_fpxregs_struct_sz);
+ POST_WRITE((void *)data_arg, struct_user_fpxregs_struct_sz);
} else if (request == ptrace_getsiginfo) {
- POST_WRITE((void *)data, siginfo_t_sz);
+ POST_WRITE((void *)data_arg, siginfo_t_sz);
} else if (request == ptrace_getregset) {
- __sanitizer_iovec *iov = (__sanitizer_iovec *)data;
+ __sanitizer_iovec *iov = (__sanitizer_iovec *)data_arg;
POST_WRITE(iov->iov_base, iov->iov_len);
} else if (request == ptrace_peekdata || request == ptrace_peektext ||
request == ptrace_peekuser) {
- POST_WRITE((void *)data, sizeof(void *));
+ POST_WRITE((void *)data_arg, sizeof(void *));
}
}
# endif
@@ -2808,6 +2825,15 @@ PRE_SYSCALL(fchownat)
POST_SYSCALL(fchownat)
(long res, long dfd, const void *filename, long user, long group, long flag) {}
+PRE_SYSCALL(fchmodat2)(long dfd, const void *filename, long mode, long flag) {
+ if (filename)
+ PRE_READ(filename,
+ __sanitizer::internal_strlen((const char *)filename) + 1);
+}
+
+POST_SYSCALL(fchmodat2)
+(long res, long dfd, const void *filename, long mode, long flag) {}
+
PRE_SYSCALL(openat)(long dfd, const void *filename, long flags, long mode) {
if (filename)
PRE_READ(filename,
@@ -3167,6 +3193,18 @@ POST_SYSCALL(sigaltstack)(long res, void *ss, void *oss) {
}
}
}
+
+PRE_SYSCALL(futex)
+(void *uaddr, long futex_op, long val, void *timeout, void *uaddr2, long val3) {
+ COMMON_SYSCALL_BLOCKING_START();
+}
+
+POST_SYSCALL(futex)
+(long res, void *uaddr, long futex_op, long val, void *timeout, void *uaddr2,
+ long val3) {
+ COMMON_SYSCALL_BLOCKING_END();
+}
+
} // extern "C"
# undef PRE_SYSCALL
@@ -35,7 +35,7 @@
#include "sanitizer_common.h"
#include "sanitizer_interface_internal.h"
#include "sanitizer_internal_defs.h"
-#include "sanitizer_symbolizer_fuchsia.h"
+# include "sanitizer_symbolizer_markup_constants.h"
using namespace __sanitizer;
@@ -75,7 +75,8 @@ static void SanitizerDumpCoverage(const uptr* unsorted_pcs, uptr len) {
if (!pc) continue;
if (!GetModuleAndOffsetForPc(pc, nullptr, 0, &pcs[i])) {
- Printf("ERROR: unknown pc 0x%zx (may happen if dlclose is used)\n", pc);
+ Printf("ERROR: unknown pc %p (may happen if dlclose is used)\n",
+ (void*)pc);
continue;
}
uptr module_base = pc - pcs[i];
deleted file mode 100644
@@ -1,20 +0,0 @@
-//===-- sanitizer_coverage_win_dll_thunk.cpp ------------------------------===//
-//
-// 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 defines a family of thunks that should be statically linked into
-// the DLLs that have instrumentation in order to delegate the calls to the
-// shared runtime that lives in the main binary.
-// See https://github.com/google/sanitizers/issues/209 for the details.
-//===----------------------------------------------------------------------===//
-#ifdef SANITIZER_DLL_THUNK
-#include "sanitizer_win_dll_thunk.h"
-// Sanitizer Coverage interface functions.
-#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
-#include "sanitizer_coverage_interface.inc"
-#endif // SANITIZER_DLL_THUNK
similarity index 59%
rename from libsanitizer/sanitizer_common/sanitizer_coverage_win_dynamic_runtime_thunk.cpp
rename to libsanitizer/sanitizer_common/sanitizer_coverage_win_runtime_thunk.cpp
@@ -1,4 +1,4 @@
-//===-- sanitizer_coverage_win_dynamic_runtime_thunk.cpp ------------------===//
+//===-- sanitizer_coverage_win_runtime_thunk.cpp --------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -10,17 +10,20 @@
// to interact with Sanitizer Coverage, when it is included in a dll.
//
//===----------------------------------------------------------------------===//
-#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK
-#define SANITIZER_IMPORT_INTERFACE 1
-#include "sanitizer_win_defs.h"
+#if defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) || \
+ defined(SANITIZER_STATIC_RUNTIME_THUNK)
+# define SANITIZER_IMPORT_INTERFACE 1
+# include "sanitizer_win_defs.h"
+# include "sanitizer_win_thunk_interception.h"
// Define weak alias for all weak functions imported from sanitizer coverage.
-#define INTERFACE_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name)
-#include "sanitizer_coverage_interface.inc"
-#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK
+# define INTERFACE_FUNCTION(Name)
+# define INTERFACE_WEAK_FUNCTION(Name) REGISTER_WEAK_FUNCTION(Name)
+# include "sanitizer_coverage_interface.inc"
+#endif // defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) ||
+ // defined(SANITIZER_STATIC_RUNTIME_THUNK)
namespace __sanitizer {
// Add one, otherwise unused, external symbol to this object file so that the
// Visual C++ linker includes it and reads the .drective section.
void ForceWholeArchiveIncludeForSanCov() {}
-}
+} // namespace __sanitizer
deleted file mode 100644
@@ -1,23 +0,0 @@
-//===-- sanitizer_coverage_win_weak_interception.cpp ----------------------===//
-//
-// 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 module should be included in Sanitizer Coverage when it implemented as a
-// shared library on Windows (dll), in order to delegate the calls of weak
-// functions to the implementation in the main executable when a strong
-// definition is provided.
-//===----------------------------------------------------------------------===//
-#ifdef SANITIZER_DYNAMIC
-#include "sanitizer_win_weak_interception.h"
-#include "sanitizer_interface_internal.h"
-#include "sancov_flags.h"
-// Check if strong definitions for weak functions are present in the main
-// executable. If that is the case, override dll functions to point to strong
-// implementations.
-#define INTERFACE_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
-#include "sanitizer_coverage_interface.inc"
-#endif // SANITIZER_DYNAMIC
@@ -69,24 +69,14 @@ class DenseMapBase {
setNumTombstones(0);
}
+ /// Return true if the specified key is in the map, false otherwise.
+ bool contains(const KeyT &Key) const { return doFind(Key) != nullptr; }
+
/// Return 1 if the specified key is in the map, 0 otherwise.
- size_type count(const KeyT &Key) const {
- const BucketT *TheBucket;
- return LookupBucketFor(Key, TheBucket) ? 1 : 0;
- }
+ size_type count(const KeyT &Key) const { return contains(Key) ? 1 : 0; }
- value_type *find(const KeyT &Key) {
- BucketT *TheBucket;
- if (LookupBucketFor(Key, TheBucket))
- return TheBucket;
- return nullptr;
- }
- const value_type *find(const KeyT &Key) const {
- const BucketT *TheBucket;
- if (LookupBucketFor(Key, TheBucket))
- return TheBucket;
- return nullptr;
- }
+ value_type *find(const KeyT &Key) { return doFind(Key); }
+ const value_type *find(const KeyT &Key) const { return doFind(Key); }
/// Alternate version of find() which allows a different, and possibly
/// less expensive, key type.
@@ -95,25 +85,18 @@ class DenseMapBase {
/// type used.
template <class LookupKeyT>
value_type *find_as(const LookupKeyT &Key) {
- BucketT *TheBucket;
- if (LookupBucketFor(Key, TheBucket))
- return TheBucket;
- return nullptr;
+ return doFind(Key);
}
template <class LookupKeyT>
const value_type *find_as(const LookupKeyT &Key) const {
- const BucketT *TheBucket;
- if (LookupBucketFor(Key, TheBucket))
- return TheBucket;
- return nullptr;
+ return doFind(Key);
}
/// lookup - Return the entry for the specified key, or a default
/// constructed value if no such entry exists.
ValueT lookup(const KeyT &Key) const {
- const BucketT *TheBucket;
- if (LookupBucketFor(Key, TheBucket))
- return TheBucket->getSecond();
+ if (const BucketT *Bucket = doFind(Key))
+ return Bucket->getSecond();
return ValueT();
}
@@ -184,8 +167,8 @@ class DenseMapBase {
}
bool erase(const KeyT &Val) {
- BucketT *TheBucket;
- if (!LookupBucketFor(Val, TheBucket))
+ BucketT *TheBucket = doFind(Val);
+ if (!TheBucket)
return false; // not in map.
TheBucket->getSecond().~ValueT();
@@ -449,6 +432,35 @@ class DenseMapBase {
return TheBucket;
}
+ template <typename LookupKeyT>
+ BucketT *doFind(const LookupKeyT &Val) {
+ BucketT *BucketsPtr = getBuckets();
+ const unsigned NumBuckets = getNumBuckets();
+ if (NumBuckets == 0)
+ return nullptr;
+
+ const KeyT EmptyKey = getEmptyKey();
+ unsigned BucketNo = getHashValue(Val) & (NumBuckets - 1);
+ unsigned ProbeAmt = 1;
+ while (true) {
+ BucketT *Bucket = BucketsPtr + BucketNo;
+ if (LIKELY(KeyInfoT::isEqual(Val, Bucket->getFirst())))
+ return Bucket;
+ if (LIKELY(KeyInfoT::isEqual(Bucket->getFirst(), EmptyKey)))
+ return nullptr;
+
+ // Otherwise, it's a hash collision or a tombstone, continue quadratic
+ // probing.
+ BucketNo += ProbeAmt++;
+ BucketNo &= NumBuckets - 1;
+ }
+ }
+
+ template <typename LookupKeyT>
+ const BucketT *doFind(const LookupKeyT &Val) const {
+ return const_cast<DenseMapBase *>(this)->doFind(Val);
+ }
+
/// LookupBucketFor - Lookup the appropriate bucket for Val, returning it in
/// FoundBucket. If the bucket contains the key and a value, this returns
/// true, otherwise it returns a bucket with an empty marker or tombstone and
@@ -23,6 +23,7 @@ namespace __sanitizer {
COMPILER_CHECK(errno_ENOMEM == ENOMEM);
COMPILER_CHECK(errno_EBUSY == EBUSY);
COMPILER_CHECK(errno_EINVAL == EINVAL);
+COMPILER_CHECK(errno_ERANGE == ERANGE);
// EOWNERDEAD is not present in some older platforms.
#if defined(EOWNERDEAD)
@@ -24,6 +24,7 @@ namespace __sanitizer {
#define errno_ENOMEM 12
#define errno_EBUSY 16
#define errno_EINVAL 22
+#define errno_ERANGE 34
#define errno_ENAMETOOLONG 36
#define errno_ENOSYS 38
@@ -69,7 +69,7 @@ void ReportFile::ReopenIfNecessary() {
WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
WriteToFile(kStderrFd, full_path, internal_strlen(full_path));
char errmsg[100];
- internal_snprintf(errmsg, sizeof(errmsg), " (reason: %d)", err);
+ internal_snprintf(errmsg, sizeof(errmsg), " (reason: %d)\n", err);
WriteToFile(kStderrFd, errmsg, internal_strlen(errmsg));
Die();
}
@@ -88,6 +88,8 @@ static void RecursiveCreateParentDirs(char *path) {
const char *ErrorMsgPrefix = "ERROR: Can't create directory: ";
WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
WriteToFile(kStderrFd, path, internal_strlen(path));
+ const char *ErrorMsgSuffix = "\n";
+ WriteToFile(kStderrFd, ErrorMsgSuffix, internal_strlen(ErrorMsgSuffix));
Die();
}
path[i] = save;
@@ -275,3 +275,10 @@ COMMON_FLAG(bool, test_only_emulate_no_memorymap, false,
// program.
COMMON_FLAG(bool, test_only_replace_dlopen_main_program, false,
"TEST ONLY replace dlopen(<main program>,...) with dlopen(NULL)")
+
+COMMON_FLAG(bool, enable_symbolizer_markup, SANITIZER_FUCHSIA,
+ "Use sanitizer symbolizer markup, available on Linux "
+ "and always set true for Fuchsia.")
+
+COMMON_FLAG(bool, detect_invalid_join, true,
+ "If set, check invalid joins of threads.")
@@ -109,6 +109,10 @@ class TwoLevelMap {
return *AddressSpaceView::LoadWritable(&map2[idx % kSize2]);
}
+ void Lock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS { mu_.Lock(); }
+
+ void Unlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS { mu_.Unlock(); }
+
private:
constexpr uptr MmapSize() const {
return RoundUpTo(kSize2 * sizeof(T), GetPageSizeCached());
deleted file mode 100644
@@ -1,137 +0,0 @@
-//===-- sanitizer_freebsd.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
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of Sanitizer runtime. It contains FreeBSD-specific
-// definitions.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef SANITIZER_FREEBSD_H
-#define SANITIZER_FREEBSD_H
-
-#include "sanitizer_internal_defs.h"
-
-// x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in
-// 32-bit mode.
-#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
-#include <osreldate.h>
-#if __FreeBSD_version <= 902001 // v9.2
-#include <link.h>
-#include <sys/param.h>
-#include <ucontext.h>
-
-namespace __sanitizer {
-
-typedef unsigned long long __xuint64_t;
-
-typedef __int32_t __xregister_t;
-
-typedef struct __xmcontext {
- __xregister_t mc_onstack;
- __xregister_t mc_gs;
- __xregister_t mc_fs;
- __xregister_t mc_es;
- __xregister_t mc_ds;
- __xregister_t mc_edi;
- __xregister_t mc_esi;
- __xregister_t mc_ebp;
- __xregister_t mc_isp;
- __xregister_t mc_ebx;
- __xregister_t mc_edx;
- __xregister_t mc_ecx;
- __xregister_t mc_eax;
- __xregister_t mc_trapno;
- __xregister_t mc_err;
- __xregister_t mc_eip;
- __xregister_t mc_cs;
- __xregister_t mc_eflags;
- __xregister_t mc_esp;
- __xregister_t mc_ss;
-
- int mc_len;
- int mc_fpformat;
- int mc_ownedfp;
- __xregister_t mc_flags;
-
- int mc_fpstate[128] __aligned(16);
- __xregister_t mc_fsbase;
- __xregister_t mc_gsbase;
- __xregister_t mc_xfpustate;
- __xregister_t mc_xfpustate_len;
-
- int mc_spare2[4];
-} xmcontext_t;
-
-typedef struct __xucontext {
- sigset_t uc_sigmask;
- xmcontext_t uc_mcontext;
-
- struct __ucontext *uc_link;
- stack_t uc_stack;
- int uc_flags;
- int __spare__[4];
-} xucontext_t;
-
-struct xkinfo_vmentry {
- int kve_structsize;
- int kve_type;
- __xuint64_t kve_start;
- __xuint64_t kve_end;
- __xuint64_t kve_offset;
- __xuint64_t kve_vn_fileid;
- __uint32_t kve_vn_fsid;
- int kve_flags;
- int kve_resident;
- int kve_private_resident;
- int kve_protection;
- int kve_ref_count;
- int kve_shadow_count;
- int kve_vn_type;
- __xuint64_t kve_vn_size;
- __uint32_t kve_vn_rdev;
- __uint16_t kve_vn_mode;
- __uint16_t kve_status;
- int _kve_ispare[12];
- char kve_path[PATH_MAX];
-};
-
-typedef struct {
- __uint32_t p_type;
- __uint32_t p_offset;
- __uint32_t p_vaddr;
- __uint32_t p_paddr;
- __uint32_t p_filesz;
- __uint32_t p_memsz;
- __uint32_t p_flags;
- __uint32_t p_align;
-} XElf32_Phdr;
-
-struct xdl_phdr_info {
- Elf_Addr dlpi_addr;
- const char *dlpi_name;
- const XElf32_Phdr *dlpi_phdr;
- Elf_Half dlpi_phnum;
- unsigned long long int dlpi_adds;
- unsigned long long int dlpi_subs;
- size_t dlpi_tls_modid;
- void *dlpi_tls_data;
-};
-
-typedef int (*__xdl_iterate_hdr_callback)(struct xdl_phdr_info *, size_t,
- void *);
-typedef int xdl_iterate_phdr_t(__xdl_iterate_hdr_callback, void *);
-
-#define xdl_iterate_phdr(callback, param) \
- (((xdl_iterate_phdr_t *)dl_iterate_phdr)((callback), (param)))
-
-} // namespace __sanitizer
-
-#endif // __FreeBSD_version <= 902001
-#endif // SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
-
-#endif // SANITIZER_FREEBSD_H
@@ -94,7 +94,6 @@ void DisableCoreDumperIfNecessary() {}
void InstallDeadlySignalHandlers(SignalHandlerType handler) {}
void SetAlternateSignalStack() {}
void UnsetAlternateSignalStack() {}
-void InitTlsSize() {}
bool SignalContext::IsStackOverflow() const { return false; }
void SignalContext::DumpAllRegisters(void *context) { UNIMPLEMENTED(); }
@@ -129,6 +128,60 @@ uptr GetMaxVirtualAddress() { return GetMaxUserVirtualAddress(); }
bool ErrorIsOOM(error_t err) { return err == ZX_ERR_NO_MEMORY; }
+// For any sanitizer internal that needs to map something which can be unmapped
+// later, first attempt to map to a pre-allocated VMAR. This helps reduce
+// fragmentation from many small anonymous mmap calls. A good value for this
+// VMAR size would be the total size of your typical sanitizer internal objects
+// allocated in an "average" process lifetime. Examples of this include:
+// FakeStack, LowLevelAllocator mappings, TwoLevelMap, InternalMmapVector,
+// StackStore, CreateAsanThread, etc.
+//
+// This is roughly equal to the total sum of sanitizer internal mappings for a
+// large test case.
+constexpr size_t kSanitizerHeapVmarSize = 13ULL << 20;
+static zx_handle_t gSanitizerHeapVmar = ZX_HANDLE_INVALID;
+
+static zx_status_t GetSanitizerHeapVmar(zx_handle_t *vmar) {
+ zx_status_t status = ZX_OK;
+ if (gSanitizerHeapVmar == ZX_HANDLE_INVALID) {
+ CHECK_EQ(kSanitizerHeapVmarSize % GetPageSizeCached(), 0);
+ uintptr_t base;
+ status = _zx_vmar_allocate(
+ _zx_vmar_root_self(),
+ ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | ZX_VM_CAN_MAP_SPECIFIC, 0,
+ kSanitizerHeapVmarSize, &gSanitizerHeapVmar, &base);
+ }
+ *vmar = gSanitizerHeapVmar;
+ if (status == ZX_OK)
+ CHECK_NE(gSanitizerHeapVmar, ZX_HANDLE_INVALID);
+ return status;
+}
+
+static zx_status_t TryVmoMapSanitizerVmar(zx_vm_option_t options,
+ size_t vmar_offset, zx_handle_t vmo,
+ size_t size, uintptr_t *addr,
+ zx_handle_t *vmar_used = nullptr) {
+ zx_handle_t vmar;
+ zx_status_t status = GetSanitizerHeapVmar(&vmar);
+ if (status != ZX_OK)
+ return status;
+
+ status = _zx_vmar_map(gSanitizerHeapVmar, options, vmar_offset, vmo,
+ /*vmo_offset=*/0, size, addr);
+ if (vmar_used)
+ *vmar_used = gSanitizerHeapVmar;
+ if (status == ZX_ERR_NO_RESOURCES || status == ZX_ERR_INVALID_ARGS) {
+ // This means there's no space in the heap VMAR, so fallback to the root
+ // VMAR.
+ status = _zx_vmar_map(_zx_vmar_root_self(), options, vmar_offset, vmo,
+ /*vmo_offset=*/0, size, addr);
+ if (vmar_used)
+ *vmar_used = _zx_vmar_root_self();
+ }
+
+ return status;
+}
+
static void *DoAnonymousMmapOrDie(uptr size, const char *mem_type,
bool raw_report, bool die_for_nomem) {
size = RoundUpTo(size, GetPageSize());
@@ -144,11 +197,9 @@ static void *DoAnonymousMmapOrDie(uptr size, const char *mem_type,
_zx_object_set_property(vmo, ZX_PROP_NAME, mem_type,
internal_strlen(mem_type));
- // TODO(mcgrathr): Maybe allocate a VMAR for all sanitizer heap and use that?
uintptr_t addr;
- status =
- _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
- vmo, 0, size, &addr);
+ status = TryVmoMapSanitizerVmar(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
+ /*vmar_offset=*/0, vmo, size, &addr);
_zx_handle_close(vmo);
if (status != ZX_OK) {
@@ -236,18 +287,22 @@ uptr ReservedAddressRange::MapOrDie(uptr fixed_addr, uptr map_size,
name ? name : name_, true);
}
-void UnmapOrDieVmar(void *addr, uptr size, zx_handle_t target_vmar) {
+void UnmapOrDieVmar(void *addr, uptr size, zx_handle_t target_vmar,
+ bool raw_report) {
if (!addr || !size)
return;
size = RoundUpTo(size, GetPageSize());
zx_status_t status =
_zx_vmar_unmap(target_vmar, reinterpret_cast<uintptr_t>(addr), size);
- if (status != ZX_OK) {
- Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n",
- SanitizerToolName, size, size, addr);
- CHECK("unable to unmap" && 0);
+ if (status == ZX_ERR_INVALID_ARGS && target_vmar == gSanitizerHeapVmar) {
+ // If there wasn't any space in the heap vmar, the fallback was the root
+ // vmar.
+ status = _zx_vmar_unmap(_zx_vmar_root_self(),
+ reinterpret_cast<uintptr_t>(addr), size);
}
+ if (status != ZX_OK)
+ ReportMunmapFailureAndDie(addr, size, status, raw_report);
DecreaseTotalMmap(size);
}
@@ -269,7 +324,8 @@ void ReservedAddressRange::Unmap(uptr addr, uptr size) {
}
// Partial unmapping does not affect the fact that the initial range is still
// reserved, and the resulting unmapped memory can't be reused.
- UnmapOrDieVmar(reinterpret_cast<void *>(addr), size, vmar);
+ UnmapOrDieVmar(reinterpret_cast<void *>(addr), size, vmar,
+ /*raw_report=*/false);
}
// This should never be called.
@@ -308,17 +364,16 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
_zx_object_set_property(vmo, ZX_PROP_NAME, mem_type,
internal_strlen(mem_type));
- // TODO(mcgrathr): Maybe allocate a VMAR for all sanitizer heap and use that?
-
// Map a larger size to get a chunk of address space big enough that
// it surely contains an aligned region of the requested size. Then
// overwrite the aligned middle portion with a mapping from the
// beginning of the VMO, and unmap the excess before and after.
size_t map_size = size + alignment;
uintptr_t addr;
- status =
- _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
- vmo, 0, map_size, &addr);
+ zx_handle_t vmar_used;
+ status = TryVmoMapSanitizerVmar(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
+ /*vmar_offset=*/0, vmo, map_size, &addr,
+ &vmar_used);
if (status == ZX_OK) {
uintptr_t map_addr = addr;
uintptr_t map_end = map_addr + map_size;
@@ -326,12 +381,12 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
uintptr_t end = addr + size;
if (addr != map_addr) {
zx_info_vmar_t info;
- status = _zx_object_get_info(_zx_vmar_root_self(), ZX_INFO_VMAR, &info,
- sizeof(info), NULL, NULL);
+ status = _zx_object_get_info(vmar_used, ZX_INFO_VMAR, &info, sizeof(info),
+ NULL, NULL);
if (status == ZX_OK) {
uintptr_t new_addr;
status = _zx_vmar_map(
- _zx_vmar_root_self(),
+ vmar_used,
ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC_OVERWRITE,
addr - info.base, vmo, 0, size, &new_addr);
if (status == ZX_OK)
@@ -339,9 +394,9 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
}
}
if (status == ZX_OK && addr != map_addr)
- status = _zx_vmar_unmap(_zx_vmar_root_self(), map_addr, addr - map_addr);
+ status = _zx_vmar_unmap(vmar_used, map_addr, addr - map_addr);
if (status == ZX_OK && end != map_end)
- status = _zx_vmar_unmap(_zx_vmar_root_self(), end, map_end - end);
+ status = _zx_vmar_unmap(vmar_used, end, map_end - end);
}
_zx_handle_close(vmo);
@@ -356,8 +411,8 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
return reinterpret_cast<void *>(addr);
}
-void UnmapOrDie(void *addr, uptr size) {
- UnmapOrDieVmar(addr, size, _zx_vmar_root_self());
+void UnmapOrDie(void *addr, uptr size, bool raw_report) {
+ UnmapOrDieVmar(addr, size, gSanitizerHeapVmar, raw_report);
}
void ReleaseMemoryPagesToOS(uptr beg, uptr end) {
@@ -389,6 +444,11 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size) {
return status == ZX_OK;
}
+bool TryMemCpy(void *dest, const void *src, uptr n) {
+ // TODO: implement.
+ return false;
+}
+
// FIXME implement on this platform.
void GetMemoryProfile(fill_profile_f cb, uptr *stats) {}
@@ -463,7 +523,6 @@ uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {
uptr MainThreadStackBase, MainThreadStackSize;
bool GetRandom(void *buffer, uptr length, bool blocking) {
- CHECK_LE(length, ZX_CPRNG_DRAW_MAX_LEN);
_zx_cprng_draw(buffer, length);
return true;
}
@@ -62,6 +62,6 @@ class MurMur2Hash64Builder {
return x;
}
};
-} //namespace __sanitizer
+} // namespace __sanitizer
#endif // SANITIZER_HASH_H
@@ -49,6 +49,11 @@ __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args);
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void
__sanitizer_report_error_summary(const char *error_summary);
+// Returns size of dynamically allocated block. This function can be overridden
+// by the client.
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE __sanitizer::uptr
+__sanitizer_get_dtls_size(const void *tls_begin);
+
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump();
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_coverage(
const __sanitizer::uptr *pcs, const __sanitizer::uptr len);
@@ -71,6 +76,11 @@ void __sanitizer_annotate_double_ended_contiguous_container(
const void *old_container_beg, const void *old_container_end,
const void *new_container_beg, const void *new_container_end);
SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_copy_contiguous_container_annotations(const void *src_begin,
+ const void *src_end,
+ const void *dst_begin,
+ const void *dst_end);
+SANITIZER_INTERFACE_ATTRIBUTE
int __sanitizer_verify_contiguous_container(const void *beg, const void *mid,
const void *end);
SANITIZER_INTERFACE_ATTRIBUTE
@@ -35,13 +35,20 @@
# define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllexport)
#endif
# define SANITIZER_WEAK_ATTRIBUTE
+# define SANITIZER_WEAK_IMPORT
#elif SANITIZER_GO
# define SANITIZER_INTERFACE_ATTRIBUTE
# define SANITIZER_WEAK_ATTRIBUTE
+# define SANITIZER_WEAK_IMPORT
#else
# define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("default")))
# define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak))
-#endif
+# if SANITIZER_APPLE
+# define SANITIZER_WEAK_IMPORT extern "C" __attribute((weak_import))
+# else
+# define SANITIZER_WEAK_IMPORT extern "C" SANITIZER_WEAK_ATTRIBUTE
+# endif // SANITIZER_APPLE
+#endif // SANITIZER_WINDOWS
//--------------------------- WEAK FUNCTIONS ---------------------------------//
// When working with weak functions, to simplify the code and make it more
@@ -131,19 +138,25 @@
// in a portable way by the language itself.
namespace __sanitizer {
-#if defined(_WIN64)
+#if defined(__UINTPTR_TYPE__)
+# if defined(__arm__) && defined(__linux__)
+// Linux Arm headers redefine __UINTPTR_TYPE__ and disagree with clang/gcc.
+typedef unsigned int uptr;
+typedef int sptr;
+# else
+typedef __UINTPTR_TYPE__ uptr;
+typedef __INTPTR_TYPE__ sptr;
+# endif
+#elif defined(_WIN64)
// 64-bit Windows uses LLP64 data model.
typedef unsigned long long uptr;
typedef signed long long sptr;
-#else
-# if (SANITIZER_WORDSIZE == 64) || SANITIZER_APPLE || SANITIZER_WINDOWS
-typedef unsigned long uptr;
-typedef signed long sptr;
-# else
+#elif defined(_WIN32)
typedef unsigned int uptr;
typedef signed int sptr;
-# endif
-#endif // defined(_WIN64)
+#else
+# error Unsupported compiler, missing __UINTPTR_TYPE__
+#endif // defined(__UINTPTR_TYPE__)
#if defined(__x86_64__)
// Since x32 uses ILP32 data model in 64-bit hardware mode, we must use
// 64-bit pointer to unwind stack frame.
@@ -184,15 +197,10 @@ typedef uptr OFF_T;
#endif
typedef u64 OFF64_T;
-#if (SANITIZER_WORDSIZE == 64) || SANITIZER_APPLE
-typedef uptr operator_new_size_type;
+#ifdef __SIZE_TYPE__
+typedef __SIZE_TYPE__ usize;
#else
-# if defined(__s390__) && !defined(__s390x__)
-// Special case: 31-bit s390 has unsigned long as size_t.
-typedef unsigned long operator_new_size_type;
-# else
-typedef u32 operator_new_size_type;
-# endif
+typedef uptr usize;
#endif
typedef u64 tid_t;
@@ -455,6 +463,9 @@ using namespace __sanitizer;
namespace __msan {
using namespace __sanitizer;
}
+namespace __nsan {
+using namespace __sanitizer;
+}
namespace __hwasan {
using namespace __sanitizer;
}
@@ -32,7 +32,7 @@ void LibIgnore::AddIgnoredLibrary(const char *name_templ) {
lib->templ = internal_strdup(name_templ);
lib->name = nullptr;
lib->real_name = nullptr;
- lib->loaded = false;
+ lib->range_id = kInvalidCodeRangeId;
}
void LibIgnore::OnLibraryLoaded(const char *name) {
@@ -43,7 +43,7 @@ void LibIgnore::OnLibraryLoaded(const char *name) {
buf[0]) {
for (uptr i = 0; i < count_; i++) {
Lib *lib = &libs_[i];
- if (!lib->loaded && (!lib->real_name) &&
+ if (!lib->loaded() && (!lib->real_name) &&
TemplateMatch(lib->templ, name))
lib->real_name = internal_strdup(buf.data());
}
@@ -70,28 +70,31 @@ void LibIgnore::OnLibraryLoaded(const char *name) {
Die();
}
loaded = true;
- if (lib->loaded)
+ if (lib->loaded())
continue;
VReport(1,
"Matched called_from_lib suppression '%s' against library"
" '%s'\n",
lib->templ, mod.full_name());
- lib->loaded = true;
lib->name = internal_strdup(mod.full_name());
const uptr idx =
atomic_load(&ignored_ranges_count_, memory_order_relaxed);
CHECK_LT(idx, ARRAY_SIZE(ignored_code_ranges_));
- ignored_code_ranges_[idx].begin = range.beg;
- ignored_code_ranges_[idx].end = range.end;
+ ignored_code_ranges_[idx].OnLoad(range.beg, range.end);
+ // Record the index of the ignored range.
+ lib->range_id = idx;
atomic_store(&ignored_ranges_count_, idx + 1, memory_order_release);
break;
}
}
- if (lib->loaded && !loaded) {
- Report("%s: library '%s' that was matched against called_from_lib"
- " suppression '%s' is unloaded\n",
- SanitizerToolName, lib->name, lib->templ);
- Die();
+ if (lib->loaded() && !loaded) {
+ VReport(1,
+ "%s: library '%s' that was matched against called_from_lib"
+ " suppression '%s' is unloaded\n",
+ SanitizerToolName, lib->name, lib->templ);
+ // The library is unloaded so mark the ignored code range as unloaded.
+ ignored_code_ranges_[lib->range_id].OnUnload();
+ lib->range_id = kInvalidCodeRangeId;
}
}
@@ -105,13 +108,12 @@ void LibIgnore::OnLibraryLoaded(const char *name) {
continue;
if (IsPcInstrumented(range.beg) && IsPcInstrumented(range.end - 1))
continue;
- VReport(1, "Adding instrumented range 0x%zx-0x%zx from library '%s'\n",
- range.beg, range.end, mod.full_name());
+ VReport(1, "Adding instrumented range %p-%p from library '%s'\n",
+ (void *)range.beg, (void *)range.end, mod.full_name());
const uptr idx =
atomic_load(&instrumented_ranges_count_, memory_order_relaxed);
CHECK_LT(idx, ARRAY_SIZE(instrumented_code_ranges_));
- instrumented_code_ranges_[idx].begin = range.beg;
- instrumented_code_ranges_[idx].end = range.end;
+ instrumented_code_ranges_[idx].OnLoad(range.beg, range.end);
atomic_store(&instrumented_ranges_count_, idx + 1,
memory_order_release);
}
@@ -49,25 +49,36 @@ class LibIgnore {
bool IsPcInstrumented(uptr pc) const;
private:
+ static const uptr kMaxIgnoredRanges = 128;
+ static const uptr kMaxInstrumentedRanges = 1024;
+ static const uptr kMaxLibs = 1024;
+ static const uptr kInvalidCodeRangeId = -1;
+
struct Lib {
char *templ;
char *name;
char *real_name; // target of symlink
- bool loaded;
+ uptr range_id;
+ bool loaded() const { return range_id != kInvalidCodeRangeId; };
};
struct LibCodeRange {
- uptr begin;
- uptr end;
- };
+ bool IsInRange(uptr pc) const {
+ return (pc >= begin && pc < atomic_load(&end, memory_order_acquire));
+ }
- inline bool IsInRange(uptr pc, const LibCodeRange &range) const {
- return (pc >= range.begin && pc < range.end);
- }
+ void OnLoad(uptr b, uptr e) {
+ begin = b;
+ atomic_store(&end, e, memory_order_release);
+ }
- static const uptr kMaxIgnoredRanges = 128;
- static const uptr kMaxInstrumentedRanges = 1024;
- static const uptr kMaxLibs = 1024;
+ void OnUnload() { atomic_store(&end, 0, memory_order_release); }
+
+ private:
+ uptr begin;
+ // A value of 0 means the associated module was unloaded.
+ atomic_uintptr_t end;
+ };
// Hot part:
atomic_uintptr_t ignored_ranges_count_;
@@ -90,7 +101,7 @@ class LibIgnore {
inline bool LibIgnore::IsIgnored(uptr pc, bool *pc_in_ignored_lib) const {
const uptr n = atomic_load(&ignored_ranges_count_, memory_order_acquire);
for (uptr i = 0; i < n; i++) {
- if (IsInRange(pc, ignored_code_ranges_[i])) {
+ if (ignored_code_ranges_[i].IsInRange(pc)) {
*pc_in_ignored_lib = true;
return true;
}
@@ -104,7 +115,7 @@ inline bool LibIgnore::IsIgnored(uptr pc, bool *pc_in_ignored_lib) const {
inline bool LibIgnore::IsPcInstrumented(uptr pc) const {
const uptr n = atomic_load(&instrumented_ranges_count_, memory_order_acquire);
for (uptr i = 0; i < n; i++) {
- if (IsInRange(pc, instrumented_code_ranges_[i]))
+ if (instrumented_code_ranges_[i].IsInRange(pc))
return true;
}
return false;
@@ -16,101 +16,107 @@
#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
SANITIZER_SOLARIS
-#include "sanitizer_common.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_getauxval.h"
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_libc.h"
-#include "sanitizer_linux.h"
-#include "sanitizer_mutex.h"
-#include "sanitizer_placement_new.h"
-#include "sanitizer_procmaps.h"
-
-#if SANITIZER_LINUX && !SANITIZER_GO
-#include <asm/param.h>
-#endif
+# include "sanitizer_common.h"
+# include "sanitizer_flags.h"
+# include "sanitizer_getauxval.h"
+# include "sanitizer_internal_defs.h"
+# include "sanitizer_libc.h"
+# include "sanitizer_linux.h"
+# include "sanitizer_mutex.h"
+# include "sanitizer_placement_new.h"
+# include "sanitizer_procmaps.h"
+
+# if SANITIZER_LINUX && !SANITIZER_GO
+# include <asm/param.h>
+# endif
// For mips64, syscall(__NR_stat) fills the buffer in the 'struct kernel_stat'
// format. Struct kernel_stat is defined as 'struct stat' in asm/stat.h. To
// access stat from asm/stat.h, without conflicting with definition in
-// sys/stat.h, we use this trick.
-#if SANITIZER_MIPS64
-#include <asm/unistd.h>
-#include <sys/types.h>
-#define stat kernel_stat
-#if SANITIZER_GO
-#undef st_atime
-#undef st_mtime
-#undef st_ctime
-#define st_atime st_atim
-#define st_mtime st_mtim
-#define st_ctime st_ctim
-#endif
-#include <asm/stat.h>
-#undef stat
-#endif
+// sys/stat.h, we use this trick. sparc64 is similar, using
+// syscall(__NR_stat64) and struct kernel_stat64.
+# if SANITIZER_LINUX && (SANITIZER_MIPS64 || SANITIZER_SPARC64)
+# include <asm/unistd.h>
+# include <sys/types.h>
+# define stat kernel_stat
+# if SANITIZER_SPARC64
+# define stat64 kernel_stat64
+# endif
+# if SANITIZER_GO
+# undef st_atime
+# undef st_mtime
+# undef st_ctime
+# define st_atime st_atim
+# define st_mtime st_mtim
+# define st_ctime st_ctim
+# endif
+# include <asm/stat.h>
+# undef stat
+# undef stat64
+# endif
-#include <dlfcn.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <link.h>
-#include <pthread.h>
-#include <sched.h>
-#include <signal.h>
-#include <sys/mman.h>
-#include <sys/param.h>
-#if !SANITIZER_SOLARIS
-#include <sys/ptrace.h>
-#endif
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/syscall.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <ucontext.h>
-#include <unistd.h>
-
-#if SANITIZER_LINUX
-#include <sys/utsname.h>
-#endif
+# include <dlfcn.h>
+# include <errno.h>
+# include <fcntl.h>
+# include <link.h>
+# include <pthread.h>
+# include <sched.h>
+# include <signal.h>
+# include <sys/mman.h>
+# if !SANITIZER_SOLARIS
+# include <sys/ptrace.h>
+# endif
+# include <sys/resource.h>
+# include <sys/stat.h>
+# include <sys/syscall.h>
+# include <sys/time.h>
+# include <sys/types.h>
+# include <ucontext.h>
+# include <unistd.h>
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
-#include <sys/personality.h>
-#endif
+# if SANITIZER_LINUX
+# include <sys/utsname.h>
+# endif
-#if SANITIZER_LINUX && defined(__loongarch__)
-# include <sys/sysmacros.h>
-#endif
+# if SANITIZER_LINUX && !SANITIZER_ANDROID
+# include <sys/personality.h>
+# endif
+
+# if SANITIZER_LINUX && defined(__loongarch__)
+# include <sys/sysmacros.h>
+# endif
-#if SANITIZER_FREEBSD
-#include <sys/exec.h>
-#include <sys/procctl.h>
-#include <sys/sysctl.h>
-#include <machine/atomic.h>
+# if SANITIZER_FREEBSD
+# include <machine/atomic.h>
+# include <sys/exec.h>
+# include <sys/procctl.h>
+# include <sys/sysctl.h>
extern "C" {
// <sys/umtx.h> must be included after <errno.h> and <sys/types.h> on
// FreeBSD 9.2 and 10.0.
-#include <sys/umtx.h>
+# include <sys/umtx.h>
}
-#include <sys/thr.h>
-#endif // SANITIZER_FREEBSD
+# include <sys/thr.h>
+# endif // SANITIZER_FREEBSD
-#if SANITIZER_NETBSD
-#include <limits.h> // For NAME_MAX
-#include <sys/sysctl.h>
-#include <sys/exec.h>
+# if SANITIZER_NETBSD
+# include <limits.h> // For NAME_MAX
+# include <sys/exec.h>
+# include <sys/sysctl.h>
extern struct ps_strings *__ps_strings;
-#endif // SANITIZER_NETBSD
-
-#if SANITIZER_SOLARIS
-#include <stdlib.h>
-#include <thread.h>
-#define environ _environ
-#endif
+# endif // SANITIZER_NETBSD
+
+# if SANITIZER_SOLARIS
+# include <stddef.h>
+# include <stdlib.h>
+# include <sys/frame.h>
+# include <thread.h>
+# define environ _environ
+# endif
extern char **environ;
-#if SANITIZER_LINUX
+# if SANITIZER_LINUX
// <linux/time.h>
struct kernel_timeval {
long tv_sec;
@@ -123,36 +129,34 @@ const int FUTEX_WAKE = 1;
const int FUTEX_PRIVATE_FLAG = 128;
const int FUTEX_WAIT_PRIVATE = FUTEX_WAIT | FUTEX_PRIVATE_FLAG;
const int FUTEX_WAKE_PRIVATE = FUTEX_WAKE | FUTEX_PRIVATE_FLAG;
-#endif // SANITIZER_LINUX
+# endif // SANITIZER_LINUX
// Are we using 32-bit or 64-bit Linux syscalls?
// x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32
// but it still needs to use 64-bit syscalls.
-#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__powerpc64__) || \
- SANITIZER_WORDSIZE == 64 || \
- (defined(__mips__) && _MIPS_SIM == _ABIN32))
-# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1
-#else
-# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0
-#endif
+# if SANITIZER_LINUX && (defined(__x86_64__) || defined(__powerpc64__) || \
+ SANITIZER_WORDSIZE == 64 || \
+ (defined(__mips__) && _MIPS_SIM == _ABIN32))
+# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1
+# else
+# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0
+# endif
-// Note : FreeBSD had implemented both
-// Linux apis, available from
-// future 12.x version most likely
-#if SANITIZER_LINUX && defined(__NR_getrandom)
-# if !defined(GRND_NONBLOCK)
-# define GRND_NONBLOCK 1
-# endif
-# define SANITIZER_USE_GETRANDOM 1
-#else
-# define SANITIZER_USE_GETRANDOM 0
-#endif // SANITIZER_LINUX && defined(__NR_getrandom)
-
-#if SANITIZER_FREEBSD && __FreeBSD_version >= 1200000
-# define SANITIZER_USE_GETENTROPY 1
-#else
-# define SANITIZER_USE_GETENTROPY 0
-#endif
+// Note : FreeBSD implemented both Linux and OpenBSD apis.
+# if SANITIZER_LINUX && defined(__NR_getrandom)
+# if !defined(GRND_NONBLOCK)
+# define GRND_NONBLOCK 1
+# endif
+# define SANITIZER_USE_GETRANDOM 1
+# else
+# define SANITIZER_USE_GETRANDOM 0
+# endif // SANITIZER_LINUX && defined(__NR_getrandom)
+
+# if SANITIZER_FREEBSD
+# define SANITIZER_USE_GETENTROPY 1
+extern "C" void *__sys_mmap(void *addr, size_t len, int prot, int flags, int fd,
+ off_t offset);
+# endif
namespace __sanitizer {
@@ -160,6 +164,7 @@ void SetSigProcMask(__sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset) {
CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, set, oldset));
}
+// Block asynchronous signals
void BlockSignals(__sanitizer_sigset_t *oldset) {
__sanitizer_sigset_t set;
internal_sigfillset(&set);
@@ -174,7 +179,17 @@ void BlockSignals(__sanitizer_sigset_t *oldset) {
// If this signal is blocked, such calls cannot be handled and the process may
// hang.
internal_sigdelset(&set, 31);
+
+ // Don't block synchronous signals
+ internal_sigdelset(&set, SIGSEGV);
+ internal_sigdelset(&set, SIGBUS);
+ internal_sigdelset(&set, SIGILL);
+ internal_sigdelset(&set, SIGTRAP);
+ internal_sigdelset(&set, SIGABRT);
+ internal_sigdelset(&set, SIGFPE);
+ internal_sigdelset(&set, SIGPIPE);
# endif
+
SetSigProcMask(&set, oldset);
}
@@ -203,33 +218,35 @@ ScopedBlockSignals::~ScopedBlockSignals() { SetSigProcMask(&saved_, nullptr); }
# endif
// --------------- sanitizer_libc.h
-#if !SANITIZER_SOLARIS && !SANITIZER_NETBSD
-#if !SANITIZER_S390
+# if !SANITIZER_SOLARIS && !SANITIZER_NETBSD
+# if !SANITIZER_S390
uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
u64 offset) {
-#if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS
+# if SANITIZER_FREEBSD
+ return (uptr)__sys_mmap(addr, length, prot, flags, fd, offset);
+# elif SANITIZER_LINUX_USES_64BIT_SYSCALLS
return internal_syscall(SYSCALL(mmap), (uptr)addr, length, prot, flags, fd,
offset);
-#else
+# else
// mmap2 specifies file offset in 4096-byte units.
CHECK(IsAligned(offset, 4096));
return internal_syscall(SYSCALL(mmap2), addr, length, prot, flags, fd,
- offset / 4096);
-#endif
+ (OFF_T)(offset / 4096));
+# endif
}
-#endif // !SANITIZER_S390
+# endif // !SANITIZER_S390
uptr internal_munmap(void *addr, uptr length) {
return internal_syscall(SYSCALL(munmap), (uptr)addr, length);
}
-#if SANITIZER_LINUX
+# if SANITIZER_LINUX
uptr internal_mremap(void *old_address, uptr old_size, uptr new_size, int flags,
void *new_address) {
return internal_syscall(SYSCALL(mremap), (uptr)old_address, old_size,
new_size, flags, (uptr)new_address);
}
-#endif
+# endif
int internal_mprotect(void *addr, uptr length, int prot) {
return internal_syscall(SYSCALL(mprotect), (uptr)addr, length, prot);
@@ -239,25 +256,28 @@ int internal_madvise(uptr addr, uptr length, int advice) {
return internal_syscall(SYSCALL(madvise), addr, length, advice);
}
-uptr internal_close(fd_t fd) {
- return internal_syscall(SYSCALL(close), fd);
+# if SANITIZER_FREEBSD
+uptr internal_close_range(fd_t lowfd, fd_t highfd, int flags) {
+ return internal_syscall(SYSCALL(close_range), lowfd, highfd, flags);
}
+# endif
+uptr internal_close(fd_t fd) { return internal_syscall(SYSCALL(close), fd); }
uptr internal_open(const char *filename, int flags) {
# if SANITIZER_LINUX
return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags);
-#else
+# else
return internal_syscall(SYSCALL(open), (uptr)filename, flags);
-#endif
+# endif
}
uptr internal_open(const char *filename, int flags, u32 mode) {
# if SANITIZER_LINUX
return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags,
mode);
-#else
+# else
return internal_syscall(SYSCALL(open), (uptr)filename, flags, mode);
-#endif
+# endif
}
uptr internal_read(fd_t fd, void *buf, uptr count) {
@@ -276,12 +296,12 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) {
uptr internal_ftruncate(fd_t fd, uptr size) {
sptr res;
- HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd,
- (OFF_T)size));
+ HANDLE_EINTR(res,
+ (sptr)internal_syscall(SYSCALL(ftruncate), fd, (OFF_T)size));
return res;
}
-#if (!SANITIZER_LINUX_USES_64BIT_SYSCALLS || SANITIZER_SPARC) && SANITIZER_LINUX
+# if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && SANITIZER_LINUX
static void stat64_to_stat(struct stat64 *in, struct stat *out) {
internal_memset(out, 0, sizeof(*out));
out->st_dev = in->st_dev;
@@ -298,9 +318,9 @@ static void stat64_to_stat(struct stat64 *in, struct stat *out) {
out->st_mtime = in->st_mtime;
out->st_ctime = in->st_ctime;
}
-#endif
+# endif
-#if SANITIZER_LINUX && defined(__loongarch__)
+# if SANITIZER_LINUX && defined(__loongarch__)
static void statx_to_stat(struct statx *in, struct stat *out) {
internal_memset(out, 0, sizeof(*out));
out->st_dev = makedev(in->stx_dev_major, in->stx_dev_minor);
@@ -320,27 +340,32 @@ static void statx_to_stat(struct statx *in, struct stat *out) {
out->st_ctime = in->stx_ctime.tv_sec;
out->st_ctim.tv_nsec = in->stx_ctime.tv_nsec;
}
-#endif
+# endif
-#if SANITIZER_MIPS64
+# if SANITIZER_MIPS64 || SANITIZER_SPARC64
+# if SANITIZER_MIPS64
+typedef struct kernel_stat kstat_t;
+# else
+typedef struct kernel_stat64 kstat_t;
+# endif
// Undefine compatibility macros from <sys/stat.h>
// so that they would not clash with the kernel_stat
// st_[a|m|c]time fields
-#if !SANITIZER_GO
-#undef st_atime
-#undef st_mtime
-#undef st_ctime
-#endif
-#if defined(SANITIZER_ANDROID)
+# if !SANITIZER_GO
+# undef st_atime
+# undef st_mtime
+# undef st_ctime
+# endif
+# if defined(SANITIZER_ANDROID)
// Bionic sys/stat.h defines additional macros
// for compatibility with the old NDKs and
// they clash with the kernel_stat structure
// st_[a|m|c]time_nsec fields.
-#undef st_atime_nsec
-#undef st_mtime_nsec
-#undef st_ctime_nsec
-#endif
-static void kernel_stat_to_stat(struct kernel_stat *in, struct stat *out) {
+# undef st_atime_nsec
+# undef st_mtime_nsec
+# undef st_ctime_nsec
+# endif
+static void kernel_stat_to_stat(kstat_t *in, struct stat *out) {
internal_memset(out, 0, sizeof(*out));
out->st_dev = in->st_dev;
out->st_ino = in->st_ino;
@@ -352,96 +377,113 @@ static void kernel_stat_to_stat(struct kernel_stat *in, struct stat *out) {
out->st_size = in->st_size;
out->st_blksize = in->st_blksize;
out->st_blocks = in->st_blocks;
-#if defined(__USE_MISC) || \
- defined(__USE_XOPEN2K8) || \
- defined(SANITIZER_ANDROID)
+# if defined(__USE_MISC) || defined(__USE_XOPEN2K8) || \
+ defined(SANITIZER_ANDROID)
out->st_atim.tv_sec = in->st_atime;
out->st_atim.tv_nsec = in->st_atime_nsec;
out->st_mtim.tv_sec = in->st_mtime;
out->st_mtim.tv_nsec = in->st_mtime_nsec;
out->st_ctim.tv_sec = in->st_ctime;
out->st_ctim.tv_nsec = in->st_ctime_nsec;
-#else
+# else
out->st_atime = in->st_atime;
out->st_atimensec = in->st_atime_nsec;
out->st_mtime = in->st_mtime;
out->st_mtimensec = in->st_mtime_nsec;
out->st_ctime = in->st_ctime;
out->st_atimensec = in->st_ctime_nsec;
-#endif
+# endif
}
-#endif
+# endif
uptr internal_stat(const char *path, void *buf) {
-# if SANITIZER_FREEBSD
+# if SANITIZER_FREEBSD
return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf, 0);
-# elif SANITIZER_LINUX
-# if defined(__loongarch__)
+# elif SANITIZER_LINUX
+# if defined(__loongarch__)
struct statx bufx;
int res = internal_syscall(SYSCALL(statx), AT_FDCWD, (uptr)path,
AT_NO_AUTOMOUNT, STATX_BASIC_STATS, (uptr)&bufx);
statx_to_stat(&bufx, (struct stat *)buf);
return res;
-# elif (SANITIZER_WORDSIZE == 64 || SANITIZER_X32 || \
- (defined(__mips__) && _MIPS_SIM == _ABIN32)) && \
- !SANITIZER_SPARC
+# elif (SANITIZER_WORDSIZE == 64 || SANITIZER_X32 || \
+ (defined(__mips__) && _MIPS_SIM == _ABIN32)) && \
+ !SANITIZER_SPARC
return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf,
0);
-# else
+# elif SANITIZER_SPARC64
+ kstat_t buf64;
+ int res = internal_syscall(SYSCALL(fstatat64), AT_FDCWD, (uptr)path,
+ (uptr)&buf64, 0);
+ kernel_stat_to_stat(&buf64, (struct stat *)buf);
+ return res;
+# else
struct stat64 buf64;
int res = internal_syscall(SYSCALL(fstatat64), AT_FDCWD, (uptr)path,
(uptr)&buf64, 0);
stat64_to_stat(&buf64, (struct stat *)buf);
return res;
-# endif
-# else
+# endif
+# else
struct stat64 buf64;
int res = internal_syscall(SYSCALL(stat64), path, &buf64);
stat64_to_stat(&buf64, (struct stat *)buf);
return res;
-# endif
+# endif
}
uptr internal_lstat(const char *path, void *buf) {
-# if SANITIZER_FREEBSD
+# if SANITIZER_FREEBSD
return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf,
AT_SYMLINK_NOFOLLOW);
-# elif SANITIZER_LINUX
-# if defined(__loongarch__)
+# elif SANITIZER_LINUX
+# if defined(__loongarch__)
struct statx bufx;
int res = internal_syscall(SYSCALL(statx), AT_FDCWD, (uptr)path,
AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT,
STATX_BASIC_STATS, (uptr)&bufx);
statx_to_stat(&bufx, (struct stat *)buf);
return res;
-# elif (defined(_LP64) || SANITIZER_X32 || \
- (defined(__mips__) && _MIPS_SIM == _ABIN32)) && \
- !SANITIZER_SPARC
+# elif (defined(_LP64) || SANITIZER_X32 || \
+ (defined(__mips__) && _MIPS_SIM == _ABIN32)) && \
+ !SANITIZER_SPARC
return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf,
AT_SYMLINK_NOFOLLOW);
-# else
+# elif SANITIZER_SPARC64
+ kstat_t buf64;
+ int res = internal_syscall(SYSCALL(fstatat64), AT_FDCWD, (uptr)path,
+ (uptr)&buf64, AT_SYMLINK_NOFOLLOW);
+ kernel_stat_to_stat(&buf64, (struct stat *)buf);
+ return res;
+# else
struct stat64 buf64;
int res = internal_syscall(SYSCALL(fstatat64), AT_FDCWD, (uptr)path,
(uptr)&buf64, AT_SYMLINK_NOFOLLOW);
stat64_to_stat(&buf64, (struct stat *)buf);
return res;
-# endif
-# else
+# endif
+# else
struct stat64 buf64;
int res = internal_syscall(SYSCALL(lstat64), path, &buf64);
stat64_to_stat(&buf64, (struct stat *)buf);
return res;
-# endif
+# endif
}
uptr internal_fstat(fd_t fd, void *buf) {
-#if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS
-#if SANITIZER_MIPS64
+# if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS
+# if SANITIZER_MIPS64
// For mips64, fstat syscall fills buffer in the format of kernel_stat
- struct kernel_stat kbuf;
+ kstat_t kbuf;
int res = internal_syscall(SYSCALL(fstat), fd, &kbuf);
kernel_stat_to_stat(&kbuf, (struct stat *)buf);
return res;
+# elif SANITIZER_LINUX && SANITIZER_SPARC64
+ // For sparc64, fstat64 syscall fills buffer in the format of kernel_stat64
+ kstat_t kbuf;
+ int res = internal_syscall(SYSCALL(fstat64), fd, &kbuf);
+ kernel_stat_to_stat(&kbuf, (struct stat *)buf);
+ return res;
# elif SANITIZER_LINUX && defined(__loongarch__)
struct statx bufx;
int res = internal_syscall(SYSCALL(statx), fd, "", AT_EMPTY_PATH,
@@ -451,12 +493,12 @@ uptr internal_fstat(fd_t fd, void *buf) {
# else
return internal_syscall(SYSCALL(fstat), fd, (uptr)buf);
# endif
-#else
+# else
struct stat64 buf64;
int res = internal_syscall(SYSCALL(fstat64), fd, &buf64);
stat64_to_stat(&buf64, (struct stat *)buf);
return res;
-#endif
+# endif
}
uptr internal_filesize(fd_t fd) {
@@ -466,50 +508,46 @@ uptr internal_filesize(fd_t fd) {
return (uptr)st.st_size;
}
-uptr internal_dup(int oldfd) {
- return internal_syscall(SYSCALL(dup), oldfd);
-}
+uptr internal_dup(int oldfd) { return internal_syscall(SYSCALL(dup), oldfd); }
uptr internal_dup2(int oldfd, int newfd) {
# if SANITIZER_LINUX
return internal_syscall(SYSCALL(dup3), oldfd, newfd, 0);
-#else
+# else
return internal_syscall(SYSCALL(dup2), oldfd, newfd);
-#endif
+# endif
}
uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
# if SANITIZER_LINUX
return internal_syscall(SYSCALL(readlinkat), AT_FDCWD, (uptr)path, (uptr)buf,
bufsize);
-#else
+# else
return internal_syscall(SYSCALL(readlink), (uptr)path, (uptr)buf, bufsize);
-#endif
+# endif
}
uptr internal_unlink(const char *path) {
# if SANITIZER_LINUX
return internal_syscall(SYSCALL(unlinkat), AT_FDCWD, (uptr)path, 0);
-#else
+# else
return internal_syscall(SYSCALL(unlink), (uptr)path);
-#endif
+# endif
}
uptr internal_rename(const char *oldpath, const char *newpath) {
-# if (defined(__riscv) || defined(__loongarch__)) && defined(__linux__)
+# if (defined(__riscv) || defined(__loongarch__)) && defined(__linux__)
return internal_syscall(SYSCALL(renameat2), AT_FDCWD, (uptr)oldpath, AT_FDCWD,
(uptr)newpath, 0);
-# elif SANITIZER_LINUX
+# elif SANITIZER_LINUX
return internal_syscall(SYSCALL(renameat), AT_FDCWD, (uptr)oldpath, AT_FDCWD,
(uptr)newpath);
-# else
+# else
return internal_syscall(SYSCALL(rename), (uptr)oldpath, (uptr)newpath);
-# endif
+# endif
}
-uptr internal_sched_yield() {
- return internal_syscall(SYSCALL(sched_yield));
-}
+uptr internal_sched_yield() { return internal_syscall(SYSCALL(sched_yield)); }
void internal_usleep(u64 useconds) {
struct timespec ts;
@@ -523,18 +561,18 @@ uptr internal_execve(const char *filename, char *const argv[],
return internal_syscall(SYSCALL(execve), (uptr)filename, (uptr)argv,
(uptr)envp);
}
-#endif // !SANITIZER_SOLARIS && !SANITIZER_NETBSD
+# endif // !SANITIZER_SOLARIS && !SANITIZER_NETBSD
-#if !SANITIZER_NETBSD
+# if !SANITIZER_NETBSD
void internal__exit(int exitcode) {
-#if SANITIZER_FREEBSD || SANITIZER_SOLARIS
+# if SANITIZER_FREEBSD || SANITIZER_SOLARIS
internal_syscall(SYSCALL(exit), exitcode);
-#else
+# else
internal_syscall(SYSCALL(exit_group), exitcode);
-#endif
+# endif
Die(); // Unreachable.
}
-#endif // !SANITIZER_NETBSD
+# endif // !SANITIZER_NETBSD
// ----------------- sanitizer_common.h
bool FileExists(const char *filename) {
@@ -556,30 +594,32 @@ bool DirExists(const char *path) {
# if !SANITIZER_NETBSD
tid_t GetTid() {
-#if SANITIZER_FREEBSD
+# if SANITIZER_FREEBSD
long Tid;
thr_self(&Tid);
return Tid;
-#elif SANITIZER_SOLARIS
+# elif SANITIZER_SOLARIS
return thr_self();
-#else
+# else
return internal_syscall(SYSCALL(gettid));
-#endif
+# endif
}
int TgKill(pid_t pid, tid_t tid, int sig) {
-#if SANITIZER_LINUX
+# if SANITIZER_LINUX
return internal_syscall(SYSCALL(tgkill), pid, tid, sig);
-#elif SANITIZER_FREEBSD
+# elif SANITIZER_FREEBSD
return internal_syscall(SYSCALL(thr_kill2), pid, tid, sig);
-#elif SANITIZER_SOLARIS
+# elif SANITIZER_SOLARIS
(void)pid;
- return thr_kill(tid, sig);
-#endif
+ errno = thr_kill(tid, sig);
+ // TgKill is expected to return -1 on error, not an errno.
+ return errno != 0 ? -1 : 0;
+# endif
}
-#endif
+# endif
-#if SANITIZER_GLIBC
+# if SANITIZER_GLIBC
u64 NanoTime() {
kernel_timeval tv;
internal_memset(&tv, 0, sizeof(tv));
@@ -590,19 +630,19 @@ u64 NanoTime() {
uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp) {
return internal_syscall(SYSCALL(clock_gettime), clk_id, tp);
}
-#elif !SANITIZER_SOLARIS && !SANITIZER_NETBSD
+# elif !SANITIZER_SOLARIS && !SANITIZER_NETBSD
u64 NanoTime() {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
return (u64)ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
}
-#endif
+# endif
// Like getenv, but reads env directly from /proc (on Linux) or parses the
// 'environ' array (on some others) and does not use libc. This function
// should be called first inside __asan_init.
const char *GetEnv(const char *name) {
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_SOLARIS
+# if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_SOLARIS
if (::environ != 0) {
uptr NameLen = internal_strlen(name);
for (char **Env = ::environ; *Env != 0; Env++) {
@@ -611,7 +651,7 @@ const char *GetEnv(const char *name) {
}
}
return 0; // Not found.
-#elif SANITIZER_LINUX
+# elif SANITIZER_LINUX
static char *environ;
static uptr len;
static bool inited;
@@ -621,13 +661,13 @@ const char *GetEnv(const char *name) {
if (!ReadFileToBuffer("/proc/self/environ", &environ, &environ_size, &len))
environ = nullptr;
}
- if (!environ || len == 0) return nullptr;
+ if (!environ || len == 0)
+ return nullptr;
uptr namelen = internal_strlen(name);
const char *p = environ;
while (*p != '\0') { // will happen at the \0\0 that terminates the buffer
// proc file has the format NAME=value\0NAME=value\0NAME=value\0...
- const char* endp =
- (char*)internal_memchr(p, '\0', len - (p - environ));
+ const char *endp = (char *)internal_memchr(p, '\0', len - (p - environ));
if (!endp) // this entry isn't NUL terminated
return nullptr;
else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=') // Match.
@@ -635,18 +675,18 @@ const char *GetEnv(const char *name) {
p = endp + 1;
}
return nullptr; // Not found.
-#else
-#error "Unsupported platform"
-#endif
+# else
+# error "Unsupported platform"
+# endif
}
-#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD && !SANITIZER_GO
+# if !SANITIZER_FREEBSD && !SANITIZER_NETBSD && !SANITIZER_GO
extern "C" {
SANITIZER_WEAK_ATTRIBUTE extern void *__libc_stack_end;
}
-#endif
+# endif
-#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
+# if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
static void ReadNullSepFileToArray(const char *path, char ***arr,
int arr_size) {
char *buff;
@@ -659,20 +699,21 @@ static void ReadNullSepFileToArray(const char *path, char ***arr,
}
(*arr)[0] = buff;
int count, i;
- for (count = 1, i = 1; ; i++) {
+ for (count = 1, i = 1;; i++) {
if (buff[i] == 0) {
- if (buff[i+1] == 0) break;
- (*arr)[count] = &buff[i+1];
+ if (buff[i + 1] == 0)
+ break;
+ (*arr)[count] = &buff[i + 1];
CHECK_LE(count, arr_size - 1); // FIXME: make this more flexible.
count++;
}
}
(*arr)[count] = nullptr;
}
-#endif
+# endif
static void GetArgsAndEnv(char ***argv, char ***envp) {
-#if SANITIZER_FREEBSD
+# if SANITIZER_FREEBSD
// On FreeBSD, retrieving the argument and environment arrays is done via the
// kern.ps_strings sysctl, which returns a pointer to a structure containing
// this information. See also <sys/exec.h>.
@@ -684,30 +725,35 @@ static void GetArgsAndEnv(char ***argv, char ***envp) {
}
*argv = pss->ps_argvstr;
*envp = pss->ps_envstr;
-#elif SANITIZER_NETBSD
+# elif SANITIZER_NETBSD
*argv = __ps_strings->ps_argvstr;
*envp = __ps_strings->ps_envstr;
-#else // SANITIZER_FREEBSD
-#if !SANITIZER_GO
+# else // SANITIZER_FREEBSD
+# if !SANITIZER_GO
if (&__libc_stack_end) {
- uptr* stack_end = (uptr*)__libc_stack_end;
+ uptr *stack_end = (uptr *)__libc_stack_end;
+ // Linux/sparc64 needs an adjustment, cf. glibc
+ // sysdeps/sparc/sparc{32,64}/dl-machine.h (DL_STACK_END).
+# if SANITIZER_LINUX && defined(__sparc__)
+ stack_end = &stack_end[16];
+# endif
// Normally argc can be obtained from *stack_end, however, on ARM glibc's
// _start clobbers it:
// https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/arm/start.S;hb=refs/heads/release/2.31/master#l75
// Do not special-case ARM and infer argc from argv everywhere.
int argc = 0;
while (stack_end[argc + 1]) argc++;
- *argv = (char**)(stack_end + 1);
- *envp = (char**)(stack_end + argc + 2);
+ *argv = (char **)(stack_end + 1);
+ *envp = (char **)(stack_end + argc + 2);
} else {
-#endif // !SANITIZER_GO
+# endif // !SANITIZER_GO
static const int kMaxArgv = 2000, kMaxEnvp = 2000;
ReadNullSepFileToArray("/proc/self/cmdline", argv, kMaxArgv);
ReadNullSepFileToArray("/proc/self/environ", envp, kMaxEnvp);
-#if !SANITIZER_GO
+# if !SANITIZER_GO
}
-#endif // !SANITIZER_GO
-#endif // SANITIZER_FREEBSD
+# endif // !SANITIZER_GO
+# endif // SANITIZER_FREEBSD
}
char **GetArgv() {
@@ -722,12 +768,12 @@ char **GetEnviron() {
return envp;
}
-#if !SANITIZER_SOLARIS
+# if !SANITIZER_SOLARIS
void FutexWait(atomic_uint32_t *p, u32 cmp) {
# if SANITIZER_FREEBSD
_umtx_op(p, UMTX_OP_WAIT_UINT, cmp, 0, 0);
# elif SANITIZER_NETBSD
- sched_yield(); /* No userspace futex-like synchronization */
+ sched_yield(); /* No userspace futex-like synchronization */
# else
internal_syscall(SYSCALL(futex), (uptr)p, FUTEX_WAIT_PRIVATE, cmp, 0, 0, 0);
# endif
@@ -737,7 +783,7 @@ void FutexWake(atomic_uint32_t *p, u32 count) {
# if SANITIZER_FREEBSD
_umtx_op(p, UMTX_OP_WAKE, count, 0, 0);
# elif SANITIZER_NETBSD
- /* No userspace futex-like synchronization */
+ /* No userspace futex-like synchronization */
# else
internal_syscall(SYSCALL(futex), (uptr)p, FUTEX_WAKE_PRIVATE, count, 0, 0, 0);
# endif
@@ -749,26 +795,26 @@ void FutexWake(atomic_uint32_t *p, u32 count) {
// The actual size of this structure is specified by d_reclen.
// Note that getdents64 uses a different structure format. We only provide the
// 32-bit syscall here.
-#if SANITIZER_NETBSD
+# if SANITIZER_NETBSD
// Not used
-#else
+# else
struct linux_dirent {
# if SANITIZER_X32 || SANITIZER_LINUX
u64 d_ino;
u64 d_off;
# else
- unsigned long d_ino;
- unsigned long d_off;
+ unsigned long d_ino;
+ unsigned long d_off;
# endif
- unsigned short d_reclen;
+ unsigned short d_reclen;
# if SANITIZER_LINUX
- unsigned char d_type;
+ unsigned char d_type;
# endif
- char d_name[256];
+ char d_name[256];
};
-#endif
+# endif
-#if !SANITIZER_SOLARIS && !SANITIZER_NETBSD
+# if !SANITIZER_SOLARIS && !SANITIZER_NETBSD
// Syscall wrappers.
uptr internal_ptrace(int request, int pid, void *addr, void *data) {
return internal_syscall(SYSCALL(ptrace), request, pid, (uptr)addr,
@@ -780,24 +826,20 @@ uptr internal_waitpid(int pid, int *status, int options) {
0 /* rusage */);
}
-uptr internal_getpid() {
- return internal_syscall(SYSCALL(getpid));
-}
+uptr internal_getpid() { return internal_syscall(SYSCALL(getpid)); }
-uptr internal_getppid() {
- return internal_syscall(SYSCALL(getppid));
-}
+uptr internal_getppid() { return internal_syscall(SYSCALL(getppid)); }
int internal_dlinfo(void *handle, int request, void *p) {
-#if SANITIZER_FREEBSD
+# if SANITIZER_FREEBSD
return dlinfo(handle, request, p);
-#else
+# else
UNIMPLEMENTED();
-#endif
+# endif
}
uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) {
-#if SANITIZER_FREEBSD
+# if SANITIZER_FREEBSD
return internal_syscall(SYSCALL(getdirentries), fd, (uptr)dirp, count, NULL);
# elif SANITIZER_LINUX
return internal_syscall(SYSCALL(getdents64), fd, (uptr)dirp, count);
@@ -810,7 +852,7 @@ uptr internal_lseek(fd_t fd, OFF_T offset, int whence) {
return internal_syscall(SYSCALL(lseek), fd, offset, whence);
}
-#if SANITIZER_LINUX
+# if SANITIZER_LINUX
uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) {
return internal_syscall(SYSCALL(prctl), option, arg2, arg3, arg4, arg5);
}
@@ -827,10 +869,16 @@ uptr internal_sigaltstack(const void *ss, void *oss) {
return internal_syscall(SYSCALL(sigaltstack), (uptr)ss, (uptr)oss);
}
+extern "C" pid_t __fork(void);
+
int internal_fork() {
# if SANITIZER_LINUX
# if SANITIZER_S390
return internal_syscall(SYSCALL(clone), 0, SIGCHLD);
+# elif SANITIZER_SPARC
+ // The clone syscall interface on SPARC differs massively from the rest,
+ // so fall back to __fork.
+ return __fork();
# else
return internal_syscall(SYSCALL(clone), SIGCHLD, 0);
# endif
@@ -839,7 +887,7 @@ int internal_fork() {
# endif
}
-#if SANITIZER_FREEBSD
+# if SANITIZER_FREEBSD
int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
uptr *oldlenp, const void *newp, uptr newlen) {
return internal_syscall(SYSCALL(__sysctl), name, namelen, oldp,
@@ -854,11 +902,11 @@ int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
// followed by sysctl(). To avoid calling the intercepted version and
// asserting if this happens during startup, call the real sysctlnametomib()
// followed by internal_sysctl() if the syscall is not available.
-#ifdef SYS___sysctlbyname
+# ifdef SYS___sysctlbyname
return internal_syscall(SYSCALL(__sysctlbyname), sname,
internal_strlen(sname), oldp, (size_t *)oldlenp, newp,
(size_t)newlen);
-#else
+# else
static decltype(sysctlnametomib) *real_sysctlnametomib = nullptr;
if (!real_sysctlnametomib)
real_sysctlnametomib =
@@ -870,12 +918,12 @@ int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
if (real_sysctlnametomib(sname, oid, &len) == -1)
return (-1);
return internal_sysctl(oid, len, oldp, oldlenp, newp, newlen);
-#endif
+# endif
}
-#endif
+# endif
-#if SANITIZER_LINUX
-#define SA_RESTORER 0x04000000
+# if SANITIZER_LINUX
+# define SA_RESTORER 0x04000000
// Doesn't set sa_restorer if the caller did not set it, so use with caution
//(see below).
int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {
@@ -899,15 +947,15 @@ int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {
// rt_sigaction, so we need to do the same (we'll need to reimplement the
// restorers; for x86_64 the restorer address can be obtained from
// oldact->sa_restorer upon a call to sigaction(xxx, NULL, oldact).
-#if !SANITIZER_ANDROID || !SANITIZER_MIPS32
+# if !SANITIZER_ANDROID || !SANITIZER_MIPS32
k_act.sa_restorer = u_act->sa_restorer;
-#endif
+# endif
}
uptr result = internal_syscall(SYSCALL(rt_sigaction), (uptr)signum,
- (uptr)(u_act ? &k_act : nullptr),
- (uptr)(u_oldact ? &k_oldact : nullptr),
- (uptr)sizeof(__sanitizer_kernel_sigset_t));
+ (uptr)(u_act ? &k_act : nullptr),
+ (uptr)(u_oldact ? &k_oldact : nullptr),
+ (uptr)sizeof(__sanitizer_kernel_sigset_t));
if ((result == 0) && u_oldact) {
u_oldact->handler = k_oldact.handler;
@@ -915,24 +963,24 @@ int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {
internal_memcpy(&u_oldact->sa_mask, &k_oldact.sa_mask,
sizeof(__sanitizer_kernel_sigset_t));
u_oldact->sa_flags = k_oldact.sa_flags;
-#if !SANITIZER_ANDROID || !SANITIZER_MIPS32
+# if !SANITIZER_ANDROID || !SANITIZER_MIPS32
u_oldact->sa_restorer = k_oldact.sa_restorer;
-#endif
+# endif
}
return result;
}
-#endif // SANITIZER_LINUX
+# endif // SANITIZER_LINUX
uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
__sanitizer_sigset_t *oldset) {
-#if SANITIZER_FREEBSD
+# if SANITIZER_FREEBSD
return internal_syscall(SYSCALL(sigprocmask), how, set, oldset);
-#else
+# else
__sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set;
__sanitizer_kernel_sigset_t *k_oldset = (__sanitizer_kernel_sigset_t *)oldset;
return internal_syscall(SYSCALL(rt_sigprocmask), (uptr)how, (uptr)k_set,
(uptr)k_oldset, sizeof(__sanitizer_kernel_sigset_t));
-#endif
+# endif
}
void internal_sigfillset(__sanitizer_sigset_t *set) {
@@ -943,7 +991,7 @@ void internal_sigemptyset(__sanitizer_sigset_t *set) {
internal_memset(set, 0, sizeof(*set));
}
-#if SANITIZER_LINUX
+# if SANITIZER_LINUX
void internal_sigdelset(__sanitizer_sigset_t *set, int signum) {
signum -= 1;
CHECK_GE(signum, 0);
@@ -963,7 +1011,7 @@ bool internal_sigismember(__sanitizer_sigset_t *set, int signum) {
const uptr bit = signum % (sizeof(k_set->sig[0]) * 8);
return k_set->sig[idx] & ((uptr)1 << bit);
}
-#elif SANITIZER_FREEBSD
+# elif SANITIZER_FREEBSD
uptr internal_procctl(int type, int id, int cmd, void *data) {
return internal_syscall(SYSCALL(procctl), type, id, cmd, data);
}
@@ -977,39 +1025,34 @@ bool internal_sigismember(__sanitizer_sigset_t *set, int signum) {
sigset_t *rset = reinterpret_cast<sigset_t *>(set);
return sigismember(rset, signum);
}
-#endif
-#endif // !SANITIZER_SOLARIS
+# endif
+# endif // !SANITIZER_SOLARIS
-#if !SANITIZER_NETBSD
+# if !SANITIZER_NETBSD
// ThreadLister implementation.
-ThreadLister::ThreadLister(pid_t pid) : pid_(pid), buffer_(4096) {
- char task_directory_path[80];
- internal_snprintf(task_directory_path, sizeof(task_directory_path),
- "/proc/%d/task/", pid);
- descriptor_ = internal_open(task_directory_path, O_RDONLY | O_DIRECTORY);
- if (internal_iserror(descriptor_)) {
- Report("Can't open /proc/%d/task for reading.\n", pid);
- }
+ThreadLister::ThreadLister(pid_t pid) : buffer_(4096) {
+ task_path_.AppendF("/proc/%d/task", pid);
}
ThreadLister::Result ThreadLister::ListThreads(
InternalMmapVector<tid_t> *threads) {
- if (internal_iserror(descriptor_))
+ int descriptor = internal_open(task_path_.data(), O_RDONLY | O_DIRECTORY);
+ if (internal_iserror(descriptor)) {
+ Report("Can't open %s for reading.\n", task_path_.data());
return Error;
- internal_lseek(descriptor_, 0, SEEK_SET);
+ }
+ auto cleanup = at_scope_exit([&] { internal_close(descriptor); });
threads->clear();
Result result = Ok;
for (bool first_read = true;; first_read = false) {
- // Resize to max capacity if it was downsized by IsAlive.
- buffer_.resize(buffer_.capacity());
CHECK_GE(buffer_.size(), 4096);
uptr read = internal_getdents(
- descriptor_, (struct linux_dirent *)buffer_.data(), buffer_.size());
+ descriptor, (struct linux_dirent *)buffer_.data(), buffer_.size());
if (!read)
return result;
if (internal_iserror(read)) {
- Report("Can't read directory entries from /proc/%d/task.\n", pid_);
+ Report("Can't read directory entries from %s.\n", task_path_.data());
return Error;
}
@@ -1047,45 +1090,53 @@ ThreadLister::Result ThreadLister::ListThreads(
}
}
-bool ThreadLister::IsAlive(int tid) {
+const char *ThreadLister::LoadStatus(tid_t tid) {
+ status_path_.clear();
+ status_path_.AppendF("%s/%llu/status", task_path_.data(), tid);
+ auto cleanup = at_scope_exit([&] {
+ // Resize back to capacity if it is downsized by `ReadFileToVector`.
+ buffer_.resize(buffer_.capacity());
+ });
+ if (!ReadFileToVector(status_path_.data(), &buffer_) || buffer_.empty())
+ return nullptr;
+ buffer_.push_back('\0');
+ return buffer_.data();
+}
+
+bool ThreadLister::IsAlive(tid_t tid) {
// /proc/%d/task/%d/status uses same call to detect alive threads as
// proc_task_readdir. See task_state implementation in Linux.
- char path[80];
- internal_snprintf(path, sizeof(path), "/proc/%d/task/%d/status", pid_, tid);
- if (!ReadFileToVector(path, &buffer_) || buffer_.empty())
- return false;
- buffer_.push_back(0);
static const char kPrefix[] = "\nPPid:";
- const char *field = internal_strstr(buffer_.data(), kPrefix);
+ const char *status = LoadStatus(tid);
+ if (!status)
+ return false;
+ const char *field = internal_strstr(status, kPrefix);
if (!field)
return false;
field += internal_strlen(kPrefix);
return (int)internal_atoll(field) != 0;
}
-ThreadLister::~ThreadLister() {
- if (!internal_iserror(descriptor_))
- internal_close(descriptor_);
-}
-#endif
+# endif
-#if SANITIZER_WORDSIZE == 32
+# if SANITIZER_WORDSIZE == 32
// Take care of unusable kernel area in top gigabyte.
static uptr GetKernelAreaSize() {
-#if SANITIZER_LINUX && !SANITIZER_X32
+# if SANITIZER_LINUX && !SANITIZER_X32
const uptr gbyte = 1UL << 30;
// Firstly check if there are writable segments
// mapped to top gigabyte (e.g. stack).
- MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+ MemoryMappingLayout proc_maps(/*cache_enabled*/ true);
if (proc_maps.Error())
return 0;
MemoryMappedSegment segment;
while (proc_maps.Next(&segment)) {
- if ((segment.end >= 3 * gbyte) && segment.IsWritable()) return 0;
+ if ((segment.end >= 3 * gbyte) && segment.IsWritable())
+ return 0;
}
-#if !SANITIZER_ANDROID
+# if !SANITIZER_ANDROID
// Even if nothing is mapped, top Gb may still be accessible
// if we are running on 64-bit kernel.
// Uname may report misleading results if personality type
@@ -1095,21 +1146,22 @@ static uptr GetKernelAreaSize() {
if (!(pers & PER_MASK) && internal_uname(&uname_info) == 0 &&
internal_strstr(uname_info.machine, "64"))
return 0;
-#endif // SANITIZER_ANDROID
+# endif // SANITIZER_ANDROID
// Top gigabyte is reserved for kernel.
return gbyte;
-#else
+# else
return 0;
-#endif // SANITIZER_LINUX && !SANITIZER_X32
+# endif // SANITIZER_LINUX && !SANITIZER_X32
}
-#endif // SANITIZER_WORDSIZE == 32
+# endif // SANITIZER_WORDSIZE == 32
uptr GetMaxVirtualAddress() {
-#if SANITIZER_NETBSD && defined(__x86_64__)
+# if SANITIZER_NETBSD && defined(__x86_64__)
return 0x7f7ffffff000ULL; // (0x00007f8000000000 - PAGE_SIZE)
-#elif SANITIZER_WORDSIZE == 64
-# if defined(__powerpc64__) || defined(__aarch64__) || defined(__loongarch__)
+# elif SANITIZER_WORDSIZE == 64
+# if defined(__powerpc64__) || defined(__aarch64__) || \
+ defined(__loongarch__) || SANITIZER_RISCV64
// On PowerPC64 we have two different address space layouts: 44- and 46-bit.
// We somehow need to figure out which one we are using now and choose
// one of 0x00000fffffffffffUL and 0x00003fffffffffffUL.
@@ -1118,97 +1170,97 @@ uptr GetMaxVirtualAddress() {
// This should (does) work for both PowerPC64 Endian modes.
// Similarly, aarch64 has multiple address space layouts: 39, 42 and 47-bit.
// loongarch64 also has multiple address space layouts: default is 47-bit.
+ // RISC-V 64 also has multiple address space layouts: 39, 48 and 57-bit.
return (1ULL << (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1)) - 1;
-#elif SANITIZER_RISCV64
- return (1ULL << 38) - 1;
-# elif SANITIZER_MIPS64
+# elif SANITIZER_MIPS64
return (1ULL << 40) - 1; // 0x000000ffffffffffUL;
-# elif defined(__s390x__)
+# elif defined(__s390x__)
return (1ULL << 53) - 1; // 0x001fffffffffffffUL;
-#elif defined(__sparc__)
+# elif defined(__sparc__)
return ~(uptr)0;
-# else
+# else
return (1ULL << 47) - 1; // 0x00007fffffffffffUL;
-# endif
-#else // SANITIZER_WORDSIZE == 32
-# if defined(__s390__)
+# endif
+# else // SANITIZER_WORDSIZE == 32
+# if defined(__s390__)
return (1ULL << 31) - 1; // 0x7fffffff;
-# else
+# else
return (1ULL << 32) - 1; // 0xffffffff;
-# endif
-#endif // SANITIZER_WORDSIZE
+# endif
+# endif // SANITIZER_WORDSIZE
}
uptr GetMaxUserVirtualAddress() {
uptr addr = GetMaxVirtualAddress();
-#if SANITIZER_WORDSIZE == 32 && !defined(__s390__)
+# if SANITIZER_WORDSIZE == 32 && !defined(__s390__)
if (!common_flags()->full_address_space)
addr -= GetKernelAreaSize();
CHECK_LT(reinterpret_cast<uptr>(&addr), addr);
-#endif
+# endif
return addr;
}
-#if !SANITIZER_ANDROID
+# if !SANITIZER_ANDROID || defined(__aarch64__)
uptr GetPageSize() {
-#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__i386__)) && \
- defined(EXEC_PAGESIZE)
+# if SANITIZER_LINUX && (defined(__x86_64__) || defined(__i386__)) && \
+ defined(EXEC_PAGESIZE)
return EXEC_PAGESIZE;
-#elif SANITIZER_FREEBSD || SANITIZER_NETBSD
-// Use sysctl as sysconf can trigger interceptors internally.
+# elif SANITIZER_FREEBSD || SANITIZER_NETBSD
+ // Use sysctl as sysconf can trigger interceptors internally.
int pz = 0;
uptr pzl = sizeof(pz);
int mib[2] = {CTL_HW, HW_PAGESIZE};
int rv = internal_sysctl(mib, 2, &pz, &pzl, nullptr, 0);
CHECK_EQ(rv, 0);
return (uptr)pz;
-#elif SANITIZER_USE_GETAUXVAL
+# elif SANITIZER_USE_GETAUXVAL
return getauxval(AT_PAGESZ);
-#else
+# else
return sysconf(_SC_PAGESIZE); // EXEC_PAGESIZE may not be trustworthy.
-#endif
+# endif
}
-#endif // !SANITIZER_ANDROID
+# endif
-uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
-#if SANITIZER_SOLARIS
+uptr ReadBinaryName(/*out*/ char *buf, uptr buf_len) {
+# if SANITIZER_SOLARIS
const char *default_module_name = getexecname();
CHECK_NE(default_module_name, NULL);
return internal_snprintf(buf, buf_len, "%s", default_module_name);
-#else
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD
-#if SANITIZER_FREEBSD
+# else
+# if SANITIZER_FREEBSD || SANITIZER_NETBSD
+# if SANITIZER_FREEBSD
const int Mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
-#else
+# else
const int Mib[4] = {CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME};
-#endif
+# endif
const char *default_module_name = "kern.proc.pathname";
uptr Size = buf_len;
bool IsErr =
(internal_sysctl(Mib, ARRAY_SIZE(Mib), buf, &Size, NULL, 0) != 0);
int readlink_error = IsErr ? errno : 0;
uptr module_name_len = Size;
-#else
+# else
const char *default_module_name = "/proc/self/exe";
- uptr module_name_len = internal_readlink(
- default_module_name, buf, buf_len);
+ uptr module_name_len = internal_readlink(default_module_name, buf, buf_len);
int readlink_error;
bool IsErr = internal_iserror(module_name_len, &readlink_error);
-#endif // SANITIZER_SOLARIS
+# endif
if (IsErr) {
// We can't read binary name for some reason, assume it's unknown.
- Report("WARNING: reading executable name failed with errno %d, "
- "some stack frames may not be symbolized\n", readlink_error);
- module_name_len = internal_snprintf(buf, buf_len, "%s",
- default_module_name);
+ Report(
+ "WARNING: reading executable name failed with errno %d, "
+ "some stack frames may not be symbolized\n",
+ readlink_error);
+ module_name_len =
+ internal_snprintf(buf, buf_len, "%s", default_module_name);
CHECK_LT(module_name_len, buf_len);
}
return module_name_len;
-#endif
+# endif
}
uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {
-#if SANITIZER_LINUX
+# if SANITIZER_LINUX
char *tmpbuf;
uptr tmpsize;
uptr tmplen;
@@ -1218,7 +1270,7 @@ uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {
UnmapOrDie(tmpbuf, tmpsize);
return internal_strlen(buf);
}
-#endif
+# endif
return ReadBinaryName(buf, buf_len);
}
@@ -1228,20 +1280,22 @@ bool LibraryNameIs(const char *full_name, const char *base_name) {
// Strip path.
while (*name != '\0') name++;
while (name > full_name && *name != '/') name--;
- if (*name == '/') name++;
+ if (*name == '/')
+ name++;
uptr base_name_length = internal_strlen(base_name);
- if (internal_strncmp(name, base_name, base_name_length)) return false;
+ if (internal_strncmp(name, base_name, base_name_length))
+ return false;
return (name[base_name_length] == '-' || name[base_name_length] == '.');
}
-#if !SANITIZER_ANDROID
+# if !SANITIZER_ANDROID
// Call cb for each region mapped by map.
void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) {
CHECK_NE(map, nullptr);
-#if !SANITIZER_FREEBSD
+# if !SANITIZER_FREEBSD
typedef ElfW(Phdr) Elf_Phdr;
typedef ElfW(Ehdr) Elf_Ehdr;
-#endif // !SANITIZER_FREEBSD
+# endif // !SANITIZER_FREEBSD
char *base = (char *)map->l_addr;
Elf_Ehdr *ehdr = (Elf_Ehdr *)base;
char *phdrs = base + ehdr->e_phoff;
@@ -1273,10 +1327,10 @@ void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) {
}
}
}
-#endif
+# endif
-#if SANITIZER_LINUX
-#if defined(__x86_64__)
+# if SANITIZER_LINUX
+# if defined(__x86_64__)
// We cannot use glibc's clone wrapper, because it messes with the child
// task's TLS. It writes the PID and TID of the child task to its thread
// descriptor, but in our case the child task shares the thread descriptor with
@@ -1295,50 +1349,46 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
register void *r8 __asm__("r8") = newtls;
register int *r10 __asm__("r10") = child_tidptr;
__asm__ __volatile__(
- /* %rax = syscall(%rax = SYSCALL(clone),
- * %rdi = flags,
- * %rsi = child_stack,
- * %rdx = parent_tidptr,
- * %r8 = new_tls,
- * %r10 = child_tidptr)
- */
- "syscall\n"
-
- /* if (%rax != 0)
- * return;
- */
- "testq %%rax,%%rax\n"
- "jnz 1f\n"
-
- /* In the child. Terminate unwind chain. */
- // XXX: We should also terminate the CFI unwind chain
- // here. Unfortunately clang 3.2 doesn't support the
- // necessary CFI directives, so we skip that part.
- "xorq %%rbp,%%rbp\n"
-
- /* Call "fn(arg)". */
- "popq %%rax\n"
- "popq %%rdi\n"
- "call *%%rax\n"
-
- /* Call _exit(%rax). */
- "movq %%rax,%%rdi\n"
- "movq %2,%%rax\n"
- "syscall\n"
-
- /* Return to parent. */
- "1:\n"
- : "=a" (res)
- : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)),
- "S"(child_stack),
- "D"(flags),
- "d"(parent_tidptr),
- "r"(r8),
- "r"(r10)
- : "memory", "r11", "rcx");
+ /* %rax = syscall(%rax = SYSCALL(clone),
+ * %rdi = flags,
+ * %rsi = child_stack,
+ * %rdx = parent_tidptr,
+ * %r8 = new_tls,
+ * %r10 = child_tidptr)
+ */
+ "syscall\n"
+
+ /* if (%rax != 0)
+ * return;
+ */
+ "testq %%rax,%%rax\n"
+ "jnz 1f\n"
+
+ /* In the child. Terminate unwind chain. */
+ // XXX: We should also terminate the CFI unwind chain
+ // here. Unfortunately clang 3.2 doesn't support the
+ // necessary CFI directives, so we skip that part.
+ "xorq %%rbp,%%rbp\n"
+
+ /* Call "fn(arg)". */
+ "popq %%rax\n"
+ "popq %%rdi\n"
+ "call *%%rax\n"
+
+ /* Call _exit(%rax). */
+ "movq %%rax,%%rdi\n"
+ "movq %2,%%rax\n"
+ "syscall\n"
+
+ /* Return to parent. */
+ "1:\n"
+ : "=a"(res)
+ : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)), "S"(child_stack), "D"(flags),
+ "d"(parent_tidptr), "r"(r8), "r"(r10)
+ : "memory", "r11", "rcx");
return res;
}
-#elif defined(__mips__)
+# elif defined(__mips__)
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
int *parent_tidptr, void *newtls, int *child_tidptr) {
long long res;
@@ -1353,68 +1403,63 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
// We don't have proper CFI directives here because it requires alot of code
// for very marginal benefits.
__asm__ __volatile__(
- /* $v0 = syscall($v0 = __NR_clone,
- * $a0 = flags,
- * $a1 = child_stack,
- * $a2 = parent_tidptr,
- * $a3 = new_tls,
- * $a4 = child_tidptr)
- */
- ".cprestore 16;\n"
- "move $4,%1;\n"
- "move $5,%2;\n"
- "move $6,%3;\n"
- "move $7,%4;\n"
- /* Store the fifth argument on stack
- * if we are using 32-bit abi.
- */
-#if SANITIZER_WORDSIZE == 32
- "lw %5,16($29);\n"
-#else
- "move $8,%5;\n"
-#endif
- "li $2,%6;\n"
- "syscall;\n"
-
- /* if ($v0 != 0)
- * return;
- */
- "bnez $2,1f;\n"
-
- /* Call "fn(arg)". */
-#if SANITIZER_WORDSIZE == 32
-#ifdef __BIG_ENDIAN__
- "lw $25,4($29);\n"
- "lw $4,12($29);\n"
-#else
- "lw $25,0($29);\n"
- "lw $4,8($29);\n"
-#endif
-#else
- "ld $25,0($29);\n"
- "ld $4,8($29);\n"
-#endif
- "jal $25;\n"
-
- /* Call _exit($v0). */
- "move $4,$2;\n"
- "li $2,%7;\n"
- "syscall;\n"
-
- /* Return to parent. */
- "1:\n"
- : "=r" (res)
- : "r"(flags),
- "r"(child_stack),
- "r"(parent_tidptr),
- "r"(a3),
- "r"(a4),
- "i"(__NR_clone),
- "i"(__NR_exit)
- : "memory", "$29" );
+ /* $v0 = syscall($v0 = __NR_clone,
+ * $a0 = flags,
+ * $a1 = child_stack,
+ * $a2 = parent_tidptr,
+ * $a3 = new_tls,
+ * $a4 = child_tidptr)
+ */
+ ".cprestore 16;\n"
+ "move $4,%1;\n"
+ "move $5,%2;\n"
+ "move $6,%3;\n"
+ "move $7,%4;\n"
+ /* Store the fifth argument on stack
+ * if we are using 32-bit abi.
+ */
+# if SANITIZER_WORDSIZE == 32
+ "lw %5,16($29);\n"
+# else
+ "move $8,%5;\n"
+# endif
+ "li $2,%6;\n"
+ "syscall;\n"
+
+ /* if ($v0 != 0)
+ * return;
+ */
+ "bnez $2,1f;\n"
+
+ /* Call "fn(arg)". */
+# if SANITIZER_WORDSIZE == 32
+# ifdef __BIG_ENDIAN__
+ "lw $25,4($29);\n"
+ "lw $4,12($29);\n"
+# else
+ "lw $25,0($29);\n"
+ "lw $4,8($29);\n"
+# endif
+# else
+ "ld $25,0($29);\n"
+ "ld $4,8($29);\n"
+# endif
+ "jal $25;\n"
+
+ /* Call _exit($v0). */
+ "move $4,$2;\n"
+ "li $2,%7;\n"
+ "syscall;\n"
+
+ /* Return to parent. */
+ "1:\n"
+ : "=r"(res)
+ : "r"(flags), "r"(child_stack), "r"(parent_tidptr), "r"(a3), "r"(a4),
+ "i"(__NR_clone), "i"(__NR_exit)
+ : "memory", "$29");
return res;
}
-#elif SANITIZER_RISCV64
+# elif SANITIZER_RISCV64
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
int *parent_tidptr, void *newtls, int *child_tidptr) {
if (!fn || !child_stack)
@@ -1455,7 +1500,7 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
: "memory");
return res;
}
-#elif defined(__aarch64__)
+# elif defined(__aarch64__)
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
int *parent_tidptr, void *newtls, int *child_tidptr) {
register long long res __asm__("x0");
@@ -1466,47 +1511,45 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
((unsigned long long *)child_stack)[0] = (uptr)fn;
((unsigned long long *)child_stack)[1] = (uptr)arg;
- register int (*__fn)(void *) __asm__("x0") = fn;
+ register int (*__fn)(void *) __asm__("x0") = fn;
register void *__stack __asm__("x1") = child_stack;
- register int __flags __asm__("x2") = flags;
- register void *__arg __asm__("x3") = arg;
- register int *__ptid __asm__("x4") = parent_tidptr;
- register void *__tls __asm__("x5") = newtls;
- register int *__ctid __asm__("x6") = child_tidptr;
+ register int __flags __asm__("x2") = flags;
+ register void *__arg __asm__("x3") = arg;
+ register int *__ptid __asm__("x4") = parent_tidptr;
+ register void *__tls __asm__("x5") = newtls;
+ register int *__ctid __asm__("x6") = child_tidptr;
__asm__ __volatile__(
- "mov x0,x2\n" /* flags */
- "mov x2,x4\n" /* ptid */
- "mov x3,x5\n" /* tls */
- "mov x4,x6\n" /* ctid */
- "mov x8,%9\n" /* clone */
-
- "svc 0x0\n"
-
- /* if (%r0 != 0)
- * return %r0;
- */
- "cmp x0, #0\n"
- "bne 1f\n"
-
- /* In the child, now. Call "fn(arg)". */
- "ldp x1, x0, [sp], #16\n"
- "blr x1\n"
-
- /* Call _exit(%r0). */
- "mov x8, %10\n"
- "svc 0x0\n"
- "1:\n"
-
- : "=r" (res)
- : "i"(-EINVAL),
- "r"(__fn), "r"(__stack), "r"(__flags), "r"(__arg),
- "r"(__ptid), "r"(__tls), "r"(__ctid),
- "i"(__NR_clone), "i"(__NR_exit)
- : "x30", "memory");
+ "mov x0,x2\n" /* flags */
+ "mov x2,x4\n" /* ptid */
+ "mov x3,x5\n" /* tls */
+ "mov x4,x6\n" /* ctid */
+ "mov x8,%9\n" /* clone */
+
+ "svc 0x0\n"
+
+ /* if (%r0 != 0)
+ * return %r0;
+ */
+ "cmp x0, #0\n"
+ "bne 1f\n"
+
+ /* In the child, now. Call "fn(arg)". */
+ "ldp x1, x0, [sp], #16\n"
+ "blr x1\n"
+
+ /* Call _exit(%r0). */
+ "mov x8, %10\n"
+ "svc 0x0\n"
+ "1:\n"
+
+ : "=r"(res)
+ : "i"(-EINVAL), "r"(__fn), "r"(__stack), "r"(__flags), "r"(__arg),
+ "r"(__ptid), "r"(__tls), "r"(__ctid), "i"(__NR_clone), "i"(__NR_exit)
+ : "x30", "memory");
return res;
}
-#elif SANITIZER_LOONGARCH64
+# elif SANITIZER_LOONGARCH64
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
int *parent_tidptr, void *newtls, int *child_tidptr) {
if (!fn || !child_stack)
@@ -1544,119 +1587,110 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
: "=r"(res)
: "0"(__flags), "r"(__stack), "r"(__ptid), "r"(__ctid), "r"(__tls),
"r"(__fn), "r"(__arg), "r"(nr_clone), "i"(__NR_exit)
- : "memory", "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", "$t8");
+ : "memory", "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
+ "$t8");
return res;
}
-#elif defined(__powerpc64__)
+# elif defined(__powerpc64__)
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
- int *parent_tidptr, void *newtls, int *child_tidptr) {
+ int *parent_tidptr, void *newtls, int *child_tidptr) {
long long res;
// Stack frame structure.
-#if SANITIZER_PPC64V1
-// Back chain == 0 (SP + 112)
-// Frame (112 bytes):
-// Parameter save area (SP + 48), 8 doublewords
-// TOC save area (SP + 40)
-// Link editor doubleword (SP + 32)
-// Compiler doubleword (SP + 24)
-// LR save area (SP + 16)
-// CR save area (SP + 8)
-// Back chain (SP + 0)
-# define FRAME_SIZE 112
-# define FRAME_TOC_SAVE_OFFSET 40
-#elif SANITIZER_PPC64V2
-// Back chain == 0 (SP + 32)
-// Frame (32 bytes):
-// TOC save area (SP + 24)
-// LR save area (SP + 16)
-// CR save area (SP + 8)
-// Back chain (SP + 0)
-# define FRAME_SIZE 32
-# define FRAME_TOC_SAVE_OFFSET 24
-#else
-# error "Unsupported PPC64 ABI"
-#endif
+# if SANITIZER_PPC64V1
+ // Back chain == 0 (SP + 112)
+ // Frame (112 bytes):
+ // Parameter save area (SP + 48), 8 doublewords
+ // TOC save area (SP + 40)
+ // Link editor doubleword (SP + 32)
+ // Compiler doubleword (SP + 24)
+ // LR save area (SP + 16)
+ // CR save area (SP + 8)
+ // Back chain (SP + 0)
+# define FRAME_SIZE 112
+# define FRAME_TOC_SAVE_OFFSET 40
+# elif SANITIZER_PPC64V2
+ // Back chain == 0 (SP + 32)
+ // Frame (32 bytes):
+ // TOC save area (SP + 24)
+ // LR save area (SP + 16)
+ // CR save area (SP + 8)
+ // Back chain (SP + 0)
+# define FRAME_SIZE 32
+# define FRAME_TOC_SAVE_OFFSET 24
+# else
+# error "Unsupported PPC64 ABI"
+# endif
if (!fn || !child_stack)
return -EINVAL;
CHECK_EQ(0, (uptr)child_stack % 16);
register int (*__fn)(void *) __asm__("r3") = fn;
- register void *__cstack __asm__("r4") = child_stack;
- register int __flags __asm__("r5") = flags;
- register void *__arg __asm__("r6") = arg;
- register int *__ptidptr __asm__("r7") = parent_tidptr;
- register void *__newtls __asm__("r8") = newtls;
- register int *__ctidptr __asm__("r9") = child_tidptr;
-
- __asm__ __volatile__(
- /* fn and arg are saved across the syscall */
- "mr 28, %5\n\t"
- "mr 27, %8\n\t"
-
- /* syscall
- r0 == __NR_clone
- r3 == flags
- r4 == child_stack
- r5 == parent_tidptr
- r6 == newtls
- r7 == child_tidptr */
- "mr 3, %7\n\t"
- "mr 5, %9\n\t"
- "mr 6, %10\n\t"
- "mr 7, %11\n\t"
- "li 0, %3\n\t"
- "sc\n\t"
-
- /* Test if syscall was successful */
- "cmpdi cr1, 3, 0\n\t"
- "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t"
- "bne- cr1, 1f\n\t"
-
- /* Set up stack frame */
- "li 29, 0\n\t"
- "stdu 29, -8(1)\n\t"
- "stdu 1, -%12(1)\n\t"
- /* Do the function call */
- "std 2, %13(1)\n\t"
-#if SANITIZER_PPC64V1
- "ld 0, 0(28)\n\t"
- "ld 2, 8(28)\n\t"
- "mtctr 0\n\t"
-#elif SANITIZER_PPC64V2
- "mr 12, 28\n\t"
- "mtctr 12\n\t"
-#else
-# error "Unsupported PPC64 ABI"
-#endif
- "mr 3, 27\n\t"
- "bctrl\n\t"
- "ld 2, %13(1)\n\t"
-
- /* Call _exit(r3) */
- "li 0, %4\n\t"
- "sc\n\t"
-
- /* Return to parent */
- "1:\n\t"
- "mr %0, 3\n\t"
- : "=r" (res)
- : "0" (-1),
- "i" (EINVAL),
- "i" (__NR_clone),
- "i" (__NR_exit),
- "r" (__fn),
- "r" (__cstack),
- "r" (__flags),
- "r" (__arg),
- "r" (__ptidptr),
- "r" (__newtls),
- "r" (__ctidptr),
- "i" (FRAME_SIZE),
- "i" (FRAME_TOC_SAVE_OFFSET)
- : "cr0", "cr1", "memory", "ctr", "r0", "r27", "r28", "r29");
+ register void *__cstack __asm__("r4") = child_stack;
+ register int __flags __asm__("r5") = flags;
+ register void *__arg __asm__("r6") = arg;
+ register int *__ptidptr __asm__("r7") = parent_tidptr;
+ register void *__newtls __asm__("r8") = newtls;
+ register int *__ctidptr __asm__("r9") = child_tidptr;
+
+ __asm__ __volatile__(
+ /* fn and arg are saved across the syscall */
+ "mr 28, %5\n\t"
+ "mr 27, %8\n\t"
+
+ /* syscall
+ r0 == __NR_clone
+ r3 == flags
+ r4 == child_stack
+ r5 == parent_tidptr
+ r6 == newtls
+ r7 == child_tidptr */
+ "mr 3, %7\n\t"
+ "mr 5, %9\n\t"
+ "mr 6, %10\n\t"
+ "mr 7, %11\n\t"
+ "li 0, %3\n\t"
+ "sc\n\t"
+
+ /* Test if syscall was successful */
+ "cmpdi cr1, 3, 0\n\t"
+ "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t"
+ "bne- cr1, 1f\n\t"
+
+ /* Set up stack frame */
+ "li 29, 0\n\t"
+ "stdu 29, -8(1)\n\t"
+ "stdu 1, -%12(1)\n\t"
+ /* Do the function call */
+ "std 2, %13(1)\n\t"
+# if SANITIZER_PPC64V1
+ "ld 0, 0(28)\n\t"
+ "ld 2, 8(28)\n\t"
+ "mtctr 0\n\t"
+# elif SANITIZER_PPC64V2
+ "mr 12, 28\n\t"
+ "mtctr 12\n\t"
+# else
+# error "Unsupported PPC64 ABI"
+# endif
+ "mr 3, 27\n\t"
+ "bctrl\n\t"
+ "ld 2, %13(1)\n\t"
+
+ /* Call _exit(r3) */
+ "li 0, %4\n\t"
+ "sc\n\t"
+
+ /* Return to parent */
+ "1:\n\t"
+ "mr %0, 3\n\t"
+ : "=r"(res)
+ : "0"(-1), "i"(EINVAL), "i"(__NR_clone), "i"(__NR_exit), "r"(__fn),
+ "r"(__cstack), "r"(__flags), "r"(__arg), "r"(__ptidptr), "r"(__newtls),
+ "r"(__ctidptr), "i"(FRAME_SIZE), "i"(FRAME_TOC_SAVE_OFFSET)
+ : "cr0", "cr1", "memory", "ctr", "r0", "r27", "r28", "r29");
return res;
}
-#elif defined(__i386__)
+# elif defined(__i386__)
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
int *parent_tidptr, void *newtls, int *child_tidptr) {
int res;
@@ -1669,59 +1703,56 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
((unsigned int *)child_stack)[2] = (uptr)fn;
((unsigned int *)child_stack)[3] = (uptr)arg;
__asm__ __volatile__(
- /* %eax = syscall(%eax = SYSCALL(clone),
- * %ebx = flags,
- * %ecx = child_stack,
- * %edx = parent_tidptr,
- * %esi = new_tls,
- * %edi = child_tidptr)
- */
-
- /* Obtain flags */
- "movl (%%ecx), %%ebx\n"
- /* Do the system call */
- "pushl %%ebx\n"
- "pushl %%esi\n"
- "pushl %%edi\n"
- /* Remember the flag value. */
- "movl %%ebx, (%%ecx)\n"
- "int $0x80\n"
- "popl %%edi\n"
- "popl %%esi\n"
- "popl %%ebx\n"
-
- /* if (%eax != 0)
- * return;
- */
-
- "test %%eax,%%eax\n"
- "jnz 1f\n"
-
- /* terminate the stack frame */
- "xorl %%ebp,%%ebp\n"
- /* Call FN. */
- "call *%%ebx\n"
-#ifdef PIC
- "call here\n"
- "here:\n"
- "popl %%ebx\n"
- "addl $_GLOBAL_OFFSET_TABLE_+[.-here], %%ebx\n"
-#endif
- /* Call exit */
- "movl %%eax, %%ebx\n"
- "movl %2, %%eax\n"
- "int $0x80\n"
- "1:\n"
- : "=a" (res)
- : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)),
- "c"(child_stack),
- "d"(parent_tidptr),
- "S"(newtls),
- "D"(child_tidptr)
- : "memory");
+ /* %eax = syscall(%eax = SYSCALL(clone),
+ * %ebx = flags,
+ * %ecx = child_stack,
+ * %edx = parent_tidptr,
+ * %esi = new_tls,
+ * %edi = child_tidptr)
+ */
+
+ /* Obtain flags */
+ "movl (%%ecx), %%ebx\n"
+ /* Do the system call */
+ "pushl %%ebx\n"
+ "pushl %%esi\n"
+ "pushl %%edi\n"
+ /* Remember the flag value. */
+ "movl %%ebx, (%%ecx)\n"
+ "int $0x80\n"
+ "popl %%edi\n"
+ "popl %%esi\n"
+ "popl %%ebx\n"
+
+ /* if (%eax != 0)
+ * return;
+ */
+
+ "test %%eax,%%eax\n"
+ "jnz 1f\n"
+
+ /* terminate the stack frame */
+ "xorl %%ebp,%%ebp\n"
+ /* Call FN. */
+ "call *%%ebx\n"
+# ifdef PIC
+ "call here\n"
+ "here:\n"
+ "popl %%ebx\n"
+ "addl $_GLOBAL_OFFSET_TABLE_+[.-here], %%ebx\n"
+# endif
+ /* Call exit */
+ "movl %%eax, %%ebx\n"
+ "movl %2, %%eax\n"
+ "int $0x80\n"
+ "1:\n"
+ : "=a"(res)
+ : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)), "c"(child_stack),
+ "d"(parent_tidptr), "S"(newtls), "D"(child_tidptr)
+ : "memory");
return res;
}
-#elif defined(__arm__)
+# elif defined(__arm__)
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
int *parent_tidptr, void *newtls, int *child_tidptr) {
unsigned int res;
@@ -1737,70 +1768,68 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
register int *r4 __asm__("r4") = child_tidptr;
register int r7 __asm__("r7") = __NR_clone;
-#if __ARM_ARCH > 4 || defined (__ARM_ARCH_4T__)
-# define ARCH_HAS_BX
-#endif
-#if __ARM_ARCH > 4
-# define ARCH_HAS_BLX
-#endif
+# if __ARM_ARCH > 4 || defined(__ARM_ARCH_4T__)
+# define ARCH_HAS_BX
+# endif
+# if __ARM_ARCH > 4
+# define ARCH_HAS_BLX
+# endif
-#ifdef ARCH_HAS_BX
-# ifdef ARCH_HAS_BLX
-# define BLX(R) "blx " #R "\n"
-# else
-# define BLX(R) "mov lr, pc; bx " #R "\n"
-# endif
-#else
-# define BLX(R) "mov lr, pc; mov pc," #R "\n"
-#endif
+# ifdef ARCH_HAS_BX
+# ifdef ARCH_HAS_BLX
+# define BLX(R) "blx " #R "\n"
+# else
+# define BLX(R) "mov lr, pc; bx " #R "\n"
+# endif
+# else
+# define BLX(R) "mov lr, pc; mov pc," #R "\n"
+# endif
__asm__ __volatile__(
- /* %r0 = syscall(%r7 = SYSCALL(clone),
- * %r0 = flags,
- * %r1 = child_stack,
- * %r2 = parent_tidptr,
- * %r3 = new_tls,
- * %r4 = child_tidptr)
- */
-
- /* Do the system call */
- "swi 0x0\n"
-
- /* if (%r0 != 0)
- * return %r0;
- */
- "cmp r0, #0\n"
- "bne 1f\n"
-
- /* In the child, now. Call "fn(arg)". */
- "ldr r0, [sp, #4]\n"
- "ldr ip, [sp], #8\n"
- BLX(ip)
- /* Call _exit(%r0). */
- "mov r7, %7\n"
- "swi 0x0\n"
- "1:\n"
- "mov %0, r0\n"
- : "=r"(res)
- : "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r7),
- "i"(__NR_exit)
- : "memory");
+ /* %r0 = syscall(%r7 = SYSCALL(clone),
+ * %r0 = flags,
+ * %r1 = child_stack,
+ * %r2 = parent_tidptr,
+ * %r3 = new_tls,
+ * %r4 = child_tidptr)
+ */
+
+ /* Do the system call */
+ "swi 0x0\n"
+
+ /* if (%r0 != 0)
+ * return %r0;
+ */
+ "cmp r0, #0\n"
+ "bne 1f\n"
+
+ /* In the child, now. Call "fn(arg)". */
+ "ldr r0, [sp, #4]\n"
+ "ldr ip, [sp], #8\n" BLX(ip)
+ /* Call _exit(%r0). */
+ "mov r7, %7\n"
+ "swi 0x0\n"
+ "1:\n"
+ "mov %0, r0\n"
+ : "=r"(res)
+ : "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r7), "i"(__NR_exit)
+ : "memory");
return res;
}
-#endif
-#endif // SANITIZER_LINUX
+# endif
+# endif // SANITIZER_LINUX
-#if SANITIZER_LINUX
+# if SANITIZER_LINUX
int internal_uname(struct utsname *buf) {
return internal_syscall(SYSCALL(uname), buf);
}
-#endif
+# endif
-#if SANITIZER_ANDROID
-#if __ANDROID_API__ < 21
+# if SANITIZER_ANDROID
+# if __ANDROID_API__ < 21
extern "C" __attribute__((weak)) int dl_iterate_phdr(
int (*)(struct dl_phdr_info *, size_t, void *), void *);
-#endif
+# endif
static int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size,
void *data) {
@@ -1817,40 +1846,41 @@ static int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size,
static atomic_uint32_t android_api_level;
static AndroidApiLevel AndroidDetectApiLevelStatic() {
-#if __ANDROID_API__ <= 19
+# if __ANDROID_API__ <= 19
return ANDROID_KITKAT;
-#elif __ANDROID_API__ <= 22
+# elif __ANDROID_API__ <= 22
return ANDROID_LOLLIPOP_MR1;
-#else
+# else
return ANDROID_POST_LOLLIPOP;
-#endif
+# endif
}
static AndroidApiLevel AndroidDetectApiLevel() {
if (!&dl_iterate_phdr)
- return ANDROID_KITKAT; // K or lower
+ return ANDROID_KITKAT; // K or lower
bool base_name_seen = false;
dl_iterate_phdr(dl_iterate_phdr_test_cb, &base_name_seen);
if (base_name_seen)
- return ANDROID_LOLLIPOP_MR1; // L MR1
+ return ANDROID_LOLLIPOP_MR1; // L MR1
return ANDROID_POST_LOLLIPOP; // post-L
// Plain L (API level 21) is completely broken wrt ASan and not very
// interesting to detect.
}
-extern "C" __attribute__((weak)) void* _DYNAMIC;
+extern "C" __attribute__((weak)) void *_DYNAMIC;
AndroidApiLevel AndroidGetApiLevel() {
AndroidApiLevel level =
(AndroidApiLevel)atomic_load(&android_api_level, memory_order_relaxed);
- if (level) return level;
+ if (level)
+ return level;
level = &_DYNAMIC == nullptr ? AndroidDetectApiLevelStatic()
: AndroidDetectApiLevel();
atomic_store(&android_api_level, level, memory_order_relaxed);
return level;
}
-#endif
+# endif
static HandleSignalMode GetHandleSignalModeImpl(int signum) {
switch (signum) {
@@ -1877,28 +1907,28 @@ HandleSignalMode GetHandleSignalMode(int signum) {
return result;
}
-#if !SANITIZER_GO
+# if !SANITIZER_GO
void *internal_start_thread(void *(*func)(void *arg), void *arg) {
- if (&real_pthread_create == 0)
+ if (&internal_pthread_create == 0)
return nullptr;
// Start the thread with signals blocked, otherwise it can steal user signals.
ScopedBlockSignals block(nullptr);
void *th;
- real_pthread_create(&th, nullptr, func, arg);
+ internal_pthread_create(&th, nullptr, func, arg);
return th;
}
void internal_join_thread(void *th) {
- if (&real_pthread_join)
- real_pthread_join(th, nullptr);
+ if (&internal_pthread_join)
+ internal_pthread_join(th, nullptr);
}
-#else
+# else
void *internal_start_thread(void *(*func)(void *), void *arg) { return 0; }
void internal_join_thread(void *th) {}
-#endif
+# endif
-#if SANITIZER_LINUX && defined(__aarch64__)
+# if SANITIZER_LINUX && defined(__aarch64__)
// Android headers in the older NDK releases miss this definition.
struct __sanitizer_esr_context {
struct _aarch64_ctx head;
@@ -1910,7 +1940,8 @@ static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) {
u8 *aux = reinterpret_cast<u8 *>(ucontext->uc_mcontext.__reserved);
while (true) {
_aarch64_ctx *ctx = (_aarch64_ctx *)aux;
- if (ctx->size == 0) break;
+ if (ctx->size == 0)
+ break;
if (ctx->magic == kEsrMagic) {
*esr = ((__sanitizer_esr_context *)ctx)->esr;
return true;
@@ -1919,31 +1950,29 @@ static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) {
}
return false;
}
-#elif SANITIZER_FREEBSD && defined(__aarch64__)
+# elif SANITIZER_FREEBSD && defined(__aarch64__)
// FreeBSD doesn't provide ESR in the ucontext.
-static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) {
- return false;
-}
-#endif
+static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) { return false; }
+# endif
using Context = ucontext_t;
SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
Context *ucontext = (Context *)context;
-#if defined(__x86_64__) || defined(__i386__)
+# if defined(__x86_64__) || defined(__i386__)
static const uptr PF_WRITE = 1U << 1;
-#if SANITIZER_FREEBSD
+# if SANITIZER_FREEBSD
uptr err = ucontext->uc_mcontext.mc_err;
-#elif SANITIZER_NETBSD
+# elif SANITIZER_NETBSD
uptr err = ucontext->uc_mcontext.__gregs[_REG_ERR];
-#elif SANITIZER_SOLARIS && defined(__i386__)
+# elif SANITIZER_SOLARIS && defined(__i386__)
const int Err = 13;
uptr err = ucontext->uc_mcontext.gregs[Err];
-#else
+# else
uptr err = ucontext->uc_mcontext.gregs[REG_ERR];
-#endif // SANITIZER_FREEBSD
+# endif // SANITIZER_FREEBSD
return err & PF_WRITE ? Write : Read;
-#elif defined(__mips__)
+# elif defined(__mips__)
uint32_t *exception_source;
uint32_t faulty_instruction;
uint32_t op_code;
@@ -1959,12 +1988,12 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
case 0x29: // sh
case 0x2b: // sw
case 0x3f: // sd
-#if __mips_isa_rev < 6
+# if __mips_isa_rev < 6
case 0x2c: // sdl
case 0x2d: // sdr
case 0x2a: // swl
case 0x2e: // swr
-#endif
+# endif
return SignalContext::Write;
case 0x20: // lb
@@ -1974,14 +2003,14 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
case 0x23: // lw
case 0x27: // lwu
case 0x37: // ld
-#if __mips_isa_rev < 6
+# if __mips_isa_rev < 6
case 0x1a: // ldl
case 0x1b: // ldr
case 0x22: // lwl
case 0x26: // lwr
-#endif
+# endif
return SignalContext::Read;
-#if __mips_isa_rev == 6
+# if __mips_isa_rev == 6
case 0x3b: // pcrel
op_code = (faulty_instruction >> 19) & 0x3;
switch (op_code) {
@@ -1989,50 +2018,63 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
case 0x2: // lwupc
return SignalContext::Read;
}
-#endif
+# endif
}
return SignalContext::Unknown;
-#elif defined(__arm__)
+# elif defined(__arm__)
static const uptr FSR_WRITE = 1U << 11;
uptr fsr = ucontext->uc_mcontext.error_code;
return fsr & FSR_WRITE ? Write : Read;
-#elif defined(__aarch64__)
+# elif defined(__aarch64__)
static const u64 ESR_ELx_WNR = 1U << 6;
u64 esr;
- if (!Aarch64GetESR(ucontext, &esr)) return Unknown;
+ if (!Aarch64GetESR(ucontext, &esr))
+ return Unknown;
return esr & ESR_ELx_WNR ? Write : Read;
-#elif defined(__loongarch__)
+# elif defined(__loongarch__)
+ // In the musl environment, the Linux kernel uapi sigcontext.h is not
+ // included in signal.h. To avoid missing the SC_ADDRERR_{RD,WR} macros,
+ // copy them here. The LoongArch Linux kernel uapi is already stable,
+ // so there's no need to worry about the value changing.
+# ifndef SC_ADDRERR_RD
+ // Address error was due to memory load
+# define SC_ADDRERR_RD (1 << 30)
+# endif
+# ifndef SC_ADDRERR_WR
+ // Address error was due to memory store
+# define SC_ADDRERR_WR (1 << 31)
+# endif
u32 flags = ucontext->uc_mcontext.__flags;
if (flags & SC_ADDRERR_RD)
return SignalContext::Read;
if (flags & SC_ADDRERR_WR)
return SignalContext::Write;
return SignalContext::Unknown;
-#elif defined(__sparc__)
+# elif defined(__sparc__)
// Decode the instruction to determine the access type.
// From OpenSolaris $SRC/uts/sun4/os/trap.c (get_accesstype).
-#if SANITIZER_SOLARIS
+# if SANITIZER_SOLARIS
uptr pc = ucontext->uc_mcontext.gregs[REG_PC];
-#else
+# else
// Historical BSDism here.
struct sigcontext *scontext = (struct sigcontext *)context;
-#if defined(__arch64__)
+# if defined(__arch64__)
uptr pc = scontext->sigc_regs.tpc;
-#else
+# else
uptr pc = scontext->si_regs.pc;
-#endif
-#endif
+# endif
+# endif
u32 instr = *(u32 *)pc;
- return (instr >> 21) & 1 ? Write: Read;
-#elif defined(__riscv)
-#if SANITIZER_FREEBSD
+ return (instr >> 21) & 1 ? Write : Read;
+# elif defined(__riscv)
+# if SANITIZER_FREEBSD
unsigned long pc = ucontext->uc_mcontext.mc_gpregs.gp_sepc;
-#else
+# else
unsigned long pc = ucontext->uc_mcontext.__gregs[REG_PC];
-#endif
+# endif
unsigned faulty_instruction = *(uint16_t *)pc;
-#if defined(__riscv_compressed)
+# if defined(__riscv_compressed)
if ((faulty_instruction & 0x3) != 0x3) { // it's a compressed instruction
// set op_bits to the instruction bits [1, 0, 15, 14, 13]
unsigned op_bits =
@@ -2040,38 +2082,38 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
unsigned rd = faulty_instruction & 0xF80; // bits 7-11, inclusive
switch (op_bits) {
case 0b10'010: // c.lwsp (rd != x0)
-#if __riscv_xlen == 64
+# if __riscv_xlen == 64
case 0b10'011: // c.ldsp (rd != x0)
-#endif
+# endif
return rd ? SignalContext::Read : SignalContext::Unknown;
case 0b00'010: // c.lw
-#if __riscv_flen >= 32 && __riscv_xlen == 32
+# if __riscv_flen >= 32 && __riscv_xlen == 32
case 0b10'011: // c.flwsp
-#endif
-#if __riscv_flen >= 32 || __riscv_xlen == 64
+# endif
+# if __riscv_flen >= 32 || __riscv_xlen == 64
case 0b00'011: // c.flw / c.ld
-#endif
-#if __riscv_flen == 64
+# endif
+# if __riscv_flen == 64
case 0b00'001: // c.fld
case 0b10'001: // c.fldsp
-#endif
+# endif
return SignalContext::Read;
case 0b00'110: // c.sw
case 0b10'110: // c.swsp
-#if __riscv_flen >= 32 || __riscv_xlen == 64
+# if __riscv_flen >= 32 || __riscv_xlen == 64
case 0b00'111: // c.fsw / c.sd
case 0b10'111: // c.fswsp / c.sdsp
-#endif
-#if __riscv_flen == 64
+# endif
+# if __riscv_flen == 64
case 0b00'101: // c.fsd
case 0b10'101: // c.fsdsp
-#endif
+# endif
return SignalContext::Write;
default:
return SignalContext::Unknown;
}
}
-#endif
+# endif
unsigned opcode = faulty_instruction & 0x7f; // lower 7 bits
unsigned funct3 = (faulty_instruction >> 12) & 0x7; // bits 12-14, inclusive
@@ -2081,9 +2123,9 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
case 0b000: // lb
case 0b001: // lh
case 0b010: // lw
-#if __riscv_xlen == 64
+# if __riscv_xlen == 64
case 0b011: // ld
-#endif
+# endif
case 0b100: // lbu
case 0b101: // lhu
return SignalContext::Read;
@@ -2095,20 +2137,20 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
case 0b000: // sb
case 0b001: // sh
case 0b010: // sw
-#if __riscv_xlen == 64
+# if __riscv_xlen == 64
case 0b011: // sd
-#endif
+# endif
return SignalContext::Write;
default:
return SignalContext::Unknown;
}
-#if __riscv_flen >= 32
+# if __riscv_flen >= 32
case 0b0000111: // floating-point loads
switch (funct3) {
case 0b010: // flw
-#if __riscv_flen == 64
+# if __riscv_flen == 64
case 0b011: // fld
-#endif
+# endif
return SignalContext::Read;
default:
return SignalContext::Unknown;
@@ -2116,21 +2158,21 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
case 0b0100111: // floating-point stores
switch (funct3) {
case 0b010: // fsw
-#if __riscv_flen == 64
+# if __riscv_flen == 64
case 0b011: // fsd
-#endif
+# endif
return SignalContext::Write;
default:
return SignalContext::Unknown;
}
-#endif
+# endif
default:
return SignalContext::Unknown;
}
-#else
+# else
(void)ucontext;
return Unknown; // FIXME: Implement.
-#endif
+# endif
}
bool SignalContext::IsTrueFaultingAddress() const {
@@ -2139,129 +2181,497 @@ bool SignalContext::IsTrueFaultingAddress() const {
return si->si_signo == SIGSEGV && si->si_code != 128;
}
+UNUSED
+static const char *RegNumToRegName(int reg) {
+ switch (reg) {
+# if SANITIZER_LINUX && SANITIZER_GLIBC || SANITIZER_NETBSD
+# if defined(__x86_64__)
+# if SANITIZER_NETBSD
+# define REG_RAX _REG_RAX
+# define REG_RBX _REG_RBX
+# define REG_RCX _REG_RCX
+# define REG_RDX _REG_RDX
+# define REG_RDI _REG_RDI
+# define REG_RSI _REG_RSI
+# define REG_RBP _REG_RBP
+# define REG_RSP _REG_RSP
+# define REG_R8 _REG_R8
+# define REG_R9 _REG_R9
+# define REG_R10 _REG_R10
+# define REG_R11 _REG_R11
+# define REG_R12 _REG_R12
+# define REG_R13 _REG_R13
+# define REG_R14 _REG_R14
+# define REG_R15 _REG_R15
+# endif
+ case REG_RAX:
+ return "rax";
+ case REG_RBX:
+ return "rbx";
+ case REG_RCX:
+ return "rcx";
+ case REG_RDX:
+ return "rdx";
+ case REG_RDI:
+ return "rdi";
+ case REG_RSI:
+ return "rsi";
+ case REG_RBP:
+ return "rbp";
+ case REG_RSP:
+ return "rsp";
+ case REG_R8:
+ return "r8";
+ case REG_R9:
+ return "r9";
+ case REG_R10:
+ return "r10";
+ case REG_R11:
+ return "r11";
+ case REG_R12:
+ return "r12";
+ case REG_R13:
+ return "r13";
+ case REG_R14:
+ return "r14";
+ case REG_R15:
+ return "r15";
+# elif defined(__i386__)
+# if SANITIZER_NETBSD
+# define REG_EAX _REG_EAX
+# define REG_EBX _REG_EBX
+# define REG_ECX _REG_ECX
+# define REG_EDX _REG_EDX
+# define REG_EDI _REG_EDI
+# define REG_ESI _REG_ESI
+# define REG_EBP _REG_EBP
+# define REG_ESP _REG_ESP
+# endif
+ case REG_EAX:
+ return "eax";
+ case REG_EBX:
+ return "ebx";
+ case REG_ECX:
+ return "ecx";
+ case REG_EDX:
+ return "edx";
+ case REG_EDI:
+ return "edi";
+ case REG_ESI:
+ return "esi";
+ case REG_EBP:
+ return "ebp";
+ case REG_ESP:
+ return "esp";
+# elif defined(__arm__)
+# ifdef MAKE_CASE
+# undef MAKE_CASE
+# endif
+# define REG_STR(reg) #reg
+# define MAKE_CASE(N) \
+ case REG_R##N: \
+ return REG_STR(r##N)
+ MAKE_CASE(0);
+ MAKE_CASE(1);
+ MAKE_CASE(2);
+ MAKE_CASE(3);
+ MAKE_CASE(4);
+ MAKE_CASE(5);
+ MAKE_CASE(6);
+ MAKE_CASE(7);
+ MAKE_CASE(8);
+ MAKE_CASE(9);
+ MAKE_CASE(10);
+ MAKE_CASE(11);
+ MAKE_CASE(12);
+ case REG_R13:
+ return "sp";
+ case REG_R14:
+ return "lr";
+ case REG_R15:
+ return "pc";
+# elif defined(__aarch64__)
+# define REG_STR(reg) #reg
+# define MAKE_CASE(N) \
+ case N: \
+ return REG_STR(x##N)
+ MAKE_CASE(0);
+ MAKE_CASE(1);
+ MAKE_CASE(2);
+ MAKE_CASE(3);
+ MAKE_CASE(4);
+ MAKE_CASE(5);
+ MAKE_CASE(6);
+ MAKE_CASE(7);
+ MAKE_CASE(8);
+ MAKE_CASE(9);
+ MAKE_CASE(10);
+ MAKE_CASE(11);
+ MAKE_CASE(12);
+ MAKE_CASE(13);
+ MAKE_CASE(14);
+ MAKE_CASE(15);
+ MAKE_CASE(16);
+ MAKE_CASE(17);
+ MAKE_CASE(18);
+ MAKE_CASE(19);
+ MAKE_CASE(20);
+ MAKE_CASE(21);
+ MAKE_CASE(22);
+ MAKE_CASE(23);
+ MAKE_CASE(24);
+ MAKE_CASE(25);
+ MAKE_CASE(26);
+ MAKE_CASE(27);
+ MAKE_CASE(28);
+ case 29:
+ return "fp";
+ case 30:
+ return "lr";
+ case 31:
+ return "sp";
+# endif
+# endif // SANITIZER_LINUX && SANITIZER_GLIBC
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+# if ((SANITIZER_LINUX && SANITIZER_GLIBC) || SANITIZER_NETBSD) && \
+ (defined(__arm__) || defined(__aarch64__))
+static uptr GetArmRegister(ucontext_t *ctx, int RegNum) {
+ switch (RegNum) {
+# if defined(__arm__) && !SANITIZER_NETBSD
+# ifdef MAKE_CASE
+# undef MAKE_CASE
+# endif
+# define MAKE_CASE(N) \
+ case REG_R##N: \
+ return ctx->uc_mcontext.arm_r##N
+ MAKE_CASE(0);
+ MAKE_CASE(1);
+ MAKE_CASE(2);
+ MAKE_CASE(3);
+ MAKE_CASE(4);
+ MAKE_CASE(5);
+ MAKE_CASE(6);
+ MAKE_CASE(7);
+ MAKE_CASE(8);
+ MAKE_CASE(9);
+ MAKE_CASE(10);
+ case REG_R11:
+ return ctx->uc_mcontext.arm_fp;
+ case REG_R12:
+ return ctx->uc_mcontext.arm_ip;
+ case REG_R13:
+ return ctx->uc_mcontext.arm_sp;
+ case REG_R14:
+ return ctx->uc_mcontext.arm_lr;
+ case REG_R15:
+ return ctx->uc_mcontext.arm_pc;
+# elif defined(__aarch64__)
+# if SANITIZER_LINUX
+ case 0 ... 30:
+ return ctx->uc_mcontext.regs[RegNum];
+ case 31:
+ return ctx->uc_mcontext.sp;
+# elif SANITIZER_NETBSD
+ case 0 ... 31:
+ return ctx->uc_mcontext.__gregs[RegNum];
+# endif
+# endif
+ default:
+ return 0;
+ }
+ return 0;
+}
+# endif // SANITIZER_LINUX && SANITIZER_GLIBC && (defined(__arm__) ||
+ // defined(__aarch64__))
+
+UNUSED
+static void DumpSingleReg(ucontext_t *ctx, int RegNum) {
+ const char *RegName = RegNumToRegName(RegNum);
+# if SANITIZER_LINUX && SANITIZER_GLIBC || SANITIZER_NETBSD
+# if defined(__x86_64__)
+ Printf("%s%s = 0x%016llx ", internal_strlen(RegName) == 2 ? " " : "",
+ RegName,
+# if SANITIZER_LINUX
+ ctx->uc_mcontext.gregs[RegNum]
+# elif SANITIZER_NETBSD
+ ctx->uc_mcontext.__gregs[RegNum]
+# endif
+ );
+# elif defined(__i386__)
+ Printf("%s = 0x%08x ", RegName,
+# if SANITIZER_LINUX
+ ctx->uc_mcontext.gregs[RegNum]
+# elif SANITIZER_NETBSD
+ ctx->uc_mcontext.__gregs[RegNum]
+# endif
+ );
+# elif defined(__arm__)
+ Printf("%s%s = 0x%08zx ", internal_strlen(RegName) == 2 ? " " : "", RegName,
+ GetArmRegister(ctx, RegNum));
+# elif defined(__aarch64__)
+ Printf("%s%s = 0x%016zx ", internal_strlen(RegName) == 2 ? " " : "", RegName,
+ GetArmRegister(ctx, RegNum));
+# else
+ (void)RegName;
+# endif
+# else
+ (void)RegName;
+# endif
+}
+
void SignalContext::DumpAllRegisters(void *context) {
- // FIXME: Implement this.
+ ucontext_t *ucontext = (ucontext_t *)context;
+# if SANITIZER_LINUX && SANITIZER_GLIBC || SANITIZER_NETBSD
+# if defined(__x86_64__)
+ Report("Register values:\n");
+ DumpSingleReg(ucontext, REG_RAX);
+ DumpSingleReg(ucontext, REG_RBX);
+ DumpSingleReg(ucontext, REG_RCX);
+ DumpSingleReg(ucontext, REG_RDX);
+ Printf("\n");
+ DumpSingleReg(ucontext, REG_RDI);
+ DumpSingleReg(ucontext, REG_RSI);
+ DumpSingleReg(ucontext, REG_RBP);
+ DumpSingleReg(ucontext, REG_RSP);
+ Printf("\n");
+ DumpSingleReg(ucontext, REG_R8);
+ DumpSingleReg(ucontext, REG_R9);
+ DumpSingleReg(ucontext, REG_R10);
+ DumpSingleReg(ucontext, REG_R11);
+ Printf("\n");
+ DumpSingleReg(ucontext, REG_R12);
+ DumpSingleReg(ucontext, REG_R13);
+ DumpSingleReg(ucontext, REG_R14);
+ DumpSingleReg(ucontext, REG_R15);
+ Printf("\n");
+# elif defined(__i386__)
+ // Duplication of this report print is caused by partial support
+ // of register values dumping. In case of unsupported yet architecture let's
+ // avoid printing 'Register values:' without actual values in the following
+ // output.
+ Report("Register values:\n");
+ DumpSingleReg(ucontext, REG_EAX);
+ DumpSingleReg(ucontext, REG_EBX);
+ DumpSingleReg(ucontext, REG_ECX);
+ DumpSingleReg(ucontext, REG_EDX);
+ Printf("\n");
+ DumpSingleReg(ucontext, REG_EDI);
+ DumpSingleReg(ucontext, REG_ESI);
+ DumpSingleReg(ucontext, REG_EBP);
+ DumpSingleReg(ucontext, REG_ESP);
+ Printf("\n");
+# elif defined(__arm__) && !SANITIZER_NETBSD
+ Report("Register values:\n");
+ DumpSingleReg(ucontext, REG_R0);
+ DumpSingleReg(ucontext, REG_R1);
+ DumpSingleReg(ucontext, REG_R2);
+ DumpSingleReg(ucontext, REG_R3);
+ Printf("\n");
+ DumpSingleReg(ucontext, REG_R4);
+ DumpSingleReg(ucontext, REG_R5);
+ DumpSingleReg(ucontext, REG_R6);
+ DumpSingleReg(ucontext, REG_R7);
+ Printf("\n");
+ DumpSingleReg(ucontext, REG_R8);
+ DumpSingleReg(ucontext, REG_R9);
+ DumpSingleReg(ucontext, REG_R10);
+ DumpSingleReg(ucontext, REG_R11);
+ Printf("\n");
+ DumpSingleReg(ucontext, REG_R12);
+ DumpSingleReg(ucontext, REG_R13);
+ DumpSingleReg(ucontext, REG_R14);
+ DumpSingleReg(ucontext, REG_R15);
+ Printf("\n");
+# elif defined(__aarch64__)
+ Report("Register values:\n");
+ for (int i = 0; i <= 31; ++i) {
+ DumpSingleReg(ucontext, i);
+ if (i % 4 == 3)
+ Printf("\n");
+ }
+# else
+ (void)ucontext;
+# endif
+# elif SANITIZER_FREEBSD
+# if defined(__x86_64__)
+ Report("Register values:\n");
+ Printf("rax = 0x%016lx ", ucontext->uc_mcontext.mc_rax);
+ Printf("rbx = 0x%016lx ", ucontext->uc_mcontext.mc_rbx);
+ Printf("rcx = 0x%016lx ", ucontext->uc_mcontext.mc_rcx);
+ Printf("rdx = 0x%016lx ", ucontext->uc_mcontext.mc_rdx);
+ Printf("\n");
+ Printf("rdi = 0x%016lx ", ucontext->uc_mcontext.mc_rdi);
+ Printf("rsi = 0x%016lx ", ucontext->uc_mcontext.mc_rsi);
+ Printf("rbp = 0x%016lx ", ucontext->uc_mcontext.mc_rbp);
+ Printf("rsp = 0x%016lx ", ucontext->uc_mcontext.mc_rsp);
+ Printf("\n");
+ Printf(" r8 = 0x%016lx ", ucontext->uc_mcontext.mc_r8);
+ Printf(" r9 = 0x%016lx ", ucontext->uc_mcontext.mc_r9);
+ Printf("r10 = 0x%016lx ", ucontext->uc_mcontext.mc_r10);
+ Printf("r11 = 0x%016lx ", ucontext->uc_mcontext.mc_r11);
+ Printf("\n");
+ Printf("r12 = 0x%016lx ", ucontext->uc_mcontext.mc_r12);
+ Printf("r13 = 0x%016lx ", ucontext->uc_mcontext.mc_r13);
+ Printf("r14 = 0x%016lx ", ucontext->uc_mcontext.mc_r14);
+ Printf("r15 = 0x%016lx ", ucontext->uc_mcontext.mc_r15);
+ Printf("\n");
+# elif defined(__i386__)
+ Report("Register values:\n");
+ Printf("eax = 0x%08x ", ucontext->uc_mcontext.mc_eax);
+ Printf("ebx = 0x%08x ", ucontext->uc_mcontext.mc_ebx);
+ Printf("ecx = 0x%08x ", ucontext->uc_mcontext.mc_ecx);
+ Printf("edx = 0x%08x ", ucontext->uc_mcontext.mc_edx);
+ Printf("\n");
+ Printf("edi = 0x%08x ", ucontext->uc_mcontext.mc_edi);
+ Printf("esi = 0x%08x ", ucontext->uc_mcontext.mc_esi);
+ Printf("ebp = 0x%08x ", ucontext->uc_mcontext.mc_ebp);
+ Printf("esp = 0x%08x ", ucontext->uc_mcontext.mc_esp);
+ Printf("\n");
+# else
+ (void)ucontext;
+# endif
+# else
+ (void)ucontext;
+# endif
+ // FIXME: Implement this for other OSes and architectures.
}
static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
-#if SANITIZER_NETBSD
+# if SANITIZER_NETBSD
// This covers all NetBSD architectures
ucontext_t *ucontext = (ucontext_t *)context;
*pc = _UC_MACHINE_PC(ucontext);
*bp = _UC_MACHINE_FP(ucontext);
*sp = _UC_MACHINE_SP(ucontext);
-#elif defined(__arm__)
- ucontext_t *ucontext = (ucontext_t*)context;
+# elif defined(__arm__)
+ ucontext_t *ucontext = (ucontext_t *)context;
*pc = ucontext->uc_mcontext.arm_pc;
*bp = ucontext->uc_mcontext.arm_fp;
*sp = ucontext->uc_mcontext.arm_sp;
-#elif defined(__aarch64__)
-# if SANITIZER_FREEBSD
- ucontext_t *ucontext = (ucontext_t*)context;
+# elif defined(__aarch64__)
+# if SANITIZER_FREEBSD
+ ucontext_t *ucontext = (ucontext_t *)context;
*pc = ucontext->uc_mcontext.mc_gpregs.gp_elr;
*bp = ucontext->uc_mcontext.mc_gpregs.gp_x[29];
*sp = ucontext->uc_mcontext.mc_gpregs.gp_sp;
-# else
- ucontext_t *ucontext = (ucontext_t*)context;
+# else
+ ucontext_t *ucontext = (ucontext_t *)context;
*pc = ucontext->uc_mcontext.pc;
*bp = ucontext->uc_mcontext.regs[29];
*sp = ucontext->uc_mcontext.sp;
-# endif
-#elif defined(__hppa__)
- ucontext_t *ucontext = (ucontext_t*)context;
+# endif
+# elif defined(__hppa__)
+ ucontext_t *ucontext = (ucontext_t *)context;
*pc = ucontext->uc_mcontext.sc_iaoq[0];
/* GCC uses %r3 whenever a frame pointer is needed. */
*bp = ucontext->uc_mcontext.sc_gr[3];
*sp = ucontext->uc_mcontext.sc_gr[30];
-#elif defined(__x86_64__)
-# if SANITIZER_FREEBSD
- ucontext_t *ucontext = (ucontext_t*)context;
+# elif defined(__x86_64__)
+# if SANITIZER_FREEBSD
+ ucontext_t *ucontext = (ucontext_t *)context;
*pc = ucontext->uc_mcontext.mc_rip;
*bp = ucontext->uc_mcontext.mc_rbp;
*sp = ucontext->uc_mcontext.mc_rsp;
-# else
- ucontext_t *ucontext = (ucontext_t*)context;
+# else
+ ucontext_t *ucontext = (ucontext_t *)context;
*pc = ucontext->uc_mcontext.gregs[REG_RIP];
*bp = ucontext->uc_mcontext.gregs[REG_RBP];
*sp = ucontext->uc_mcontext.gregs[REG_RSP];
-# endif
-#elif defined(__i386__)
-# if SANITIZER_FREEBSD
- ucontext_t *ucontext = (ucontext_t*)context;
+# endif
+# elif defined(__i386__)
+# if SANITIZER_FREEBSD
+ ucontext_t *ucontext = (ucontext_t *)context;
*pc = ucontext->uc_mcontext.mc_eip;
*bp = ucontext->uc_mcontext.mc_ebp;
*sp = ucontext->uc_mcontext.mc_esp;
-# else
- ucontext_t *ucontext = (ucontext_t*)context;
-# if SANITIZER_SOLARIS
+# else
+ ucontext_t *ucontext = (ucontext_t *)context;
+# if SANITIZER_SOLARIS
/* Use the numeric values: the symbolic ones are undefined by llvm
include/llvm/Support/Solaris.h. */
-# ifndef REG_EIP
-# define REG_EIP 14 // REG_PC
-# endif
-# ifndef REG_EBP
-# define REG_EBP 6 // REG_FP
-# endif
-# ifndef REG_UESP
-# define REG_UESP 17 // REG_SP
-# endif
-# endif
+# ifndef REG_EIP
+# define REG_EIP 14 // REG_PC
+# endif
+# ifndef REG_EBP
+# define REG_EBP 6 // REG_FP
+# endif
+# ifndef REG_UESP
+# define REG_UESP 17 // REG_SP
+# endif
+# endif
*pc = ucontext->uc_mcontext.gregs[REG_EIP];
*bp = ucontext->uc_mcontext.gregs[REG_EBP];
*sp = ucontext->uc_mcontext.gregs[REG_UESP];
-# endif
-#elif defined(__powerpc__) || defined(__powerpc64__)
+# endif
+# elif defined(__powerpc__) || defined(__powerpc64__)
# if SANITIZER_FREEBSD
ucontext_t *ucontext = (ucontext_t *)context;
*pc = ucontext->uc_mcontext.mc_srr0;
*sp = ucontext->uc_mcontext.mc_frame[1];
*bp = ucontext->uc_mcontext.mc_frame[31];
# else
- ucontext_t *ucontext = (ucontext_t*)context;
+ ucontext_t *ucontext = (ucontext_t *)context;
*pc = ucontext->uc_mcontext.regs->nip;
*sp = ucontext->uc_mcontext.regs->gpr[PT_R1];
// The powerpc{,64}-linux ABIs do not specify r31 as the frame
// pointer, but GCC always uses r31 when we need a frame pointer.
*bp = ucontext->uc_mcontext.regs->gpr[PT_R31];
# endif
-#elif defined(__sparc__)
-#if defined(__arch64__) || defined(__sparcv9)
-#define STACK_BIAS 2047
-#else
-#define STACK_BIAS 0
-# endif
-# if SANITIZER_SOLARIS
+# elif defined(__sparc__)
+# if defined(__arch64__) || defined(__sparcv9)
+# define STACK_BIAS 2047
+# else
+# define STACK_BIAS 0
+# endif
+# if SANITIZER_SOLARIS
ucontext_t *ucontext = (ucontext_t *)context;
*pc = ucontext->uc_mcontext.gregs[REG_PC];
- *sp = ucontext->uc_mcontext.gregs[REG_O6] + STACK_BIAS;
-#else
+ *sp = ucontext->uc_mcontext.gregs[REG_SP] + STACK_BIAS;
+ // Avoid SEGV when dereferencing sp on stack overflow with non-faulting load.
+ // This requires a SPARC V9 CPU. Cannot use #ASI_PNF here: only supported
+ // since clang-19.
+# if defined(__sparcv9)
+ asm("ldxa [%[fp]] 0x82, %[bp]"
+# else
+ asm("lduwa [%[fp]] 0x82, %[bp]"
+# endif
+ : [bp] "=r"(*bp)
+ : [fp] "r"(&((struct frame *)*sp)->fr_savfp));
+ if (*bp)
+ *bp += STACK_BIAS;
+# else
// Historical BSDism here.
struct sigcontext *scontext = (struct sigcontext *)context;
-#if defined(__arch64__)
+# if defined(__arch64__)
*pc = scontext->sigc_regs.tpc;
*sp = scontext->sigc_regs.u_regs[14] + STACK_BIAS;
-#else
+# else
*pc = scontext->si_regs.pc;
*sp = scontext->si_regs.u_regs[14];
-#endif
-# endif
+# endif
*bp = (uptr)((uhwptr *)*sp)[14] + STACK_BIAS;
-#elif defined(__mips__)
- ucontext_t *ucontext = (ucontext_t*)context;
+# endif
+# elif defined(__mips__)
+ ucontext_t *ucontext = (ucontext_t *)context;
*pc = ucontext->uc_mcontext.pc;
*bp = ucontext->uc_mcontext.gregs[30];
*sp = ucontext->uc_mcontext.gregs[29];
-#elif defined(__s390__)
- ucontext_t *ucontext = (ucontext_t*)context;
-# if defined(__s390x__)
+# elif defined(__s390__)
+ ucontext_t *ucontext = (ucontext_t *)context;
+# if defined(__s390x__)
*pc = ucontext->uc_mcontext.psw.addr;
-# else
+# else
*pc = ucontext->uc_mcontext.psw.addr & 0x7fffffff;
-# endif
+# endif
*bp = ucontext->uc_mcontext.gregs[11];
*sp = ucontext->uc_mcontext.gregs[15];
-#elif defined(__riscv)
- ucontext_t *ucontext = (ucontext_t*)context;
+# elif defined(__riscv)
+ ucontext_t *ucontext = (ucontext_t *)context;
# if SANITIZER_FREEBSD
*pc = ucontext->uc_mcontext.mc_gpregs.gp_sepc;
*bp = ucontext->uc_mcontext.mc_gpregs.gp_s[0];
@@ -2288,12 +2698,10 @@ static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
void SignalContext::InitPcSpBp() { GetPcSpBp(context, &pc, &sp, &bp); }
-void InitializePlatformEarly() {
- // Do nothing.
-}
+void InitializePlatformEarly() { InitTlsSize(); }
void CheckASLR() {
-#if SANITIZER_NETBSD
+# if SANITIZER_NETBSD
int mib[3];
int paxflags;
uptr len = sizeof(paxflags);
@@ -2308,12 +2716,13 @@ void CheckASLR() {
}
if (UNLIKELY(paxflags & CTL_PROC_PAXFLAGS_ASLR)) {
- Printf("This sanitizer is not compatible with enabled ASLR.\n"
- "To disable ASLR, please run \"paxctl +a %s\" and try again.\n",
- GetArgv()[0]);
+ Printf(
+ "This sanitizer is not compatible with enabled ASLR.\n"
+ "To disable ASLR, please run \"paxctl +a %s\" and try again.\n",
+ GetArgv()[0]);
Die();
}
-#elif SANITIZER_FREEBSD
+# elif SANITIZER_FREEBSD
int aslr_status;
int r = internal_procctl(P_PID, 0, PROC_ASLR_STATUS, &aslr_status);
if (UNLIKELY(r == -1)) {
@@ -2323,9 +2732,13 @@ void CheckASLR() {
return;
}
if ((aslr_status & PROC_ASLR_ACTIVE) != 0) {
- Printf("This sanitizer is not compatible with enabled ASLR "
- "and binaries compiled with PIE\n");
- Die();
+ VReport(1,
+ "This sanitizer is not compatible with enabled ASLR "
+ "and binaries compiled with PIE\n"
+ "ASLR will be disabled and the program re-executed.\n");
+ int aslr_ctl = PROC_ASLR_FORCE_DISABLE;
+ CHECK_NE(internal_procctl(P_PID, 0, PROC_ASLR_CTL, &aslr_ctl), -1);
+ ReExec();
}
# elif SANITIZER_PPC64V2
// Disable ASLR for Linux PPC64LE.
@@ -2345,7 +2758,7 @@ void CheckASLR() {
}
void CheckMPROTECT() {
-#if SANITIZER_NETBSD
+# if SANITIZER_NETBSD
int mib[3];
int paxflags;
uptr len = sizeof(paxflags);
@@ -2363,13 +2776,13 @@ void CheckMPROTECT() {
Printf("This sanitizer is not compatible with enabled MPROTECT\n");
Die();
}
-#else
+# else
// Do nothing
-#endif
+# endif
}
void CheckNoDeepBind(const char *filename, int flag) {
-#ifdef RTLD_DEEPBIND
+# ifdef RTLD_DEEPBIND
if (flag & RTLD_DEEPBIND) {
Report(
"You are trying to dlopen a %s shared library with RTLD_DEEPBIND flag"
@@ -2380,7 +2793,7 @@ void CheckNoDeepBind(const char *filename, int flag) {
filename, filename);
Die();
}
-#endif
+# endif
}
uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding,
@@ -2393,16 +2806,16 @@ uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding,
bool GetRandom(void *buffer, uptr length, bool blocking) {
if (!buffer || !length || length > 256)
return false;
-#if SANITIZER_USE_GETENTROPY
+# if SANITIZER_USE_GETENTROPY
uptr rnd = getentropy(buffer, length);
int rverrno = 0;
if (internal_iserror(rnd, &rverrno) && rverrno == EFAULT)
return false;
else if (rnd == 0)
return true;
-#endif // SANITIZER_USE_GETENTROPY
+# endif // SANITIZER_USE_GETENTROPY
-#if SANITIZER_USE_GETRANDOM
+# if SANITIZER_USE_GETRANDOM
static atomic_uint8_t skip_getrandom_syscall;
if (!atomic_load_relaxed(&skip_getrandom_syscall)) {
// Up to 256 bytes, getrandom will not be interrupted.
@@ -2414,7 +2827,7 @@ bool GetRandom(void *buffer, uptr length, bool blocking) {
else if (res == length)
return true;
}
-#endif // SANITIZER_USE_GETRANDOM
+# endif // SANITIZER_USE_GETRANDOM
// Up to 256 bytes, a read off /dev/urandom will not be interrupted.
// blocking is moot here, O_NONBLOCK has no effect when opening /dev/urandom.
uptr fd = internal_open("/dev/urandom", O_RDONLY);
@@ -2427,6 +2840,6 @@ bool GetRandom(void *buffer, uptr length, bool blocking) {
return true;
}
-} // namespace __sanitizer
+} // namespace __sanitizer
#endif
@@ -13,15 +13,15 @@
#define SANITIZER_LINUX_H
#include "sanitizer_platform.h"
-#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
+#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
SANITIZER_SOLARIS
-#include "sanitizer_common.h"
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_platform_limits_freebsd.h"
-#include "sanitizer_platform_limits_netbsd.h"
-#include "sanitizer_platform_limits_posix.h"
-#include "sanitizer_platform_limits_solaris.h"
-#include "sanitizer_posix.h"
+# include "sanitizer_common.h"
+# include "sanitizer_internal_defs.h"
+# include "sanitizer_platform_limits_freebsd.h"
+# include "sanitizer_platform_limits_netbsd.h"
+# include "sanitizer_platform_limits_posix.h"
+# include "sanitizer_platform_limits_solaris.h"
+# include "sanitizer_posix.h"
struct link_map; // Opaque type returned by dlopen().
struct utsname;
@@ -46,9 +46,9 @@ void ReadProcMaps(ProcSelfMapsBuff *proc_maps);
// Syscall wrappers.
uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count);
-uptr internal_sigaltstack(const void* ss, void* oss);
+uptr internal_sigaltstack(const void *ss, void *oss);
uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
- __sanitizer_sigset_t *oldset);
+ __sanitizer_sigset_t *oldset);
void SetSigProcMask(__sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset);
void BlockSignals(__sanitizer_sigset_t *oldset = nullptr);
@@ -65,10 +65,10 @@ struct ScopedBlockSignals {
# if SANITIZER_GLIBC
uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp);
-#endif
+# endif
// Linux-only syscalls.
-#if SANITIZER_LINUX
+# if SANITIZER_LINUX
uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5);
# if defined(__x86_64__)
uptr internal_arch_prctl(int option, uptr arg2);
@@ -83,33 +83,33 @@ void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
defined(__arm__) || SANITIZER_RISCV64 || SANITIZER_LOONGARCH64
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
int *parent_tidptr, void *newtls, int *child_tidptr);
-#endif
+# endif
int internal_uname(struct utsname *buf);
-#elif SANITIZER_FREEBSD
+# elif SANITIZER_FREEBSD
uptr internal_procctl(int type, int id, int cmd, void *data);
void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
-#elif SANITIZER_NETBSD
+# elif SANITIZER_NETBSD
void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg);
-#endif // SANITIZER_LINUX
+# endif // SANITIZER_LINUX
// This class reads thread IDs from /proc/<pid>/task using only syscalls.
class ThreadLister {
public:
explicit ThreadLister(pid_t pid);
- ~ThreadLister();
enum Result {
Error,
Incomplete,
Ok,
};
Result ListThreads(InternalMmapVector<tid_t> *threads);
+ const char *LoadStatus(tid_t tid);
private:
- bool IsAlive(int tid);
+ bool IsAlive(tid_t tid);
- pid_t pid_;
- int descriptor_ = -1;
+ InternalScopedString task_path_;
+ InternalScopedString status_path_;
InternalMmapVector<char> buffer_;
};
@@ -135,36 +135,60 @@ inline void ReleaseMemoryPagesToOSAndZeroFill(uptr beg, uptr end) {
ReleaseMemoryPagesToOS(beg, end);
}
-#if SANITIZER_ANDROID
-
-#if defined(__aarch64__)
-# define __get_tls() \
- ({ void** __v; __asm__("mrs %0, tpidr_el0" : "=r"(__v)); __v; })
-#elif defined(__arm__)
-# define __get_tls() \
- ({ void** __v; __asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(__v)); __v; })
-#elif defined(__mips__)
+# if SANITIZER_ANDROID
+
+# if defined(__aarch64__)
+# define __get_tls() \
+ ({ \
+ void **__v; \
+ __asm__("mrs %0, tpidr_el0" : "=r"(__v)); \
+ __v; \
+ })
+# elif defined(__arm__)
+# define __get_tls() \
+ ({ \
+ void **__v; \
+ __asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(__v)); \
+ __v; \
+ })
+# elif defined(__mips__)
// On mips32r1, this goes via a kernel illegal instruction trap that's
// optimized for v1.
-# define __get_tls() \
- ({ register void** __v asm("v1"); \
- __asm__(".set push\n" \
- ".set mips32r2\n" \
- "rdhwr %0,$29\n" \
- ".set pop\n" : "=r"(__v)); \
- __v; })
-#elif defined (__riscv)
-# define __get_tls() \
- ({ void** __v; __asm__("mv %0, tp" : "=r"(__v)); __v; })
-#elif defined(__i386__)
-# define __get_tls() \
- ({ void** __v; __asm__("movl %%gs:0, %0" : "=r"(__v)); __v; })
-#elif defined(__x86_64__)
-# define __get_tls() \
- ({ void** __v; __asm__("mov %%fs:0, %0" : "=r"(__v)); __v; })
-#else
-#error "Unsupported architecture."
-#endif
+# define __get_tls() \
+ ({ \
+ register void **__v asm("v1"); \
+ __asm__( \
+ ".set push\n" \
+ ".set mips32r2\n" \
+ "rdhwr %0,$29\n" \
+ ".set pop\n" \
+ : "=r"(__v)); \
+ __v; \
+ })
+# elif defined(__riscv)
+# define __get_tls() \
+ ({ \
+ void **__v; \
+ __asm__("mv %0, tp" : "=r"(__v)); \
+ __v; \
+ })
+# elif defined(__i386__)
+# define __get_tls() \
+ ({ \
+ void **__v; \
+ __asm__("movl %%gs:0, %0" : "=r"(__v)); \
+ __v; \
+ })
+# elif defined(__x86_64__)
+# define __get_tls() \
+ ({ \
+ void **__v; \
+ __asm__("mov %%fs:0, %0" : "=r"(__v)); \
+ __v; \
+ })
+# else
+# error "Unsupported architecture."
+# endif
// The Android Bionic team has allocated a TLS slot for sanitizers starting
// with Q, given that Android currently doesn't support ELF TLS. It is used to
@@ -175,7 +199,7 @@ ALWAYS_INLINE uptr *get_android_tls_ptr() {
return reinterpret_cast<uptr *>(&__get_tls()[TLS_SLOT_SANITIZER]);
}
-#endif // SANITIZER_ANDROID
+# endif // SANITIZER_ANDROID
} // namespace __sanitizer
@@ -16,89 +16,105 @@
#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
SANITIZER_SOLARIS
-#include "sanitizer_allocator_internal.h"
-#include "sanitizer_atomic.h"
-#include "sanitizer_common.h"
-#include "sanitizer_file.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_freebsd.h"
-#include "sanitizer_getauxval.h"
-#include "sanitizer_glibc_version.h"
-#include "sanitizer_linux.h"
-#include "sanitizer_placement_new.h"
-#include "sanitizer_procmaps.h"
-#include "sanitizer_solaris.h"
-
-#if SANITIZER_NETBSD
-#define _RTLD_SOURCE // for __lwp_gettcb_fast() / __lwp_getprivate_fast()
-#endif
+# include "sanitizer_allocator_internal.h"
+# include "sanitizer_atomic.h"
+# include "sanitizer_common.h"
+# include "sanitizer_file.h"
+# include "sanitizer_flags.h"
+# include "sanitizer_getauxval.h"
+# include "sanitizer_glibc_version.h"
+# include "sanitizer_linux.h"
+# include "sanitizer_placement_new.h"
+# include "sanitizer_procmaps.h"
+# include "sanitizer_solaris.h"
+
+# if SANITIZER_NETBSD
+# define _RTLD_SOURCE // for __lwp_gettcb_fast() / __lwp_getprivate_fast()
+# endif
-#include <dlfcn.h> // for dlsym()
-#include <link.h>
-#include <pthread.h>
-#include <signal.h>
-#include <sys/mman.h>
-#include <sys/resource.h>
-#include <syslog.h>
+# include <dlfcn.h> // for dlsym()
+# include <link.h>
+# include <pthread.h>
+# include <signal.h>
+# include <sys/mman.h>
+# include <sys/resource.h>
+# include <syslog.h>
-#if !defined(ElfW)
-#define ElfW(type) Elf_##type
-#endif
+# if SANITIZER_GLIBC
+# include <gnu/libc-version.h>
+# endif
+
+# if !defined(ElfW)
+# define ElfW(type) Elf_##type
+# endif
-#if SANITIZER_FREEBSD
-#include <pthread_np.h>
-#include <osreldate.h>
-#include <sys/sysctl.h>
-#define pthread_getattr_np pthread_attr_get_np
+# if SANITIZER_FREEBSD
+# include <pthread_np.h>
+# include <sys/auxv.h>
+# include <sys/sysctl.h>
+# define pthread_getattr_np pthread_attr_get_np
// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
// that, it was never implemented. So just define it to zero.
-#undef MAP_NORESERVE
-#define MAP_NORESERVE 0
-#endif
+# undef MAP_NORESERVE
+# define MAP_NORESERVE 0
+extern const Elf_Auxinfo *__elf_aux_vector;
+extern "C" int __sys_sigaction(int signum, const struct sigaction *act,
+ struct sigaction *oldact);
+# endif
-#if SANITIZER_NETBSD
-#include <sys/sysctl.h>
-#include <sys/tls.h>
-#include <lwp.h>
-#endif
+# if SANITIZER_NETBSD
+# include <lwp.h>
+# include <sys/sysctl.h>
+# include <sys/tls.h>
+# endif
-#if SANITIZER_SOLARIS
-#include <stddef.h>
-#include <stdlib.h>
-#include <thread.h>
-#endif
+# if SANITIZER_SOLARIS
+# include <stddef.h>
+# include <stdlib.h>
+# include <thread.h>
+# endif
-#if SANITIZER_ANDROID
-#include <android/api-level.h>
-#if !defined(CPU_COUNT) && !defined(__aarch64__)
-#include <dirent.h>
-#include <fcntl.h>
+# if SANITIZER_ANDROID
+# include <android/api-level.h>
+# if !defined(CPU_COUNT) && !defined(__aarch64__)
+# include <dirent.h>
+# include <fcntl.h>
struct __sanitizer::linux_dirent {
- long d_ino;
- off_t d_off;
+ long d_ino;
+ off_t d_off;
unsigned short d_reclen;
- char d_name[];
+ char d_name[];
};
-#endif
-#endif
+# endif
+# endif
-#if !SANITIZER_ANDROID
-#include <elf.h>
-#include <unistd.h>
-#endif
+# if !SANITIZER_ANDROID
+# include <elf.h>
+# include <unistd.h>
+# endif
namespace __sanitizer {
-SANITIZER_WEAK_ATTRIBUTE int
-real_sigaction(int signum, const void *act, void *oldact);
+SANITIZER_WEAK_ATTRIBUTE int real_sigaction(int signum, const void *act,
+ void *oldact);
int internal_sigaction(int signum, const void *act, void *oldact) {
-#if !SANITIZER_GO
+# if SANITIZER_FREEBSD
+ // On FreeBSD, call the sigaction syscall directly (part of libsys in FreeBSD
+ // 15) since the libc version goes via a global interposing table. Due to
+ // library initialization order the table can be relocated after the call to
+ // InitializeDeadlySignals() which then crashes when dereferencing the
+ // uninitialized pointer in libc.
+ return __sys_sigaction(signum, (const struct sigaction *)act,
+ (struct sigaction *)oldact);
+# else
+# if !SANITIZER_GO
if (&real_sigaction)
return real_sigaction(signum, act, oldact);
-#endif
+# endif
return sigaction(signum, (const struct sigaction *)act,
(struct sigaction *)oldact);
+# endif
}
void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
@@ -111,7 +127,7 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
// Find the mapping that contains a stack variable.
- MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+ MemoryMappingLayout proc_maps(/*cache_enabled*/ true);
if (proc_maps.Error()) {
*stack_top = *stack_bottom = 0;
return;
@@ -119,7 +135,8 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
MemoryMappedSegment segment;
uptr prev_end = 0;
while (proc_maps.Next(&segment)) {
- if ((uptr)&rl < segment.end) break;
+ if ((uptr)&rl < segment.end)
+ break;
prev_end = segment.end;
}
CHECK((uptr)&rl >= segment.start && (uptr)&rl < segment.end);
@@ -127,7 +144,8 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
// Get stacksize from rlimit, but clip it so that it does not overlap
// with other mappings.
uptr stacksize = rl.rlim_cur;
- if (stacksize > segment.end - prev_end) stacksize = segment.end - prev_end;
+ if (stacksize > segment.end - prev_end)
+ stacksize = segment.end - prev_end;
// When running with unlimited stack size, we still want to set some limit.
// The unlimited stack size is caused by 'ulimit -s unlimited'.
// Also, for some reason, GNU make spawns subprocesses with unlimited stack.
@@ -135,60 +153,52 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
stacksize = kMaxThreadStackSize;
*stack_top = segment.end;
*stack_bottom = segment.end - stacksize;
+
+ uptr maxAddr = GetMaxUserVirtualAddress();
+ // Edge case: the stack mapping on some systems may be off-by-one e.g.,
+ // fffffffdf000-1000000000000 rw-p 00000000 00:00 0 [stack]
+ // instead of:
+ // fffffffdf000- ffffffffffff
+ // The out-of-range stack_top can result in an invalid shadow address
+ // calculation, since those usually assume the parameters are in range.
+ if (*stack_top == maxAddr + 1)
+ *stack_top = maxAddr;
+ else
+ CHECK_LE(*stack_top, maxAddr);
+
return;
}
uptr stacksize = 0;
void *stackaddr = nullptr;
-#if SANITIZER_SOLARIS
+# if SANITIZER_SOLARIS
stack_t ss;
CHECK_EQ(thr_stksegment(&ss), 0);
stacksize = ss.ss_size;
stackaddr = (char *)ss.ss_sp - stacksize;
-#else // !SANITIZER_SOLARIS
+# else // !SANITIZER_SOLARIS
pthread_attr_t attr;
pthread_attr_init(&attr);
CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);
internal_pthread_attr_getstack(&attr, &stackaddr, &stacksize);
pthread_attr_destroy(&attr);
-#endif // SANITIZER_SOLARIS
+# endif // SANITIZER_SOLARIS
*stack_top = (uptr)stackaddr + stacksize;
*stack_bottom = (uptr)stackaddr;
}
-#if !SANITIZER_GO
+# if !SANITIZER_GO
bool SetEnv(const char *name, const char *value) {
void *f = dlsym(RTLD_NEXT, "setenv");
if (!f)
return false;
- typedef int(*setenv_ft)(const char *name, const char *value, int overwrite);
+ typedef int (*setenv_ft)(const char *name, const char *value, int overwrite);
setenv_ft setenv_f;
CHECK_EQ(sizeof(setenv_f), sizeof(f));
internal_memcpy(&setenv_f, &f, sizeof(f));
return setenv_f(name, value, 1) == 0;
}
-#endif
-
-__attribute__((unused)) static bool GetLibcVersion(int *major, int *minor,
- int *patch) {
-#ifdef _CS_GNU_LIBC_VERSION
- char buf[64];
- uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf));
- if (len >= sizeof(buf))
- return false;
- buf[len] = 0;
- static const char kGLibC[] = "glibc ";
- if (internal_strncmp(buf, kGLibC, sizeof(kGLibC) - 1) != 0)
- return false;
- const char *p = buf + sizeof(kGLibC) - 1;
- *major = internal_simple_strtoll(p, &p, 10);
- *minor = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0;
- *patch = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0;
- return true;
-#else
- return false;
-#endif
-}
+# endif
// True if we can use dlpi_tls_data. glibc before 2.25 may leave NULL (BZ
// #19826) so dlpi_tls_data cannot be used.
@@ -198,136 +208,196 @@ __attribute__((unused)) static bool GetLibcVersion(int *major, int *minor,
// https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=254774
__attribute__((unused)) static int g_use_dlpi_tls_data;
-#if SANITIZER_GLIBC && !SANITIZER_GO
-__attribute__((unused)) static size_t g_tls_size;
-void InitTlsSize() {
- int major, minor, patch;
- g_use_dlpi_tls_data =
- GetLibcVersion(&major, &minor, &patch) && major == 2 && minor >= 25;
-
-#if defined(__aarch64__) || defined(__x86_64__) || defined(__powerpc64__) || \
- defined(__loongarch__)
- void *get_tls_static_info = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
- size_t tls_align;
- ((void (*)(size_t *, size_t *))get_tls_static_info)(&g_tls_size, &tls_align);
-#endif
+# if SANITIZER_GLIBC && !SANITIZER_GO
+static void GetGLibcVersion(int *major, int *minor, int *patch) {
+ const char *p = gnu_get_libc_version();
+ *major = internal_simple_strtoll(p, &p, 10);
+ // Caller does not expect anything else.
+ CHECK_EQ(*major, 2);
+ *minor = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0;
+ *patch = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0;
}
-#else
-void InitTlsSize() { }
-#endif // SANITIZER_GLIBC && !SANITIZER_GO
-
-// On glibc x86_64, ThreadDescriptorSize() needs to be precise due to the usage
-// of g_tls_size. On other targets, ThreadDescriptorSize() is only used by lsan
-// to get the pointer to thread-specific data keys in the thread control block.
-#if (SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_SOLARIS) && \
- !SANITIZER_ANDROID && !SANITIZER_GO
-// sizeof(struct pthread) from glibc.
-static atomic_uintptr_t thread_descriptor_size;
static uptr ThreadDescriptorSizeFallback() {
- uptr val = 0;
-#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
+# if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || \
+ SANITIZER_RISCV64
int major;
int minor;
int patch;
- if (GetLibcVersion(&major, &minor, &patch) && major == 2) {
- /* sizeof(struct pthread) values from various glibc versions. */
- if (SANITIZER_X32)
- val = 1728; // Assume only one particular version for x32.
- // For ARM sizeof(struct pthread) changed in Glibc 2.23.
- else if (SANITIZER_ARM)
- val = minor <= 22 ? 1120 : 1216;
- else if (minor <= 3)
- val = FIRST_32_SECOND_64(1104, 1696);
- else if (minor == 4)
- val = FIRST_32_SECOND_64(1120, 1728);
- else if (minor == 5)
- val = FIRST_32_SECOND_64(1136, 1728);
- else if (minor <= 9)
- val = FIRST_32_SECOND_64(1136, 1712);
- else if (minor == 10)
- val = FIRST_32_SECOND_64(1168, 1776);
- else if (minor == 11 || (minor == 12 && patch == 1))
- val = FIRST_32_SECOND_64(1168, 2288);
- else if (minor <= 14)
- val = FIRST_32_SECOND_64(1168, 2304);
- else if (minor < 32) // Unknown version
- val = FIRST_32_SECOND_64(1216, 2304);
- else // minor == 32
- val = FIRST_32_SECOND_64(1344, 2496);
- }
-#elif defined(__s390__) || defined(__sparc__)
+ GetGLibcVersion(&major, &minor, &patch);
+# endif
+
+# if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
+ /* sizeof(struct pthread) values from various glibc versions. */
+ if (SANITIZER_X32)
+ return 1728; // Assume only one particular version for x32.
+ // For ARM sizeof(struct pthread) changed in Glibc 2.23.
+ if (SANITIZER_ARM)
+ return minor <= 22 ? 1120 : 1216;
+ if (minor <= 3)
+ return FIRST_32_SECOND_64(1104, 1696);
+ if (minor == 4)
+ return FIRST_32_SECOND_64(1120, 1728);
+ if (minor == 5)
+ return FIRST_32_SECOND_64(1136, 1728);
+ if (minor <= 9)
+ return FIRST_32_SECOND_64(1136, 1712);
+ if (minor == 10)
+ return FIRST_32_SECOND_64(1168, 1776);
+ if (minor == 11 || (minor == 12 && patch == 1))
+ return FIRST_32_SECOND_64(1168, 2288);
+ if (minor <= 14)
+ return FIRST_32_SECOND_64(1168, 2304);
+ if (minor < 32) // Unknown version
+ return FIRST_32_SECOND_64(1216, 2304);
+ // minor == 32
+ return FIRST_32_SECOND_64(1344, 2496);
+# endif
+
+# if SANITIZER_RISCV64
+ // TODO: consider adding an optional runtime check for an unknown (untested)
+ // glibc version
+ if (minor <= 28) // WARNING: the highest tested version is 2.29
+ return 1772; // no guarantees for this one
+ if (minor <= 31)
+ return 1772; // tested against glibc 2.29, 2.31
+ return 1936; // tested against glibc 2.32
+# endif
+
+# if defined(__s390__) || defined(__sparc__)
// The size of a prefix of TCB including pthread::{specific_1stblock,specific}
// suffices. Just return offsetof(struct pthread, specific_used), which hasn't
// changed since 2007-05. Technically this applies to i386/x86_64 as well but
// we call _dl_get_tls_static_info and need the precise size of struct
// pthread.
return FIRST_32_SECOND_64(524, 1552);
-#elif defined(__mips__)
+# endif
+
+# if defined(__mips__)
// TODO(sagarthakur): add more values as per different glibc versions.
- val = FIRST_32_SECOND_64(1152, 1776);
-#elif SANITIZER_LOONGARCH64
- val = 1856; // from glibc 2.36
-#elif SANITIZER_RISCV64
- int major;
- int minor;
- int patch;
- if (GetLibcVersion(&major, &minor, &patch) && major == 2) {
- // TODO: consider adding an optional runtime check for an unknown (untested)
- // glibc version
- if (minor <= 28) // WARNING: the highest tested version is 2.29
- val = 1772; // no guarantees for this one
- else if (minor <= 31)
- val = 1772; // tested against glibc 2.29, 2.31
- else
- val = 1936; // tested against glibc 2.32
- }
+ return FIRST_32_SECOND_64(1152, 1776);
+# endif
-#elif defined(__aarch64__)
+# if SANITIZER_LOONGARCH64
+ return 1856; // from glibc 2.36
+# endif
+
+# if defined(__aarch64__)
// The sizeof (struct pthread) is the same from GLIBC 2.17 to 2.22.
- val = 1776;
-#elif defined(__powerpc64__)
- val = 1776; // from glibc.ppc64le 2.20-8.fc21
-#endif
- return val;
+ return 1776;
+# endif
+
+# if defined(__powerpc64__)
+ return 1776; // from glibc.ppc64le 2.20-8.fc21
+# endif
+}
+# endif // SANITIZER_GLIBC && !SANITIZER_GO
+
+# if SANITIZER_FREEBSD && !SANITIZER_GO
+// FIXME: Implementation is very GLIBC specific, but it's used by FreeBSD.
+static uptr ThreadDescriptorSizeFallback() {
+# if defined(__s390__) || defined(__sparc__)
+ // The size of a prefix of TCB including pthread::{specific_1stblock,specific}
+ // suffices. Just return offsetof(struct pthread, specific_used), which hasn't
+ // changed since 2007-05. Technically this applies to i386/x86_64 as well but
+ // we call _dl_get_tls_static_info and need the precise size of struct
+ // pthread.
+ return FIRST_32_SECOND_64(524, 1552);
+# endif
+
+# if defined(__mips__)
+ // TODO(sagarthakur): add more values as per different glibc versions.
+ return FIRST_32_SECOND_64(1152, 1776);
+# endif
+
+# if SANITIZER_LOONGARCH64
+ return 1856; // from glibc 2.36
+# endif
+
+# if defined(__aarch64__)
+ // The sizeof (struct pthread) is the same from GLIBC 2.17 to 2.22.
+ return 1776;
+# endif
+
+# if defined(__powerpc64__)
+ return 1776; // from glibc.ppc64le 2.20-8.fc21
+# endif
+
+ return 0;
}
+# endif // SANITIZER_FREEBSD && !SANITIZER_GO
+
+# if (SANITIZER_FREEBSD || SANITIZER_GLIBC) && !SANITIZER_GO
+// On glibc x86_64, ThreadDescriptorSize() needs to be precise due to the usage
+// of g_tls_size. On other targets, ThreadDescriptorSize() is only used by lsan
+// to get the pointer to thread-specific data keys in the thread control block.
+// sizeof(struct pthread) from glibc.
+static uptr thread_descriptor_size;
-uptr ThreadDescriptorSize() {
- uptr val = atomic_load_relaxed(&thread_descriptor_size);
- if (val)
- return val;
- // _thread_db_sizeof_pthread is a GLIBC_PRIVATE symbol that is exported in
- // glibc 2.34 and later.
- if (unsigned *psizeof = static_cast<unsigned *>(
- dlsym(RTLD_DEFAULT, "_thread_db_sizeof_pthread")))
- val = *psizeof;
- if (!val)
- val = ThreadDescriptorSizeFallback();
- atomic_store_relaxed(&thread_descriptor_size, val);
- return val;
+uptr ThreadDescriptorSize() { return thread_descriptor_size; }
+
+# if SANITIZER_GLIBC
+__attribute__((unused)) static size_t g_tls_size;
+# endif
+
+void InitTlsSize() {
+# if SANITIZER_GLIBC
+ int major, minor, patch;
+ GetGLibcVersion(&major, &minor, &patch);
+ g_use_dlpi_tls_data = major == 2 && minor >= 25;
+
+ if (major == 2 && minor >= 34) {
+ // _thread_db_sizeof_pthread is a GLIBC_PRIVATE symbol that is exported in
+ // glibc 2.34 and later.
+ if (unsigned *psizeof = static_cast<unsigned *>(
+ dlsym(RTLD_DEFAULT, "_thread_db_sizeof_pthread"))) {
+ thread_descriptor_size = *psizeof;
+ }
+ }
+
+# if defined(__aarch64__) || defined(__x86_64__) || \
+ defined(__powerpc64__) || defined(__loongarch__)
+ auto *get_tls_static_info = (void (*)(size_t *, size_t *))dlsym(
+ RTLD_DEFAULT, "_dl_get_tls_static_info");
+ size_t tls_align;
+ // Can be null if static link.
+ if (get_tls_static_info)
+ get_tls_static_info(&g_tls_size, &tls_align);
+# endif
+
+# endif // SANITIZER_GLIBC
+
+ if (!thread_descriptor_size)
+ thread_descriptor_size = ThreadDescriptorSizeFallback();
}
-#if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64 || \
- SANITIZER_LOONGARCH64
+# if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64 || \
+ SANITIZER_LOONGARCH64
// TlsPreTcbSize includes size of struct pthread_descr and size of tcb
// head structure. It lies before the static tls blocks.
static uptr TlsPreTcbSize() {
-#if defined(__mips__)
- const uptr kTcbHead = 16; // sizeof (tcbhead_t)
-#elif defined(__powerpc64__)
- const uptr kTcbHead = 88; // sizeof (tcbhead_t)
-#elif SANITIZER_RISCV64
+# if defined(__mips__)
const uptr kTcbHead = 16; // sizeof (tcbhead_t)
-#elif SANITIZER_LOONGARCH64
+# elif defined(__powerpc64__)
+ const uptr kTcbHead = 88; // sizeof (tcbhead_t)
+# elif SANITIZER_RISCV64
const uptr kTcbHead = 16; // sizeof (tcbhead_t)
-#endif
+# elif SANITIZER_LOONGARCH64
+ const uptr kTcbHead = 16; // sizeof (tcbhead_t)
+# endif
const uptr kTlsAlign = 16;
const uptr kTlsPreTcbSize =
RoundUpTo(ThreadDescriptorSize() + kTcbHead, kTlsAlign);
return kTlsPreTcbSize;
}
-#endif
-
+# endif
+# else // (SANITIZER_FREEBSD || SANITIZER_GLIBC) && !SANITIZER_GO
+void InitTlsSize() {}
+uptr ThreadDescriptorSize() { return 0; }
+# endif // (SANITIZER_FREEBSD || SANITIZER_GLIBC) && !SANITIZER_GO
+
+# if (SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_SOLARIS) && \
+ !SANITIZER_ANDROID && !SANITIZER_GO
namespace {
struct TlsBlock {
uptr begin, end, align;
@@ -336,7 +406,7 @@ struct TlsBlock {
};
} // namespace
-#ifdef __s390__
+# ifdef __s390__
extern "C" uptr __tls_get_offset(void *arg);
static uptr TlsGetOffset(uptr ti_module, uptr ti_offset) {
@@ -354,16 +424,16 @@ static uptr TlsGetOffset(uptr ti_module, uptr ti_offset) {
: "memory", "cc", "0", "1", "3", "4", "5", "14");
return r2;
}
-#else
+# else
extern "C" void *__tls_get_addr(size_t *);
-#endif
+# endif
static size_t main_tls_modid;
static int CollectStaticTlsBlocks(struct dl_phdr_info *info, size_t size,
void *data) {
size_t tls_modid;
-#if SANITIZER_SOLARIS
+# if SANITIZER_SOLARIS
// dlpi_tls_modid is only available since Solaris 11.4 SRU 10. Use
// dlinfo(RTLD_DI_LINKMAP) instead which works on all of Solaris 11.3,
// 11.4, and Illumos. The tlsmodid of the executable was changed to 1 in
@@ -376,27 +446,26 @@ static int CollectStaticTlsBlocks(struct dl_phdr_info *info, size_t size,
Rt_map *map;
dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &map);
tls_modid = map->rt_tlsmodid;
-#else
+# else
main_tls_modid = 1;
tls_modid = info->dlpi_tls_modid;
-#endif
+# endif
if (tls_modid < main_tls_modid)
return 0;
uptr begin;
-#if !SANITIZER_SOLARIS
+# if !SANITIZER_SOLARIS
begin = (uptr)info->dlpi_tls_data;
-#endif
+# endif
if (!g_use_dlpi_tls_data) {
// Call __tls_get_addr as a fallback. This forces TLS allocation on glibc
// and FreeBSD.
-#ifdef __s390__
- begin = (uptr)__builtin_thread_pointer() +
- TlsGetOffset(tls_modid, 0);
-#else
+# ifdef __s390__
+ begin = (uptr)__builtin_thread_pointer() + TlsGetOffset(tls_modid, 0);
+# else
size_t mod_and_off[2] = {tls_modid, 0};
begin = (uptr)__tls_get_addr(mod_and_off);
-#endif
+# endif
}
for (unsigned i = 0; i != info->dlpi_phnum; ++i)
if (info->dlpi_phdr[i].p_type == PT_TLS) {
@@ -439,23 +508,21 @@ __attribute__((unused)) static void GetStaticTlsBoundary(uptr *addr, uptr *size,
*addr = ranges[l].begin;
*size = ranges[r - 1].end - ranges[l].begin;
}
-#endif // (x86_64 || i386 || mips || ...) && (SANITIZER_FREEBSD ||
- // SANITIZER_LINUX) && !SANITIZER_ANDROID && !SANITIZER_GO
+# endif // (x86_64 || i386 || mips || ...) && (SANITIZER_FREEBSD ||
+ // SANITIZER_LINUX) && !SANITIZER_ANDROID && !SANITIZER_GO
-#if SANITIZER_NETBSD
-static struct tls_tcb * ThreadSelfTlsTcb() {
+# if SANITIZER_NETBSD
+static struct tls_tcb *ThreadSelfTlsTcb() {
struct tls_tcb *tcb = nullptr;
-#ifdef __HAVE___LWP_GETTCB_FAST
+# ifdef __HAVE___LWP_GETTCB_FAST
tcb = (struct tls_tcb *)__lwp_gettcb_fast();
-#elif defined(__HAVE___LWP_GETPRIVATE_FAST)
+# elif defined(__HAVE___LWP_GETPRIVATE_FAST)
tcb = (struct tls_tcb *)__lwp_getprivate_fast();
-#endif
+# endif
return tcb;
}
-uptr ThreadSelf() {
- return (uptr)ThreadSelfTlsTcb()->tcb_pthread;
-}
+uptr ThreadSelf() { return (uptr)ThreadSelfTlsTcb()->tcb_pthread; }
int GetSizeFromHdr(struct dl_phdr_info *info, size_t size, void *data) {
const Elf_Phdr *hdr = info->dlpi_phdr;
@@ -463,23 +530,23 @@ int GetSizeFromHdr(struct dl_phdr_info *info, size_t size, void *data) {
for (; hdr != last_hdr; ++hdr) {
if (hdr->p_type == PT_TLS && info->dlpi_tls_modid == 1) {
- *(uptr*)data = hdr->p_memsz;
+ *(uptr *)data = hdr->p_memsz;
break;
}
}
return 0;
}
-#endif // SANITIZER_NETBSD
+# endif // SANITIZER_NETBSD
-#if SANITIZER_ANDROID
+# if SANITIZER_ANDROID
// Bionic provides this API since S.
extern "C" SANITIZER_WEAK_ATTRIBUTE void __libc_get_static_tls_bounds(void **,
void **);
-#endif
+# endif
-#if !SANITIZER_GO
+# if !SANITIZER_GO
static void GetTls(uptr *addr, uptr *size) {
-#if SANITIZER_ANDROID
+# if SANITIZER_ANDROID
if (&__libc_get_static_tls_bounds) {
void *start_addr;
void *end_addr;
@@ -491,48 +558,48 @@ static void GetTls(uptr *addr, uptr *size) {
*addr = 0;
*size = 0;
}
-#elif SANITIZER_GLIBC && defined(__x86_64__)
+# elif SANITIZER_GLIBC && defined(__x86_64__)
// For aarch64 and x86-64, use an O(1) approach which requires relatively
// precise ThreadDescriptorSize. g_tls_size was initialized in InitTlsSize.
-# if SANITIZER_X32
+# if SANITIZER_X32
asm("mov %%fs:8,%0" : "=r"(*addr));
-# else
+# else
asm("mov %%fs:16,%0" : "=r"(*addr));
-# endif
+# endif
*size = g_tls_size;
*addr -= *size;
*addr += ThreadDescriptorSize();
-#elif SANITIZER_GLIBC && defined(__aarch64__)
+# elif SANITIZER_GLIBC && defined(__aarch64__)
*addr = reinterpret_cast<uptr>(__builtin_thread_pointer()) -
ThreadDescriptorSize();
*size = g_tls_size + ThreadDescriptorSize();
-#elif SANITIZER_GLIBC && defined(__loongarch__)
-# ifdef __clang__
+# elif SANITIZER_GLIBC && defined(__loongarch__)
+# ifdef __clang__
*addr = reinterpret_cast<uptr>(__builtin_thread_pointer()) -
ThreadDescriptorSize();
-# else
+# else
asm("or %0,$tp,$zero" : "=r"(*addr));
*addr -= ThreadDescriptorSize();
-# endif
+# endif
*size = g_tls_size + ThreadDescriptorSize();
-#elif SANITIZER_GLIBC && defined(__powerpc64__)
+# elif SANITIZER_GLIBC && defined(__powerpc64__)
// Workaround for glibc<2.25(?). 2.27 is known to not need this.
uptr tp;
asm("addi %0,13,-0x7000" : "=r"(tp));
const uptr pre_tcb_size = TlsPreTcbSize();
*addr = tp - pre_tcb_size;
*size = g_tls_size + pre_tcb_size;
-#elif SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_SOLARIS
+# elif SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_SOLARIS
uptr align;
GetStaticTlsBoundary(addr, size, &align);
-#if defined(__x86_64__) || defined(__i386__) || defined(__s390__) || \
- defined(__sparc__)
+# if defined(__x86_64__) || defined(__i386__) || defined(__s390__) || \
+ defined(__sparc__)
if (SANITIZER_GLIBC) {
-#if defined(__x86_64__) || defined(__i386__)
+# if defined(__x86_64__) || defined(__i386__)
align = Max<uptr>(align, 64);
-#else
+# else
align = Max<uptr>(align, 16);
-#endif
+# endif
}
const uptr tp = RoundUpTo(*addr + *size, align);
@@ -551,26 +618,26 @@ static void GetTls(uptr *addr, uptr *size) {
// because the number of bytes after pthread::specific is larger.
*addr = tp - RoundUpTo(*size, align);
*size = tp - *addr + ThreadDescriptorSize();
-#else
+# else
if (SANITIZER_GLIBC)
*size += 1664;
else if (SANITIZER_FREEBSD)
*size += 128; // RTLD_STATIC_TLS_EXTRA
-#if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64
+# if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64
const uptr pre_tcb_size = TlsPreTcbSize();
*addr -= pre_tcb_size;
*size += pre_tcb_size;
-#else
+# else
// arm and aarch64 reserve two words at TP, so this underestimates the range.
// However, this is sufficient for the purpose of finding the pointers to
// thread-specific data keys.
const uptr tcb_size = ThreadDescriptorSize();
*addr -= tcb_size;
*size += tcb_size;
-#endif
-#endif
-#elif SANITIZER_NETBSD
- struct tls_tcb * const tcb = ThreadSelfTlsTcb();
+# endif
+# endif
+# elif SANITIZER_NETBSD
+ struct tls_tcb *const tcb = ThreadSelfTlsTcb();
*addr = 0;
*size = 0;
if (tcb != 0) {
@@ -583,56 +650,59 @@ static void GetTls(uptr *addr, uptr *size) {
*addr = (uptr)tcb->tcb_dtv[1];
}
}
-#else
-#error "Unknown OS"
-#endif
+# else
+# error "Unknown OS"
+# endif
}
-#endif
+# endif
-#if !SANITIZER_GO
+# if !SANITIZER_GO
uptr GetTlsSize() {
-#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
- SANITIZER_SOLARIS
+# if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
+ SANITIZER_SOLARIS
uptr addr, size;
GetTls(&addr, &size);
return size;
-#else
+# else
return 0;
-#endif
+# endif
}
-#endif
+# endif
-void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
- uptr *tls_addr, uptr *tls_size) {
-#if SANITIZER_GO
+void GetThreadStackAndTls(bool main, uptr *stk_begin, uptr *stk_end,
+ uptr *tls_begin, uptr *tls_end) {
+# if SANITIZER_GO
// Stub implementation for Go.
- *stk_addr = *stk_size = *tls_addr = *tls_size = 0;
-#else
- GetTls(tls_addr, tls_size);
+ *stk_begin = 0;
+ *stk_end = 0;
+ *tls_begin = 0;
+ *tls_end = 0;
+# else
+ uptr tls_addr = 0;
+ uptr tls_size = 0;
+ GetTls(&tls_addr, &tls_size);
+ *tls_begin = tls_addr;
+ *tls_end = tls_addr + tls_size;
uptr stack_top, stack_bottom;
GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
- *stk_addr = stack_bottom;
- *stk_size = stack_top - stack_bottom;
+ *stk_begin = stack_bottom;
+ *stk_end = stack_top;
if (!main) {
// If stack and tls intersect, make them non-intersecting.
- if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) {
- if (*stk_addr + *stk_size < *tls_addr + *tls_size)
- *tls_size = *stk_addr + *stk_size - *tls_addr;
- *stk_size = *tls_addr - *stk_addr;
+ if (*tls_begin > *stk_begin && *tls_begin < *stk_end) {
+ if (*stk_end < *tls_end)
+ *tls_end = *stk_end;
+ *stk_end = *tls_begin;
}
}
-#endif
+# endif
}
-#if !SANITIZER_FREEBSD
+# if !SANITIZER_FREEBSD
typedef ElfW(Phdr) Elf_Phdr;
-#elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2
-#define Elf_Phdr XElf32_Phdr
-#define dl_phdr_info xdl_phdr_info
-#define dl_iterate_phdr(c, b) xdl_iterate_phdr((c), (b))
-#endif // !SANITIZER_FREEBSD
+# endif
struct DlIteratePhdrData {
InternalMmapVectorNoCtor<LoadedModule> *modules;
@@ -652,8 +722,7 @@ static int AddModuleSegments(const char *module_name, dl_phdr_info *info,
uptr cur_end = cur_beg + phdr->p_memsz;
bool executable = phdr->p_flags & PF_X;
bool writable = phdr->p_flags & PF_W;
- cur_module.addAddressRange(cur_beg, cur_end, executable,
- writable);
+ cur_module.addAddressRange(cur_beg, cur_end, executable, writable);
} else if (phdr->p_type == PT_NOTE) {
# ifdef NT_GNU_BUILD_ID
uptr off = 0;
@@ -704,24 +773,24 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
return 0;
}
-#if SANITIZER_ANDROID && __ANDROID_API__ < 21
+# if SANITIZER_ANDROID && __ANDROID_API__ < 21
extern "C" __attribute__((weak)) int dl_iterate_phdr(
int (*)(struct dl_phdr_info *, size_t, void *), void *);
-#endif
+# endif
static bool requiresProcmaps() {
-#if SANITIZER_ANDROID && __ANDROID_API__ <= 22
+# if SANITIZER_ANDROID && __ANDROID_API__ <= 22
// Fall back to /proc/maps if dl_iterate_phdr is unavailable or broken.
// The runtime check allows the same library to work with
// both K and L (and future) Android releases.
return AndroidGetApiLevel() <= ANDROID_LOLLIPOP_MR1;
-#else
+# else
return false;
-#endif
+# endif
}
static void procmapsInit(InternalMmapVectorNoCtor<LoadedModule> *modules) {
- MemoryMappingLayout memory_mapping(/*cache_enabled*/true);
+ MemoryMappingLayout memory_mapping(/*cache_enabled*/ true);
memory_mapping.DumpListOfModules(modules);
}
@@ -773,22 +842,19 @@ uptr GetRSS() {
// We need the second number which is RSS in pages.
char *pos = buf;
// Skip the first number.
- while (*pos >= '0' && *pos <= '9')
- pos++;
+ while (*pos >= '0' && *pos <= '9') pos++;
// Skip whitespaces.
- while (!(*pos >= '0' && *pos <= '9') && *pos != 0)
- pos++;
+ while (!(*pos >= '0' && *pos <= '9') && *pos != 0) pos++;
// Read the number.
uptr rss = 0;
- while (*pos >= '0' && *pos <= '9')
- rss = rss * 10 + *pos++ - '0';
+ while (*pos >= '0' && *pos <= '9') rss = rss * 10 + *pos++ - '0';
return rss * GetPageSizeCached();
}
// sysconf(_SC_NPROCESSORS_{CONF,ONLN}) cannot be used on most platforms as
// they allocate memory.
u32 GetNumberOfCPUs() {
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD
+# if SANITIZER_FREEBSD || SANITIZER_NETBSD
u32 ncpu;
int req[2];
uptr len = sizeof(ncpu);
@@ -796,7 +862,7 @@ u32 GetNumberOfCPUs() {
req[1] = HW_NCPU;
CHECK_EQ(internal_sysctl(req, 2, &ncpu, &len, NULL, 0), 0);
return ncpu;
-#elif SANITIZER_ANDROID && !defined(CPU_COUNT) && !defined(__aarch64__)
+# elif SANITIZER_ANDROID && !defined(CPU_COUNT) && !defined(__aarch64__)
// Fall back to /sys/devices/system/cpu on Android when cpu_set_t doesn't
// exist in sched.h. That is the case for toolchains generated with older
// NDKs.
@@ -824,30 +890,26 @@ u32 GetNumberOfCPUs() {
break;
if (entry->d_ino != 0 && *d_type == DT_DIR) {
if (entry->d_name[0] == 'c' && entry->d_name[1] == 'p' &&
- entry->d_name[2] == 'u' &&
- entry->d_name[3] >= '0' && entry->d_name[3] <= '9')
+ entry->d_name[2] == 'u' && entry->d_name[3] >= '0' &&
+ entry->d_name[3] <= '9')
n_cpus++;
}
entry = (struct linux_dirent *)(((u8 *)entry) + entry->d_reclen);
}
internal_close(fd);
return n_cpus;
-#elif SANITIZER_SOLARIS
+# elif SANITIZER_SOLARIS
return sysconf(_SC_NPROCESSORS_ONLN);
-#else
-#if defined(CPU_COUNT)
+# else
cpu_set_t CPUs;
CHECK_EQ(sched_getaffinity(0, sizeof(cpu_set_t), &CPUs), 0);
return CPU_COUNT(&CPUs);
-#else
- return 1;
-#endif
-#endif
+# endif
}
-#if SANITIZER_LINUX
+# if SANITIZER_LINUX
-#if SANITIZER_ANDROID
+# if SANITIZER_ANDROID
static atomic_uint8_t android_log_initialized;
void AndroidLogInit() {
@@ -859,13 +921,15 @@ static bool ShouldLogAfterPrintf() {
return atomic_load(&android_log_initialized, memory_order_acquire);
}
-extern "C" SANITIZER_WEAK_ATTRIBUTE
-int async_safe_write_log(int pri, const char* tag, const char* msg);
-extern "C" SANITIZER_WEAK_ATTRIBUTE
-int __android_log_write(int prio, const char* tag, const char* msg);
+extern "C" SANITIZER_WEAK_ATTRIBUTE int async_safe_write_log(int pri,
+ const char *tag,
+ const char *msg);
+extern "C" SANITIZER_WEAK_ATTRIBUTE int __android_log_write(int prio,
+ const char *tag,
+ const char *msg);
// ANDROID_LOG_INFO is 4, but can't be resolved at runtime.
-#define SANITIZER_ANDROID_LOG_INFO 4
+# define SANITIZER_ANDROID_LOG_INFO 4
// async_safe_write_log is a new public version of __libc_write_log that is
// used behind syslog. It is preferable to syslog as it will not do any dynamic
@@ -884,14 +948,14 @@ void WriteOneLineToSyslog(const char *s) {
}
}
-extern "C" SANITIZER_WEAK_ATTRIBUTE
-void android_set_abort_message(const char *);
+extern "C" SANITIZER_WEAK_ATTRIBUTE void android_set_abort_message(
+ const char *);
void SetAbortMessage(const char *str) {
if (&android_set_abort_message)
android_set_abort_message(str);
}
-#else
+# else
void AndroidLogInit() {}
static bool ShouldLogAfterPrintf() { return true; }
@@ -899,16 +963,16 @@ static bool ShouldLogAfterPrintf() { return true; }
void WriteOneLineToSyslog(const char *s) { syslog(LOG_INFO, "%s", s); }
void SetAbortMessage(const char *str) {}
-#endif // SANITIZER_ANDROID
+# endif // SANITIZER_ANDROID
void LogMessageOnPrintf(const char *str) {
if (common_flags()->log_to_syslog && ShouldLogAfterPrintf())
WriteToSyslog(str);
}
-#endif // SANITIZER_LINUX
+# endif // SANITIZER_LINUX
-#if SANITIZER_GLIBC && !SANITIZER_GO
+# if SANITIZER_GLIBC && !SANITIZER_GO
// glibc crashes when using clock_gettime from a preinit_array function as the
// vDSO function pointers haven't been initialized yet. __progname is
// initialized after the vDSO function pointers, so if it exists, is not null
@@ -919,8 +983,8 @@ inline bool CanUseVDSO() { return &__progname && __progname && *__progname; }
// MonotonicNanoTime is a timing function that can leverage the vDSO by calling
// clock_gettime. real_clock_gettime only exists if clock_gettime is
// intercepted, so define it weakly and use it if available.
-extern "C" SANITIZER_WEAK_ATTRIBUTE
-int real_clock_gettime(u32 clk_id, void *tp);
+extern "C" SANITIZER_WEAK_ATTRIBUTE int real_clock_gettime(u32 clk_id,
+ void *tp);
u64 MonotonicNanoTime() {
timespec ts;
if (CanUseVDSO()) {
@@ -933,19 +997,26 @@ u64 MonotonicNanoTime() {
}
return (u64)ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec;
}
-#else
+# else
// Non-glibc & Go always use the regular function.
u64 MonotonicNanoTime() {
timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (u64)ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec;
}
-#endif // SANITIZER_GLIBC && !SANITIZER_GO
+# endif // SANITIZER_GLIBC && !SANITIZER_GO
void ReExec() {
const char *pathname = "/proc/self/exe";
-#if SANITIZER_NETBSD
+# if SANITIZER_FREEBSD
+ for (const auto *aux = __elf_aux_vector; aux->a_type != AT_NULL; aux++) {
+ if (aux->a_type == AT_EXECPATH) {
+ pathname = static_cast<const char *>(aux->a_un.a_ptr);
+ break;
+ }
+ }
+# elif SANITIZER_NETBSD
static const int name[] = {
CTL_KERN,
KERN_PROC_ARGS,
@@ -958,14 +1029,14 @@ void ReExec() {
len = sizeof(path);
if (internal_sysctl(name, ARRAY_SIZE(name), path, &len, NULL, 0) != -1)
pathname = path;
-#elif SANITIZER_SOLARIS
+# elif SANITIZER_SOLARIS
pathname = getexecname();
CHECK_NE(pathname, NULL);
-#elif SANITIZER_USE_GETAUXVAL
+# elif SANITIZER_USE_GETAUXVAL
// Calling execve with /proc/self/exe sets that as $EXEC_ORIGIN. Binaries that
// rely on that will fail to load shared libraries. Query AT_EXECFN instead.
pathname = reinterpret_cast<const char *>(getauxval(AT_EXECFN));
-#endif
+# endif
uptr rv = internal_execve(pathname, GetArgv(), GetEnviron());
int rverrno;
@@ -987,9 +1058,8 @@ void UnmapFromTo(uptr from, uptr to) {
}
uptr MapDynamicShadow(uptr shadow_size_bytes, uptr shadow_scale,
- uptr min_shadow_base_alignment,
- UNUSED uptr &high_mem_end) {
- const uptr granularity = GetMmapGranularity();
+ uptr min_shadow_base_alignment, UNUSED uptr &high_mem_end,
+ uptr granularity) {
const uptr alignment =
Max<uptr>(granularity << shadow_scale, 1ULL << min_shadow_base_alignment);
const uptr left_padding =
@@ -1017,14 +1087,14 @@ static uptr MmapSharedNoReserve(uptr addr, uptr size) {
static uptr MremapCreateAlias(uptr base_addr, uptr alias_addr,
uptr alias_size) {
-#if SANITIZER_LINUX
+# if SANITIZER_LINUX
return internal_mremap(reinterpret_cast<void *>(base_addr), 0, alias_size,
MREMAP_MAYMOVE | MREMAP_FIXED,
reinterpret_cast<void *>(alias_addr));
-#else
+# else
CHECK(false && "mremap is not supported outside of Linux");
return 0;
-#endif
+# endif
}
static void CreateAliases(uptr start_addr, uptr alias_size, uptr num_aliases) {
@@ -1069,12 +1139,12 @@ uptr MapDynamicShadowAndAliases(uptr shadow_size, uptr alias_size,
}
void InitializePlatformCommonFlags(CommonFlags *cf) {
-#if SANITIZER_ANDROID
+# if SANITIZER_ANDROID
if (&__libc_get_static_tls_bounds == nullptr)
cf->detect_leaks = false;
-#endif
+# endif
}
-} // namespace __sanitizer
+} // namespace __sanitizer
#endif
@@ -15,14 +15,14 @@
#if SANITIZER_LINUX && SANITIZER_S390
-#include <dlfcn.h>
-#include <errno.h>
-#include <sys/syscall.h>
-#include <sys/utsname.h>
-#include <unistd.h>
+# include <dlfcn.h>
+# include <errno.h>
+# include <sys/syscall.h>
+# include <sys/utsname.h>
+# include <unistd.h>
-#include "sanitizer_libc.h"
-#include "sanitizer_linux.h"
+# include "sanitizer_libc.h"
+# include "sanitizer_linux.h"
namespace __sanitizer {
@@ -37,22 +37,19 @@ uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
unsigned long fd;
unsigned long offset;
} params = {
- (unsigned long)addr,
- (unsigned long)length,
- (unsigned long)prot,
- (unsigned long)flags,
- (unsigned long)fd,
-# ifdef __s390x__
- (unsigned long)offset,
-# else
+ (unsigned long)addr, (unsigned long)length, (unsigned long)prot,
+ (unsigned long)flags, (unsigned long)fd,
+# ifdef __s390x__
+ (unsigned long)offset,
+# else
(unsigned long)(offset / 4096),
-# endif
+# endif
};
-# ifdef __s390x__
+# ifdef __s390x__
return syscall(__NR_mmap, ¶ms);
-# else
+# else
return syscall(__NR_mmap2, ¶ms);
-# endif
+# endif
}
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
@@ -63,58 +60,54 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
}
CHECK_EQ(0, (uptr)child_stack % 16);
// Minimum frame size.
-#ifdef __s390x__
+# ifdef __s390x__
child_stack = (char *)child_stack - 160;
-#else
+# else
child_stack = (char *)child_stack - 96;
-#endif
+# endif
// Terminate unwind chain.
((unsigned long *)child_stack)[0] = 0;
// And pass parameters.
((unsigned long *)child_stack)[1] = (uptr)fn;
((unsigned long *)child_stack)[2] = (uptr)arg;
register uptr res __asm__("r2");
- register void *__cstack __asm__("r2") = child_stack;
- register long __flags __asm__("r3") = flags;
- register int * __ptidptr __asm__("r4") = parent_tidptr;
- register int * __ctidptr __asm__("r5") = child_tidptr;
- register void * __newtls __asm__("r6") = newtls;
+ register void *__cstack __asm__("r2") = child_stack;
+ register long __flags __asm__("r3") = flags;
+ register int *__ptidptr __asm__("r4") = parent_tidptr;
+ register int *__ctidptr __asm__("r5") = child_tidptr;
+ register void *__newtls __asm__("r6") = newtls;
__asm__ __volatile__(
- /* Clone. */
- "svc %1\n"
-
- /* if (%r2 != 0)
- * return;
- */
-#ifdef __s390x__
- "cghi %%r2, 0\n"
-#else
- "chi %%r2, 0\n"
-#endif
- "jne 1f\n"
-
- /* Call "fn(arg)". */
-#ifdef __s390x__
- "lmg %%r1, %%r2, 8(%%r15)\n"
-#else
- "lm %%r1, %%r2, 4(%%r15)\n"
-#endif
- "basr %%r14, %%r1\n"
-
- /* Call _exit(%r2). */
- "svc %2\n"
-
- /* Return to parent. */
- "1:\n"
- : "=r" (res)
- : "i"(__NR_clone), "i"(__NR_exit),
- "r"(__cstack),
- "r"(__flags),
- "r"(__ptidptr),
- "r"(__ctidptr),
- "r"(__newtls)
- : "memory", "cc");
+ /* Clone. */
+ "svc %1\n"
+
+ /* if (%r2 != 0)
+ * return;
+ */
+# ifdef __s390x__
+ "cghi %%r2, 0\n"
+# else
+ "chi %%r2, 0\n"
+# endif
+ "jne 1f\n"
+
+ /* Call "fn(arg)". */
+# ifdef __s390x__
+ "lmg %%r1, %%r2, 8(%%r15)\n"
+# else
+ "lm %%r1, %%r2, 4(%%r15)\n"
+# endif
+ "basr %%r14, %%r1\n"
+
+ /* Call _exit(%r2). */
+ "svc %2\n"
+
+ /* Return to parent. */
+ "1:\n"
+ : "=r"(res)
+ : "i"(__NR_clone), "i"(__NR_exit), "r"(__cstack), "r"(__flags),
+ "r"(__ptidptr), "r"(__ctidptr), "r"(__newtls)
+ : "memory", "cc");
if (res >= (uptr)-4095) {
errno = -res;
return -1;
@@ -122,7 +115,7 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
return res;
}
-#if SANITIZER_S390_64
+# if SANITIZER_S390_64
static bool FixedCVE_2016_2143() {
// Try to determine if the running kernel has a fix for CVE-2016-2143,
// return false if in doubt (better safe than sorry). Distros may want to
@@ -137,20 +130,20 @@ static bool FixedCVE_2016_2143() {
// At least first 2 should be matched.
if (ptr[0] != '.')
return false;
- minor = internal_simple_strtoll(ptr+1, &ptr, 10);
+ minor = internal_simple_strtoll(ptr + 1, &ptr, 10);
// Third is optional.
if (ptr[0] == '.')
- patch = internal_simple_strtoll(ptr+1, &ptr, 10);
+ patch = internal_simple_strtoll(ptr + 1, &ptr, 10);
if (major < 3) {
if (major == 2 && minor == 6 && patch == 32 && ptr[0] == '-' &&
internal_strstr(ptr, ".el6")) {
// Check RHEL6
- int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
- if (r1 >= 657) // 2.6.32-657.el6 or later
+ int r1 = internal_simple_strtoll(ptr + 1, &ptr, 10);
+ if (r1 >= 657) // 2.6.32-657.el6 or later
return true;
if (r1 == 642 && ptr[0] == '.') {
- int r2 = internal_simple_strtoll(ptr+1, &ptr, 10);
- if (r2 >= 9) // 2.6.32-642.9.1.el6 or later
+ int r2 = internal_simple_strtoll(ptr + 1, &ptr, 10);
+ if (r2 >= 9) // 2.6.32-642.9.1.el6 or later
return true;
}
}
@@ -166,12 +159,12 @@ static bool FixedCVE_2016_2143() {
if (minor == 10 && patch == 0 && ptr[0] == '-' &&
internal_strstr(ptr, ".el7")) {
// Check RHEL7
- int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
- if (r1 >= 426) // 3.10.0-426.el7 or later
+ int r1 = internal_simple_strtoll(ptr + 1, &ptr, 10);
+ if (r1 >= 426) // 3.10.0-426.el7 or later
return true;
if (r1 == 327 && ptr[0] == '.') {
- int r2 = internal_simple_strtoll(ptr+1, &ptr, 10);
- if (r2 >= 27) // 3.10.0-327.27.1.el7 or later
+ int r2 = internal_simple_strtoll(ptr + 1, &ptr, 10);
+ if (r2 >= 27) // 3.10.0-327.27.1.el7 or later
return true;
}
}
@@ -187,8 +180,8 @@ static bool FixedCVE_2016_2143() {
if (minor == 4 && patch == 0 && ptr[0] == '-' &&
internal_strstr(buf.version, "Ubuntu")) {
// Check Ubuntu 16.04
- int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
- if (r1 >= 13) // 4.4.0-13 or later
+ int r1 = internal_simple_strtoll(ptr + 1, &ptr, 10);
+ if (r1 >= 13) // 4.4.0-13 or later
return true;
}
// Otherwise, OK if 4.5+.
@@ -211,18 +204,19 @@ void AvoidCVE_2016_2143() {
if (GetEnv("SANITIZER_IGNORE_CVE_2016_2143"))
return;
Report(
- "ERROR: Your kernel seems to be vulnerable to CVE-2016-2143. Using ASan,\n"
- "MSan, TSan, DFSan or LSan with such kernel can and will crash your\n"
- "machine, or worse.\n"
- "\n"
- "If you are certain your kernel is not vulnerable (you have compiled it\n"
- "yourself, or are using an unrecognized distribution kernel), you can\n"
- "override this safety check by exporting SANITIZER_IGNORE_CVE_2016_2143\n"
- "with any value.\n");
+ "ERROR: Your kernel seems to be vulnerable to CVE-2016-2143. Using "
+ "ASan,\n"
+ "MSan, TSan, DFSan or LSan with such kernel can and will crash your\n"
+ "machine, or worse.\n"
+ "\n"
+ "If you are certain your kernel is not vulnerable (you have compiled it\n"
+ "yourself, or are using an unrecognized distribution kernel), you can\n"
+ "override this safety check by exporting SANITIZER_IGNORE_CVE_2016_2143\n"
+ "with any value.\n");
Die();
}
-#endif
+# endif
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // SANITIZER_LINUX && SANITIZER_S390
+#endif // SANITIZER_LINUX && SANITIZER_S390
@@ -38,7 +38,7 @@
extern char **environ;
# endif
-# if defined(__has_include) && __has_include(<os/trace.h>) && defined(__BLOCKS__)
+# if defined(__has_include) && __has_include(<os/trace.h>)
# define SANITIZER_OS_TRACE 1
# include <os/trace.h>
# else
@@ -71,15 +71,7 @@ extern char ***_NSGetArgv(void);
# include <mach/mach_time.h>
# include <mach/vm_statistics.h>
# include <malloc/malloc.h>
-# if defined(__has_builtin) && __has_builtin(__builtin_os_log_format)
-# include <os/log.h>
-# else
- /* Without support for __builtin_os_log_format, fall back to the older
- method. */
-# define OS_LOG_DEFAULT 0
-# define os_log_error(A,B,C) \
- asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", (C));
-# endif
+# include <os/log.h>
# include <pthread.h>
# include <pthread/introspection.h>
# include <sched.h>
@@ -553,9 +545,6 @@ uptr GetTlsSize() {
return 0;
}
-void InitTlsSize() {
-}
-
uptr TlsBaseAddr() {
uptr segbase = 0;
#if defined(__x86_64__)
@@ -580,21 +569,18 @@ uptr TlsSize() {
#endif
}
-void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
- uptr *tls_addr, uptr *tls_size) {
-#if !SANITIZER_GO
- uptr stack_top, stack_bottom;
- GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
- *stk_addr = stack_bottom;
- *stk_size = stack_top - stack_bottom;
- *tls_addr = TlsBaseAddr();
- *tls_size = TlsSize();
-#else
- *stk_addr = 0;
- *stk_size = 0;
- *tls_addr = 0;
- *tls_size = 0;
-#endif
+void GetThreadStackAndTls(bool main, uptr *stk_begin, uptr *stk_end,
+ uptr *tls_begin, uptr *tls_end) {
+# if !SANITIZER_GO
+ GetThreadStackTopAndBottom(main, stk_end, stk_begin);
+ *tls_begin = TlsBaseAddr();
+ *tls_end = *tls_begin + TlsSize();
+# else
+ *stk_begin = 0;
+ *stk_end = 0;
+ *tls_begin = 0;
+ *tls_end = 0;
+# endif
}
void ListOfModules::init() {
@@ -796,7 +782,11 @@ void WriteOneLineToSyslog(const char *s) {
if (GetMacosAlignedVersion() >= MacosVersion(10, 12)) {
os_log_error(OS_LOG_DEFAULT, "%{public}s", s);
} else {
+#pragma clang diagnostic push
+// as_log is deprecated.
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", s);
+#pragma clang diagnostic pop
}
#endif
}
@@ -851,6 +841,9 @@ void LogFullErrorReport(const char *buffer) {
#if !SANITIZER_GO
// Log with os_trace. This will make it into the crash log.
#if SANITIZER_OS_TRACE
+#pragma clang diagnostic push
+// os_trace is deprecated.
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
if (GetMacosAlignedVersion() >= MacosVersion(10, 10)) {
// os_trace requires the message (format parameter) to be a string literal.
if (internal_strncmp(SanitizerToolName, "AddressSanitizer",
@@ -868,6 +861,7 @@ void LogFullErrorReport(const char *buffer) {
if (common_flags()->log_to_syslog)
os_trace("Consult syslog for more information.");
}
+#pragma clang diagnostic pop
#endif
// Log to syslog.
@@ -1196,8 +1190,8 @@ uptr GetMaxVirtualAddress() {
}
uptr MapDynamicShadow(uptr shadow_size_bytes, uptr shadow_scale,
- uptr min_shadow_base_alignment, uptr &high_mem_end) {
- const uptr granularity = GetMmapGranularity();
+ uptr min_shadow_base_alignment, uptr &high_mem_end,
+ uptr granularity) {
const uptr alignment =
Max<uptr>(granularity << shadow_scale, 1ULL << min_shadow_base_alignment);
const uptr left_padding =
@@ -1380,8 +1374,8 @@ void DumpProcessMap() {
for (uptr i = 0; i < modules.size(); ++i) {
char uuid_str[128];
FormatUUID(uuid_str, sizeof(uuid_str), modules[i].uuid());
- Printf("0x%zx-0x%zx %s (%s) %s\n", modules[i].base_address(),
- modules[i].max_address(), modules[i].full_name(),
+ Printf("%p-%p %s (%s) %s\n", (void *)modules[i].base_address(),
+ (void *)modules[i].max_address(), modules[i].full_name(),
ModuleArchToString(modules[i].arch()), uuid_str);
}
Printf("End of module map.\n");
@@ -14,26 +14,6 @@
#include "sanitizer_common.h"
#include "sanitizer_platform.h"
-
-/* TARGET_OS_OSX is not present in SDKs before Darwin16 (macOS 10.12) use
- TARGET_OS_MAC (we have no support for iOS in any form for these versions,
- so there's no ambiguity). */
-#if !defined(TARGET_OS_OSX) && TARGET_OS_MAC
-# define TARGET_OS_OSX 1
-#endif
-
-/* Other TARGET_OS_xxx are not present on earlier versions, define them to
- 0 (we have no support for them; they are not valid targets anyway). */
-#ifndef TARGET_OS_IOS
-#define TARGET_OS_IOS 0
-#endif
-#ifndef TARGET_OS_TV
-#define TARGET_OS_TV 0
-#endif
-#ifndef TARGET_OS_WATCH
-#define TARGET_OS_WATCH 0
-#endif
-
#if SANITIZER_APPLE
#include "sanitizer_posix.h"
@@ -31,6 +31,10 @@ struct __sanitizer_struct_mallinfo {
int v[10];
};
+struct __sanitizer_struct_mallinfo2 {
+ uptr v[10];
+};
+
#endif
} // namespace __sanitizer
@@ -212,8 +212,10 @@ struct InternalDeadlockDetector {
return initialized > 0;
}
};
-
-static THREADLOCAL InternalDeadlockDetector deadlock_detector;
+// This variable is used by the __tls_get_addr interceptor, so cannot use the
+// global-dynamic TLS model, as that would result in crashes.
+__attribute__((tls_model("initial-exec"))) static THREADLOCAL
+ InternalDeadlockDetector deadlock_detector;
void CheckedMutex::LockImpl(uptr pc) { deadlock_detector.Lock(type_, pc); }
@@ -17,8 +17,6 @@
#include "sanitizer_internal_defs.h"
-inline void *operator new(__sanitizer::operator_new_size_type sz, void *p) {
- return p;
-}
+inline void *operator new(__sanitizer::usize sz, void *p) { return p; }
#endif // SANITIZER_PLACEMENT_NEW_H
@@ -260,6 +260,17 @@
# define SANITIZER_ARM64 0
#endif
+#if SANITIZER_WINDOWS64 && SANITIZER_ARM64
+# define SANITIZER_WINDOWS_ARM64 1
+# define SANITIZER_WINDOWS_x64 0
+#elif SANITIZER_WINDOWS64 && !SANITIZER_ARM64
+# define SANITIZER_WINDOWS_ARM64 0
+# define SANITIZER_WINDOWS_x64 1
+#else
+# define SANITIZER_WINDOWS_ARM64 0
+# define SANITIZER_WINDOWS_x64 0
+#endif
+
#if SANITIZER_SOLARIS && SANITIZER_WORDSIZE == 32
# define SANITIZER_SOLARIS32 1
#else
@@ -284,8 +295,8 @@
// For such platforms build this code with -DSANITIZER_CAN_USE_ALLOCATOR64=0 or
// change the definition of SANITIZER_CAN_USE_ALLOCATOR64 here.
#ifndef SANITIZER_CAN_USE_ALLOCATOR64
-# if (SANITIZER_RISCV64 && !SANITIZER_FUCHSIA) || SANITIZER_IOS || \
- SANITIZER_DRIVERKIT
+# if (SANITIZER_RISCV64 && !SANITIZER_FUCHSIA && !SANITIZER_LINUX) || \
+ SANITIZER_IOS || SANITIZER_DRIVERKIT
# define SANITIZER_CAN_USE_ALLOCATOR64 0
# elif defined(__mips64) || defined(__hexagon__)
# define SANITIZER_CAN_USE_ALLOCATOR64 0
@@ -311,7 +322,7 @@
# if SANITIZER_FUCHSIA
# define SANITIZER_MMAP_RANGE_SIZE (1ULL << 38)
# else
-# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47)
+# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 56)
# endif
#elif defined(__aarch64__)
# if SANITIZER_APPLE
@@ -84,6 +84,25 @@
#define SI_NOT_MAC 1
#endif
+#if SANITIZER_APPLE
+# include <Availability.h>
+
+// aligned_alloc was introduced in OSX 10.15
+// Linking will fail when using an older SDK
+# if defined(__MAC_10_15)
+// macOS 10.15 is greater than our minimal deployment target. To ensure we
+// generate a weak reference so the dylib continues to work on older
+// systems, we need to forward declare the intercepted function as "weak
+// imports".
+SANITIZER_WEAK_IMPORT void *aligned_alloc(__sanitizer::usize __alignment,
+ __sanitizer::usize __size);
+# define SI_MAC_SDK_10_15_AVAILABLE 1
+# else
+# define SI_MAC_SDK_10_15_AVAILABLE 0
+# endif // defined(__MAC_10_15)
+
+#endif // SANITIZER_APPLE
+
#if SANITIZER_IOS
#define SI_IOS 1
#else
@@ -183,6 +202,11 @@
#define SANITIZER_INTERCEPT_FPUTS SI_POSIX
#define SANITIZER_INTERCEPT_PUTS SI_POSIX
+#define SANITIZER_INTERCEPT_CREAT64 (SI_GLIBC || SI_SOLARIS32)
+#define SANITIZER_INTERCEPT_FCNTL64 (SI_GLIBC || SI_SOLARIS32)
+#define SANITIZER_INTERCEPT_OPEN64 (SI_GLIBC || SI_SOLARIS32)
+#define SANITIZER_INTERCEPT_OPENAT64 (SI_GLIBC || SI_SOLARIS32)
+
#define SANITIZER_INTERCEPT_PREAD64 (SI_GLIBC || SI_SOLARIS32)
#define SANITIZER_INTERCEPT_PWRITE64 (SI_GLIBC || SI_SOLARIS32)
@@ -191,7 +215,8 @@
#define SANITIZER_INTERCEPT_PREADV \
(SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID)
-#define SANITIZER_INTERCEPT_PWRITEV SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PWRITEV \
+ (SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID)
#define SANITIZER_INTERCEPT_PREADV64 SI_GLIBC
#define SANITIZER_INTERCEPT_PWRITEV64 SI_GLIBC
@@ -273,8 +298,9 @@
#if SI_LINUX_NOT_ANDROID && \
(defined(__i386) || defined(__x86_64) || defined(__mips64) || \
defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \
- defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64)
-#define SANITIZER_INTERCEPT_PTRACE 1
+ defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64 || \
+ defined(__sparc__))
+# define SANITIZER_INTERCEPT_PTRACE 1
#else
#define SANITIZER_INTERCEPT_PTRACE 0
#endif
@@ -301,7 +327,8 @@
#define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME (SI_GLIBC || SI_SOLARIS)
#define SANITIZER_INTERCEPT_CONFSTR \
(SI_FREEBSD || SI_NETBSD || SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
-#define SANITIZER_INTERCEPT_SCHED_GETAFFINITY SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SCHED_GETAFFINITY \
+ (SI_LINUX_NOT_ANDROID || SI_FREEBSD)
#define SANITIZER_INTERCEPT_SCHED_GETPARAM SI_LINUX_NOT_ANDROID || SI_SOLARIS
#define SANITIZER_INTERCEPT_STRERROR SI_POSIX
#define SANITIZER_INTERCEPT_STRERROR_R SI_POSIX
@@ -462,7 +489,7 @@
(SI_LINUX || SI_MAC || SI_WINDOWS || SI_FREEBSD || SI_NETBSD || SI_SOLARIS)
#define SANITIZER_INTERCEPT_RECV_RECVFROM SI_POSIX
#define SANITIZER_INTERCEPT_SEND_SENDTO SI_POSIX
-#define SANITIZER_INTERCEPT_EVENTFD_READ_WRITE SI_LINUX
+#define SANITIZER_INTERCEPT_EVENTFD_READ_WRITE (SI_LINUX || SI_FREEBSD)
#define SI_STAT_LINUX (SI_LINUX && __GLIBC_PREREQ(2, 33))
#define SANITIZER_INTERCEPT_STAT \
@@ -492,7 +519,8 @@
#define SANITIZER_INTERCEPT_PVALLOC (SI_GLIBC || SI_ANDROID)
#define SANITIZER_INTERCEPT_CFREE (SI_GLIBC && !SANITIZER_RISCV64)
#define SANITIZER_INTERCEPT_REALLOCARRAY SI_POSIX
-#define SANITIZER_INTERCEPT_ALIGNED_ALLOC (!SI_MAC)
+#define SANITIZER_INTERCEPT_ALIGNED_ALLOC \
+ (!SI_MAC || SI_MAC_SDK_10_15_AVAILABLE)
#define SANITIZER_INTERCEPT_MALLOC_USABLE_SIZE (!SI_MAC && !SI_NETBSD)
#define SANITIZER_INTERCEPT_MCHECK_MPROBE SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_WCSLEN 1
@@ -557,10 +585,8 @@
#define SANITIZER_INTERCEPT_SHA1 SI_NETBSD
#define SANITIZER_INTERCEPT_MD4 SI_NETBSD
#define SANITIZER_INTERCEPT_RMD160 SI_NETBSD
-#define SANITIZER_INTERCEPT_MD5 (SI_NETBSD || SI_FREEBSD)
#define SANITIZER_INTERCEPT_FSEEK (SI_NETBSD || SI_FREEBSD)
#define SANITIZER_INTERCEPT_MD2 SI_NETBSD
-#define SANITIZER_INTERCEPT_SHA2 (SI_NETBSD || SI_FREEBSD)
#define SANITIZER_INTERCEPT_CDB SI_NETBSD
#define SANITIZER_INTERCEPT_VIS (SI_NETBSD || SI_FREEBSD)
#define SANITIZER_INTERCEPT_POPEN SI_POSIX
@@ -594,10 +620,18 @@
#define SANITIZER_INTERCEPT___XUNAME SI_FREEBSD
#define SANITIZER_INTERCEPT_FLOPEN SI_FREEBSD
#define SANITIZER_INTERCEPT_PROCCTL SI_FREEBSD
-#define SANITIZER_INTERCEPT_HEXDUMP SI_FREEBSD
#define SANITIZER_INTERCEPT_ARGP_PARSE SI_GLIBC
#define SANITIZER_INTERCEPT_CPUSET_GETAFFINITY SI_FREEBSD
-
+// FIXME: also available from musl 1.2.5
+#define SANITIZER_INTERCEPT_PREADV2 (SI_LINUX && __GLIBC_PREREQ(2, 26))
+#define SANITIZER_INTERCEPT_PWRITEV2 (SI_LINUX && __GLIBC_PREREQ(2, 26))
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \
+ __MAC_OS_X_VERSION_MIN_REQUIRED >= 130000
+# define SI_MAC_OS_DEPLOYMENT_MIN_13_00 1
+#else
+# define SI_MAC_OS_DEPLOYMENT_MIN_13_00 0
+#endif
+#define SANITIZER_INTERCEPT_FREADLINK (SI_MAC && SI_MAC_OS_DEPLOYMENT_MIN_13_00)
// This macro gives a way for downstream users to override the above
// interceptor macros irrespective of the platform they are on. They have
// to do two things:
@@ -475,6 +475,8 @@ CHECK_TYPE_SIZE(nfds_t);
CHECK_TYPE_SIZE(sigset_t);
COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction));
+COMPILER_CHECK(sizeof(__sanitizer_siginfo) == sizeof(siginfo_t));
+CHECK_SIZE_AND_OFFSET(siginfo_t, si_value);
// Can't write checks for sa_handler and sa_sigaction due to them being
// preprocessor macros.
CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
@@ -301,11 +301,29 @@ struct __sanitizer_sigset_t {
typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t;
+union __sanitizer_sigval {
+ int sival_int;
+ void *sival_ptr;
+};
+
struct __sanitizer_siginfo {
- // The size is determined by looking at sizeof of real siginfo_t on linux.
- u64 opaque[128 / sizeof(u64)];
+ int si_signo;
+ int si_errno;
+ int si_code;
+ pid_t si_pid;
+ u32 si_uid;
+ int si_status;
+ void *si_addr;
+ union __sanitizer_sigval si_value;
+# if SANITIZER_WORDSIZE == 64
+ char data[40];
+# else
+ char data[32];
+# endif
};
+typedef __sanitizer_siginfo __sanitizer_siginfo_t;
+
using __sanitizer_sighandler_ptr = void (*)(int sig);
using __sanitizer_sigactionhandler_ptr = void (*)(int sig,
__sanitizer_siginfo *siginfo,
@@ -726,6 +744,8 @@ struct __sanitizer_cpuset {
typedef struct __sanitizer_cpuset __sanitizer_cpuset_t;
extern unsigned struct_cpuset_sz;
+
+typedef unsigned long long __sanitizer_eventfd_t;
} // namespace __sanitizer
# define CHECK_TYPE_SIZE(TYPE) \
@@ -26,10 +26,7 @@
// With old kernels (and even new kernels on powerpc) asm/stat.h uses types that
// are not defined anywhere in userspace headers. Fake them. This seems to work
-// fine with newer headers, too. Beware that with <sys/stat.h>, struct stat
-// takes the form of struct stat64 on 32-bit platforms if _FILE_OFFSET_BITS=64.
-// Also, for some platforms (e.g. mips) there are additional members in the
-// <sys/stat.h> struct stat:s.
+// fine with newer headers, too.
#include <linux/posix_types.h>
# if defined(__x86_64__) || defined(__mips__) || defined(__hexagon__)
# include <sys/stat.h>
deleted file mode 100644
deleted file mode 100644
@@ -94,8 +94,9 @@
#if SANITIZER_LINUX
# include <utime.h>
# include <sys/ptrace.h>
-# if defined(__mips64) || defined(__aarch64__) || defined(__arm__) || \
- defined(__hexagon__) || defined(__loongarch__) ||SANITIZER_RISCV64
+# if defined(__mips64) || defined(__aarch64__) || defined(__arm__) || \
+ defined(__hexagon__) || defined(__loongarch__) || SANITIZER_RISCV64 || \
+ defined(__sparc__)
# include <asm/ptrace.h>
# ifdef __arm__
typedef struct user_fpregs elf_fpregset_t;
@@ -117,15 +118,16 @@ typedef struct user_fpregs elf_fpregset_t;
#if SANITIZER_LINUX
#if SANITIZER_GLIBC
#include <fstab.h>
-#include <net/if_ppp.h>
-#include <netax25/ax25.h>
-#include <netipx/ipx.h>
-#include <netrom/netrom.h>
-#include <obstack.h>
-#if HAVE_RPC_XDR_H
-# include <rpc/xdr.h>
-#endif
-#include <scsi/scsi.h>
+# include <linux/filter.h>
+# include <net/if_ppp.h>
+# include <netax25/ax25.h>
+# include <netipx/ipx.h>
+# include <netrom/netrom.h>
+# include <obstack.h>
+# if HAVE_RPC_XDR_H
+# include <rpc/xdr.h>
+# endif
+# include <scsi/scsi.h>
#else
#include <linux/if_ppp.h>
#include <linux/kd.h>
@@ -358,11 +360,12 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
const int wordexp_wrde_dooffs = WRDE_DOOFFS;
# endif // !SANITIZER_ANDROID
-#if SANITIZER_LINUX && !SANITIZER_ANDROID && \
- (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
- defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \
- defined(__s390__) || defined(__loongarch__)|| SANITIZER_RISCV64)
-#if defined(__mips64) || defined(__powerpc64__) || defined(__arm__)
+# if SANITIZER_LINUX && !SANITIZER_ANDROID && \
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \
+ defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64 || \
+ defined(__sparc__))
+# if defined(__mips64) || defined(__powerpc64__) || defined(__arm__)
unsigned struct_user_regs_struct_sz = sizeof(struct pt_regs);
unsigned struct_user_fpregs_struct_sz = sizeof(elf_fpregset_t);
#elif SANITIZER_RISCV64
@@ -377,19 +380,22 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
#elif defined(__s390__)
unsigned struct_user_regs_struct_sz = sizeof(struct _user_regs_struct);
unsigned struct_user_fpregs_struct_sz = sizeof(struct _user_fpregs_struct);
-#else
+# elif defined(__sparc__)
+ unsigned struct_user_regs_struct_sz = sizeof(struct sunos_regs);
+ unsigned struct_user_fpregs_struct_sz = sizeof(struct sunos_fp);
+# else
unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct);
unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct);
-#endif // __mips64 || __powerpc64__ || __aarch64__ || __loongarch__
-#if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) || \
- defined(__aarch64__) || defined(__arm__) || defined(__s390__) || \
- defined(__loongarch__) || SANITIZER_RISCV64
+# endif // __mips64 || __powerpc64__ || __aarch64__ || __loongarch__
+# if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) || \
+ defined(__aarch64__) || defined(__arm__) || defined(__s390__) || \
+ defined(__loongarch__) || SANITIZER_RISCV64 || defined(__sparc__)
unsigned struct_user_fpxregs_struct_sz = 0;
#else
unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct);
#endif // __x86_64 || __mips64 || __powerpc64__ || __aarch64__ || __arm__
-// || __s390__ || __loongarch__
-#ifdef __arm__
+ // || __s390__ || __loongarch__ || SANITIZER_RISCV64 || __sparc__
+# ifdef __arm__
unsigned struct_user_vfpregs_struct_sz = ARM_VFPREGS_SIZE;
#else
unsigned struct_user_vfpregs_struct_sz = 0;
@@ -531,9 +537,10 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info);
unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats);
-#endif // SANITIZER_GLIBC
+ unsigned struct_sock_fprog_sz = sizeof(struct sock_fprog);
+# endif // SANITIZER_GLIBC
-#if !SANITIZER_ANDROID && !SANITIZER_APPLE
+# if !SANITIZER_ANDROID && !SANITIZER_APPLE
unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req);
unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
#endif
@@ -523,6 +523,7 @@ typedef long __sanitizer_clock_t;
#if SANITIZER_LINUX
typedef int __sanitizer_clockid_t;
+typedef unsigned long long __sanitizer_eventfd_t;
#endif
#if SANITIZER_LINUX
@@ -854,10 +855,11 @@ typedef void __sanitizer_FILE;
# define SANITIZER_HAS_STRUCT_FILE 0
#endif
-#if SANITIZER_LINUX && !SANITIZER_ANDROID && \
- (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
- defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \
- defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64)
+# if SANITIZER_LINUX && !SANITIZER_ANDROID && \
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \
+ defined(__s390__) || defined(__loongarch__) || SANITIZER_RISCV64 || \
+ defined(__sparc__))
extern unsigned struct_user_regs_struct_sz;
extern unsigned struct_user_fpregs_struct_sz;
extern unsigned struct_user_fpxregs_struct_sz;
@@ -879,9 +881,24 @@ extern int ptrace_setsiginfo;
extern int ptrace_getregset;
extern int ptrace_setregset;
extern int ptrace_geteventmsg;
-#endif
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
+// Helper for the ptrace interceptor.
+template <class T>
+inline T ptrace_data_arg(int request, T addr, T data) {
+# if SANITIZER_LINUX && SANITIZER_SPARC
+ // As described in ptrace(2), the meanings of addr and data are reversed
+ // for the PTRACE_GETREGS, PTRACE_GETFPREGS, PTRACE_GETREGS, and
+ // PTRACE_GETFPREGS requests on Linux/sparc64.
+ if (request == ptrace_getregs || request == ptrace_getfpregs ||
+ request == ptrace_setregs || request == ptrace_setfpregs)
+ return addr;
+ else
+# endif
+ return data;
+}
+# endif
+
+# if SANITIZER_LINUX && !SANITIZER_ANDROID
extern unsigned struct_shminfo_sz;
extern unsigned struct_shm_info_sz;
extern int shmctl_ipc_stat;
@@ -1049,7 +1066,8 @@ extern unsigned struct_serial_struct_sz;
extern unsigned struct_sockaddr_ax25_sz;
extern unsigned struct_unimapdesc_sz;
extern unsigned struct_unimapinit_sz;
-#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+extern unsigned struct_sock_fprog_sz;
+# endif // SANITIZER_LINUX && !SANITIZER_ANDROID
extern const unsigned long __sanitizer_bufsiz;
@@ -54,12 +54,12 @@ void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
return (void *)res;
}
-void UnmapOrDie(void *addr, uptr size) {
+void UnmapOrDie(void *addr, uptr size, bool raw_report) {
if (!addr || !size) return;
uptr res = internal_munmap(addr, size);
int reserrno;
if (UNLIKELY(internal_iserror(res, &reserrno)))
- ReportMunmapFailureAndDie(addr, size, reserrno);
+ ReportMunmapFailureAndDie(addr, size, reserrno, raw_report);
DecreaseTotalMmap(size);
}
@@ -85,8 +85,8 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
CHECK(IsPowerOfTwo(size));
CHECK(IsPowerOfTwo(alignment));
uptr map_size = size + alignment;
- // mmap maps entire pages and rounds up map_size needs to be a an integral
- // number of pages.
+ // mmap maps entire pages and rounds up map_size needs to be a an integral
+ // number of pages.
// We need to be aware of this size for calculating end and for unmapping
// fragments before and after the alignment region.
map_size = RoundUpTo(map_size, GetPageSizeCached());
@@ -130,8 +130,8 @@ static void *MmapFixedImpl(uptr fixed_addr, uptr size, bool tolerate_enomem,
if (tolerate_enomem && reserrno == ENOMEM)
return nullptr;
char mem_type[40];
- internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx",
- fixed_addr);
+ internal_snprintf(mem_type, sizeof(mem_type), "memory at address %p",
+ (void *)fixed_addr);
ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
}
IncreaseTotalMmap(size);
@@ -353,7 +353,15 @@ bool ShouldMockFailureToOpen(const char *path) {
internal_strncmp(path, "/proc/", 6) == 0;
}
-#if SANITIZER_LINUX && !SANITIZER_ANDROID && !SANITIZER_GO
+bool OpenReadsVaArgs(int oflag) {
+# ifdef O_TMPFILE
+ return (oflag & (O_CREAT | O_TMPFILE)) != 0;
+# else
+ return (oflag & O_CREAT) != 0;
+# endif
+}
+
+# if SANITIZER_LINUX && !SANITIZER_ANDROID && !SANITIZER_GO
int GetNamedMappingFd(const char *name, uptr size, int *flags) {
if (!common_flags()->decorate_proc_maps || !name)
return -1;
@@ -28,6 +28,9 @@ namespace __sanitizer {
// Don't use directly, use __sanitizer::OpenFile() instead.
uptr internal_open(const char *filename, int flags);
uptr internal_open(const char *filename, int flags, u32 mode);
+# if SANITIZER_FREEBSD
+uptr internal_close_range(fd_t lowfd, fd_t highfd, int flags);
+# endif
uptr internal_close(fd_t fd);
uptr internal_read(fd_t fd, void *buf, uptr count);
@@ -74,21 +77,21 @@ int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
// These functions call appropriate pthread_ functions directly, bypassing
// the interceptor. They are weak and may not be present in some tools.
SANITIZER_WEAK_ATTRIBUTE
-int real_pthread_create(void *th, void *attr, void *(*callback)(void *),
- void *param);
+int internal_pthread_create(void *th, void *attr, void *(*callback)(void *),
+ void *param);
SANITIZER_WEAK_ATTRIBUTE
-int real_pthread_join(void *th, void **ret);
-
-#define DEFINE_REAL_PTHREAD_FUNCTIONS \
- namespace __sanitizer { \
- int real_pthread_create(void *th, void *attr, void *(*callback)(void *), \
- void *param) { \
- return REAL(pthread_create)(th, attr, callback, param); \
- } \
- int real_pthread_join(void *th, void **ret) { \
- return REAL(pthread_join(th, ret)); \
- } \
- } // namespace __sanitizer
+int internal_pthread_join(void *th, void **ret);
+
+# define DEFINE_INTERNAL_PTHREAD_FUNCTIONS \
+ namespace __sanitizer { \
+ int internal_pthread_create(void *th, void *attr, \
+ void *(*callback)(void *), void *param) { \
+ return REAL(pthread_create)(th, attr, callback, param); \
+ } \
+ int internal_pthread_join(void *th, void **ret) { \
+ return REAL(pthread_join)(th, ret); \
+ } \
+ } // namespace __sanitizer
int internal_pthread_attr_getstack(void *attr, void **addr, uptr *size);
@@ -108,6 +111,7 @@ bool IsStateDetached(int state);
fd_t ReserveStandardFds(fd_t fd);
bool ShouldMockFailureToOpen(const char *path);
+bool OpenReadsVaArgs(int oflag);
// Create a non-file mapping with a given /proc/self/maps name.
uptr MmapNamed(void *addr, uptr length, int prot, int flags, const char *name);
@@ -91,12 +91,12 @@ static rlim_t getlim(int res) {
static void setlim(int res, rlim_t lim) {
struct rlimit rlim;
- if (getrlimit(res, const_cast<struct rlimit *>(&rlim))) {
+ if (getrlimit(res, &rlim)) {
Report("ERROR: %s getrlimit() failed %d\n", SanitizerToolName, errno);
Die();
}
rlim.rlim_cur = lim;
- if (setrlimit(res, const_cast<struct rlimit *>(&rlim))) {
+ if (setrlimit(res, &rlim)) {
Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno);
Die();
}
@@ -104,7 +104,27 @@ static void setlim(int res, rlim_t lim) {
void DisableCoreDumperIfNecessary() {
if (common_flags()->disable_coredump) {
- setlim(RLIMIT_CORE, 0);
+ rlimit rlim;
+ CHECK_EQ(0, getrlimit(RLIMIT_CORE, &rlim));
+ // On Linux, if the kernel.core_pattern sysctl starts with a '|' (i.e. it
+ // is being piped to a coredump handler such as systemd-coredumpd), the
+ // kernel ignores RLIMIT_CORE (since we aren't creating a file in the file
+ // system) except for the magic value of 1, which disables coredumps when
+ // piping. 1 byte is too small for any kind of valid core dump, so it
+ // also disables coredumps if kernel.core_pattern creates files directly.
+ // While most piped coredump handlers do respect the crashing processes'
+ // RLIMIT_CORE, this is notable not the case for Debian's systemd-coredump
+ // due to a local patch that changes sysctl.d/50-coredump.conf to ignore
+ // the specified limit and instead use RLIM_INFINITY.
+ //
+ // The alternative to using RLIMIT_CORE=1 would be to use prctl() with the
+ // PR_SET_DUMPABLE flag, however that also prevents ptrace(), so makes it
+ // impossible to attach a debugger.
+ //
+ // Note: we use rlim_max in the Min() call here since that is the upper
+ // limit for what can be set without getting an EINVAL error.
+ rlim.rlim_cur = Min<rlim_t>(SANITIZER_LINUX ? 1 : 0, rlim.rlim_max);
+ CHECK_EQ(0, setrlimit(RLIMIT_CORE, &rlim));
}
}
@@ -268,26 +288,86 @@ bool SignalContext::IsStackOverflow() const {
#endif // SANITIZER_GO
+static void SetNonBlock(int fd) {
+ int res = fcntl(fd, F_GETFL, 0);
+ CHECK(!internal_iserror(res, nullptr));
+
+ res |= O_NONBLOCK;
+ res = fcntl(fd, F_SETFL, res);
+ CHECK(!internal_iserror(res, nullptr));
+}
+
bool IsAccessibleMemoryRange(uptr beg, uptr size) {
- uptr page_size = GetPageSizeCached();
- // Checking too large memory ranges is slow.
- CHECK_LT(size, page_size * 10);
- int sock_pair[2];
- if (pipe(sock_pair))
- return false;
- uptr bytes_written =
- internal_write(sock_pair[1], reinterpret_cast<void *>(beg), size);
- int write_errno;
- bool result;
- if (internal_iserror(bytes_written, &write_errno)) {
- CHECK_EQ(EFAULT, write_errno);
- result = false;
- } else {
- result = (bytes_written == size);
+ while (size) {
+ // `read` from `fds[0]` into a dummy buffer to free up the pipe buffer for
+ // more `write` is slower than just recreating a pipe.
+ int fds[2];
+ CHECK_EQ(0, pipe(fds));
+
+ auto cleanup = at_scope_exit([&]() {
+ internal_close(fds[0]);
+ internal_close(fds[1]);
+ });
+
+ SetNonBlock(fds[1]);
+
+ int write_errno;
+ uptr w = internal_write(fds[1], reinterpret_cast<char *>(beg), size);
+ if (internal_iserror(w, &write_errno)) {
+ if (write_errno == EINTR)
+ continue;
+ CHECK_EQ(EFAULT, write_errno);
+ return false;
+ }
+ size -= w;
+ beg += w;
+ }
+
+ return true;
+}
+
+bool TryMemCpy(void *dest, const void *src, uptr n) {
+ if (!n)
+ return true;
+ int fds[2];
+ CHECK_EQ(0, pipe(fds));
+
+ auto cleanup = at_scope_exit([&]() {
+ internal_close(fds[0]);
+ internal_close(fds[1]);
+ });
+
+ SetNonBlock(fds[0]);
+ SetNonBlock(fds[1]);
+
+ char *d = static_cast<char *>(dest);
+ const char *s = static_cast<const char *>(src);
+
+ while (n) {
+ int e;
+ uptr w = internal_write(fds[1], s, n);
+ if (internal_iserror(w, &e)) {
+ if (e == EINTR)
+ continue;
+ CHECK_EQ(EFAULT, e);
+ return false;
+ }
+ s += w;
+ n -= w;
+
+ while (w) {
+ uptr r = internal_read(fds[0], d, w);
+ if (internal_iserror(r, &e)) {
+ CHECK_EQ(EINTR, e);
+ continue;
+ }
+
+ d += r;
+ w -= r;
+ }
}
- internal_close(sock_pair[0]);
- internal_close(sock_pair[1]);
- return result;
+
+ return true;
}
void PlatformPrepareForSandboxing(void *args) {
@@ -307,9 +387,10 @@ static bool MmapFixed(uptr fixed_addr, uptr size, int additional_flags,
MAP_PRIVATE | MAP_FIXED | additional_flags | MAP_ANON, name);
int reserrno;
if (internal_iserror(p, &reserrno)) {
- Report("ERROR: %s failed to "
- "allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n",
- SanitizerToolName, size, size, fixed_addr, reserrno);
+ Report(
+ "ERROR: %s failed to "
+ "allocate 0x%zx (%zd) bytes at address %p (errno: %d)\n",
+ SanitizerToolName, size, size, (void *)fixed_addr, reserrno);
return false;
}
IncreaseTotalMmap(size);
@@ -462,7 +543,11 @@ pid_t StartSubprocess(const char *program, const char *const argv[],
internal_close(stderr_fd);
}
+# if SANITIZER_FREEBSD
+ internal_close_range(3, ~static_cast<fd_t>(0), 0);
+# else
for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--) internal_close(fd);
+# endif
internal_execve(program, const_cast<char **>(&argv[0]),
const_cast<char *const *>(envp));
@@ -54,7 +54,7 @@ static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value,
uptr num_buffer[kMaxLen];
int pos = 0;
do {
- RAW_CHECK_MSG((uptr)pos < kMaxLen, "AppendNumber buffer overflow");
+ RAW_CHECK_MSG((uptr)pos < kMaxLen, "AppendNumber buffer overflow",);
num_buffer[pos++] = absolute_value % base;
absolute_value /= base;
} while (absolute_value > 0);
@@ -13,9 +13,6 @@
#include "sanitizer_platform.h"
#if SANITIZER_FREEBSD || SANITIZER_NETBSD
#include "sanitizer_common.h"
-#if SANITIZER_FREEBSD
-#include "sanitizer_freebsd.h"
-#endif
#include "sanitizer_procmaps.h"
// clang-format off
@@ -29,29 +26,35 @@
#include <limits.h>
-// Fix 'kinfo_vmentry' definition on FreeBSD prior v9.2 in 32-bit mode.
-#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
-#include <osreldate.h>
-#if __FreeBSD_version <= 902001 // v9.2
-#define kinfo_vmentry xkinfo_vmentry
-#endif
-#endif
-
namespace __sanitizer {
#if SANITIZER_FREEBSD
void GetMemoryProfile(fill_profile_f cb, uptr *stats) {
- const int Mib[] = {
- CTL_KERN,
- KERN_PROC,
- KERN_PROC_PID,
- getpid()
- };
-
- struct kinfo_proc InfoProc;
- uptr Len = sizeof(InfoProc);
- CHECK_EQ(internal_sysctl(Mib, ARRAY_SIZE(Mib), nullptr, (uptr *)&InfoProc, &Len, 0), 0);
- cb(0, InfoProc.ki_rssize * GetPageSizeCached(), false, stats);
+ const int Mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
+
+ struct kinfo_proc *InfoProc;
+ uptr Len = sizeof(*InfoProc);
+ uptr Size = Len;
+ InfoProc = (struct kinfo_proc *)MmapOrDie(Size, "GetMemoryProfile()");
+ CHECK_EQ(
+ internal_sysctl(Mib, ARRAY_SIZE(Mib), nullptr, (uptr *)InfoProc, &Len, 0),
+ 0);
+ cb(0, InfoProc->ki_rssize * GetPageSizeCached(), false, stats);
+ UnmapOrDie(InfoProc, Size, true);
+}
+#elif SANITIZER_NETBSD
+void GetMemoryProfile(fill_profile_f cb, uptr *stats) {
+ struct kinfo_proc2 *InfoProc;
+ uptr Len = sizeof(*InfoProc);
+ uptr Size = Len;
+ const int Mib[] = {CTL_KERN, KERN_PROC2, KERN_PROC_PID,
+ getpid(), (int)Size, 1};
+ InfoProc = (struct kinfo_proc2 *)MmapOrDie(Size, "GetMemoryProfile()");
+ CHECK_EQ(
+ internal_sysctl(Mib, ARRAY_SIZE(Mib), nullptr, (uptr *)InfoProc, &Len, 0),
+ 0);
+ cb(0, InfoProc->p_vm_rssize * GetPageSizeCached(), false, stats);
+ UnmapOrDie(InfoProc, Size, true);
}
#endif
@@ -145,7 +145,7 @@ void MemoryMappingLayout::DumpListOfModules(
}
}
-#if SANITIZER_LINUX || SANITIZER_ANDROID || SANITIZER_SOLARIS || SANITIZER_NETBSD
+#if SANITIZER_LINUX || SANITIZER_ANDROID || SANITIZER_SOLARIS
void GetMemoryProfile(fill_profile_f cb, uptr *stats) {
char *smaps = nullptr;
uptr smaps_cap = 0;
@@ -146,13 +146,8 @@ static bool IsDyldHdr(const mach_header *hdr) {
// until we hit a Mach header matching dyld instead. These recurse
// calls are expensive, but the first memory map generation occurs
// early in the process, when dyld is one of the only images loaded,
-// so it will be hit after only a few iterations. These assumptions don't
-// hold on macOS 13+ anymore (dyld itself has moved into the shared cache).
-
-// FIXME: Unfortunately, the upstream revised version to deal with macOS 13+
-// is incompatible with GCC and also uses APIs not available on earlier
-// systems which we support; backed out for now.
-
+// so it will be hit after only a few iterations. These assumptions don't hold
+// on macOS 13+ anymore (dyld itself has moved into the shared cache).
static mach_header *GetDyldImageHeaderViaVMRegion() {
vm_address_t address = 0;
@@ -176,17 +171,64 @@ static mach_header *GetDyldImageHeaderViaVMRegion() {
}
}
+extern "C" {
+struct dyld_shared_cache_dylib_text_info {
+ uint64_t version; // current version 2
+ // following fields all exist in version 1
+ uint64_t loadAddressUnslid;
+ uint64_t textSegmentSize;
+ uuid_t dylibUuid;
+ const char *path; // pointer invalid at end of iterations
+ // following fields all exist in version 2
+ uint64_t textSegmentOffset; // offset from start of cache
+};
+typedef struct dyld_shared_cache_dylib_text_info
+ dyld_shared_cache_dylib_text_info;
+
+extern bool _dyld_get_shared_cache_uuid(uuid_t uuid);
+extern const void *_dyld_get_shared_cache_range(size_t *length);
+extern int dyld_shared_cache_iterate_text(
+ const uuid_t cacheUuid,
+ void (^callback)(const dyld_shared_cache_dylib_text_info *info));
+} // extern "C"
+
+static mach_header *GetDyldImageHeaderViaSharedCache() {
+ uuid_t uuid;
+ bool hasCache = _dyld_get_shared_cache_uuid(uuid);
+ if (!hasCache)
+ return nullptr;
+
+ size_t cacheLength;
+ __block uptr cacheStart = (uptr)_dyld_get_shared_cache_range(&cacheLength);
+ CHECK(cacheStart && cacheLength);
+
+ __block mach_header *dyldHdr = nullptr;
+ int res = dyld_shared_cache_iterate_text(
+ uuid, ^(const dyld_shared_cache_dylib_text_info *info) {
+ CHECK_GE(info->version, 2);
+ mach_header *hdr =
+ (mach_header *)(cacheStart + info->textSegmentOffset);
+ if (IsDyldHdr(hdr))
+ dyldHdr = hdr;
+ });
+ CHECK_EQ(res, 0);
+
+ return dyldHdr;
+}
+
const mach_header *get_dyld_hdr() {
if (!dyld_hdr) {
// On macOS 13+, dyld itself has moved into the shared cache. Looking it up
// via vm_region_recurse_64() causes spins/hangs/crashes.
- // FIXME: find a way to do this compatible with GCC.
if (GetMacosAlignedVersion() >= MacosVersion(13, 0)) {
+ dyld_hdr = GetDyldImageHeaderViaSharedCache();
+ if (!dyld_hdr) {
VReport(1,
- "looking up the dyld image header in the shared cache on "
- "macOS 13+ is not yet supported. Falling back to "
+ "Failed to lookup the dyld image header in the shared cache on "
+ "macOS 13+ (or no shared cache in use). Falling back to "
"lookup via vm_region_recurse_64().\n");
dyld_hdr = GetDyldImageHeaderViaVMRegion();
+ }
} else {
dyld_hdr = GetDyldImageHeaderViaVMRegion();
}
@@ -391,7 +433,9 @@ void MemoryMappingLayout::DumpListOfModules(
MemoryMappedSegmentData data;
segment.data_ = &data;
while (Next(&segment)) {
- if (segment.filename[0] == '\0') continue;
+ // skip the __PAGEZERO segment, its vmsize is 0
+ if (segment.filename[0] == '\0' || (segment.start == segment.end))
+ continue;
LoadedModule *cur_module = nullptr;
if (!modules->empty() &&
0 == internal_strcmp(segment.filename, modules->back().full_name())) {
@@ -11,6 +11,10 @@
// Before Solaris 11.4, <procfs.h> doesn't work in a largefile environment.
#undef _FILE_OFFSET_BITS
+
+// Avoid conflict between `_TIME_BITS` defined vs. `_FILE_OFFSET_BITS`
+// undefined in some Linux configurations.
+#undef _TIME_BITS
#include "sanitizer_platform.h"
#if SANITIZER_SOLARIS
# include <fcntl.h>
@@ -9,31 +9,33 @@
#ifndef SANITIZER_PTRAUTH_H
#define SANITIZER_PTRAUTH_H
-#if __has_feature(ptrauth_calls)
-#include <ptrauth.h>
+#if __has_feature(ptrauth_intrinsics)
+# include <ptrauth.h>
#elif defined(__ARM_FEATURE_PAC_DEFAULT) && !defined(__APPLE__)
-inline unsigned long ptrauth_strip(void* __value, unsigned int __key) {
- // On the stack the link register is protected with Pointer
- // Authentication Code when compiled with -mbranch-protection.
- // Let's stripping the PAC unconditionally because xpaclri is in
- // the NOP space so will do nothing when it is not enabled or not available.
- unsigned long ret;
- asm volatile(
- "mov x30, %1\n\t"
- "hint #7\n\t" // xpaclri
- "mov %0, x30\n\t"
- : "=r"(ret)
- : "r"(__value)
- : "x30");
- return ret;
-}
-#define ptrauth_auth_data(__value, __old_key, __old_data) __value
-#define ptrauth_string_discriminator(__string) ((int)0)
+// On the stack the link register is protected with Pointer
+// Authentication Code when compiled with -mbranch-protection.
+// Let's stripping the PAC unconditionally because xpaclri is in
+// the NOP space so will do nothing when it is not enabled or not available.
+# define ptrauth_strip(__value, __key) \
+ ({ \
+ __typeof(__value) ret; \
+ asm volatile( \
+ "mov x30, %1\n\t" \
+ "hint #7\n\t" \
+ "mov %0, x30\n\t" \
+ "mov x30, xzr\n\t" \
+ : "=r"(ret) \
+ : "r"(__value) \
+ : "x30"); \
+ ret; \
+ })
+# define ptrauth_auth_data(__value, __old_key, __old_data) __value
+# define ptrauth_string_discriminator(__string) ((int)0)
#else
// Copied from <ptrauth.h>
-#define ptrauth_strip(__value, __key) __value
-#define ptrauth_auth_data(__value, __old_key, __old_data) __value
-#define ptrauth_string_discriminator(__string) ((int)0)
+# define ptrauth_strip(__value, __key) __value
+# define ptrauth_auth_data(__value, __old_key, __old_data) __value
+# define ptrauth_string_discriminator(__string) ((int)0)
#endif
#define STRIP_PAC_PC(pc) ((uptr)ptrauth_strip(pc, 0))
@@ -15,11 +15,13 @@
# define SANITIZER_REDEFINE_BUILTINS_H
// The asm hack only works with GCC and Clang.
-# if !defined(_WIN32) && defined(HAVE_AS_SYM_ASSIGN)
+# if !defined(_WIN32)
-asm("memcpy = __sanitizer_internal_memcpy");
-asm("memmove = __sanitizer_internal_memmove");
-asm("memset = __sanitizer_internal_memset");
+asm(R"(
+ .set memcpy, __sanitizer_internal_memcpy
+ .set memmove, __sanitizer_internal_memmove
+ .set memset, __sanitizer_internal_memset
+ )");
# if defined(__cplusplus) && \
!defined(SANITIZER_COMMON_REDEFINE_BUILTINS_IN_STD)
@@ -50,7 +52,7 @@ using vector = Define_SANITIZER_COMMON_NO_REDEFINE_BUILTINS_in_cpp_file;
} // namespace std
# endif // __cpluplus
-# endif // !_WIN32 && HAVE_AS_SYM_ASSIGN
+# endif // !_WIN32
# endif // SANITIZER_REDEFINE_BUILTINS_H
#endif // SANITIZER_COMMON_NO_REDEFINE_BUILTINS
@@ -44,6 +44,9 @@ StackStore::Id StackStore::Store(const StackTrace &trace, uptr *pack) {
uptr idx = 0;
*pack = 0;
uptr *stack_trace = Alloc(h.size + 1, &idx, pack);
+ // No more space.
+ if (stack_trace == nullptr)
+ return 0;
*stack_trace = h.ToUptr();
internal_memcpy(stack_trace + 1, trace.trace, h.size * sizeof(uptr));
*pack += blocks_[GetBlockIdx(idx)].Stored(h.size + 1);
@@ -76,8 +79,10 @@ uptr *StackStore::Alloc(uptr count, uptr *idx, uptr *pack) {
uptr block_idx = GetBlockIdx(start);
uptr last_idx = GetBlockIdx(start + count - 1);
if (LIKELY(block_idx == last_idx)) {
- // Fits into the a single block.
- CHECK_LT(block_idx, ARRAY_SIZE(blocks_));
+ // Fits into a single block.
+ // No more available blocks. Indicate inability to allocate more memory.
+ if (block_idx >= ARRAY_SIZE(blocks_))
+ return nullptr;
*idx = start;
return blocks_[block_idx].GetOrCreate(this) + GetInBlockIdx(start);
}
@@ -215,16 +215,16 @@ StackTrace StackDepotGet(u32 id) {
return theDepot.Get(id);
}
-void StackDepotLockAll() {
- theDepot.LockAll();
+void StackDepotLockBeforeFork() {
+ theDepot.LockBeforeFork();
compress_thread.LockAndStop();
stackStore.LockAll();
}
-void StackDepotUnlockAll() {
+void StackDepotUnlockAfterFork(bool fork_child) {
stackStore.UnlockAll();
compress_thread.Unlock();
- theDepot.UnlockAll();
+ theDepot.UnlockAfterFork(fork_child);
}
void StackDepotPrintAll() {
@@ -39,8 +39,8 @@ StackDepotHandle StackDepotPut_WithHandle(StackTrace stack);
// Retrieves a stored stack trace by the id.
StackTrace StackDepotGet(u32 id);
-void StackDepotLockAll();
-void StackDepotUnlockAll();
+void StackDepotLockBeforeFork();
+void StackDepotUnlockAfterFork(bool fork_child);
void StackDepotPrintAll();
void StackDepotStopBackgroundThread();
@@ -52,8 +52,8 @@ class StackDepotBase {
};
}
- void LockAll();
- void UnlockAll();
+ void LockBeforeFork();
+ void UnlockAfterFork(bool fork_child);
void PrintAll();
void TestOnlyUnmap() {
@@ -160,18 +160,33 @@ StackDepotBase<Node, kReservedBits, kTabSizeLog>::Get(u32 id) {
}
template <class Node, int kReservedBits, int kTabSizeLog>
-void StackDepotBase<Node, kReservedBits, kTabSizeLog>::LockAll() {
- for (int i = 0; i < kTabSize; ++i) {
- lock(&tab[i]);
- }
+void StackDepotBase<Node, kReservedBits, kTabSizeLog>::LockBeforeFork() {
+ // Do not lock hash table. It's very expensive, but it's not rely needed. The
+ // parent process will neither lock nor unlock. Child process risks to be
+ // deadlocked on already locked buckets. To avoid deadlock we will unlock
+ // every locked buckets in `UnlockAfterFork`. This may affect consistency of
+ // the hash table, but the only issue is a few items inserted by parent
+ // process will be not found by child, and the child may insert them again,
+ // wasting some space in `stackStore`.
+
+ // We still need to lock nodes.
+ nodes.Lock();
}
template <class Node, int kReservedBits, int kTabSizeLog>
-void StackDepotBase<Node, kReservedBits, kTabSizeLog>::UnlockAll() {
+void StackDepotBase<Node, kReservedBits, kTabSizeLog>::UnlockAfterFork(
+ bool fork_child) {
+ nodes.Unlock();
+
+ // Only unlock in child process to avoid deadlock. See `LockBeforeFork`.
+ if (!fork_child)
+ return;
+
for (int i = 0; i < kTabSize; ++i) {
atomic_uint32_t *p = &tab[i];
uptr s = atomic_load(p, memory_order_relaxed);
- unlock(p, s & kUnlockMask);
+ if (s & kLockMask)
+ unlock(p, s & kUnlockMask);
}
}
@@ -87,8 +87,8 @@ static inline uhwptr *GetCanonicFrame(uptr bp,
// Nope, this does not look right either. This means the frame after next does
// not have a valid frame pointer, but we can still extract the caller PC.
// Unfortunately, there is no way to decide between GCC and LLVM frame
- // layouts. Assume GCC.
- return bp_prev - 1;
+ // layouts. Assume LLVM.
+ return bp_prev;
#else
return (uhwptr*)bp;
#endif
@@ -111,21 +111,14 @@ void BufferedStackTrace::UnwindFast(uptr pc, uptr bp, uptr stack_top,
IsAligned((uptr)frame, sizeof(*frame)) &&
size < max_depth) {
#ifdef __powerpc__
- // PowerPC ABIs specify that the return address is saved on the
- // *caller's* stack frame. Thus we must dereference the back chain
- // to find the caller frame before extracting it.
+ // PowerPC ABIs specify that the return address is saved at offset
+ // 16 of the *caller's* stack frame. Thus we must dereference the
+ // back chain to find the caller frame before extracting it.
uhwptr *caller_frame = (uhwptr*)frame[0];
if (!IsValidFrame((uptr)caller_frame, stack_top, bottom) ||
!IsAligned((uptr)caller_frame, sizeof(uhwptr)))
break;
- // For most ABIs the offset where the return address is saved is two
- // register sizes. The exception is the SVR4 ABI, which uses an
- // offset of only one register size.
-#ifdef _CALL_SYSV
- uhwptr pc1 = caller_frame[1];
-#else
uhwptr pc1 = caller_frame[2];
-#endif
#elif defined(__s390__)
uhwptr pc1 = frame[14];
#elif defined(__loongarch__) || defined(__riscv)
@@ -33,13 +33,14 @@ class StackTraceTextPrinter {
stack_trace_fmt)) {}
bool ProcessAddressFrames(uptr pc) {
- SymbolizedStack *frames = symbolize_
- ? Symbolizer::GetOrInit()->SymbolizePC(pc)
- : SymbolizedStack::New(pc);
+ SymbolizedStackHolder symbolized_stack(
+ symbolize_ ? Symbolizer::GetOrInit()->SymbolizePC(pc)
+ : SymbolizedStack::New(pc));
+ const SymbolizedStack *frames = symbolized_stack.get();
if (!frames)
return false;
- for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
+ for (const SymbolizedStack *cur = frames; cur; cur = cur->next) {
uptr prev_len = output_->length();
StackTracePrinter::GetOrInit()->RenderFrame(
output_, stack_trace_fmt_, frame_num_++, cur->info.address,
@@ -51,19 +52,18 @@ class StackTraceTextPrinter {
ExtendDedupToken(cur);
}
- frames->ClearAll();
return true;
}
private:
// Extend the dedup token by appending a new frame.
- void ExtendDedupToken(SymbolizedStack *stack) {
+ void ExtendDedupToken(const SymbolizedStack *stack) {
if (!dedup_token_)
return;
if (dedup_frames_-- > 0) {
if (dedup_token_->length())
- dedup_token_->AppendF("--");
+ dedup_token_->Append("--");
if (stack->info.function)
dedup_token_->Append(stack->info.function);
}
@@ -99,7 +99,7 @@ void StackTrace::PrintTo(InternalScopedString *output) const {
output, &dedup_token);
if (trace == nullptr || size == 0) {
- output->AppendF(" <empty stack>\n\n");
+ output->Append(" <empty stack>\n\n");
return;
}
@@ -111,7 +111,7 @@ void StackTrace::PrintTo(InternalScopedString *output) const {
}
// Always add a trailing empty line after stack trace.
- output->AppendF("\n");
+ output->Append("\n");
// Append deduplication token, if non-empty.
if (dedup_token.length())
@@ -198,7 +198,7 @@ void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf,
StackTraceTextPrinter printer(fmt, '\0', &output, nullptr);
if (!printer.ProcessAddressFrames(pc)) {
output.clear();
- output.AppendF("<can't symbolize>");
+ output.Append("<can't symbolize>");
}
CopyStringToBuffer(output, out_buf, out_buf_size);
}
@@ -12,9 +12,11 @@
#include "sanitizer_stacktrace_printer.h"
+#include "sanitizer_common.h"
#include "sanitizer_file.h"
#include "sanitizer_flags.h"
#include "sanitizer_fuchsia.h"
+#include "sanitizer_symbolizer_markup.h"
namespace __sanitizer {
@@ -25,15 +27,13 @@ StackTracePrinter *StackTracePrinter::GetOrInit() {
if (stacktrace_printer)
return stacktrace_printer;
- stacktrace_printer =
- new (GetGlobalLowLevelAllocator()) FormattedStackTracePrinter();
+ stacktrace_printer = StackTracePrinter::NewStackTracePrinter();
CHECK(stacktrace_printer);
return stacktrace_printer;
}
-const char *FormattedStackTracePrinter::StripFunctionName(
- const char *function) {
+const char *StackTracePrinter::StripFunctionName(const char *function) {
if (!common_flags()->demangle)
return function;
if (!function)
@@ -62,6 +62,13 @@ const char *FormattedStackTracePrinter::StripFunctionName(
// sanitizer_symbolizer_markup.cpp implements these differently.
#if !SANITIZER_SYMBOLIZER_MARKUP
+StackTracePrinter *StackTracePrinter::NewStackTracePrinter() {
+ if (common_flags()->enable_symbolizer_markup)
+ return new (GetGlobalLowLevelAllocator()) MarkupStackTracePrinter();
+
+ return new (GetGlobalLowLevelAllocator()) FormattedStackTracePrinter();
+}
+
static const char *DemangleFunctionName(const char *function) {
if (!common_flags()->demangle)
return function;
@@ -145,12 +152,12 @@ static void MaybeBuildIdToBuffer(const AddressInfo &info, bool PrefixSpace,
InternalScopedString *buffer) {
if (info.uuid_size) {
if (PrefixSpace)
- buffer->AppendF(" ");
- buffer->AppendF("(BuildId: ");
+ buffer->Append(" ");
+ buffer->Append("(BuildId: ");
for (uptr i = 0; i < info.uuid_size; ++i) {
buffer->AppendF("%02x", info.uuid[i]);
}
- buffer->AppendF(")");
+ buffer->Append(")");
}
}
@@ -185,7 +192,7 @@ void FormattedStackTracePrinter::RenderFrame(InternalScopedString *buffer,
buffer->AppendF("%u", frame_no);
break;
case 'p':
- buffer->AppendF("0x%zx", address);
+ buffer->AppendF("%p", (void *)address);
break;
case 'm':
buffer->AppendF("%s", StripPathPrefix(info->module, strip_path_prefix));
@@ -242,7 +249,7 @@ void FormattedStackTracePrinter::RenderFrame(InternalScopedString *buffer,
MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer);
#endif
} else {
- buffer->AppendF("(<unknown module>)");
+ buffer->Append("(<unknown module>)");
}
break;
case 'M':
@@ -262,7 +269,7 @@ void FormattedStackTracePrinter::RenderFrame(InternalScopedString *buffer,
break;
default:
Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p,
- (void *)p);
+ (const void *)p);
Die();
}
}
@@ -316,7 +323,7 @@ void FormattedStackTracePrinter::RenderData(InternalScopedString *buffer,
break;
default:
Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p,
- (void *)p);
+ (const void *)p);
Die();
}
}
@@ -324,14 +331,15 @@ void FormattedStackTracePrinter::RenderData(InternalScopedString *buffer,
#endif // !SANITIZER_SYMBOLIZER_MARKUP
-void FormattedStackTracePrinter::RenderSourceLocation(
- InternalScopedString *buffer, const char *file, int line, int column,
- bool vs_style, const char *strip_path_prefix) {
+void StackTracePrinter::RenderSourceLocation(InternalScopedString *buffer,
+ const char *file, int line,
+ int column, bool vs_style,
+ const char *strip_path_prefix) {
if (vs_style && line > 0) {
buffer->AppendF("%s(%d", StripPathPrefix(file, strip_path_prefix), line);
if (column > 0)
buffer->AppendF(",%d", column);
- buffer->AppendF(")");
+ buffer->Append(")");
return;
}
@@ -343,9 +351,10 @@ void FormattedStackTracePrinter::RenderSourceLocation(
}
}
-void FormattedStackTracePrinter::RenderModuleLocation(
- InternalScopedString *buffer, const char *module, uptr offset,
- ModuleArch arch, const char *strip_path_prefix) {
+void StackTracePrinter::RenderModuleLocation(InternalScopedString *buffer,
+ const char *module, uptr offset,
+ ModuleArch arch,
+ const char *strip_path_prefix) {
buffer->AppendF("(%s", StripPathPrefix(module, strip_path_prefix));
if (arch != kModuleArchUnknown) {
buffer->AppendF(":%s", ModuleArchToString(arch));
@@ -25,27 +25,38 @@ class StackTracePrinter {
public:
static StackTracePrinter *GetOrInit();
- virtual const char *StripFunctionName(const char *function) = 0;
+ // Strip interceptor prefixes from function name.
+ const char *StripFunctionName(const char *function);
virtual void RenderFrame(InternalScopedString *buffer, const char *format,
int frame_no, uptr address, const AddressInfo *info,
- bool vs_style,
- const char *strip_path_prefix = "") = 0;
+ bool vs_style, const char *strip_path_prefix = "") {
+ // Should be pure virtual, but we can't depend on __cxa_pure_virtual.
+ UNIMPLEMENTED();
+ }
- virtual bool RenderNeedsSymbolization(const char *format) = 0;
+ virtual bool RenderNeedsSymbolization(const char *format) {
+ // Should be pure virtual, but we can't depend on __cxa_pure_virtual.
+ UNIMPLEMENTED();
+ }
- virtual void RenderSourceLocation(InternalScopedString *buffer,
- const char *file, int line, int column,
- bool vs_style,
- const char *strip_path_prefix) = 0;
+ void RenderSourceLocation(InternalScopedString *buffer, const char *file,
+ int line, int column, bool vs_style,
+ const char *strip_path_prefix);
- virtual void RenderModuleLocation(InternalScopedString *buffer,
- const char *module, uptr offset,
- ModuleArch arch,
- const char *strip_path_prefix) = 0;
+ void RenderModuleLocation(InternalScopedString *buffer, const char *module,
+ uptr offset, ModuleArch arch,
+ const char *strip_path_prefix);
virtual void RenderData(InternalScopedString *buffer, const char *format,
const DataInfo *DI,
- const char *strip_path_prefix = "") = 0;
+ const char *strip_path_prefix = "") {
+ // Should be pure virtual, but we can't depend on __cxa_pure_virtual.
+ UNIMPLEMENTED();
+ }
+
+ private:
+ // To be called from StackTracePrinter::GetOrInit
+ static StackTracePrinter *NewStackTracePrinter();
protected:
~StackTracePrinter() {}
@@ -53,9 +64,6 @@ class StackTracePrinter {
class FormattedStackTracePrinter : public StackTracePrinter {
public:
- // Strip interceptor prefixes from function name.
- const char *StripFunctionName(const char *function) override;
-
// Render the contents of "info" structure, which represents the contents of
// stack frame "frame_no" and appends it to the "buffer". "format" is a
// string with placeholders, which is copied to the output with
@@ -90,14 +98,6 @@ class FormattedStackTracePrinter : public StackTracePrinter {
bool RenderNeedsSymbolization(const char *format) override;
- void RenderSourceLocation(InternalScopedString *buffer, const char *file,
- int line, int column, bool vs_style,
- const char *strip_path_prefix) override;
-
- void RenderModuleLocation(InternalScopedString *buffer, const char *module,
- uptr offset, ModuleArch arch,
- const char *strip_path_prefix) override;
-
// Same as RenderFrame, but for data section (global variables).
// Accepts %s, %l from above.
// Also accepts:
@@ -58,17 +58,16 @@ void BufferedStackTrace::UnwindFast(uptr pc, uptr bp, uptr stack_top,
// Avoid infinite loop when frame == frame[0] by using frame > prev_frame.
while (IsValidFrame(bp, stack_top, bottom) && IsAligned(bp, sizeof(uhwptr)) &&
size < max_depth) {
- uhwptr pc1 = ((uhwptr *)bp)[15];
+ // %o7 contains the address of the call instruction and not the
+ // return address, so we need to compensate.
+ uhwptr pc1 = GetNextInstructionPc(((uhwptr *)bp)[15]);
// Let's assume that any pointer in the 0th page is invalid and
// stop unwinding here. If we're adding support for a platform
// where this isn't true, we need to reconsider this check.
if (pc1 < kPageSize)
break;
- if (pc1 != pc) {
- // %o7 contains the address of the call instruction and not the
- // return address, so we need to compensate.
- trace_buffer[size++] = GetNextInstructionPc((uptr)pc1);
- }
+ if (pc1 != pc)
+ trace_buffer[size++] = pc1;
bottom = bp;
bp = (uptr)((uhwptr *)bp)[14] + STACK_BIAS;
}
@@ -137,10 +137,6 @@ class ThreadSuspender {
};
bool ThreadSuspender::SuspendThread(tid_t tid) {
- // Are we already attached to this thread?
- // Currently this check takes linear time, however the number of threads is
- // usually small.
- if (suspended_threads_list_.ContainsTid(tid)) return false;
int pterrno;
if (internal_iserror(internal_ptrace(PTRACE_ATTACH, tid, nullptr, nullptr),
&pterrno)) {
@@ -217,17 +213,28 @@ bool ThreadSuspender::SuspendAllThreads() {
switch (thread_lister.ListThreads(&threads)) {
case ThreadLister::Error:
ResumeAllThreads();
+ VReport(1, "Failed to list threads\n");
return false;
case ThreadLister::Incomplete:
+ VReport(1, "Incomplete list\n");
retry = true;
break;
case ThreadLister::Ok:
break;
}
for (tid_t tid : threads) {
+ // Are we already attached to this thread?
+ // Currently this check takes linear time, however the number of threads
+ // is usually small.
+ if (suspended_threads_list_.ContainsTid(tid))
+ continue;
if (SuspendThread(tid))
retry = true;
+ else
+ VReport(2, "%llu/status: %s\n", tid, thread_lister.LoadStatus(tid));
}
+ if (retry)
+ VReport(1, "SuspendAllThreads retry: %d\n", i);
}
return suspended_threads_list_.ThreadCount();
}
@@ -257,8 +264,8 @@ static void TracerThreadDieCallback() {
static void TracerThreadSignalHandler(int signum, __sanitizer_siginfo *siginfo,
void *uctx) {
SignalContext ctx(siginfo, uctx);
- Printf("Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n", signum,
- ctx.addr, ctx.pc, ctx.sp);
+ Printf("Tracer caught signal %d: addr=%p pc=%p sp=%p\n", signum,
+ (void *)ctx.addr, (void *)ctx.pc, (void *)ctx.sp);
ThreadSuspender *inst = thread_suspender_instance;
if (inst) {
if (signum == SIGABRT)
@@ -158,8 +158,8 @@ static void TracerThreadDieCallback() {
static void TracerThreadSignalHandler(int signum, __sanitizer_siginfo *siginfo,
void *uctx) {
SignalContext ctx(siginfo, uctx);
- Printf("Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n", signum,
- ctx.addr, ctx.pc, ctx.sp);
+ Printf("Tracer caught signal %d: addr=%p pc=%p sp=%p\n", signum,
+ (void *)ctx.addr, (void *)ctx.pc, (void *)ctx.sp);
ThreadSuspender *inst = thread_suspender_instance;
if (inst) {
if (signum == SIGABRT)
@@ -86,7 +86,7 @@ void SuppressionContext::ParseFromFile(const char *filename) {
}
Parse(file_contents);
- UnmapOrDie(file_contents, contents_size);
+ UnmapOrDie(file_contents, buffer_size);
}
bool SuppressionContext::Match(const char *str, const char *type,
@@ -138,7 +138,10 @@ void SuppressionContext::Parse(const char *str) {
}
}
if (type == suppression_types_num_) {
- Printf("%s: failed to parse suppressions\n", SanitizerToolName);
+ Printf("%s: failed to parse suppressions.\n", SanitizerToolName);
+ Printf("Supported suppression types are:\n");
+ for (type = 0; type < suppression_types_num_; type++)
+ Printf("- %s\n", suppression_types_[type]);
Die();
}
Suppression s;
@@ -64,6 +64,26 @@ struct SymbolizedStack {
SymbolizedStack();
};
+class SymbolizedStackHolder {
+ SymbolizedStack *Stack;
+
+ void clear() {
+ if (Stack)
+ Stack->ClearAll();
+ }
+
+ public:
+ explicit SymbolizedStackHolder(SymbolizedStack *Stack = nullptr)
+ : Stack(Stack) {}
+ ~SymbolizedStackHolder() { clear(); }
+ void reset(SymbolizedStack *S = nullptr) {
+ if (Stack != S)
+ clear();
+ Stack = S;
+ }
+ const SymbolizedStack *get() const { return Stack; }
+};
+
// For now, DataInfo is used to describe global variable.
struct DataInfo {
// Owns all the string members. Storage for them is
@@ -154,6 +174,8 @@ class Symbolizer final {
void InvalidateModuleList();
+ const ListOfModules &GetRefreshedListOfModules();
+
private:
// GetModuleNameAndOffsetForPC has to return a string to the caller.
// Since the corresponding module might get unloaded later, we should create
@@ -163,17 +185,17 @@ class Symbolizer final {
class ModuleNameOwner {
public:
explicit ModuleNameOwner(Mutex *synchronized_by)
- : last_match_(nullptr), mu_(synchronized_by) {
+ : mu_(synchronized_by), last_match_(nullptr) {
storage_.reserve(kInitialCapacity);
}
const char *GetOwnedCopy(const char *str);
private:
static const uptr kInitialCapacity = 1000;
- InternalMmapVector<const char*> storage_;
- const char *last_match_;
Mutex *mu_;
+ const char *last_match_ SANITIZER_GUARDED_BY(mu_);
+ InternalMmapVector<const char *> storage_ SANITIZER_GUARDED_BY(*mu_);
} module_names_;
/// Platform-specific function for creating a Symbolizer object.
@@ -198,7 +220,7 @@ class Symbolizer final {
// always synchronized.
Mutex mu_;
- IntrusiveList<SymbolizerTool> tools_;
+ IntrusiveList<SymbolizerTool> tools_ SANITIZER_GUARDED_BY(mu_);
explicit Symbolizer(IntrusiveList<SymbolizerTool> tools);
@@ -191,6 +191,13 @@ void Symbolizer::RefreshModules() {
modules_fresh_ = true;
}
+const ListOfModules &Symbolizer::GetRefreshedListOfModules() {
+ if (!modules_fresh_)
+ RefreshModules();
+
+ return modules_;
+}
+
static const LoadedModule *SearchForModule(const ListOfModules &modules,
uptr address) {
for (uptr i = 0; i < modules.size(); i++) {
@@ -30,7 +30,7 @@ namespace __sanitizer {
bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
Dl_info info;
int result = dladdr((const void *)addr, &info);
- if (!result) return false;
+ if (!result || !info.dli_sname) return false;
// Compute offset if possible. `dladdr()` doesn't always ensure that `addr >=
// sym_addr` so only compute the offset when this holds. Failure to find the
@@ -51,7 +51,7 @@ bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
Dl_info info;
int result = dladdr((const void *)addr, &info);
- if (!result) return false;
+ if (!result || !info.dli_sname) return false;
const char *demangled = DemangleSwiftAndCXX(info.dli_sname);
if (!demangled)
demangled = info.dli_sname;
@@ -8,151 +8,155 @@
//
// This file is shared between various sanitizers' runtime libraries.
//
-// Implementation of offline markup symbolizer.
+// This generic support for offline symbolizing is based on the
+// Fuchsia port. We don't do any actual symbolization per se.
+// Instead, we emit text containing raw addresses and raw linkage
+// symbol names, embedded in Fuchsia's symbolization markup format.
+// See the spec at:
+// https://llvm.org/docs/SymbolizerMarkupFormat.html
//===----------------------------------------------------------------------===//
-#include "sanitizer_platform.h"
-#if SANITIZER_SYMBOLIZER_MARKUP
-
-#if SANITIZER_FUCHSIA
-#include "sanitizer_symbolizer_fuchsia.h"
-# endif
+#include "sanitizer_symbolizer_markup.h"
-# include <limits.h>
-# include <unwind.h>
-
-# include "sanitizer_stacktrace.h"
-# include "sanitizer_stacktrace_printer.h"
-# include "sanitizer_symbolizer.h"
+#include "sanitizer_common.h"
+#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_markup_constants.h"
namespace __sanitizer {
-// This generic support for offline symbolizing is based on the
-// Fuchsia port. We don't do any actual symbolization per se.
-// Instead, we emit text containing raw addresses and raw linkage
-// symbol names, embedded in Fuchsia's symbolization markup format.
-// Fuchsia's logging infrastructure emits enough information about
-// process memory layout that a post-processing filter can do the
-// symbolization and pretty-print the markup. See the spec at:
-// https://fuchsia.googlesource.com/zircon/+/master/docs/symbolizer_markup.md
-
-// This is used by UBSan for type names, and by ASan for global variable names.
-// It's expected to return a static buffer that will be reused on each call.
-const char *Symbolizer::Demangle(const char *name) {
- static char buffer[kFormatDemangleMax];
- internal_snprintf(buffer, sizeof(buffer), kFormatDemangle, name);
- return buffer;
+void MarkupStackTracePrinter::RenderData(InternalScopedString *buffer,
+ const char *format, const DataInfo *DI,
+ const char *strip_path_prefix) {
+ RenderContext(buffer);
+ buffer->AppendF(kFormatData, reinterpret_cast<void *>(DI->start));
}
-// This is used mostly for suppression matching. Making it work
-// would enable "interceptor_via_lib" suppressions. It's also used
-// once in UBSan to say "in module ..." in a message that also
-// includes an address in the module, so post-processing can already
-// pretty-print that so as to indicate the module.
-bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
- uptr *module_address) {
+bool MarkupStackTracePrinter::RenderNeedsSymbolization(const char *format) {
return false;
}
-// This is mainly used by hwasan for online symbolization. This isn't needed
-// since hwasan can always just dump stack frames for offline symbolization.
-bool Symbolizer::SymbolizeFrame(uptr addr, FrameInfo *info) { return false; }
-
-// This is used in some places for suppression checking, which we
-// don't really support for Fuchsia. It's also used in UBSan to
-// identify a PC location to a function name, so we always fill in
-// the function member with a string containing markup around the PC
-// value.
-// TODO(mcgrathr): Under SANITIZER_GO, it's currently used by TSan
-// to render stack frames, but that should be changed to use
-// RenderStackFrame.
-SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) {
- SymbolizedStack *s = SymbolizedStack::New(addr);
+// We don't support the stack_trace_format flag at all.
+void MarkupStackTracePrinter::RenderFrame(InternalScopedString *buffer,
+ const char *format, int frame_no,
+ uptr address, const AddressInfo *info,
+ bool vs_style,
+ const char *strip_path_prefix) {
+ CHECK(!RenderNeedsSymbolization(format));
+ RenderContext(buffer);
+ buffer->AppendF(kFormatFrame, frame_no, reinterpret_cast<void *>(address));
+}
+
+bool MarkupSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *stack) {
char buffer[kFormatFunctionMax];
- internal_snprintf(buffer, sizeof(buffer), kFormatFunction, addr);
- s->info.function = internal_strdup(buffer);
- return s;
+ internal_snprintf(buffer, sizeof(buffer), kFormatFunction,
+ reinterpret_cast<void *>(addr));
+ stack->info.function = internal_strdup(buffer);
+ return true;
}
-// Always claim we succeeded, so that RenderDataInfo will be called.
-bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+bool MarkupSymbolizerTool::SymbolizeData(uptr addr, DataInfo *info) {
info->Clear();
info->start = addr;
return true;
}
-// We ignore the format argument to __sanitizer_symbolize_global.
-void FormattedStackTracePrinter::RenderData(InternalScopedString *buffer,
- const char *format,
- const DataInfo *DI,
- const char *strip_path_prefix) {
- buffer->AppendF(kFormatData, DI->start);
+const char *MarkupSymbolizerTool::Demangle(const char *name) {
+ static char buffer[kFormatDemangleMax];
+ internal_snprintf(buffer, sizeof(buffer), kFormatDemangle, name);
+ return buffer;
}
-bool FormattedStackTracePrinter::RenderNeedsSymbolization(const char *format) {
- return false;
+// Fuchsia's implementation of symbolizer markup doesn't need to emit contextual
+// elements at this point.
+// Fuchsia's logging infrastructure emits enough information about
+// process memory layout that a post-processing filter can do the
+// symbolization and pretty-print the markup.
+#if !SANITIZER_FUCHSIA
+
+static bool ModulesEq(const LoadedModule &module,
+ const RenderedModule &renderedModule) {
+ return module.base_address() == renderedModule.base_address &&
+ internal_memcmp(module.uuid(), renderedModule.uuid,
+ module.uuid_size()) == 0 &&
+ internal_strcmp(module.full_name(), renderedModule.full_name) == 0;
}
-// We don't support the stack_trace_format flag at all.
-void FormattedStackTracePrinter::RenderFrame(InternalScopedString *buffer,
- const char *format, int frame_no,
- uptr address,
- const AddressInfo *info,
- bool vs_style,
- const char *strip_path_prefix) {
- CHECK(!RenderNeedsSymbolization(format));
- buffer->AppendF(kFormatFrame, frame_no, address);
-}
+static bool ModuleHasBeenRendered(
+ const LoadedModule &module,
+ const InternalMmapVectorNoCtor<RenderedModule> &renderedModules) {
+ for (const auto &renderedModule : renderedModules)
+ if (ModulesEq(module, renderedModule))
+ return true;
-Symbolizer *Symbolizer::PlatformInit() {
- return new (symbolizer_allocator_) Symbolizer({});
+ return false;
}
-void Symbolizer::LateInitialize() { Symbolizer::GetOrInit(); }
-
-void StartReportDeadlySignal() {}
-void ReportDeadlySignal(const SignalContext &sig, u32 tid,
- UnwindSignalStackCallbackType unwind,
- const void *unwind_context) {}
-
-#if SANITIZER_CAN_SLOW_UNWIND
-struct UnwindTraceArg {
- BufferedStackTrace *stack;
- u32 max_depth;
-};
-
-_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
- UnwindTraceArg *arg = static_cast<UnwindTraceArg *>(param);
- CHECK_LT(arg->stack->size, arg->max_depth);
- uptr pc = _Unwind_GetIP(ctx);
- if (pc < PAGE_SIZE) return _URC_NORMAL_STOP;
- arg->stack->trace_buffer[arg->stack->size++] = pc;
- return (arg->stack->size == arg->max_depth ? _URC_NORMAL_STOP
- : _URC_NO_REASON);
+static void RenderModule(InternalScopedString *buffer,
+ const LoadedModule &module, uptr moduleId) {
+ InternalScopedString buildIdBuffer;
+ for (uptr i = 0; i < module.uuid_size(); i++)
+ buildIdBuffer.AppendF("%02x", module.uuid()[i]);
+
+ buffer->AppendF(kFormatModule, moduleId, module.full_name(),
+ buildIdBuffer.data());
+ buffer->Append("\n");
}
-void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) {
- CHECK_GE(max_depth, 2);
- size = 0;
- UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
- _Unwind_Backtrace(Unwind_Trace, &arg);
- CHECK_GT(size, 0);
- // We need to pop a few frames so that pc is on top.
- uptr to_pop = LocatePcInTrace(pc);
- // trace_buffer[0] belongs to the current function so we always pop it,
- // unless there is only 1 frame in the stack trace (1 frame is always better
- // than 0!).
- PopStackFrames(Min(to_pop, static_cast<uptr>(1)));
- trace_buffer[0] = pc;
+static void RenderMmaps(InternalScopedString *buffer,
+ const LoadedModule &module, uptr moduleId) {
+ InternalScopedString accessBuffer;
+
+ // All module mmaps are readable at least
+ for (const auto &range : module.ranges()) {
+ accessBuffer.Append("r");
+ if (range.writable)
+ accessBuffer.Append("w");
+ if (range.executable)
+ accessBuffer.Append("x");
+
+ //{{{mmap:%starting_addr:%size_in_hex:load:%moduleId:r%(w|x):%relative_addr}}}
+
+ // module.base_address == dlpi_addr
+ // range.beg == dlpi_addr + p_vaddr
+ // relative address == p_vaddr == range.beg - module.base_address
+ buffer->AppendF(kFormatMmap, reinterpret_cast<void *>(range.beg),
+ range.end - range.beg, static_cast<int>(moduleId),
+ accessBuffer.data(), range.beg - module.base_address());
+
+ buffer->Append("\n");
+ accessBuffer.clear();
+ }
}
-void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) {
- CHECK(context);
- CHECK_GE(max_depth, 2);
- UNREACHABLE("signal context doesn't exist");
+void MarkupStackTracePrinter::RenderContext(InternalScopedString *buffer) {
+ if (renderedModules_.size() == 0)
+ buffer->Append("{{{reset}}}\n");
+
+ const auto &modules = Symbolizer::GetOrInit()->GetRefreshedListOfModules();
+
+ for (const auto &module : modules) {
+ if (ModuleHasBeenRendered(module, renderedModules_))
+ continue;
+
+ // symbolizer markup id, used to refer to this modules from other contextual
+ // elements
+ uptr moduleId = renderedModules_.size();
+
+ RenderModule(buffer, module, moduleId);
+ RenderMmaps(buffer, module, moduleId);
+
+ renderedModules_.push_back({
+ internal_strdup(module.full_name()),
+ module.base_address(),
+ {},
+ });
+
+ // kModuleUUIDSize is the size of curModule.uuid
+ CHECK_GE(kModuleUUIDSize, module.uuid_size());
+ internal_memcpy(renderedModules_.back().uuid, module.uuid(),
+ module.uuid_size());
+ }
}
-#endif // SANITIZER_CAN_SLOW_UNWIND
+#endif // !SANITIZER_FUCHSIA
} // namespace __sanitizer
-
-#endif // SANITIZER_SYMBOLIZER_MARKUP
new file mode 100644
@@ -0,0 +1,79 @@
+//===-- sanitizer_symbolizer_markup.h -----------------------------------===//
+//
+// 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 shared between various sanitizers' runtime libraries.
+//
+// Header for the offline markup symbolizer.
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_SYMBOLIZER_MARKUP_H
+#define SANITIZER_SYMBOLIZER_MARKUP_H
+
+#include "sanitizer_common.h"
+#include "sanitizer_stacktrace_printer.h"
+#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_internal.h"
+
+namespace __sanitizer {
+
+// Simplier view of a LoadedModule. It only holds information necessary to
+// identify unique modules.
+struct RenderedModule {
+ char *full_name;
+ uptr base_address;
+ u8 uuid[kModuleUUIDSize]; // BuildId
+};
+
+class MarkupStackTracePrinter : public StackTracePrinter {
+ public:
+ // We don't support the stack_trace_format flag at all.
+ void RenderFrame(InternalScopedString *buffer, const char *format,
+ int frame_no, uptr address, const AddressInfo *info,
+ bool vs_style, const char *strip_path_prefix = "") override;
+
+ bool RenderNeedsSymbolization(const char *format) override;
+
+ // We ignore the format argument to __sanitizer_symbolize_global.
+ void RenderData(InternalScopedString *buffer, const char *format,
+ const DataInfo *DI,
+ const char *strip_path_prefix = "") override;
+
+ private:
+ // Keeps track of the modules that have been rendered to avoid re-rendering
+ // them
+ InternalMmapVector<RenderedModule> renderedModules_;
+ void RenderContext(InternalScopedString *buffer);
+
+ protected:
+ ~MarkupStackTracePrinter() {}
+};
+
+class MarkupSymbolizerTool final : public SymbolizerTool {
+ public:
+ // This is used in some places for suppression checking, which we
+ // don't really support for Fuchsia. It's also used in UBSan to
+ // identify a PC location to a function name, so we always fill in
+ // the function member with a string containing markup around the PC
+ // value.
+ // TODO(mcgrathr): Under SANITIZER_GO, it's currently used by TSan
+ // to render stack frames, but that should be changed to use
+ // RenderStackFrame.
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+
+ // Always claim we succeeded, so that RenderDataInfo will be called.
+ bool SymbolizeData(uptr addr, DataInfo *info) override;
+
+ // May return NULL if demangling failed.
+ // This is used by UBSan for type names, and by ASan for global variable
+ // names. It's expected to return a static buffer that will be reused on each
+ // call.
+ const char *Demangle(const char *name) override;
+};
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_SYMBOLIZER_MARKUP_H
similarity index 69%
rename from libsanitizer/sanitizer_common/sanitizer_symbolizer_fuchsia.h
rename to libsanitizer/sanitizer_common/sanitizer_symbolizer_markup_constants.h
@@ -1,4 +1,5 @@
-//===-- sanitizer_symbolizer_fuchsia.h -----------------------------------===//
+//===-- sanitizer_symbolizer_markup_constants.h
+//-----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -8,10 +9,10 @@
//
// This file is shared between various sanitizers' runtime libraries.
//
-// Define Fuchsia's string formats and limits for the markup symbolizer.
+// Define string formats and limits for the markup symbolizer.
//===----------------------------------------------------------------------===//
-#ifndef SANITIZER_SYMBOLIZER_FUCHSIA_H
-#define SANITIZER_SYMBOLIZER_FUCHSIA_H
+#ifndef SANITIZER_SYMBOLIZER_MARKUP_CONSTANTS_H
+#define SANITIZER_SYMBOLIZER_MARKUP_CONSTANTS_H
#include "sanitizer_internal_defs.h"
@@ -32,11 +33,17 @@ constexpr uptr kFormatFunctionMax = 64; // More than big enough for 64-bit hex.
constexpr const char *kFormatData = "{{{data:%p}}}";
// One frame in a backtrace (printed on a line by itself).
-constexpr const char *kFormatFrame = "{{{bt:%u:%p}}}";
+constexpr const char *kFormatFrame = "{{{bt:%d:%p}}}";
+
+// Module contextual element.
+constexpr const char *kFormatModule = "{{{module:%zu:%s:elf:%s}}}";
+
+// mmap for a module segment.
+constexpr const char *kFormatMmap = "{{{mmap:%p:0x%zx:load:%d:%s:0x%zx}}}";
// Dump trigger element.
#define FORMAT_DUMPFILE "{{{dumpfile:%s:%s}}}"
} // namespace __sanitizer
-#endif // SANITIZER_SYMBOLIZER_FUCHSIA_H
+#endif // SANITIZER_SYMBOLIZER_MARKUP_CONSTANTS_H
new file mode 100644
@@ -0,0 +1,85 @@
+//===-- sanitizer_symbolizer_markup_fuchsia.cpp ---------------------------===//
+//
+// 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 shared between various sanitizers' runtime libraries.
+//
+// Fuchsia specific implementation of offline markup symbolizer.
+//===----------------------------------------------------------------------===//
+#include "sanitizer_platform.h"
+
+#if SANITIZER_SYMBOLIZER_MARKUP
+
+# include "sanitizer_common.h"
+# include "sanitizer_stacktrace_printer.h"
+# include "sanitizer_symbolizer.h"
+# include "sanitizer_symbolizer_markup.h"
+# include "sanitizer_symbolizer_markup_constants.h"
+
+namespace __sanitizer {
+
+// This is used by UBSan for type names, and by ASan for global variable names.
+// It's expected to return a static buffer that will be reused on each call.
+const char *Symbolizer::Demangle(const char *name) {
+ static char buffer[kFormatDemangleMax];
+ internal_snprintf(buffer, sizeof(buffer), kFormatDemangle, name);
+ return buffer;
+}
+
+// This is used mostly for suppression matching. Making it work
+// would enable "interceptor_via_lib" suppressions. It's also used
+// once in UBSan to say "in module ..." in a message that also
+// includes an address in the module, so post-processing can already
+// pretty-print that so as to indicate the module.
+bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
+ uptr *module_address) {
+ return false;
+}
+
+// This is mainly used by hwasan for online symbolization. This isn't needed
+// since hwasan can always just dump stack frames for offline symbolization.
+bool Symbolizer::SymbolizeFrame(uptr addr, FrameInfo *info) { return false; }
+
+// This is used in some places for suppression checking, which we
+// don't really support for Fuchsia. It's also used in UBSan to
+// identify a PC location to a function name, so we always fill in
+// the function member with a string containing markup around the PC
+// value.
+// TODO(mcgrathr): Under SANITIZER_GO, it's currently used by TSan
+// to render stack frames, but that should be changed to use
+// RenderStackFrame.
+SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) {
+ SymbolizedStack *s = SymbolizedStack::New(addr);
+ char buffer[kFormatFunctionMax];
+ internal_snprintf(buffer, sizeof(buffer), kFormatFunction, addr);
+ s->info.function = internal_strdup(buffer);
+ return s;
+}
+
+// Always claim we succeeded, so that RenderDataInfo will be called.
+bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+ info->Clear();
+ info->start = addr;
+ return true;
+}
+
+// Fuchsia only uses MarkupStackTracePrinter
+StackTracePrinter *StackTracePrinter::NewStackTracePrinter() {
+ return new (GetGlobalLowLevelAllocator()) MarkupStackTracePrinter();
+}
+
+void MarkupStackTracePrinter::RenderContext(InternalScopedString *) {}
+
+Symbolizer *Symbolizer::PlatformInit() {
+ return new (symbolizer_allocator_) Symbolizer({});
+}
+
+void Symbolizer::LateInitialize() { Symbolizer::GetOrInit(); }
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_SYMBOLIZER_MARKUP
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
+#include "sanitizer_symbolizer_markup.h"
#if SANITIZER_POSIX
# include <dlfcn.h> // for dlsym()
# include <errno.h>
@@ -340,15 +341,14 @@ __sanitizer_symbolize_set_inline_frames(bool InlineFrames);
class InternalSymbolizer final : public SymbolizerTool {
public:
static InternalSymbolizer *get(LowLevelAllocator *alloc) {
- if (&__sanitizer_symbolize_set_demangle)
- CHECK(__sanitizer_symbolize_set_demangle(common_flags()->demangle));
- if (&__sanitizer_symbolize_set_inline_frames)
- CHECK(__sanitizer_symbolize_set_inline_frames(
- common_flags()->symbolize_inline_frames));
- // These are essential, we don't have InternalSymbolizer without them.
- if (&__sanitizer_symbolize_code && &__sanitizer_symbolize_data)
- return new (*alloc) InternalSymbolizer();
- return 0;
+ // These one is the most used one, so we will use it to detect a presence of
+ // internal symbolizer.
+ if (&__sanitizer_symbolize_code == nullptr)
+ return nullptr;
+ CHECK(__sanitizer_symbolize_set_demangle(common_flags()->demangle));
+ CHECK(__sanitizer_symbolize_set_inline_frames(
+ common_flags()->symbolize_inline_frames));
+ return new (*alloc) InternalSymbolizer();
}
bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
@@ -370,8 +370,6 @@ class InternalSymbolizer final : public SymbolizerTool {
}
bool SymbolizeFrame(uptr addr, FrameInfo *info) override {
- if (&__sanitizer_symbolize_frame == nullptr)
- return false;
bool result = __sanitizer_symbolize_frame(info->module, info->module_offset,
buffer_, sizeof(buffer_));
if (result)
@@ -379,14 +377,10 @@ class InternalSymbolizer final : public SymbolizerTool {
return result;
}
- void Flush() override {
- if (&__sanitizer_symbolize_flush)
- __sanitizer_symbolize_flush();
- }
+ void Flush() override { __sanitizer_symbolize_flush(); }
const char *Demangle(const char *name) override {
- if (&__sanitizer_symbolize_demangle &&
- __sanitizer_symbolize_demangle(name, buffer_, sizeof(buffer_))) {
+ if (__sanitizer_symbolize_demangle(name, buffer_, sizeof(buffer_))) {
char *res_buff = nullptr;
ExtractToken(buffer_, "", &res_buff);
return res_buff;
@@ -475,6 +469,12 @@ static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
VReport(2, "Symbolizer is disabled.\n");
return;
}
+ if (common_flags()->enable_symbolizer_markup) {
+ VReport(2, "Using symbolizer markup");
+ SymbolizerTool *tool = new (*allocator) MarkupSymbolizerTool();
+ CHECK(tool);
+ list->push_back(tool);
+ }
if (IsAllocatorOutOfMemory()) {
VReport(2, "Cannot use internal symbolizer: out of memory\n");
} else if (SymbolizerTool *tool = InternalSymbolizer::get(allocator)) {
@@ -28,6 +28,33 @@
namespace __sanitizer {
#if !SANITIZER_GO
+
+static bool FrameIsInternal(const SymbolizedStack *frame) {
+ if (!frame)
+ return true;
+ const char *file = frame->info.file;
+ const char *module = frame->info.module;
+ // On Gentoo, the path is g++-*, so there's *not* a missing /.
+ if (file && (internal_strstr(file, "/compiler-rt/lib/") ||
+ internal_strstr(file, "/include/c++/") ||
+ internal_strstr(file, "/include/g++")))
+ return true;
+ if (file && internal_strstr(file, "\\compiler-rt\\lib\\"))
+ return true;
+ if (module && (internal_strstr(module, "libclang_rt.")))
+ return true;
+ if (module && (internal_strstr(module, "clang_rt.")))
+ return true;
+ return false;
+}
+
+const SymbolizedStack *SkipInternalFrames(const SymbolizedStack *frames) {
+ for (const SymbolizedStack *f = frames; f; f = f->next)
+ if (!FrameIsInternal(f))
+ return f;
+ return nullptr;
+}
+
void ReportErrorSummary(const char *error_type, const AddressInfo &info,
const char *alt_tool_name) {
if (!common_flags()->print_summary) return;
@@ -75,16 +102,33 @@ void ReportErrorSummary(const char *error_type, const StackTrace *stack,
#if !SANITIZER_GO
if (!common_flags()->print_summary)
return;
- if (stack->size == 0) {
- ReportErrorSummary(error_type);
- return;
+
+ // Find first non-internal stack frame.
+ for (uptr i = 0; i < stack->size; ++i) {
+ uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[i]);
+ SymbolizedStackHolder symbolized_stack(
+ Symbolizer::GetOrInit()->SymbolizePC(pc));
+ if (const SymbolizedStack *frame = symbolized_stack.get()) {
+ if (const SymbolizedStack *summary_frame = SkipInternalFrames(frame)) {
+ ReportErrorSummary(error_type, summary_frame->info, alt_tool_name);
+ return;
+ }
+ }
}
- // Currently, we include the first stack frame into the report summary.
- // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
- uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
- SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
- ReportErrorSummary(error_type, frame->info, alt_tool_name);
- frame->ClearAll();
+
+ // Fallback to the top one.
+ if (stack->size) {
+ uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
+ SymbolizedStackHolder symbolized_stack(
+ Symbolizer::GetOrInit()->SymbolizePC(pc));
+ if (const SymbolizedStack *frame = symbolized_stack.get()) {
+ ReportErrorSummary(error_type, frame->info, alt_tool_name);
+ return;
+ }
+ }
+
+ // Fallback to a summary without location.
+ ReportErrorSummary(error_type);
#endif
}
@@ -183,12 +227,15 @@ static void ReportStackOverflowImpl(const SignalContext &sig, u32 tid,
SanitizerToolName, kDescription, (void *)sig.addr, (void *)sig.pc,
(void *)sig.bp, (void *)sig.sp, tid);
Printf("%s", d.Default());
- InternalMmapVector<BufferedStackTrace> stack_buffer(1);
- BufferedStackTrace *stack = stack_buffer.data();
- stack->Reset();
- unwind(sig, unwind_context, stack);
- stack->Print();
- ReportErrorSummary(kDescription, stack);
+ // Avoid SEGVs in the unwinder when bp couldn't be determined.
+ if (sig.bp) {
+ InternalMmapVector<BufferedStackTrace> stack_buffer(1);
+ BufferedStackTrace *stack = stack_buffer.data();
+ stack->Reset();
+ unwind(sig, unwind_context, stack);
+ stack->Print();
+ ReportErrorSummary(kDescription, stack);
+ }
}
static void ReportDeadlySignalImpl(const SignalContext &sig, u32 tid,
new file mode 100644
@@ -0,0 +1,33 @@
+//===-- sanitizer_symbolizer_report_fuchsia.cpp
+//-----------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of the report functions for fuchsia.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_SYMBOLIZER_MARKUP
+
+# include "sanitizer_common.h"
+
+namespace __sanitizer {
+void StartReportDeadlySignal() {}
+
+void ReportDeadlySignal(const SignalContext &sig, u32 tid,
+ UnwindSignalStackCallbackType unwind,
+ const void *unwind_context) {}
+
+void HandleDeadlySignal(void *siginfo, void *context, u32 tid,
+ UnwindSignalStackCallbackType unwind,
+ const void *unwind_context) {}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_SYMBOLIZER_MARKUP
@@ -65,12 +65,13 @@ void InitializeDbgHelpIfNeeded() {
HMODULE dbghelp = LoadLibraryA("dbghelp.dll");
CHECK(dbghelp && "failed to load dbghelp.dll");
-#define DBGHELP_IMPORT(name) \
- do { \
- name = \
- reinterpret_cast<decltype(::name) *>(GetProcAddress(dbghelp, #name)); \
- CHECK(name != nullptr); \
- } while (0)
+# define DBGHELP_IMPORT(name) \
+ do { \
+ name = reinterpret_cast<decltype(::name) *>( \
+ (void *)GetProcAddress(dbghelp, #name)); \
+ CHECK(name != nullptr); \
+ } while (0)
+
DBGHELP_IMPORT(StackWalk64);
DBGHELP_IMPORT(SymCleanup);
DBGHELP_IMPORT(SymFromAddr);
@@ -23,6 +23,9 @@ void ThreadArgRetval::CreateLocked(uptr thread, bool detached,
Data& t = data_[thread];
t = {};
t.gen = gen_++;
+ static_assert(sizeof(gen_) == sizeof(u32) && kInvalidGen == UINT32_MAX);
+ if (gen_ == kInvalidGen)
+ gen_ = 0;
t.detached = detached;
t.args = args;
}
@@ -53,16 +56,28 @@ void ThreadArgRetval::Finish(uptr thread, void* retval) {
u32 ThreadArgRetval::BeforeJoin(uptr thread) const {
__sanitizer::Lock lock(&mtx_);
auto t = data_.find(thread);
- CHECK(t);
- CHECK(!t->second.detached);
- return t->second.gen;
+ if (t && !t->second.detached) {
+ return t->second.gen;
+ }
+ if (!common_flags()->detect_invalid_join)
+ return kInvalidGen;
+ const char* reason = "unknown";
+ if (!t) {
+ reason = "already joined";
+ } else if (t->second.detached) {
+ reason = "detached";
+ }
+ Report("ERROR: %s: Joining %s thread, aborting.\n", SanitizerToolName,
+ reason);
+ Die();
}
void ThreadArgRetval::AfterJoin(uptr thread, u32 gen) {
__sanitizer::Lock lock(&mtx_);
auto t = data_.find(thread);
if (!t || gen != t->second.gen) {
- // Thread was reused and erased by any other event.
+ // Thread was reused and erased by any other event, or we had an invalid
+ // join.
return;
}
CHECK(!t->second.detached);
@@ -93,6 +93,7 @@ class SANITIZER_MUTEX ThreadArgRetval {
// will keep pointers alive forever, missing leaks caused by cancelation.
private:
+ static const u32 kInvalidGen = UINT32_MAX;
struct Data {
Args args;
u32 gen; // Avoid collision if thread id re-used.
new file mode 100644
@@ -0,0 +1,72 @@
+//===-- sanitizer_thread_history.cpp --------------------------------------===//
+//
+// 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 "sanitizer_thread_history.h"
+
+#include "sanitizer_stackdepot.h"
+namespace __sanitizer {
+
+void PrintThreadHistory(ThreadRegistry ®istry, InternalScopedString &out) {
+ ThreadRegistryLock l(®istry);
+ // Stack traces are largest part of printout and they often the same for
+ // multiple threads, so we will deduplicate them.
+ InternalMmapVector<const ThreadContextBase *> stacks;
+
+ registry.RunCallbackForEachThreadLocked(
+ [](ThreadContextBase *context, void *arg) {
+ static_cast<decltype(&stacks)>(arg)->push_back(context);
+ },
+ &stacks);
+
+ Sort(stacks.data(), stacks.size(),
+ [](const ThreadContextBase *a, const ThreadContextBase *b) {
+ if (a->stack_id < b->stack_id)
+ return true;
+ if (a->stack_id > b->stack_id)
+ return false;
+ return a->unique_id < b->unique_id;
+ });
+
+ auto describe_thread = [&](const ThreadContextBase *context) {
+ if (!context) {
+ out.Append("T-1");
+ return;
+ }
+ out.AppendF("T%llu/%llu", context->unique_id, context->os_id);
+ if (internal_strlen(context->name))
+ out.AppendF(" (%s)", context->name);
+ };
+
+ auto get_parent =
+ [&](const ThreadContextBase *context) -> const ThreadContextBase * {
+ if (!context)
+ return nullptr;
+ ThreadContextBase *parent = registry.GetThreadLocked(context->parent_tid);
+ if (!parent)
+ return nullptr;
+ if (parent->unique_id >= context->unique_id)
+ return nullptr;
+ return parent;
+ };
+
+ const ThreadContextBase *prev = nullptr;
+ for (const ThreadContextBase *context : stacks) {
+ if (prev && prev->stack_id != context->stack_id)
+ StackDepotGet(prev->stack_id).PrintTo(&out);
+ prev = context;
+ out.Append("Thread ");
+ describe_thread(context);
+ out.Append(" was created by ");
+ describe_thread(get_parent(context));
+ out.Append("\n");
+ }
+ if (prev)
+ StackDepotGet(prev->stack_id).PrintTo(&out);
+}
+
+} // namespace __sanitizer
new file mode 100644
@@ -0,0 +1,24 @@
+//===-- sanitizer_thread_history.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Utility to print thread histroy from ThreadRegistry.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_THREAD_HISTORY_H
+#define SANITIZER_THREAD_HISTORY_H
+
+#include "sanitizer_thread_registry.h"
+
+namespace __sanitizer {
+
+void PrintThreadHistory(ThreadRegistry& registry, InternalScopedString& out);
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_THREAD_HISTORY_H
@@ -18,9 +18,17 @@
namespace __sanitizer {
ThreadContextBase::ThreadContextBase(u32 tid)
- : tid(tid), unique_id(0), reuse_count(), os_id(0), user_id(0),
- status(ThreadStatusInvalid), detached(false),
- thread_type(ThreadType::Regular), parent_tid(0), next(0) {
+ : tid(tid),
+ unique_id(0),
+ reuse_count(),
+ os_id(0),
+ user_id(0),
+ status(ThreadStatusInvalid),
+ detached(false),
+ thread_type(ThreadType::Regular),
+ parent_tid(0),
+ stack_id(0),
+ next(0) {
name[0] = '\0';
atomic_store(&thread_destroyed, 0, memory_order_release);
}
@@ -39,8 +47,7 @@ void ThreadContextBase::SetName(const char *new_name) {
}
void ThreadContextBase::SetDead() {
- CHECK(status == ThreadStatusRunning ||
- status == ThreadStatusFinished);
+ CHECK(status == ThreadStatusRunning || status == ThreadStatusFinished);
status = ThreadStatusDead;
user_id = 0;
OnDead();
@@ -68,7 +75,8 @@ void ThreadContextBase::SetFinished() {
// for a thread that never actually started. In that case the thread
// should go to ThreadStatusFinished regardless of whether it was created
// as detached.
- if (!detached || status == ThreadStatusCreated) status = ThreadStatusFinished;
+ if (!detached || status == ThreadStatusCreated)
+ status = ThreadStatusFinished;
OnFinished();
}
@@ -81,14 +89,17 @@ void ThreadContextBase::SetStarted(tid_t _os_id, ThreadType _thread_type,
}
void ThreadContextBase::SetCreated(uptr _user_id, u64 _unique_id,
- bool _detached, u32 _parent_tid, void *arg) {
+ bool _detached, u32 _parent_tid,
+ u32 _stack_tid, void *arg) {
status = ThreadStatusCreated;
user_id = _user_id;
unique_id = _unique_id;
detached = _detached;
// Parent tid makes no sense for the main thread.
- if (tid != kMainTid)
+ if (tid != kMainTid) {
parent_tid = _parent_tid;
+ stack_id = _stack_tid;
+ }
OnCreated(arg);
}
@@ -124,8 +135,10 @@ void ThreadRegistry::GetNumberOfThreads(uptr *total, uptr *running,
ThreadRegistryLock l(this);
if (total)
*total = threads_.size();
- if (running) *running = running_threads_;
- if (alive) *alive = alive_threads_;
+ if (running)
+ *running = running_threads_;
+ if (alive)
+ *alive = alive_threads_;
}
uptr ThreadRegistry::GetMaxAliveThreads() {
@@ -134,7 +147,7 @@ uptr ThreadRegistry::GetMaxAliveThreads() {
}
u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid,
- void *arg) {
+ u32 stack_tid, void *arg) {
ThreadRegistryLock l(this);
u32 tid = kInvalidTid;
ThreadContextBase *tctx = QuarantinePop();
@@ -150,8 +163,10 @@ u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid,
Report("%s: Thread limit (%u threads) exceeded. Dying.\n",
SanitizerToolName, max_threads_);
#else
- Printf("race: limit on %u simultaneously alive goroutines is exceeded,"
- " dying\n", max_threads_);
+ Printf(
+ "race: limit on %u simultaneously alive goroutines is exceeded,"
+ " dying\n",
+ max_threads_);
#endif
Die();
}
@@ -170,8 +185,8 @@ u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid,
// positives later (e.g. if we join a wrong thread).
CHECK(live_.try_emplace(user_id, tid).second);
}
- tctx->SetCreated(user_id, total_threads_++, detached,
- parent_tid, arg);
+ tctx->SetCreated(user_id, total_threads_++, detached, parent_tid, stack_tid,
+ arg);
return tid;
}
@@ -196,8 +211,8 @@ u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) {
return kInvalidTid;
}
-ThreadContextBase *
-ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) {
+ThreadContextBase *ThreadRegistry::FindThreadContextLocked(
+ FindThreadCallback cb, void *arg) {
CheckLocked();
for (u32 tid = 0; tid < threads_.size(); tid++) {
ThreadContextBase *tctx = threads_[tid];
@@ -210,7 +225,7 @@ ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) {
static bool FindThreadContextByOsIdCallback(ThreadContextBase *tctx,
void *arg) {
return (tctx->os_id == (uptr)arg && tctx->status != ThreadStatusInvalid &&
- tctx->status != ThreadStatusDead);
+ tctx->status != ThreadStatusDead);
}
ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(tid_t os_id) {
@@ -52,6 +52,7 @@ class ThreadContextBase {
ThreadType thread_type;
u32 parent_tid;
+ u32 stack_id;
ThreadContextBase *next; // For storing thread contexts in a list.
atomic_uint32_t thread_destroyed; // To address race of Joined vs Finished
@@ -63,7 +64,7 @@ class ThreadContextBase {
void SetFinished();
void SetStarted(tid_t _os_id, ThreadType _thread_type, void *arg);
void SetCreated(uptr _user_id, u64 _unique_id, bool _detached,
- u32 _parent_tid, void *arg);
+ u32 _parent_tid, u32 _stack_tid, void *arg);
void Reset();
void SetDestroyed();
@@ -101,12 +102,16 @@ class SANITIZER_MUTEX ThreadRegistry {
// Should be guarded by ThreadRegistryLock.
ThreadContextBase *GetThreadLocked(u32 tid) {
- return threads_.empty() ? nullptr : threads_[tid];
+ return tid < threads_.size() ? threads_[tid] : nullptr;
}
u32 NumThreadsLocked() const { return threads_.size(); }
- u32 CreateThread(uptr user_id, bool detached, u32 parent_tid, void *arg);
+ u32 CreateThread(uptr user_id, bool detached, u32 parent_tid, u32 stack_tid,
+ void *arg);
+ u32 CreateThread(uptr user_id, bool detached, u32 parent_tid, void *arg) {
+ return CreateThread(user_id, detached, parent_tid, 0, arg);
+ }
typedef void (*ThreadCallback)(ThreadContextBase *tctx, void *arg);
// Invokes callback with a specified arg for each thread context.
@@ -14,6 +14,8 @@
#include "sanitizer_allocator_interface.h"
#include "sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_flags.h"
#include "sanitizer_platform_interceptors.h"
@@ -66,7 +68,7 @@ static DTLS::DTVBlock *DTLS_NextBlock(atomic_uintptr_t *cur) {
}
static DTLS::DTV *DTLS_Find(uptr id) {
- VReport(2, "__tls_get_addr: DTLS_Find %p %zd\n", (void *)&dtls, id);
+ VReport(3, "__tls_get_addr: DTLS_Find %p %zd\n", (void *)&dtls, id);
static constexpr uptr kPerBlock = ARRAY_SIZE(DTLS::DTVBlock::dtvs);
DTLS::DTVBlock *cur = DTLS_NextBlock(&dtls.dtv_block);
if (!cur)
@@ -110,6 +112,21 @@ SANITIZER_WEAK_ATTRIBUTE
const void *__sanitizer_get_allocated_begin(const void *p);
}
+SANITIZER_INTERFACE_WEAK_DEF(uptr, __sanitizer_get_dtls_size,
+ const void *tls_begin) {
+ const void *start = __sanitizer_get_allocated_begin(tls_begin);
+ if (!start)
+ return 0;
+ CHECK_LE(start, tls_begin);
+ uptr tls_size = __sanitizer_get_allocated_size(start);
+ VReport(2, "__tls_get_addr: glibc DTLS suspected; tls={%p,0x%zx}\n",
+ tls_begin, tls_size);
+ uptr offset =
+ (reinterpret_cast<uptr>(tls_begin) - reinterpret_cast<uptr>(start));
+ CHECK_LE(offset, tls_size);
+ return tls_size - offset;
+}
+
DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res,
uptr static_tls_begin, uptr static_tls_end) {
if (!common_flags()->intercept_tls_get_addr) return 0;
@@ -117,44 +134,42 @@ DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res,
uptr dso_id = arg->dso_id;
DTLS::DTV *dtv = DTLS_Find(dso_id);
if (!dtv || dtv->beg)
- return 0;
- uptr tls_size = 0;
+ return nullptr;
+ CHECK_LE(static_tls_begin, static_tls_end);
uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset - kDtvOffset;
VReport(2,
- "__tls_get_addr: %p {0x%zx,0x%zx} => %p; tls_beg: 0x%zx; sp: %p "
+ "__tls_get_addr: %p {0x%zx,0x%zx} => %p; tls_beg: %p; sp: %p "
"num_live_dtls %zd\n",
- (void *)arg, arg->dso_id, arg->offset, res, tls_beg, (void *)&tls_beg,
+ (void *)arg, arg->dso_id, arg->offset, res, (void *)tls_beg,
+ (void *)&tls_beg,
atomic_load(&number_of_live_dtls, memory_order_relaxed));
- if (dtls.last_memalign_ptr == tls_beg) {
- tls_size = dtls.last_memalign_size;
- VReport(2, "__tls_get_addr: glibc <=2.24 suspected; tls={0x%zx,0x%zx}\n",
- tls_beg, tls_size);
- } else if (tls_beg >= static_tls_begin && tls_beg < static_tls_end) {
+ if (tls_beg >= static_tls_begin && tls_beg < static_tls_end) {
// This is the static TLS block which was initialized / unpoisoned at thread
// creation.
- VReport(2, "__tls_get_addr: static tls: 0x%zx\n", tls_beg);
- tls_size = 0;
- } else if (const void *start =
- __sanitizer_get_allocated_begin((void *)tls_beg)) {
- tls_beg = (uptr)start;
- tls_size = __sanitizer_get_allocated_size(start);
- VReport(2, "__tls_get_addr: glibc >=2.25 suspected; tls={0x%zx,0x%zx}\n",
- tls_beg, tls_size);
- } else {
- VReport(2, "__tls_get_addr: Can't guess glibc version\n");
- // This may happen inside the DTOR of main thread, so just ignore it.
- tls_size = 0;
+ VReport(2, "__tls_get_addr: static tls: %p\n", (void *)tls_beg);
+ dtv->beg = tls_beg;
+ dtv->size = 0;
+ return nullptr;
}
+ if (uptr tls_size =
+ __sanitizer_get_dtls_size(reinterpret_cast<void *>(tls_beg))) {
+ dtv->beg = tls_beg;
+ dtv->size = tls_size;
+ return dtv;
+ }
+ VReport(2, "__tls_get_addr: Can't guess glibc version\n");
+ // This may happen inside the DTOR a thread, or async signal handlers before
+ // thread initialization, so just ignore it.
+ //
+ // If the unknown block is dynamic TLS, unlikely we will be able to recognize
+ // it in future, mark it as done with '{tls_beg, 0}'.
+ //
+ // If the block is static TLS, possible reason of failed detection is nullptr
+ // in `static_tls_begin`. Regardless of reasons, the future handling of static
+ // TLS is still '{tls_beg, 0}'.
dtv->beg = tls_beg;
- dtv->size = tls_size;
- return dtv;
-}
-
-void DTLS_on_libc_memalign(void *ptr, uptr size) {
- if (!common_flags()->intercept_tls_get_addr) return;
- VReport(2, "DTLS_on_libc_memalign: %p 0x%zx\n", ptr, size);
- dtls.last_memalign_ptr = reinterpret_cast<uptr>(ptr);
- dtls.last_memalign_size = size;
+ dtv->size = 0;
+ return nullptr;
}
DTLS *DTLS_Get() { return &dtls; }
@@ -165,7 +180,9 @@ bool DTLSInDestruction(DTLS *dtls) {
}
#else
-void DTLS_on_libc_memalign(void *ptr, uptr size) {}
+SANITIZER_INTERFACE_WEAK_DEF(uptr, __sanitizer_get_dtls_size, const void *) {
+ return 0;
+}
DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res,
unsigned long, unsigned long) { return 0; }
DTLS *DTLS_Get() { return 0; }
@@ -55,10 +55,6 @@ struct DTLS {
static_assert(sizeof(DTVBlock) <= 4096UL, "Unexpected block size");
atomic_uintptr_t dtv_block;
-
- // Auxiliary fields, don't access them outside sanitizer_tls_get_addr.cpp
- uptr last_memalign_size;
- uptr last_memalign_ptr;
};
template <typename Fn>
new file mode 100644
@@ -0,0 +1,66 @@
+//===------------------ sanitizer_unwind_fuchsia.cpp
+//---------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+/// Sanitizer unwind Fuchsia specific functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_FUCHSIA
+
+# include <limits.h>
+# include <unwind.h>
+
+# include "sanitizer_common.h"
+# include "sanitizer_stacktrace.h"
+
+namespace __sanitizer {
+
+# if SANITIZER_CAN_SLOW_UNWIND
+struct UnwindTraceArg {
+ BufferedStackTrace *stack;
+ u32 max_depth;
+};
+
+_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
+ UnwindTraceArg *arg = static_cast<UnwindTraceArg *>(param);
+ CHECK_LT(arg->stack->size, arg->max_depth);
+ uptr pc = _Unwind_GetIP(ctx);
+ if (pc < GetPageSizeCached())
+ return _URC_NORMAL_STOP;
+ arg->stack->trace_buffer[arg->stack->size++] = pc;
+ return (arg->stack->size == arg->max_depth ? _URC_NORMAL_STOP
+ : _URC_NO_REASON);
+}
+
+void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) {
+ CHECK_GE(max_depth, 2);
+ size = 0;
+ UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
+ _Unwind_Backtrace(Unwind_Trace, &arg);
+ CHECK_GT(size, 0);
+ // We need to pop a few frames so that pc is on top.
+ uptr to_pop = LocatePcInTrace(pc);
+ // trace_buffer[0] belongs to the current function so we always pop it,
+ // unless there is only 1 frame in the stack trace (1 frame is always better
+ // than 0!).
+ PopStackFrames(Min(to_pop, static_cast<uptr>(1)));
+ trace_buffer[0] = pc;
+}
+
+void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) {
+ CHECK(context);
+ CHECK_GE(max_depth, 2);
+ UNREACHABLE("signal context doesn't exist");
+}
+# endif // SANITIZER_CAN_SLOW_UNWIND
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_FUCHSIA
@@ -70,10 +70,17 @@ void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) {
stack_frame.AddrStack.Offset = ctx.Rsp;
# endif
# else
+# if SANITIZER_ARM
+ int machine_type = IMAGE_FILE_MACHINE_ARM;
+ stack_frame.AddrPC.Offset = ctx.Pc;
+ stack_frame.AddrFrame.Offset = ctx.R11;
+ stack_frame.AddrStack.Offset = ctx.Sp;
+# else
int machine_type = IMAGE_FILE_MACHINE_I386;
stack_frame.AddrPC.Offset = ctx.Eip;
stack_frame.AddrFrame.Offset = ctx.Ebp;
stack_frame.AddrStack.Offset = ctx.Esp;
+# endif
# endif
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrFrame.Mode = AddrModeFlat;
@@ -144,7 +144,7 @@ void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
return rv;
}
-void UnmapOrDie(void *addr, uptr size) {
+void UnmapOrDie(void *addr, uptr size, bool raw_report) {
if (!size || !addr)
return;
@@ -156,10 +156,7 @@ void UnmapOrDie(void *addr, uptr size) {
// fails try MEM_DECOMMIT.
if (VirtualFree(addr, 0, MEM_RELEASE) == 0) {
if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) {
- Report("ERROR: %s failed to "
- "deallocate 0x%zx (%zd) bytes at address %p (error code: %d)\n",
- SanitizerToolName, size, size, addr, GetLastError());
- CHECK("unable to unmap" && 0);
+ ReportMunmapFailureAndDie(addr, size, GetLastError(), raw_report);
}
}
}
@@ -279,8 +276,8 @@ void *MmapFixedOrDie(uptr fixed_addr, uptr size, const char *name) {
MEM_COMMIT, PAGE_READWRITE);
if (p == 0) {
char mem_type[30];
- internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx",
- fixed_addr);
+ internal_snprintf(mem_type, sizeof(mem_type), "memory at address %p",
+ (void *)fixed_addr);
ReportMmapFailureAndDie(size, mem_type, "allocate", GetLastError());
}
return p;
@@ -311,8 +308,8 @@ void *MmapFixedOrDieOnFatalError(uptr fixed_addr, uptr size, const char *name) {
MEM_COMMIT, PAGE_READWRITE);
if (p == 0) {
char mem_type[30];
- internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx",
- fixed_addr);
+ internal_snprintf(mem_type, sizeof(mem_type), "memory at address %p",
+ (void *)fixed_addr);
return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate");
}
return p;
@@ -387,9 +384,8 @@ bool DontDumpShadowMemory(uptr addr, uptr length) {
}
uptr MapDynamicShadow(uptr shadow_size_bytes, uptr shadow_scale,
- uptr min_shadow_base_alignment,
- UNUSED uptr &high_mem_end) {
- const uptr granularity = GetMmapGranularity();
+ uptr min_shadow_base_alignment, UNUSED uptr &high_mem_end,
+ uptr granularity) {
const uptr alignment =
Max<uptr>(granularity << shadow_scale, 1ULL << min_shadow_base_alignment);
const uptr left_padding =
@@ -877,24 +873,18 @@ uptr GetTlsSize() {
return 0;
}
-void InitTlsSize() {
-}
-
-void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
- uptr *tls_addr, uptr *tls_size) {
-#if SANITIZER_GO
- *stk_addr = 0;
- *stk_size = 0;
- *tls_addr = 0;
- *tls_size = 0;
-#else
- uptr stack_top, stack_bottom;
- GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
- *stk_addr = stack_bottom;
- *stk_size = stack_top - stack_bottom;
- *tls_addr = 0;
- *tls_size = 0;
-#endif
+void GetThreadStackAndTls(bool main, uptr *stk_begin, uptr *stk_end,
+ uptr *tls_begin, uptr *tls_end) {
+# if SANITIZER_GO
+ *stk_begin = 0;
+ *stk_end = 0;
+ *tls_begin = 0;
+ *tls_end = 0;
+# else
+ GetThreadStackTopAndBottom(main, stk_end, stk_begin);
+ *tls_begin = 0;
+ *tls_end = 0;
+# endif
}
void ReportFile::Write(const char *buffer, uptr length) {
@@ -978,6 +968,11 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size) {
return true;
}
+bool TryMemCpy(void *dest, const void *src, uptr n) {
+ // TODO: implement.
+ return false;
+}
+
bool SignalContext::IsStackOverflow() const {
return (DWORD)GetType() == EXCEPTION_STACK_OVERFLOW;
}
@@ -996,8 +991,13 @@ void SignalContext::InitPcSpBp() {
sp = (uptr)context_record->Rsp;
# endif
# else
+# if SANITIZER_ARM
+ bp = (uptr)context_record->R11;
+ sp = (uptr)context_record->Sp;
+# else
bp = (uptr)context_record->Ebp;
sp = (uptr)context_record->Esp;
+# endif
# endif
}
@@ -1038,7 +1038,52 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
}
void SignalContext::DumpAllRegisters(void *context) {
- // FIXME: Implement this.
+ CONTEXT *ctx = (CONTEXT *)context;
+# if defined(_M_X64)
+ Report("Register values:\n");
+ Printf("rax = %llx ", ctx->Rax);
+ Printf("rbx = %llx ", ctx->Rbx);
+ Printf("rcx = %llx ", ctx->Rcx);
+ Printf("rdx = %llx ", ctx->Rdx);
+ Printf("\n");
+ Printf("rdi = %llx ", ctx->Rdi);
+ Printf("rsi = %llx ", ctx->Rsi);
+ Printf("rbp = %llx ", ctx->Rbp);
+ Printf("rsp = %llx ", ctx->Rsp);
+ Printf("\n");
+ Printf("r8 = %llx ", ctx->R8);
+ Printf("r9 = %llx ", ctx->R9);
+ Printf("r10 = %llx ", ctx->R10);
+ Printf("r11 = %llx ", ctx->R11);
+ Printf("\n");
+ Printf("r12 = %llx ", ctx->R12);
+ Printf("r13 = %llx ", ctx->R13);
+ Printf("r14 = %llx ", ctx->R14);
+ Printf("r15 = %llx ", ctx->R15);
+ Printf("\n");
+# elif defined(_M_IX86)
+ Report("Register values:\n");
+ Printf("eax = %lx ", ctx->Eax);
+ Printf("ebx = %lx ", ctx->Ebx);
+ Printf("ecx = %lx ", ctx->Ecx);
+ Printf("edx = %lx ", ctx->Edx);
+ Printf("\n");
+ Printf("edi = %lx ", ctx->Edi);
+ Printf("esi = %lx ", ctx->Esi);
+ Printf("ebp = %lx ", ctx->Ebp);
+ Printf("esp = %lx ", ctx->Esp);
+ Printf("\n");
+# elif defined(_M_ARM64)
+ Report("Register values:\n");
+ for (int i = 0; i <= 30; i++) {
+ Printf("x%d%s = %llx", i < 10 ? " " : "", ctx->X[i]);
+ if (i % 4 == 3)
+ Printf("\n");
+ }
+# else
+ // TODO
+ (void)ctx;
+# endif
}
int SignalContext::GetType() const {
deleted file mode 100644
@@ -1,101 +0,0 @@
-//===-- sanitizer_win_dll_thunk.cpp ---------------------------------------===//
-//
-// 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 defines a family of thunks that should be statically linked into
-// the DLLs that have instrumentation in order to delegate the calls to the
-// shared runtime that lives in the main binary.
-// See https://github.com/google/sanitizers/issues/209 for the details.
-//===----------------------------------------------------------------------===//
-
-#ifdef SANITIZER_DLL_THUNK
-#include "sanitizer_win_defs.h"
-#include "sanitizer_win_dll_thunk.h"
-#include "interception/interception.h"
-
-extern "C" {
-void *WINAPI GetModuleHandleA(const char *module_name);
-void abort();
-}
-
-namespace __sanitizer {
-uptr dllThunkGetRealAddrOrDie(const char *name) {
- uptr ret =
- __interception::InternalGetProcAddress((void *)GetModuleHandleA(0), name);
- if (!ret)
- abort();
- return ret;
-}
-
-int dllThunkIntercept(const char* main_function, uptr dll_function) {
- uptr wrapper = dllThunkGetRealAddrOrDie(main_function);
- if (!__interception::OverrideFunction(dll_function, wrapper, 0))
- abort();
- return 0;
-}
-
-int dllThunkInterceptWhenPossible(const char* main_function,
- const char* default_function, uptr dll_function) {
- uptr wrapper = __interception::InternalGetProcAddress(
- (void *)GetModuleHandleA(0), main_function);
- if (!wrapper)
- wrapper = dllThunkGetRealAddrOrDie(default_function);
- if (!__interception::OverrideFunction(dll_function, wrapper, 0))
- abort();
- return 0;
-}
-} // namespace __sanitizer
-
-// Include Sanitizer Common interface.
-#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
-#include "sanitizer_common_interface.inc"
-
-#pragma section(".DLLTH$A", read)
-#pragma section(".DLLTH$Z", read)
-
-typedef void (*DllThunkCB)();
-extern "C" {
-__declspec(allocate(".DLLTH$A")) DllThunkCB __start_dll_thunk;
-__declspec(allocate(".DLLTH$Z")) DllThunkCB __stop_dll_thunk;
-}
-
-// Disable compiler warnings that show up if we declare our own version
-// of a compiler intrinsic (e.g. strlen).
-#pragma warning(disable: 4391)
-#pragma warning(disable: 4392)
-
-extern "C" int __dll_thunk_init() {
- static bool flag = false;
- // __dll_thunk_init is expected to be called by only one thread.
- if (flag) return 0;
- flag = true;
-
- for (DllThunkCB *it = &__start_dll_thunk; it < &__stop_dll_thunk; ++it)
- if (*it)
- (*it)();
-
- // In DLLs, the callbacks are expected to return 0,
- // otherwise CRT initialization fails.
- return 0;
-}
-
-// We want to call dll_thunk_init before C/C++ initializers / constructors are
-// executed, otherwise functions like memset might be invoked.
-#pragma section(".CRT$XIB", long, read)
-__declspec(allocate(".CRT$XIB")) int (*__dll_thunk_preinit)() =
- __dll_thunk_init;
-
-static void WINAPI dll_thunk_thread_init(void *mod, unsigned long reason,
- void *reserved) {
- if (reason == /*DLL_PROCESS_ATTACH=*/1) __dll_thunk_init();
-}
-
-#pragma section(".CRT$XLAB", long, read)
-__declspec(allocate(".CRT$XLAB")) void (WINAPI *__dll_thunk_tls_init)(void *,
- unsigned long, void *) = dll_thunk_thread_init;
-
-#endif // SANITIZER_DLL_THUNK
deleted file mode 100644
@@ -1,181 +0,0 @@
-//===-- sanitizer_win_dll_thunk.h -----------------------------------------===//
-//
-// 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 header provide helper macros to delegate calls to the shared runtime
-// that lives in the main executable. It should be included to dll_thunks that
-// will be linked to the dlls, when the sanitizer is a static library included
-// in the main executable.
-//===----------------------------------------------------------------------===//
-#ifndef SANITIZER_WIN_DLL_THUNK_H
-#define SANITIZER_WIN_DLL_THUNK_H
-#include "sanitizer_internal_defs.h"
-
-namespace __sanitizer {
-uptr dllThunkGetRealAddrOrDie(const char *name);
-
-int dllThunkIntercept(const char* main_function, uptr dll_function);
-
-int dllThunkInterceptWhenPossible(const char* main_function,
- const char* default_function, uptr dll_function);
-}
-
-extern "C" int __dll_thunk_init();
-
-// ----------------- Function interception helper macros -------------------- //
-// Override dll_function with main_function from main executable.
-#define INTERCEPT_OR_DIE(main_function, dll_function) \
- static int intercept_##dll_function() { \
- return __sanitizer::dllThunkIntercept(main_function, (__sanitizer::uptr) \
- dll_function); \
- } \
- __pragma(section(".DLLTH$M", long, read)) \
- __declspec(allocate(".DLLTH$M")) int (*__dll_thunk_##dll_function)() = \
- intercept_##dll_function;
-
-// Try to override dll_function with main_function from main executable.
-// If main_function is not present, override dll_function with default_function.
-#define INTERCEPT_WHEN_POSSIBLE(main_function, default_function, dll_function) \
- static int intercept_##dll_function() { \
- return __sanitizer::dllThunkInterceptWhenPossible(main_function, \
- default_function, (__sanitizer::uptr)dll_function); \
- } \
- __pragma(section(".DLLTH$M", long, read)) \
- __declspec(allocate(".DLLTH$M")) int (*__dll_thunk_##dll_function)() = \
- intercept_##dll_function;
-
-// -------------------- Function interception macros ------------------------ //
-// Special case of hooks -- ASan own interface functions. Those are only called
-// after __asan_init, thus an empty implementation is sufficient.
-#define INTERCEPT_SANITIZER_FUNCTION(name) \
- extern "C" __declspec(noinline) void name() { \
- volatile int prevent_icf = (__LINE__ << 8) ^ __COUNTER__; \
- static const char function_name[] = #name; \
- for (const char* ptr = &function_name[0]; *ptr; ++ptr) \
- prevent_icf ^= *ptr; \
- (void)prevent_icf; \
- __debugbreak(); \
- } \
- INTERCEPT_OR_DIE(#name, name)
-
-// Special case of hooks -- Weak functions, could be redefined in the main
-// executable, but that is not necessary, so we shouldn't die if we can not find
-// a reference. Instead, when the function is not present in the main executable
-// we consider the default impl provided by asan library.
-#define INTERCEPT_SANITIZER_WEAK_FUNCTION(name) \
- extern "C" __declspec(noinline) void name() { \
- volatile int prevent_icf = (__LINE__ << 8) ^ __COUNTER__; \
- static const char function_name[] = #name; \
- for (const char* ptr = &function_name[0]; *ptr; ++ptr) \
- prevent_icf ^= *ptr; \
- (void)prevent_icf; \
- __debugbreak(); \
- } \
- INTERCEPT_WHEN_POSSIBLE(#name, STRINGIFY(WEAK_EXPORT_NAME(name)), name)
-
-// We can't define our own version of strlen etc. because that would lead to
-// link-time or even type mismatch errors. Instead, we can declare a function
-// just to be able to get its address. Me may miss the first few calls to the
-// functions since it can be called before __dll_thunk_init, but that would lead
-// to false negatives in the startup code before user's global initializers,
-// which isn't a big deal.
-#define INTERCEPT_LIBRARY_FUNCTION(name) \
- extern "C" void name(); \
- INTERCEPT_OR_DIE(STRINGIFY(WRAP(name)), name)
-
-// Use these macros for functions that could be called before __dll_thunk_init()
-// is executed and don't lead to errors if defined (free, malloc, etc).
-#define INTERCEPT_WRAP_V_V(name) \
- extern "C" void name() { \
- typedef decltype(name) *fntype; \
- static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
- fn(); \
- } \
- INTERCEPT_OR_DIE(#name, name);
-
-#define INTERCEPT_WRAP_V_W(name) \
- extern "C" void name(void *arg) { \
- typedef decltype(name) *fntype; \
- static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
- fn(arg); \
- } \
- INTERCEPT_OR_DIE(#name, name);
-
-#define INTERCEPT_WRAP_V_WW(name) \
- extern "C" void name(void *arg1, void *arg2) { \
- typedef decltype(name) *fntype; \
- static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
- fn(arg1, arg2); \
- } \
- INTERCEPT_OR_DIE(#name, name);
-
-#define INTERCEPT_WRAP_V_WWW(name) \
- extern "C" void name(void *arg1, void *arg2, void *arg3) { \
- typedef decltype(name) *fntype; \
- static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
- fn(arg1, arg2, arg3); \
- } \
- INTERCEPT_OR_DIE(#name, name);
-
-#define INTERCEPT_WRAP_W_V(name) \
- extern "C" void *name() { \
- typedef decltype(name) *fntype; \
- static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
- return fn(); \
- } \
- INTERCEPT_OR_DIE(#name, name);
-
-#define INTERCEPT_WRAP_W_W(name) \
- extern "C" void *name(void *arg) { \
- typedef decltype(name) *fntype; \
- static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
- return fn(arg); \
- } \
- INTERCEPT_OR_DIE(#name, name);
-
-#define INTERCEPT_WRAP_W_WW(name) \
- extern "C" void *name(void *arg1, void *arg2) { \
- typedef decltype(name) *fntype; \
- static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
- return fn(arg1, arg2); \
- } \
- INTERCEPT_OR_DIE(#name, name);
-
-#define INTERCEPT_WRAP_W_WWW(name) \
- extern "C" void *name(void *arg1, void *arg2, void *arg3) { \
- typedef decltype(name) *fntype; \
- static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
- return fn(arg1, arg2, arg3); \
- } \
- INTERCEPT_OR_DIE(#name, name);
-
-#define INTERCEPT_WRAP_W_WWWW(name) \
- extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \
- typedef decltype(name) *fntype; \
- static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
- return fn(arg1, arg2, arg3, arg4); \
- } \
- INTERCEPT_OR_DIE(#name, name);
-
-#define INTERCEPT_WRAP_W_WWWWW(name) \
- extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
- void *arg5) { \
- typedef decltype(name) *fntype; \
- static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
- return fn(arg1, arg2, arg3, arg4, arg5); \
- } \
- INTERCEPT_OR_DIE(#name, name);
-
-#define INTERCEPT_WRAP_W_WWWWWW(name) \
- extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
- void *arg5, void *arg6) { \
- typedef decltype(name) *fntype; \
- static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \
- return fn(arg1, arg2, arg3, arg4, arg5, arg6); \
- } \
- INTERCEPT_OR_DIE(#name, name);
-
-#endif // SANITIZER_WIN_DLL_THUNK_H
deleted file mode 100644
@@ -1,26 +0,0 @@
-//===-- santizer_win_dynamic_runtime_thunk.cpp ----------------------------===//
-//
-// 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 defines things that need to be present in the application modules
-// to interact with Sanitizer Common, when it is included in a dll.
-//
-//===----------------------------------------------------------------------===//
-#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK
-#define SANITIZER_IMPORT_INTERFACE 1
-#include "sanitizer_win_defs.h"
-// Define weak alias for all weak functions imported from sanitizer common.
-#define INTERFACE_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name)
-#include "sanitizer_common_interface.inc"
-#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK
-
-namespace __sanitizer {
-// Add one, otherwise unused, external symbol to this object file so that the
-// Visual C++ linker includes it and reads the .drective section.
-void ForceWholeArchiveIncludeForSanitizerCommon() {}
-}
new file mode 100644
@@ -0,0 +1,71 @@
+//===-- sanitizer_win_immortalize.h ---------------------------------------===//
+//
+// 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 shared between AddressSanitizer, and interception.
+//
+// Windows-specific thread-safe and pre-CRT global initialization safe
+// infrastructure to create an object whose destructor is never called.
+//===----------------------------------------------------------------------===//
+#if SANITIZER_WINDOWS
+# pragma once
+// Requires including sanitizer_placement_new.h (which is not allowed to be
+// included in headers).
+
+# include "sanitizer_win_defs.h"
+// These types are required to satisfy XFG which requires that the names of the
+// types for indirect calls to be correct as well as the name of the original
+// type for any typedefs.
+
+// TODO: There must be a better way to do this
+# ifndef _WINDOWS_
+typedef void* PVOID;
+typedef int BOOL;
+typedef union _RTL_RUN_ONCE {
+ PVOID ptr;
+} INIT_ONCE, *PINIT_ONCE;
+
+extern "C" {
+__declspec(dllimport) int WINAPI InitOnceExecuteOnce(
+ PINIT_ONCE, BOOL(WINAPI*)(PINIT_ONCE, PVOID, PVOID*), void*, void*);
+}
+# endif
+
+namespace __sanitizer {
+template <class Ty>
+BOOL WINAPI immortalize_impl(PINIT_ONCE, PVOID storage_ptr, PVOID*) noexcept {
+ // Ty must provide a placement new operator
+ new (storage_ptr) Ty();
+ return 1;
+}
+
+template <class Ty, typename Arg>
+BOOL WINAPI immortalize_impl(PINIT_ONCE, PVOID storage_ptr,
+ PVOID* param) noexcept {
+ // Ty must provide a placement new operator
+ new (storage_ptr) Ty(*((Arg*)param));
+ return 1;
+}
+
+template <class Ty>
+Ty& immortalize() { // return a reference to an object that will live forever
+ static INIT_ONCE flag;
+ alignas(Ty) static unsigned char storage[sizeof(Ty)];
+ InitOnceExecuteOnce(&flag, immortalize_impl<Ty>, &storage, nullptr);
+ return reinterpret_cast<Ty&>(storage);
+}
+
+template <class Ty, typename Arg>
+Ty& immortalize(
+ Arg arg) { // return a reference to an object that will live forever
+ static INIT_ONCE flag;
+ alignas(Ty) static unsigned char storage[sizeof(Ty)];
+ InitOnceExecuteOnce(&flag, immortalize_impl<Ty, Arg>, &storage, &arg);
+ return reinterpret_cast<Ty&>(storage);
+}
+} // namespace __sanitizer
+#endif // SANITIZER_WINDOWS
new file mode 100644
@@ -0,0 +1,156 @@
+//===-- sanitizer_win_interception.cpp -------------------- --*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Windows-specific export surface to provide interception for parts of the
+// runtime that are always statically linked, both for overriding user-defined
+// functions as well as registering weak functions that the ASAN runtime should
+// use over defaults.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_WINDOWS
+# include <stddef.h>
+
+# include "interception/interception.h"
+# include "sanitizer_addrhashmap.h"
+# include "sanitizer_common.h"
+# include "sanitizer_internal_defs.h"
+# include "sanitizer_placement_new.h"
+# include "sanitizer_win_immortalize.h"
+# include "sanitizer_win_interception.h"
+
+using namespace __sanitizer;
+
+extern "C" void *__ImageBase;
+
+namespace __sanitizer {
+
+static uptr GetSanitizerDllExport(const char *export_name) {
+ const uptr function_address =
+ __interception::InternalGetProcAddress(&__ImageBase, export_name);
+ if (function_address == 0) {
+ Report("ERROR: Failed to find sanitizer DLL export '%s'\n", export_name);
+ CHECK("Failed to find sanitizer DLL export" && 0);
+ }
+ return function_address;
+}
+
+struct WeakCallbackList {
+ explicit constexpr WeakCallbackList(RegisterWeakFunctionCallback cb)
+ : callback(cb), next(nullptr) {}
+
+ static void *operator new(size_t size) { return InternalAlloc(size); }
+
+ static void operator delete(void *p) { InternalFree(p); }
+
+ RegisterWeakFunctionCallback callback;
+ WeakCallbackList *next;
+};
+using WeakCallbackMap = AddrHashMap<WeakCallbackList *, 11>;
+
+static WeakCallbackMap *GetWeakCallbackMap() {
+ return &immortalize<WeakCallbackMap>();
+}
+
+void AddRegisterWeakFunctionCallback(uptr export_address,
+ RegisterWeakFunctionCallback cb) {
+ WeakCallbackMap::Handle h_find_or_create(GetWeakCallbackMap(), export_address,
+ false, true);
+ CHECK(h_find_or_create.exists());
+ if (h_find_or_create.created()) {
+ *h_find_or_create = new WeakCallbackList(cb);
+ } else {
+ (*h_find_or_create)->next = new WeakCallbackList(cb);
+ }
+}
+
+static void RunWeakFunctionCallbacks(uptr export_address) {
+ WeakCallbackMap::Handle h_find(GetWeakCallbackMap(), export_address, false,
+ false);
+ if (!h_find.exists()) {
+ return;
+ }
+
+ WeakCallbackList *list = *h_find;
+ do {
+ list->callback();
+ } while ((list = list->next));
+}
+
+} // namespace __sanitizer
+
+extern "C" __declspec(dllexport) bool __cdecl __sanitizer_override_function(
+ const char *export_name, const uptr user_function,
+ uptr *const old_user_function) {
+ CHECK(export_name);
+ CHECK(user_function);
+
+ const uptr sanitizer_function = GetSanitizerDllExport(export_name);
+
+ const bool function_overridden = __interception::OverrideFunction(
+ user_function, sanitizer_function, old_user_function);
+ if (!function_overridden) {
+ Report(
+ "ERROR: Failed to override local function at '%p' with sanitizer "
+ "function '%s'\n",
+ user_function, export_name);
+ CHECK("Failed to replace local function with sanitizer version." && 0);
+ }
+
+ return function_overridden;
+}
+
+extern "C"
+ __declspec(dllexport) bool __cdecl __sanitizer_override_function_by_addr(
+ const uptr source_function, const uptr target_function,
+ uptr *const old_target_function) {
+ CHECK(source_function);
+ CHECK(target_function);
+
+ const bool function_overridden = __interception::OverrideFunction(
+ target_function, source_function, old_target_function);
+ if (!function_overridden) {
+ Report(
+ "ERROR: Failed to override function at '%p' with function at "
+ "'%p'\n",
+ target_function, source_function);
+ CHECK("Failed to apply function override." && 0);
+ }
+
+ return function_overridden;
+}
+
+extern "C"
+ __declspec(dllexport) bool __cdecl __sanitizer_register_weak_function(
+ const char *export_name, const uptr user_function,
+ uptr *const old_user_function) {
+ CHECK(export_name);
+ CHECK(user_function);
+
+ const uptr sanitizer_function = GetSanitizerDllExport(export_name);
+
+ const bool function_overridden = __interception::OverrideFunction(
+ sanitizer_function, user_function, old_user_function);
+ if (!function_overridden) {
+ Report(
+ "ERROR: Failed to register local function at '%p' to be used in "
+ "place of sanitizer function '%s'\n.",
+ user_function, export_name);
+ CHECK("Failed to register weak function." && 0);
+ }
+
+ // Note that thread-safety of RunWeakFunctionCallbacks in InitializeFlags
+ // depends on __sanitizer_register_weak_functions being called during the
+ // loader lock.
+ RunWeakFunctionCallbacks(sanitizer_function);
+
+ return function_overridden;
+}
+
+#endif // SANITIZER_WINDOWS
new file mode 100644
@@ -0,0 +1,32 @@
+//===-- sanitizer_win_interception.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Windows-specific export surface to provide interception for parts of the
+// runtime that are always statically linked, both for overriding user-defined
+// functions as well as registering weak functions that the ASAN runtime should
+// use over defaults.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_WIN_INTERCEPTION_H
+#define SANITIZER_WIN_INTERCEPTION_H
+
+#include "sanitizer_platform.h"
+#if SANITIZER_WINDOWS
+
+# include "sanitizer_common.h"
+# include "sanitizer_internal_defs.h"
+
+namespace __sanitizer {
+using RegisterWeakFunctionCallback = void (*)();
+void AddRegisterWeakFunctionCallback(uptr export_address,
+ RegisterWeakFunctionCallback cb);
+} // namespace __sanitizer
+
+#endif // SANITIZER_WINDOWS
+#endif // SANITIZER_WIN_INTERCEPTION_H
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,110 @@
+//===-- sanitizer_win_thunk_interception.cpp ----------------------- -----===//
+//
+// 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 defines things that need to be present in the application modules
+// to interact with sanitizer DLL correctly and cannot be implemented using the
+// default "import library" generated when linking the DLL.
+//
+// This includes the common infrastructure required to intercept local functions
+// that must be replaced with sanitizer-aware versions, as well as the
+// registration of weak functions with the sanitizer DLL. With this in-place,
+// other sanitizer components can simply write to the .INTR and .WEAK sections.
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(SANITIZER_STATIC_RUNTIME_THUNK) || \
+ defined(SANITIZER_DYNAMIC_RUNTIME_THUNK)
+# include "sanitizer_win_thunk_interception.h"
+
+extern "C" void abort();
+
+namespace __sanitizer {
+
+int override_function(const char *export_name, const uptr user_function) {
+ if (!__sanitizer_override_function(export_name, user_function)) {
+ abort();
+ }
+
+ return 0;
+}
+
+int register_weak(const char *export_name, const uptr user_function) {
+ if (!__sanitizer_register_weak_function(export_name, user_function)) {
+ abort();
+ }
+
+ return 0;
+}
+
+void initialize_thunks(const sanitizer_thunk *first,
+ const sanitizer_thunk *last) {
+ for (const sanitizer_thunk *it = first; it < last; ++it) {
+ if (*it) {
+ (*it)();
+ }
+ }
+}
+} // namespace __sanitizer
+
+# define INTERFACE_FUNCTION(Name)
+# define INTERFACE_WEAK_FUNCTION(Name) REGISTER_WEAK_FUNCTION(Name)
+# include "sanitizer_common_interface.inc"
+
+# pragma section(".INTR$A", read) // intercept begin
+# pragma section(".INTR$Z", read) // intercept end
+# pragma section(".WEAK$A", read) // weak begin
+# pragma section(".WEAK$Z", read) // weak end
+
+extern "C" {
+__declspec(allocate(
+ ".INTR$A")) sanitizer_thunk __sanitizer_intercept_thunk_begin;
+__declspec(allocate(".INTR$Z")) sanitizer_thunk __sanitizer_intercept_thunk_end;
+
+__declspec(allocate(
+ ".WEAK$A")) sanitizer_thunk __sanitizer_register_weak_thunk_begin;
+__declspec(allocate(
+ ".WEAK$Z")) sanitizer_thunk __sanitizer_register_weak_thunk_end;
+}
+
+extern "C" int __sanitizer_thunk_init() {
+ // __sanitizer_static_thunk_init is expected to be called by only one thread.
+ static bool flag = false;
+ if (flag) {
+ return 0;
+ }
+ flag = true;
+
+ __sanitizer::initialize_thunks(&__sanitizer_intercept_thunk_begin,
+ &__sanitizer_intercept_thunk_end);
+ __sanitizer::initialize_thunks(&__sanitizer_register_weak_thunk_begin,
+ &__sanitizer_register_weak_thunk_end);
+
+ // In DLLs, the callbacks are expected to return 0,
+ // otherwise CRT initialization fails.
+ return 0;
+}
+
+// We want to call dll_thunk_init before C/C++ initializers / constructors are
+// executed, otherwise functions like memset might be invoked.
+# pragma section(".CRT$XIB", long, read)
+__declspec(allocate(".CRT$XIB")) int (*__sanitizer_thunk_init_ptr)() =
+ __sanitizer_thunk_init;
+
+static void WINAPI sanitizer_thunk_thread_init(void *mod, unsigned long reason,
+ void *reserved) {
+ if (reason == /*DLL_PROCESS_ATTACH=*/1)
+ __sanitizer_thunk_init();
+}
+
+# pragma section(".CRT$XLAB", long, read)
+__declspec(allocate(".CRT$XLAB")) void(
+ WINAPI *__sanitizer_thunk_thread_init_ptr)(void *, unsigned long, void *) =
+ sanitizer_thunk_thread_init;
+
+#endif // defined(SANITIZER_STATIC_RUNTIME_THUNK) ||
+ // defined(SANITIZER_DYNAMIC_RUNTIME_THUNK)
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,88 @@
+//===-- sanitizer_win_thunk_interception.h ------------------------- -----===//
+//
+// 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 header provide helper macros and functions to delegate calls to the
+// shared runtime that lives in the sanitizer DLL.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_WIN_THUNK_INTERCEPTION_H
+#define SANITIZER_WIN_THUNK_INTERCEPTION_H
+#include <stdint.h>
+
+#include "sanitizer_internal_defs.h"
+
+extern "C" {
+__declspec(dllimport) bool __cdecl __sanitizer_override_function(
+ const char *export_name, __sanitizer::uptr user_function,
+ __sanitizer::uptr *old_function = nullptr);
+__declspec(dllimport) bool __cdecl __sanitizer_override_function_by_addr(
+ __sanitizer::uptr source_function, __sanitizer::uptr target_function,
+ __sanitizer::uptr *old_target_function = nullptr);
+__declspec(dllimport) bool __cdecl __sanitizer_register_weak_function(
+ const char *export_name, __sanitizer::uptr user_function,
+ __sanitizer::uptr *old_function = nullptr);
+}
+
+using sanitizer_thunk = int (*)();
+
+namespace __sanitizer {
+int override_function(const char *export_name, uptr user_function);
+int register_weak(const char *export_name, uptr user_function);
+void initialize_thunks(const sanitizer_thunk *begin,
+ const sanitizer_thunk *end);
+} // namespace __sanitizer
+
+// -------------------- Function interception macros ------------------------ //
+// We can't define our own version of strlen etc. because that would lead to
+// link-time or even type mismatch errors. Instead, we can declare a function
+// just to be able to get its address. Me may miss the first few calls to the
+// functions since it can be called before __dll_thunk_init, but that would lead
+// to false negatives in the startup code before user's global initializers,
+// which isn't a big deal.
+// Use .INTR segment to register function pointers that are iterated over during
+// startup that will replace local_function with sanitizer_export.
+
+#define INTERCEPT_LIBRARY_FUNCTION(local_function, sanitizer_export) \
+ extern "C" void local_function(); \
+ static int intercept_##local_function() { \
+ return __sanitizer::override_function( \
+ sanitizer_export, \
+ reinterpret_cast<__sanitizer::uptr>(local_function)); \
+ } \
+ __pragma(section(".INTR$M", long, read)) __declspec(allocate( \
+ ".INTR$M")) int (*__sanitizer_static_thunk_##local_function)() = \
+ intercept_##local_function;
+
+// ------------------ Weak symbol registration macros ---------------------- //
+// Use .WEAK segment to register function pointers that are iterated over during
+// startup that will replace sanitizer_export with local_function
+#ifdef __clang__
+# define REGISTER_WEAK_OPTNONE __attribute__((optnone))
+# define REGISTER_WEAK_FUNCTION_ADDRESS(fn) __builtin_function_start(fn)
+#else
+# define REGISTER_WEAK_OPTNONE
+# define REGISTER_WEAK_FUNCTION_ADDRESS(fn) &fn
+#endif
+
+#define REGISTER_WEAK_FUNCTION(local_function) \
+ extern "C" void local_function(); \
+ extern "C" void WEAK_EXPORT_NAME(local_function)(); \
+ WIN_WEAK_IMPORT_DEF(local_function) \
+ REGISTER_WEAK_OPTNONE static int register_weak_##local_function() { \
+ if ((uintptr_t)REGISTER_WEAK_FUNCTION_ADDRESS(local_function) != \
+ (uintptr_t)REGISTER_WEAK_FUNCTION_ADDRESS( \
+ WEAK_EXPORT_NAME(local_function))) { \
+ return __sanitizer::register_weak( \
+ SANITIZER_STRINGIFY(WEAK_EXPORT_NAME(local_function)), \
+ reinterpret_cast<__sanitizer::uptr>(local_function)); \
+ } \
+ return 0; \
+ } \
+ __pragma(section(".WEAK$M", long, read)) __declspec(allocate( \
+ ".WEAK$M")) int (*__sanitizer_register_weak_##local_function)() = \
+ register_weak_##local_function;
+#endif // SANITIZER_WIN_STATIC_RUNTIME_THUNK_H
deleted file mode 100644
@@ -1,94 +0,0 @@
-//===-- sanitizer_win_weak_interception.cpp -------------------------------===//
-//
-// 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 module should be included in the sanitizer when it is implemented as a
-// shared library on Windows (dll), in order to delegate the calls of weak
-// functions to the implementation in the main executable when a strong
-// definition is provided.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_WINDOWS && SANITIZER_DYNAMIC
-#include "sanitizer_win_weak_interception.h"
-#include "sanitizer_allocator_interface.h"
-#include "sanitizer_interface_internal.h"
-#include "sanitizer_win_defs.h"
-#include "interception/interception.h"
-
-extern "C" {
-void *WINAPI GetModuleHandleA(const char *module_name);
-void abort();
-}
-
-namespace __sanitizer {
-// Try to get a pointer to real_function in the main module and override
-// dll_function with that pointer. If the function isn't found, nothing changes.
-int interceptWhenPossible(uptr dll_function, const char *real_function) {
- uptr real = __interception::InternalGetProcAddress(
- (void *)GetModuleHandleA(0), real_function);
- if (real && !__interception::OverrideFunction((uptr)dll_function, real, 0))
- abort();
- return 0;
-}
-} // namespace __sanitizer
-
-// Declare weak hooks.
-extern "C" {
-void __sanitizer_on_print(const char *str);
-void __sanitizer_weak_hook_memcmp(uptr called_pc, const void *s1,
- const void *s2, uptr n, int result);
-void __sanitizer_weak_hook_strcmp(uptr called_pc, const char *s1,
- const char *s2, int result);
-void __sanitizer_weak_hook_strncmp(uptr called_pc, const char *s1,
- const char *s2, uptr n, int result);
-void __sanitizer_weak_hook_strstr(uptr called_pc, const char *s1,
- const char *s2, char *result);
-}
-
-// Include Sanitizer Common interface.
-#define INTERFACE_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
-#include "sanitizer_common_interface.inc"
-
-#pragma section(".WEAK$A", read)
-#pragma section(".WEAK$Z", read)
-
-typedef void (*InterceptCB)();
-extern "C" {
-__declspec(allocate(".WEAK$A")) InterceptCB __start_weak_list;
-__declspec(allocate(".WEAK$Z")) InterceptCB __stop_weak_list;
-}
-
-static int weak_intercept_init() {
- static bool flag = false;
- // weak_interception_init is expected to be called by only one thread.
- if (flag) return 0;
- flag = true;
-
- for (InterceptCB *it = &__start_weak_list; it < &__stop_weak_list; ++it)
- if (*it)
- (*it)();
-
- // In DLLs, the callbacks are expected to return 0,
- // otherwise CRT initialization fails.
- return 0;
-}
-
-#pragma section(".CRT$XIB", long, read)
-__declspec(allocate(".CRT$XIB")) int (*__weak_intercept_preinit)() =
- weak_intercept_init;
-
-static void WINAPI weak_intercept_thread_init(void *mod, unsigned long reason,
- void *reserved) {
- if (reason == /*DLL_PROCESS_ATTACH=*/1) weak_intercept_init();
-}
-
-#pragma section(".CRT$XLAB", long, read)
-__declspec(allocate(".CRT$XLAB")) void(WINAPI *__weak_intercept_tls_init)(
- void *, unsigned long, void *) = weak_intercept_thread_init;
-
-#endif // SANITIZER_WINDOWS && SANITIZER_DYNAMIC
deleted file mode 100644
@@ -1,32 +0,0 @@
-//===-- sanitizer_win_weak_interception.h ---------------------------------===//
-//
-// 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 header provide helper macros to delegate calls of weak functions to the
-// implementation in the main executable when a strong definition is present.
-//===----------------------------------------------------------------------===//
-#ifndef SANITIZER_WIN_WEAK_INTERCEPTION_H
-#define SANITIZER_WIN_WEAK_INTERCEPTION_H
-#include "sanitizer_internal_defs.h"
-
-namespace __sanitizer {
-int interceptWhenPossible(uptr dll_function, const char *real_function);
-}
-
-// ----------------- Function interception helper macros -------------------- //
-// Weak functions, could be redefined in the main executable, but that is not
-// necessary, so we shouldn't die if we can not find a reference.
-#define INTERCEPT_WEAK(Name) interceptWhenPossible((uptr) Name, #Name);
-
-#define INTERCEPT_SANITIZER_WEAK_FUNCTION(Name) \
- static int intercept_##Name() { \
- return __sanitizer::interceptWhenPossible((__sanitizer::uptr) Name, #Name);\
- } \
- __pragma(section(".WEAK$M", long, read)) \
- __declspec(allocate(".WEAK$M")) int (*__weak_intercept_##Name)() = \
- intercept_##Name;
-
-#endif // SANITIZER_WIN_WEAK_INTERCEPTION_H
@@ -30,7 +30,7 @@
# define __MM_MALLOC_H
# include <emmintrin.h>
# include <smmintrin.h>
-# define VECTOR_ALIGNED ALIGNED(16)
+# define VECTOR_ALIGNED alignas(16)
typedef __m128i m128;
#else
# define VECTOR_ALIGNED
@@ -56,13 +56,6 @@ extern const dispatch_block_t _dispatch_data_destructor_munmap;
# define DISPATCH_NOESCAPE
#endif
-#if SANITIZER_APPLE
-# define SANITIZER_WEAK_IMPORT extern "C" __attribute((weak_import))
-#else
-# define SANITIZER_WEAK_IMPORT extern "C" __attribute((weak))
-#endif
-
-
// Data types used in dispatch APIs
typedef unsigned long size_t;
typedef unsigned long uintptr_t;
@@ -94,6 +94,10 @@ static constexpr morder kMacFailureOrder = mo_relaxed;
m_orig(int32_t, uint32_t, a32, f##32##OrigBarrier, \
__tsan_atomic32_##tsan_atomic_f, kMacOrderBarrier)
+
+#pragma clang diagnostic push
+// OSAtomic* functions are deprecated.
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
OSATOMIC_INTERCEPTORS_ARITHMETIC(OSAtomicAdd, fetch_add,
OSATOMIC_INTERCEPTOR_PLUS_X)
OSATOMIC_INTERCEPTORS_ARITHMETIC(OSAtomicIncrement, fetch_add,
@@ -123,6 +127,9 @@ OSATOMIC_INTERCEPTORS_BITWISE(OSAtomicXor, fetch_xor,
kMacOrderBarrier, kMacFailureOrder); \
}
+#pragma clang diagnostic push
+// OSAtomicCompareAndSwap* functions are deprecated.
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwapInt, __tsan_atomic32, a32, int)
OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwapLong, __tsan_atomic64, a64,
long_t)
@@ -132,6 +139,7 @@ OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwap32, __tsan_atomic32, a32,
int32_t)
OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwap64, __tsan_atomic64, a64,
int64_t)
+#pragma clang diagnostic pop
#define OSATOMIC_INTERCEPTOR_BITOP(f, op, clear, mo) \
TSAN_INTERCEPTOR(bool, f, uint32_t n, volatile void *ptr) { \
@@ -12,13 +12,15 @@
// sanitizer_common/sanitizer_common_interceptors.inc
//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_allocator_dlsym.h"
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_errno.h"
+#include "sanitizer_common/sanitizer_glibc_version.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_linux.h"
#include "sanitizer_common/sanitizer_platform_limits_netbsd.h"
#include "sanitizer_common/sanitizer_platform_limits_posix.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_posix.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_tls_get_addr.h"
@@ -207,7 +209,7 @@ struct AtExitCtx {
struct InterceptorContext {
// The object is 64-byte aligned, because we want hot data to be located
// in a single cache line if possible (it's accessed in every interceptor).
- ALIGNED(64) LibIgnore libignore;
+ alignas(64) LibIgnore libignore;
__sanitizer_sigaction sigactions[kSigCount];
#if !SANITIZER_APPLE && !SANITIZER_NETBSD
unsigned finalize_key;
@@ -219,7 +221,7 @@ struct InterceptorContext {
InterceptorContext() : libignore(LINKER_INITIALIZED), atexit_mu(MutexTypeAtExit), AtExitStack() {}
};
-static ALIGNED(64) char interceptor_placeholder[sizeof(InterceptorContext)];
+alignas(64) static char interceptor_placeholder[sizeof(InterceptorContext)];
InterceptorContext *interceptor_ctx() {
return reinterpret_cast<InterceptorContext*>(&interceptor_placeholder[0]);
}
@@ -251,6 +253,13 @@ SANITIZER_WEAK_CXX_DEFAULT_IMPL void OnPotentiallyBlockingRegionBegin() {}
SANITIZER_WEAK_CXX_DEFAULT_IMPL void OnPotentiallyBlockingRegionEnd() {}
#endif
+// FIXME: Use for `in_symbolizer()` as well. As-is we can't use
+// `DlSymAllocator`, because it uses the primary allocator only. Symbolizer
+// requires support of the secondary allocator for larger blocks.
+struct DlsymAlloc : public DlSymAllocator<DlsymAlloc> {
+ static bool UseImpl() { return (ctx && !ctx->initialized); }
+};
+
} // namespace __tsan
static ThreadSignalContext *SigCtx(ThreadState *thr) {
@@ -660,6 +669,8 @@ TSAN_INTERCEPTOR(void, _longjmp, uptr *env, int val) {
TSAN_INTERCEPTOR(void*, malloc, uptr size) {
if (in_symbolizer())
return InternalAlloc(size);
+ if (DlsymAlloc::Use())
+ return DlsymAlloc::Allocate(size);
void *p = 0;
{
SCOPED_INTERCEPTOR_RAW(malloc, size);
@@ -677,12 +688,14 @@ TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) {
return user_memalign(thr, pc, align, sz);
}
-TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) {
+TSAN_INTERCEPTOR(void *, calloc, uptr n, uptr size) {
if (in_symbolizer())
- return InternalCalloc(size, n);
+ return InternalCalloc(n, size);
+ if (DlsymAlloc::Use())
+ return DlsymAlloc::Callocate(n, size);
void *p = 0;
{
- SCOPED_INTERCEPTOR_RAW(calloc, size, n);
+ SCOPED_INTERCEPTOR_RAW(calloc, n, size);
p = user_calloc(thr, pc, size, n);
}
invoke_malloc_hook(p, n * size);
@@ -692,6 +705,8 @@ TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) {
TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) {
if (in_symbolizer())
return InternalRealloc(p, size);
+ if (DlsymAlloc::Use() || DlsymAlloc::PointerIsMine(p))
+ return DlsymAlloc::Realloc(p, size);
if (p)
invoke_free_hook(p);
{
@@ -702,13 +717,13 @@ TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) {
return p;
}
-TSAN_INTERCEPTOR(void*, reallocarray, void *p, uptr size, uptr n) {
+TSAN_INTERCEPTOR(void *, reallocarray, void *p, uptr n, uptr size) {
if (in_symbolizer())
- return InternalReallocArray(p, size, n);
+ return InternalReallocArray(p, n, size);
if (p)
invoke_free_hook(p);
{
- SCOPED_INTERCEPTOR_RAW(reallocarray, p, size, n);
+ SCOPED_INTERCEPTOR_RAW(reallocarray, p, n, size);
p = user_reallocarray(thr, pc, p, size, n);
}
invoke_malloc_hook(p, size);
@@ -716,20 +731,24 @@ TSAN_INTERCEPTOR(void*, reallocarray, void *p, uptr size, uptr n) {
}
TSAN_INTERCEPTOR(void, free, void *p) {
- if (p == 0)
+ if (UNLIKELY(!p))
return;
if (in_symbolizer())
return InternalFree(p);
+ if (DlsymAlloc::PointerIsMine(p))
+ return DlsymAlloc::Free(p);
invoke_free_hook(p);
SCOPED_INTERCEPTOR_RAW(free, p);
user_free(thr, pc, p);
}
TSAN_INTERCEPTOR(void, cfree, void *p) {
- if (p == 0)
+ if (UNLIKELY(!p))
return;
if (in_symbolizer())
return InternalFree(p);
+ if (DlsymAlloc::PointerIsMine(p))
+ return DlsymAlloc::Free(p);
invoke_free_hook(p);
SCOPED_INTERCEPTOR_RAW(cfree, p);
user_free(thr, pc, p);
@@ -1087,7 +1106,18 @@ TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) {
return res;
}
-DEFINE_REAL_PTHREAD_FUNCTIONS
+// DEFINE_INTERNAL_PTHREAD_FUNCTIONS
+namespace __sanitizer {
+int internal_pthread_create(void *th, void *attr, void *(*callback)(void *),
+ void *param) {
+ ScopedIgnoreInterceptors ignore;
+ return REAL(pthread_create)(th, attr, callback, param);
+}
+int internal_pthread_join(void *th, void **ret) {
+ ScopedIgnoreInterceptors ignore;
+ return REAL(pthread_join)(th, ret);
+}
+} // namespace __sanitizer
TSAN_INTERCEPTOR(int, pthread_detach, void *th) {
SCOPED_INTERCEPTOR_RAW(pthread_detach, th);
@@ -1340,7 +1370,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_destroy, void *m) {
TSAN_INTERCEPTOR(int, pthread_mutex_lock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_mutex_lock, m);
MutexPreLock(thr, pc, (uptr)m);
- int res = REAL(pthread_mutex_lock)(m);
+ int res = BLOCK_REAL(pthread_mutex_lock)(m);
if (res == errno_EOWNERDEAD)
MutexRepair(thr, pc, (uptr)m);
if (res == 0 || res == errno_EOWNERDEAD)
@@ -1380,6 +1410,22 @@ TSAN_INTERCEPTOR(int, pthread_mutex_unlock, void *m) {
return res;
}
+#if SANITIZER_LINUX
+TSAN_INTERCEPTOR(int, pthread_mutex_clocklock, void *m,
+ __sanitizer_clockid_t clock, void *abstime) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_mutex_clocklock, m, clock, abstime);
+ MutexPreLock(thr, pc, (uptr)m);
+ int res = BLOCK_REAL(pthread_mutex_clocklock)(m, clock, abstime);
+ if (res == errno_EOWNERDEAD)
+ MutexRepair(thr, pc, (uptr)m);
+ if (res == 0 || res == errno_EOWNERDEAD)
+ MutexPostLock(thr, pc, (uptr)m);
+ if (res == errno_EINVAL)
+ MutexInvalidAccess(thr, pc, (uptr)m);
+ return res;
+}
+#endif
+
#if SANITIZER_GLIBC
# if !__GLIBC_PREREQ(2, 34)
// glibc 2.34 applies a non-default version for the two functions. They are no
@@ -1387,7 +1433,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_unlock, void *m) {
TSAN_INTERCEPTOR(int, __pthread_mutex_lock, void *m) {
SCOPED_TSAN_INTERCEPTOR(__pthread_mutex_lock, m);
MutexPreLock(thr, pc, (uptr)m);
- int res = REAL(__pthread_mutex_lock)(m);
+ int res = BLOCK_REAL(__pthread_mutex_lock)(m);
if (res == errno_EOWNERDEAD)
MutexRepair(thr, pc, (uptr)m);
if (res == 0 || res == errno_EOWNERDEAD)
@@ -1430,7 +1476,7 @@ TSAN_INTERCEPTOR(int, pthread_spin_destroy, void *m) {
TSAN_INTERCEPTOR(int, pthread_spin_lock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_spin_lock, m);
MutexPreLock(thr, pc, (uptr)m);
- int res = REAL(pthread_spin_lock)(m);
+ int res = BLOCK_REAL(pthread_spin_lock)(m);
if (res == 0) {
MutexPostLock(thr, pc, (uptr)m);
}
@@ -1505,7 +1551,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) {
TSAN_INTERCEPTOR(int, pthread_rwlock_wrlock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_wrlock, m);
MutexPreLock(thr, pc, (uptr)m);
- int res = REAL(pthread_rwlock_wrlock)(m);
+ int res = BLOCK_REAL(pthread_rwlock_wrlock)(m);
if (res == 0) {
MutexPostLock(thr, pc, (uptr)m);
}
@@ -1597,57 +1643,60 @@ TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) {
FdAccess(thr, pc, fd);
return REAL(__fxstat)(version, fd, buf);
}
-#define TSAN_MAYBE_INTERCEPT___FXSTAT TSAN_INTERCEPT(__fxstat)
+
+TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) {
+ SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf);
+ if (fd > 0)
+ FdAccess(thr, pc, fd);
+ return REAL(__fxstat64)(version, fd, buf);
+}
+#define TSAN_MAYBE_INTERCEPT___FXSTAT TSAN_INTERCEPT(__fxstat); TSAN_INTERCEPT(__fxstat64)
#else
#define TSAN_MAYBE_INTERCEPT___FXSTAT
#endif
+#if !SANITIZER_GLIBC || __GLIBC_PREREQ(2, 33)
TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) {
-#if SANITIZER_GLIBC
- SCOPED_TSAN_INTERCEPTOR(__fxstat, 0, fd, buf);
- if (fd > 0)
- FdAccess(thr, pc, fd);
- return REAL(__fxstat)(0, fd, buf);
-#else
SCOPED_TSAN_INTERCEPTOR(fstat, fd, buf);
if (fd > 0)
FdAccess(thr, pc, fd);
return REAL(fstat)(fd, buf);
-#endif
}
-
-#if SANITIZER_GLIBC
-TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) {
- SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf);
- if (fd > 0)
- FdAccess(thr, pc, fd);
- return REAL(__fxstat64)(version, fd, buf);
-}
-#define TSAN_MAYBE_INTERCEPT___FXSTAT64 TSAN_INTERCEPT(__fxstat64)
+# define TSAN_MAYBE_INTERCEPT_FSTAT TSAN_INTERCEPT(fstat)
#else
-#define TSAN_MAYBE_INTERCEPT___FXSTAT64
+# define TSAN_MAYBE_INTERCEPT_FSTAT
#endif
-#if SANITIZER_GLIBC
+#if __GLIBC_PREREQ(2, 33)
TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) {
- SCOPED_TSAN_INTERCEPTOR(__fxstat64, 0, fd, buf);
+ SCOPED_TSAN_INTERCEPTOR(fstat64, fd, buf);
if (fd > 0)
FdAccess(thr, pc, fd);
- return REAL(__fxstat64)(0, fd, buf);
+ return REAL(fstat64)(fd, buf);
}
-#define TSAN_MAYBE_INTERCEPT_FSTAT64 TSAN_INTERCEPT(fstat64)
+# define TSAN_MAYBE_INTERCEPT_FSTAT64 TSAN_INTERCEPT(fstat64)
#else
-#define TSAN_MAYBE_INTERCEPT_FSTAT64
+# define TSAN_MAYBE_INTERCEPT_FSTAT64
#endif
TSAN_INTERCEPTOR(int, open, const char *name, int oflag, ...) {
- va_list ap;
- va_start(ap, oflag);
- mode_t mode = va_arg(ap, int);
- va_end(ap);
+ mode_t mode = 0;
+ if (OpenReadsVaArgs(oflag)) {
+ va_list ap;
+ va_start(ap, oflag);
+ mode = va_arg(ap, int);
+ va_end(ap);
+ }
+
SCOPED_TSAN_INTERCEPTOR(open, name, oflag, mode);
READ_STRING(thr, pc, name, 0);
- int fd = REAL(open)(name, oflag, mode);
+
+ int fd;
+ if (OpenReadsVaArgs(oflag))
+ fd = REAL(open)(name, oflag, mode);
+ else
+ fd = REAL(open)(name, oflag);
+
if (fd >= 0)
FdFileCreate(thr, pc, fd);
return fd;
@@ -2657,6 +2706,25 @@ static USED void syscall_fd_release(uptr pc, int fd) {
FdRelease(thr, pc, fd);
}
+static USED void sycall_blocking_start() {
+ DPrintf("sycall_blocking_start()\n");
+ ThreadState *thr = cur_thread();
+ EnterBlockingFunc(thr);
+ // When we are in a "blocking call", we process signals asynchronously
+ // (right when they arrive). In this context we do not expect to be
+ // executing any user/runtime code. The known interceptor sequence when
+ // this is not true is: pthread_join -> munmap(stack). It's fine
+ // to ignore munmap in this case -- we handle stack shadow separately.
+ thr->ignore_interceptors++;
+}
+
+static USED void sycall_blocking_end() {
+ DPrintf("sycall_blocking_end()\n");
+ ThreadState *thr = cur_thread();
+ thr->ignore_interceptors--;
+ atomic_store(&thr->in_blocking_func, 0, memory_order_relaxed);
+}
+
static void syscall_pre_fork(uptr pc) { ForkBefore(cur_thread(), pc); }
static void syscall_post_fork(uptr pc, int pid) {
@@ -2711,6 +2779,9 @@ static void syscall_post_fork(uptr pc, int pid) {
#define COMMON_SYSCALL_POST_FORK(res) \
syscall_post_fork(GET_CALLER_PC(), res)
+#define COMMON_SYSCALL_BLOCKING_START() sycall_blocking_start()
+#define COMMON_SYSCALL_BLOCKING_END() sycall_blocking_end()
+
#include "sanitizer_common/sanitizer_common_syscalls.inc"
#include "sanitizer_common/sanitizer_syscalls_netbsd.inc"
@@ -2845,8 +2916,21 @@ void InitializeInterceptors() {
REAL(memcpy) = internal_memcpy;
#endif
+ __interception::DoesNotSupportStaticLinking();
+
new(interceptor_ctx()) InterceptorContext();
+ // Interpose __tls_get_addr before the common interposers. This is needed
+ // because dlsym() may call malloc on failure which could result in other
+ // interposed functions being called that could eventually make use of TLS.
+#ifdef NEED_TLS_GET_ADDR
+# if !SANITIZER_S390
+ TSAN_INTERCEPT(__tls_get_addr);
+# else
+ TSAN_INTERCEPT(__tls_get_addr_internal);
+ TSAN_INTERCEPT(__tls_get_offset);
+# endif
+#endif
InitializeCommonInterceptors();
InitializeSignalInterceptors();
InitializeLibdispatchInterceptors();
@@ -2902,6 +2986,9 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(pthread_mutex_trylock);
TSAN_INTERCEPT(pthread_mutex_timedlock);
TSAN_INTERCEPT(pthread_mutex_unlock);
+#if SANITIZER_LINUX
+ TSAN_INTERCEPT(pthread_mutex_clocklock);
+#endif
#if SANITIZER_GLIBC
# if !__GLIBC_PREREQ(2, 34)
TSAN_INTERCEPT(__pthread_mutex_lock);
@@ -2931,10 +3018,9 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(pthread_once);
- TSAN_INTERCEPT(fstat);
TSAN_MAYBE_INTERCEPT___FXSTAT;
+ TSAN_MAYBE_INTERCEPT_FSTAT;
TSAN_MAYBE_INTERCEPT_FSTAT64;
- TSAN_MAYBE_INTERCEPT___FXSTAT64;
TSAN_INTERCEPT(open);
TSAN_MAYBE_INTERCEPT_OPEN64;
TSAN_INTERCEPT(creat);
@@ -2991,15 +3077,6 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(__cxa_atexit);
TSAN_INTERCEPT(_exit);
-#ifdef NEED_TLS_GET_ADDR
-#if !SANITIZER_S390
- TSAN_INTERCEPT(__tls_get_addr);
-#else
- TSAN_INTERCEPT(__tls_get_addr_internal);
- TSAN_INTERCEPT(__tls_get_offset);
-#endif
-#endif
-
TSAN_MAYBE_INTERCEPT__LWP_EXIT;
TSAN_MAYBE_INTERCEPT_THR_EXIT;
@@ -76,7 +76,7 @@ struct DynamicAnnContext {
};
static DynamicAnnContext *dyn_ann_ctx;
-static char dyn_ann_ctx_placeholder[sizeof(DynamicAnnContext)] ALIGNED(64);
+alignas(64) static char dyn_ann_ctx_placeholder[sizeof(DynamicAnnContext)];
static void AddExpectRace(ExpectRace *list,
char *f, int l, uptr addr, uptr size, char *desc) {
@@ -9,17 +9,19 @@
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
//===----------------------------------------------------------------------===//
+#include "tsan_mman.h"
+
#include "sanitizer_common/sanitizer_allocator_checks.h"
#include "sanitizer_common/sanitizer_allocator_interface.h"
#include "sanitizer_common/sanitizer_allocator_report.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_errno.h"
#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "tsan_flags.h"
#include "tsan_interface.h"
-#include "tsan_mman.h"
-#include "tsan_rtl.h"
#include "tsan_report.h"
-#include "tsan_flags.h"
+#include "tsan_rtl.h"
namespace __tsan {
@@ -52,7 +54,7 @@ struct MapUnmapCallback {
}
};
-static char allocator_placeholder[sizeof(Allocator)] ALIGNED(64);
+alignas(64) static char allocator_placeholder[sizeof(Allocator)];
Allocator *allocator() {
return reinterpret_cast<Allocator*>(&allocator_placeholder);
}
@@ -73,7 +75,7 @@ struct GlobalProc {
internal_alloc_mtx(MutexTypeInternalAlloc) {}
};
-static char global_proc_placeholder[sizeof(GlobalProc)] ALIGNED(64);
+alignas(64) static char global_proc_placeholder[sizeof(GlobalProc)];
GlobalProc *global_proc() {
return reinterpret_cast<GlobalProc*>(&global_proc_placeholder);
}
@@ -115,12 +117,21 @@ ScopedGlobalProcessor::~ScopedGlobalProcessor() {
gp->mtx.Unlock();
}
-void AllocatorLock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
+void AllocatorLockBeforeFork() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
global_proc()->internal_alloc_mtx.Lock();
InternalAllocatorLock();
-}
-
-void AllocatorUnlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
+#if !SANITIZER_APPLE
+ // OS X allocates from hooks, see 6a3958247a.
+ allocator()->ForceLock();
+ StackDepotLockBeforeFork();
+#endif
+}
+
+void AllocatorUnlockAfterFork(bool child) SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
+#if !SANITIZER_APPLE
+ StackDepotUnlockAfterFork(child);
+ allocator()->ForceUnlock();
+#endif
InternalAllocatorUnlock();
global_proc()->internal_alloc_mtx.Unlock();
}
@@ -241,7 +252,7 @@ void *user_reallocarray(ThreadState *thr, uptr pc, void *p, uptr size, uptr n) {
if (AllocatorMayReturnNull())
return SetErrnoOnNull(nullptr);
GET_STACK_TRACE_FATAL(thr, pc);
- ReportReallocArrayOverflow(size, n, &stack);
+ ReportReallocArrayOverflow(n, size, &stack);
}
return user_realloc(thr, pc, p, size * n);
}
@@ -24,8 +24,8 @@ void ReplaceSystemMalloc();
void AllocatorProcStart(Processor *proc);
void AllocatorProcFinish(Processor *proc);
void AllocatorPrintStats();
-void AllocatorLock();
-void AllocatorUnlock();
+void AllocatorLockBeforeFork();
+void AllocatorUnlockAfterFork(bool child);
void GlobalProcessorLock();
void GlobalProcessorUnlock();
@@ -413,18 +413,18 @@ struct MappingRiscv64_39 {
/*
C/C++ on linux/riscv64 (48-bit VMA)
-0000 0000 1000 - 0500 0000 0000: main binary ( 5 TB)
+0000 0000 1000 - 0400 0000 0000: main binary ( 4 TB)
0500 0000 0000 - 2000 0000 0000: -
2000 0000 0000 - 4000 0000 0000: shadow memory (32 TB)
4000 0000 0000 - 4800 0000 0000: metainfo ( 8 TB)
4800 0000 0000 - 5555 5555 5000: -
5555 5555 5000 - 5a00 0000 0000: main binary (PIE) (~5 TB)
5a00 0000 0000 - 7a00 0000 0000: -
-7a00 0000 0000 - 7fff ffff ffff: libraries and main thread stack ( 5 TB)
+7a00 0000 0000 - 7fff ffff ffff: libraries and main thread stack ( 6 TB)
*/
struct MappingRiscv64_48 {
static const uptr kLoAppMemBeg = 0x000000001000ull;
- static const uptr kLoAppMemEnd = 0x050000000000ull;
+ static const uptr kLoAppMemEnd = 0x040000000000ull;
static const uptr kShadowBeg = 0x200000000000ull;
static const uptr kShadowEnd = 0x400000000000ull;
static const uptr kMetaShadowBeg = 0x400000000000ull;
@@ -622,6 +622,35 @@ struct MappingGoAarch64 {
static const uptr kShadowAdd = 0x200000000000ull;
};
+/* Go on linux/loongarch64 (47-bit VMA)
+0000 0000 1000 - 0000 1000 0000: executable
+0000 1000 0000 - 00c0 0000 0000: -
+00c0 0000 0000 - 00e0 0000 0000: heap
+00e0 0000 0000 - 2000 0000 0000: -
+2000 0000 0000 - 2800 0000 0000: shadow
+2800 0000 0000 - 3000 0000 0000: -
+3000 0000 0000 - 3200 0000 0000: metainfo (memory blocks and sync objects)
+3200 0000 0000 - 8000 0000 0000: -
+*/
+struct MappingGoLoongArch64_47 {
+ static const uptr kMetaShadowBeg = 0x300000000000ull;
+ static const uptr kMetaShadowEnd = 0x320000000000ull;
+ static const uptr kShadowBeg = 0x200000000000ull;
+ static const uptr kShadowEnd = 0x280000000000ull;
+ static const uptr kLoAppMemBeg = 0x000000001000ull;
+ static const uptr kLoAppMemEnd = 0x00e000000000ull;
+ static const uptr kMidAppMemBeg = 0;
+ static const uptr kMidAppMemEnd = 0;
+ static const uptr kHiAppMemBeg = 0;
+ static const uptr kHiAppMemEnd = 0;
+ static const uptr kHeapMemBeg = 0;
+ static const uptr kHeapMemEnd = 0;
+ static const uptr kVdsoBeg = 0;
+ static const uptr kShadowMsk = 0;
+ static const uptr kShadowXor = 0;
+ static const uptr kShadowAdd = 0x200000000000ull;
+};
+
/*
Go on linux/mips64 (47-bit VMA)
0000 0000 1000 - 0000 1000 0000: executable
@@ -697,6 +726,8 @@ ALWAYS_INLINE auto SelectMapping(Arg arg) {
return Func::template Apply<MappingGoS390x>(arg);
# elif defined(__aarch64__)
return Func::template Apply<MappingGoAarch64>(arg);
+# elif defined(__loongarch_lp64)
+ return Func::template Apply<MappingGoLoongArch64_47>(arg);
# elif SANITIZER_WINDOWS
return Func::template Apply<MappingGoWindows>(arg);
# else
@@ -765,6 +796,7 @@ void ForEachMapping() {
Func::template Apply<MappingGoPPC64_46>();
Func::template Apply<MappingGoPPC64_47>();
Func::template Apply<MappingGoAarch64>();
+ Func::template Apply<MappingGoLoongArch64_47>();
Func::template Apply<MappingGoMips64_47>();
Func::template Apply<MappingGoS390x>();
}
@@ -967,7 +999,7 @@ struct RestoreAddrImpl {
Mapping::kMidAppMemEnd, Mapping::kHiAppMemBeg, Mapping::kHiAppMemEnd,
Mapping::kHeapMemBeg, Mapping::kHeapMemEnd,
};
- const uptr indicator = 0x0f0000000000ull;
+ const uptr indicator = 0x0e0000000000ull;
const uptr ind_lsb = 1ull << LeastSignificantSetBitIndex(indicator);
for (uptr i = 0; i < ARRAY_SIZE(ranges); i += 2) {
uptr beg = ranges[i];
@@ -992,7 +1024,7 @@ inline uptr RestoreAddr(uptr addr) {
void InitializePlatform();
void InitializePlatformEarly();
-void CheckAndProtect();
+bool CheckAndProtect(bool protect, bool ignore_heap, bool print_warnings);
void InitializeShadowMemoryPlatform();
void WriteMemoryProfile(char *buf, uptr buf_size, u64 uptime_ns);
int ExtractResolvFDs(void *state, int *fds, int nfd);
@@ -214,6 +214,97 @@ void InitializeShadowMemoryPlatform() {
#endif // #if !SANITIZER_GO
+# if !SANITIZER_GO
+static void ReExecIfNeeded(bool ignore_heap) {
+ // Go maps shadow memory lazily and works fine with limited address space.
+ // Unlimited stack is not a problem as well, because the executable
+ // is not compiled with -pie.
+ bool reexec = false;
+ // TSan doesn't play well with unlimited stack size (as stack
+ // overlaps with shadow memory). If we detect unlimited stack size,
+ // we re-exec the program with limited stack size as a best effort.
+ if (StackSizeIsUnlimited()) {
+ const uptr kMaxStackSize = 32 * 1024 * 1024;
+ VReport(1,
+ "Program is run with unlimited stack size, which wouldn't "
+ "work with ThreadSanitizer.\n"
+ "Re-execing with stack size limited to %zd bytes.\n",
+ kMaxStackSize);
+ SetStackSizeLimitInBytes(kMaxStackSize);
+ reexec = true;
+ }
+
+ if (!AddressSpaceIsUnlimited()) {
+ Report(
+ "WARNING: Program is run with limited virtual address space,"
+ " which wouldn't work with ThreadSanitizer.\n");
+ Report("Re-execing with unlimited virtual address space.\n");
+ SetAddressSpaceUnlimited();
+ reexec = true;
+ }
+
+# if SANITIZER_LINUX
+# if SANITIZER_ANDROID && (defined(__aarch64__) || defined(__x86_64__))
+ // ASLR personality check.
+ int old_personality = personality(0xffffffff);
+ bool aslr_on =
+ (old_personality != -1) && ((old_personality & ADDR_NO_RANDOMIZE) == 0);
+
+ // After patch "arm64: mm: support ARCH_MMAP_RND_BITS." is introduced in
+ // linux kernel, the random gap between stack and mapped area is increased
+ // from 128M to 36G on 39-bit aarch64. As it is almost impossible to cover
+ // this big range, we should disable randomized virtual space on aarch64.
+ if (aslr_on) {
+ VReport(1,
+ "WARNING: Program is run with randomized virtual address "
+ "space, which wouldn't work with ThreadSanitizer on Android.\n"
+ "Re-execing with fixed virtual address space.\n");
+ CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1);
+ reexec = true;
+ }
+# endif
+
+ if (reexec) {
+ // Don't check the address space since we're going to re-exec anyway.
+ } else if (!CheckAndProtect(false, ignore_heap, false)) {
+ // ASLR personality check.
+ // N.B. 'personality' is sometimes forbidden by sandboxes, so we only call
+ // this as a last resort (when the memory mapping is incompatible and TSan
+ // would fail anyway).
+ int old_personality = personality(0xffffffff);
+ bool aslr_on =
+ (old_personality != -1) && ((old_personality & ADDR_NO_RANDOMIZE) == 0);
+
+ if (aslr_on) {
+ // Disable ASLR if the memory layout was incompatible.
+ // Alternatively, we could just keep re-execing until we get lucky
+ // with a compatible randomized layout, but the risk is that if it's
+ // not an ASLR-related issue, we will be stuck in an infinite loop of
+ // re-execing (unless we change ReExec to pass a parameter of the
+ // number of retries allowed.)
+ VReport(1,
+ "WARNING: ThreadSanitizer: memory layout is incompatible, "
+ "possibly due to high-entropy ASLR.\n"
+ "Re-execing with fixed virtual address space.\n"
+ "N.B. reducing ASLR entropy is preferable.\n");
+ CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1);
+ reexec = true;
+ } else {
+ Printf(
+ "FATAL: ThreadSanitizer: memory layout is incompatible, "
+ "even though ASLR is disabled.\n"
+ "Please file a bug.\n");
+ DumpProcessMap();
+ Die();
+ }
+ }
+# endif // SANITIZER_LINUX
+
+ if (reexec)
+ ReExec();
+}
+# endif
+
void InitializePlatformEarly() {
vmaSize =
(MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
@@ -238,7 +329,13 @@ void InitializePlatformEarly() {
Printf("FATAL: Found %zd - Supported 47\n", vmaSize);
Die();
}
-# endif
+# else
+ if (vmaSize != 47) {
+ Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
+ Printf("FATAL: Found %zd - Supported 47\n", vmaSize);
+ Die();
+ }
+# endif
#elif defined(__powerpc64__)
# if !SANITIZER_GO
if (vmaSize != 44 && vmaSize != 46 && vmaSize != 47) {
@@ -278,6 +375,11 @@ void InitializePlatformEarly() {
}
# endif
# endif
+
+# if !SANITIZER_GO
+ // Heap has not been allocated yet
+ ReExecIfNeeded(false);
+# endif
}
void InitializePlatform() {
@@ -288,53 +390,34 @@ void InitializePlatform() {
// is not compiled with -pie.
#if !SANITIZER_GO
{
- bool reexec = false;
- // TSan doesn't play well with unlimited stack size (as stack
- // overlaps with shadow memory). If we detect unlimited stack size,
- // we re-exec the program with limited stack size as a best effort.
- if (StackSizeIsUnlimited()) {
- const uptr kMaxStackSize = 32 * 1024 * 1024;
- VReport(1, "Program is run with unlimited stack size, which wouldn't "
- "work with ThreadSanitizer.\n"
- "Re-execing with stack size limited to %zd bytes.\n",
- kMaxStackSize);
- SetStackSizeLimitInBytes(kMaxStackSize);
- reexec = true;
- }
-
- if (!AddressSpaceIsUnlimited()) {
- Report("WARNING: Program is run with limited virtual address space,"
- " which wouldn't work with ThreadSanitizer.\n");
- Report("Re-execing with unlimited virtual address space.\n");
- SetAddressSpaceUnlimited();
- reexec = true;
- }
-#if SANITIZER_ANDROID && (defined(__aarch64__) || defined(__x86_64__))
- // After patch "arm64: mm: support ARCH_MMAP_RND_BITS." is introduced in
- // linux kernel, the random gap between stack and mapped area is increased
- // from 128M to 36G on 39-bit aarch64. As it is almost impossible to cover
- // this big range, we should disable randomized virtual space on aarch64.
- // ASLR personality check.
- int old_personality = personality(0xffffffff);
- if (old_personality != -1 && (old_personality & ADDR_NO_RANDOMIZE) == 0) {
- VReport(1, "WARNING: Program is run with randomized virtual address "
- "space, which wouldn't work with ThreadSanitizer.\n"
- "Re-execing with fixed virtual address space.\n");
- CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1);
- reexec = true;
- }
-
-#endif
-#if SANITIZER_LINUX && (defined(__aarch64__) || defined(__loongarch_lp64))
+# if SANITIZER_LINUX && (defined(__aarch64__) || defined(__loongarch_lp64))
// Initialize the xor key used in {sig}{set,long}jump.
InitializeLongjmpXorKey();
-#endif
- if (reexec)
- ReExec();
+# endif
+ }
+
+ // We called ReExecIfNeeded() in InitializePlatformEarly(), but there are
+ // intervening allocations that result in an edge case:
+ // 1) InitializePlatformEarly(): memory layout is compatible
+ // 2) Intervening allocations happen
+ // 3) InitializePlatform(): memory layout is incompatible and fails
+ // CheckAndProtect()
+# if !SANITIZER_GO
+ // Heap has already been allocated
+ ReExecIfNeeded(true);
+# endif
+
+ // Earlier initialization steps already re-exec'ed until we got a compatible
+ // memory layout, so we don't expect any more issues here.
+ if (!CheckAndProtect(true, true, true)) {
+ Printf(
+ "FATAL: ThreadSanitizer: unexpectedly found incompatible memory "
+ "layout.\n");
+ Printf("FATAL: Please file a bug.\n");
+ DumpProcessMap();
+ Die();
}
- CheckAndProtect();
- InitTlsSize();
#endif // !SANITIZER_GO
}
@@ -46,8 +46,8 @@
namespace __tsan {
#if !SANITIZER_GO
-static char main_thread_state[sizeof(ThreadState)] ALIGNED(
- SANITIZER_CACHE_LINE_SIZE);
+alignas(SANITIZER_CACHE_LINE_SIZE) static char main_thread_state[sizeof(
+ ThreadState)];
static ThreadState *dead_thread_state;
static pthread_key_t thread_state_key;
@@ -239,7 +239,10 @@ static uptr longjmp_xor_key = 0;
void InitializePlatform() {
DisableCoreDumperIfNecessary();
#if !SANITIZER_GO
- CheckAndProtect();
+ if (!CheckAndProtect(true, true, true)) {
+ Printf("FATAL: ThreadSanitizer: found incompatible memory layout.\n");
+ Die();
+ }
InitializeThreadStateStorage();
@@ -94,22 +94,51 @@ static void ProtectRange(uptr beg, uptr end) {
}
}
-void CheckAndProtect() {
+// CheckAndProtect will check if the memory layout is compatible with TSan.
+// Optionally (if 'protect' is true), it will set the memory regions between
+// app memory to be inaccessible.
+// 'ignore_heap' means it will not consider heap memory allocations to be a
+// conflict. Set this based on whether we are calling CheckAndProtect before
+// or after the allocator has initialized the heap.
+bool CheckAndProtect(bool protect, bool ignore_heap, bool print_warnings) {
// Ensure that the binary is indeed compiled with -pie.
MemoryMappingLayout proc_maps(true);
MemoryMappedSegment segment;
while (proc_maps.Next(&segment)) {
- if (IsAppMem(segment.start)) continue;
+ if (segment.start >= HeapMemBeg() && segment.end <= HeapEnd()) {
+ if (ignore_heap) {
+ continue;
+ } else {
+ return false;
+ }
+ }
+
+ // Note: IsAppMem includes if it is heap memory, hence we must
+ // put this check after the heap bounds check.
+ if (IsAppMem(segment.start) && IsAppMem(segment.end - 1))
+ continue;
+
+ // Guard page after the heap end
if (segment.start >= HeapMemEnd() && segment.start < HeapEnd()) continue;
+
if (segment.protection == 0) // Zero page or mprotected.
continue;
+
if (segment.start >= VdsoBeg()) // vdso
break;
- Printf("FATAL: ThreadSanitizer: unexpected memory mapping 0x%zx-0x%zx\n",
- segment.start, segment.end);
- Die();
+
+ // Debug output can break tests. Suppress this message in most cases.
+ if (print_warnings)
+ Printf(
+ "WARNING: ThreadSanitizer: unexpected memory mapping 0x%zx-0x%zx\n",
+ segment.start, segment.end);
+
+ return false;
}
+ if (!protect)
+ return true;
+
# if SANITIZER_IOS && !SANITIZER_IOSSIM
ProtectRange(HeapMemEnd(), ShadowBeg());
ProtectRange(ShadowEnd(), MetaShadowBeg());
@@ -135,8 +164,10 @@ void CheckAndProtect() {
// Older s390x kernels may not support 5-level page tables.
TryProtectRange(user_addr_max_l4, user_addr_max_l5);
#endif
+
+ return true;
}
-#endif
+# endif
} // namespace __tsan
@@ -16,11 +16,9 @@
#if SANITIZER_CAN_USE_PREINIT_ARRAY
-// The symbol is called __local_tsan_preinit, because it's not intended to be
-// exported.
-// This code linked into the main executable when -fsanitize=thread is in
-// the link flags. It can only use exported interface functions.
-__attribute__((section(".preinit_array"), used))
-void (*__local_tsan_preinit)(void) = __tsan_init;
+// This section is linked into the main executable when -fsanitize=thread is
+// specified to perform initialization at a very early stage.
+__attribute__((section(".preinit_array"), used)) static auto preinit =
+ __tsan_init;
#endif
@@ -273,26 +273,10 @@ static ReportStack *ChooseSummaryStack(const ReportDesc *rep) {
return 0;
}
-static bool FrameIsInternal(const SymbolizedStack *frame) {
- if (frame == 0)
- return false;
- const char *file = frame->info.file;
- const char *module = frame->info.module;
- if (file != 0 &&
- (internal_strstr(file, "tsan_interceptors_posix.cpp") ||
- internal_strstr(file, "tsan_interceptors_memintrinsics.cpp") ||
- internal_strstr(file, "sanitizer_common_interceptors.inc") ||
- internal_strstr(file, "tsan_interface_")))
- return true;
- if (module != 0 && (internal_strstr(module, "libclang_rt.tsan_")))
- return true;
- return false;
-}
-
-static SymbolizedStack *SkipTsanInternalFrames(SymbolizedStack *frames) {
- while (FrameIsInternal(frames) && frames->next)
- frames = frames->next;
- return frames;
+static const SymbolizedStack *SkipTsanInternalFrames(SymbolizedStack *frames) {
+ if (const SymbolizedStack *f = SkipInternalFrames(frames))
+ return f;
+ return frames; // Fallback to the top frame.
}
void PrintReport(const ReportDesc *rep) {
@@ -366,7 +350,7 @@ void PrintReport(const ReportDesc *rep) {
Printf(" And %d more similar thread leaks.\n\n", rep->count - 1);
if (ReportStack *stack = ChooseSummaryStack(rep)) {
- if (SymbolizedStack *frame = SkipTsanInternalFrames(stack->frames))
+ if (const SymbolizedStack *frame = SkipTsanInternalFrames(stack->frames))
ReportErrorSummary(rep_typ_str, frame->info);
}
@@ -35,8 +35,10 @@ extern "C" void __tsan_resume() {
__tsan_resumed = 1;
}
+#if SANITIZER_APPLE
SANITIZER_WEAK_DEFAULT_IMPL
void __tsan_test_only_on_fork() {}
+#endif
namespace __tsan {
@@ -46,11 +48,10 @@ int (*on_finalize)(int);
#endif
#if !SANITIZER_GO && !SANITIZER_APPLE
-__attribute__((tls_model("initial-exec")))
-THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(
- SANITIZER_CACHE_LINE_SIZE);
+alignas(SANITIZER_CACHE_LINE_SIZE) THREADLOCAL __attribute__((tls_model(
+ "initial-exec"))) char cur_thread_placeholder[sizeof(ThreadState)];
#endif
-static char ctx_placeholder[sizeof(Context)] ALIGNED(SANITIZER_CACHE_LINE_SIZE);
+alignas(SANITIZER_CACHE_LINE_SIZE) static char ctx_placeholder[sizeof(Context)];
Context *ctx;
// Can be overriden by a front-end.
@@ -805,6 +806,7 @@ int Finalize(ThreadState *thr) {
#if !SANITIZER_GO
void ForkBefore(ThreadState* thr, uptr pc) SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
+ VReport(2, "BeforeFork tid: %llu\n", GetTid());
GlobalProcessorLock();
// Detaching from the slot makes OnUserFree skip writing to the shadow.
// The slot will be locked so any attempts to use it will deadlock anyway.
@@ -813,7 +815,7 @@ void ForkBefore(ThreadState* thr, uptr pc) SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
ctx->thread_registry.Lock();
ctx->slot_mtx.Lock();
ScopedErrorReportLock::Lock();
- AllocatorLock();
+ AllocatorLockBeforeFork();
// Suppress all reports in the pthread_atfork callbacks.
// Reports will deadlock on the report_mtx.
// We could ignore sync operations as well,
@@ -828,14 +830,17 @@ void ForkBefore(ThreadState* thr, uptr pc) SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
// Disables memory write in OnUserAlloc/Free.
thr->ignore_reads_and_writes++;
+# if SANITIZER_APPLE
__tsan_test_only_on_fork();
+# endif
}
-static void ForkAfter(ThreadState* thr) SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
+static void ForkAfter(ThreadState* thr,
+ bool child) SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
thr->suppress_reports--; // Enabled in ForkBefore.
thr->ignore_interceptors--;
thr->ignore_reads_and_writes--;
- AllocatorUnlock();
+ AllocatorUnlockAfterFork(child);
ScopedErrorReportLock::Unlock();
ctx->slot_mtx.Unlock();
ctx->thread_registry.Unlock();
@@ -843,12 +848,13 @@ static void ForkAfter(ThreadState* thr) SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
SlotAttachAndLock(thr);
SlotUnlock(thr);
GlobalProcessorUnlock();
+ VReport(2, "AfterFork tid: %llu\n", GetTid());
}
-void ForkParentAfter(ThreadState* thr, uptr pc) { ForkAfter(thr); }
+void ForkParentAfter(ThreadState* thr, uptr pc) { ForkAfter(thr, false); }
void ForkChildAfter(ThreadState* thr, uptr pc, bool start_thread) {
- ForkAfter(thr);
+ ForkAfter(thr, true);
u32 nthread = ctx->thread_registry.OnFork(thr->tid);
VPrintf(1,
"ThreadSanitizer: forked new process with pid %d,"
@@ -136,7 +136,7 @@ struct TidEpoch {
Epoch epoch;
};
-struct TidSlot {
+struct alignas(SANITIZER_CACHE_LINE_SIZE) TidSlot {
Mutex mtx;
Sid sid;
atomic_uint32_t raw_epoch;
@@ -153,10 +153,10 @@ struct TidSlot {
}
TidSlot();
-} ALIGNED(SANITIZER_CACHE_LINE_SIZE);
+};
// This struct is stored in TLS.
-struct ThreadState {
+struct alignas(SANITIZER_CACHE_LINE_SIZE) ThreadState {
FastState fast_state;
int ignore_sync;
#if !SANITIZER_GO
@@ -234,7 +234,7 @@ struct ThreadState {
const ReportDesc *current_report;
explicit ThreadState(Tid tid);
-} ALIGNED(SANITIZER_CACHE_LINE_SIZE);
+};
#if !SANITIZER_GO
#if SANITIZER_APPLE || SANITIZER_ANDROID
@@ -2,6 +2,7 @@
#if defined(__aarch64__)
#include "sanitizer_common/sanitizer_asm.h"
+#include "builtins/assembly.h"
#if !defined(__APPLE__)
.section .text
@@ -16,6 +17,7 @@ ASM_HIDDEN(__tsan_setjmp)
ASM_TYPE_FUNCTION(ASM_SYMBOL_INTERCEPTOR(setjmp))
ASM_SYMBOL_INTERCEPTOR(setjmp):
CFI_STARTPROC
+ BTI_C
// Save frame/link register
stp x29, x30, [sp, -32]!
@@ -66,6 +68,7 @@ ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(setjmp))
ASM_TYPE_FUNCTION(ASM_SYMBOL_INTERCEPTOR(_setjmp))
ASM_SYMBOL_INTERCEPTOR(_setjmp):
CFI_STARTPROC
+ BTI_C
// Save frame/link register
stp x29, x30, [sp, -32]!
@@ -116,6 +119,7 @@ ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(_setjmp))
ASM_TYPE_FUNCTION(ASM_SYMBOL_INTERCEPTOR(sigsetjmp))
ASM_SYMBOL_INTERCEPTOR(sigsetjmp):
CFI_STARTPROC
+ BTI_C
// Save frame/link register
stp x29, x30, [sp, -32]!
@@ -168,6 +172,7 @@ ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(sigsetjmp))
ASM_TYPE_FUNCTION(ASM_SYMBOL_INTERCEPTOR(__sigsetjmp))
ASM_SYMBOL_INTERCEPTOR(__sigsetjmp):
CFI_STARTPROC
+ BTI_C
// Save frame/link register
stp x29, x30, [sp, -32]!
@@ -217,4 +222,6 @@ ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(__sigsetjmp))
NO_EXEC_STACK_DIRECTIVE
+GNU_PROPERTY_BTI_PAC
+
#endif
@@ -672,22 +672,28 @@ void MemoryAccessRangeT(ThreadState* thr, uptr pc, uptr addr, uptr size) {
#if SANITIZER_DEBUG
if (!IsAppMem(addr)) {
- Printf("Access to non app mem %zx\n", addr);
+ Printf("Access to non app mem start: %p\n", (void*)addr);
DCHECK(IsAppMem(addr));
}
if (!IsAppMem(addr + size - 1)) {
- Printf("Access to non app mem %zx\n", addr + size - 1);
+ Printf("Access to non app mem end: %p\n", (void*)(addr + size - 1));
DCHECK(IsAppMem(addr + size - 1));
}
if (!IsShadowMem(shadow_mem)) {
- Printf("Bad shadow addr %p (%zx)\n", static_cast<void*>(shadow_mem), addr);
+ Printf("Bad shadow start addr: %p (%p)\n", shadow_mem, (void*)addr);
DCHECK(IsShadowMem(shadow_mem));
}
- if (!IsShadowMem(shadow_mem + size * kShadowCnt - 1)) {
- Printf("Bad shadow addr %p (%zx)\n",
- static_cast<void*>(shadow_mem + size * kShadowCnt - 1),
- addr + size - 1);
- DCHECK(IsShadowMem(shadow_mem + size * kShadowCnt - 1));
+
+ RawShadow* shadow_mem_end = reinterpret_cast<RawShadow*>(
+ reinterpret_cast<uptr>(shadow_mem) + size * kShadowMultiplier - 1);
+ if (!IsShadowMem(shadow_mem_end)) {
+ Printf("Bad shadow end addr: %p (%p)\n", shadow_mem_end,
+ (void*)(addr + size - 1));
+ Printf(
+ "Shadow start addr (ok): %p (%p); size: 0x%zx; kShadowMultiplier: "
+ "%zx\n",
+ shadow_mem, (void*)addr, size, kShadowMultiplier);
+ DCHECK(IsShadowMem(shadow_mem_end));
}
#endif
@@ -446,9 +446,9 @@ void Acquire(ThreadState *thr, uptr pc, uptr addr) {
if (!s)
return;
SlotLocker locker(thr);
+ ReadLock lock(&s->mtx);
if (!s->clock)
return;
- ReadLock lock(&s->mtx);
thr->clock.Acquire(s->clock);
}
@@ -1,6 +1,5 @@
#include "tsan_ppc_regs.h"
- .machine altivec
.section .text
.hidden __tsan_setjmp
.globl _setjmp
@@ -160,15 +160,21 @@ void ThreadStart(ThreadState *thr, Tid tid, tid_t os_id,
}
Free(thr->tctx->sync);
+#if !SANITIZER_GO
+ thr->is_inited = true;
+#endif
+
uptr stk_addr = 0;
- uptr stk_size = 0;
+ uptr stk_end = 0;
uptr tls_addr = 0;
- uptr tls_size = 0;
+ uptr tls_end = 0;
#if !SANITIZER_GO
if (thread_type != ThreadType::Fiber)
- GetThreadStackAndTls(tid == kMainTid, &stk_addr, &stk_size, &tls_addr,
- &tls_size);
+ GetThreadStackAndTls(tid == kMainTid, &stk_addr, &stk_end, &tls_addr,
+ &tls_end);
#endif
+ uptr stk_size = stk_end - stk_addr;
+ uptr tls_size = tls_end - tls_addr;
thr->stk_addr = stk_addr;
thr->stk_size = stk_size;
thr->tls_addr = tls_addr;
@@ -200,15 +206,11 @@ void ThreadStart(ThreadState *thr, Tid tid, tid_t os_id,
}
void ThreadContext::OnStarted(void *arg) {
- thr = static_cast<ThreadState *>(arg);
DPrintf("#%d: ThreadStart\n", tid);
- new (thr) ThreadState(tid);
+ thr = new (arg) ThreadState(tid);
if (common_flags()->detect_deadlocks)
thr->dd_lt = ctx->dd->CreateLogicalThread(tid);
thr->tctx = this;
-#if !SANITIZER_GO
- thr->is_inited = true;
-#endif
}
void ThreadFinish(ThreadState *thr) {
@@ -42,7 +42,7 @@ const char *__tsan_default_suppressions() {
namespace __tsan {
-ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
+alignas(64) static char suppression_placeholder[sizeof(SuppressionContext)];
static SuppressionContext *suppression_ctx = nullptr;
static const char *kSuppressionTypes[] = {
kSuppressionRace, kSuppressionRaceTop, kSuppressionMutex,
@@ -34,7 +34,7 @@ class VectorClock {
VectorClock& operator=(const VectorClock& other);
private:
- Epoch clk_[kThreadSlotCount] VECTOR_ALIGNED;
+ VECTOR_ALIGNED Epoch clk_[kThreadSlotCount];
};
ALWAYS_INLINE Epoch VectorClock::Get(Sid sid) const {
@@ -47,7 +47,7 @@ static void MaybePrintStackTrace(uptr pc, uptr bp) {
if (!flags()->print_stacktrace)
return;
- BufferedStackTrace stack;
+ UNINITIALIZED BufferedStackTrace stack;
ubsan_GetStackTrace(&stack, kStackTraceMax, pc, bp, nullptr,
common_flags()->fast_unwind_on_fatal);
stack.Print();
@@ -88,7 +88,7 @@ static void MaybeReportErrorSummary(Location Loc, ErrorType Type) {
AI.file = internal_strdup(SLoc.getFilename());
AI.line = SLoc.getLine();
AI.column = SLoc.getColumn();
- AI.function = internal_strdup(""); // Avoid printing ?? as function name.
+ AI.function = nullptr;
ReportErrorSummary(ErrorKind, AI, GetSanititizerToolName());
AI.Clear();
return;
@@ -402,7 +402,7 @@ ScopedReport::~ScopedReport() {
Die();
}
-ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
+alignas(64) static char suppression_placeholder[sizeof(SuppressionContext)];
static SuppressionContext *suppression_ctx = nullptr;
static const char kVptrCheck[] = "vptr_check";
static const char *kSuppressionTypes[] = {
@@ -18,26 +18,6 @@
namespace __ubsan {
-class SymbolizedStackHolder {
- SymbolizedStack *Stack;
-
- void clear() {
- if (Stack)
- Stack->ClearAll();
- }
-
-public:
- explicit SymbolizedStackHolder(SymbolizedStack *Stack = nullptr)
- : Stack(Stack) {}
- ~SymbolizedStackHolder() { clear(); }
- void reset(SymbolizedStack *S) {
- if (Stack != S)
- clear();
- Stack = S;
- }
- const SymbolizedStack *get() const { return Stack; }
-};
-
SymbolizedStack *getSymbolizedLocation(uptr PC);
inline SymbolizedStack *getCallerLocation(uptr CallerPC) {
@@ -29,7 +29,7 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_print_stack_trace() {
GET_CURRENT_PC_BP;
- BufferedStackTrace stack;
+ UNINITIALIZED BufferedStackTrace stack;
stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal);
stack.Print();
}
@@ -50,7 +50,6 @@ void InitializeFlags() {
{
CommonFlags cf;
cf.CopyFrom(*common_flags());
- cf.print_summary = false;
cf.external_symbolizer_path = GetFlag("UBSAN_SYMBOLIZER_PATH");
OverrideCommonFlags(cf);
}
@@ -555,13 +555,11 @@ static void handleImplicitConversion(ImplicitConversionData *Data,
ReportOptions Opts, ValueHandle Src,
ValueHandle Dst) {
SourceLocation Loc = Data->Loc.acquire();
- ErrorType ET = ErrorType::GenericUB;
-
const TypeDescriptor &SrcTy = Data->FromType;
const TypeDescriptor &DstTy = Data->ToType;
-
bool SrcSigned = SrcTy.isSignedIntegerTy();
bool DstSigned = DstTy.isSignedIntegerTy();
+ ErrorType ET = ErrorType::GenericUB;
switch (Data->Kind) {
case ICCK_IntegerTruncation: { // Legacy, no longer used.
@@ -594,14 +592,23 @@ static void handleImplicitConversion(ImplicitConversionData *Data,
ScopedReport R(Opts, Loc, ET);
+ // In the case we have a bitfield, we want to explicitly say so in the
+ // error message.
// FIXME: is it possible to dump the values as hex with fixed width?
-
- Diag(Loc, DL_Error, ET,
- "implicit conversion from type %0 of value %1 (%2-bit, %3signed) to "
- "type %4 changed the value to %5 (%6-bit, %7signed)")
- << SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitWidth()
- << (SrcSigned ? "" : "un") << DstTy << Value(DstTy, Dst)
- << DstTy.getIntegerBitWidth() << (DstSigned ? "" : "un");
+ if (Data->BitfieldBits)
+ Diag(Loc, DL_Error, ET,
+ "implicit conversion from type %0 of value %1 (%2-bit, %3signed) to "
+ "type %4 changed the value to %5 (%6-bit bitfield, %7signed)")
+ << SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitWidth()
+ << (SrcSigned ? "" : "un") << DstTy << Value(DstTy, Dst)
+ << Data->BitfieldBits << (DstSigned ? "" : "un");
+ else
+ Diag(Loc, DL_Error, ET,
+ "implicit conversion from type %0 of value %1 (%2-bit, %3signed) to "
+ "type %4 changed the value to %5 (%6-bit, %7signed)")
+ << SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitWidth()
+ << (SrcSigned ? "" : "un") << DstTy << Value(DstTy, Dst)
+ << DstTy.getIntegerBitWidth() << (DstSigned ? "" : "un");
}
void __ubsan::__ubsan_handle_implicit_conversion(ImplicitConversionData *Data,
@@ -626,13 +633,16 @@ static void handleInvalidBuiltin(InvalidBuiltinData *Data, ReportOptions Opts) {
ScopedReport R(Opts, Loc, ET);
- Diag(Loc, DL_Error, ET,
- "passing zero to %0, which is not a valid argument")
- << ((Data->Kind == BCK_CTZPassedZero) ? "ctz()" : "clz()");
+ if (Data->Kind == BCK_AssumePassedFalse)
+ Diag(Loc, DL_Error, ET, "assumption is violated during execution");
+ else
+ Diag(Loc, DL_Error, ET,
+ "passing zero to __builtin_%0(), which is not a valid argument")
+ << ((Data->Kind == BCK_CTZPassedZero) ? "ctz" : "clz");
}
void __ubsan::__ubsan_handle_invalid_builtin(InvalidBuiltinData *Data) {
- GET_REPORT_OPTIONS(true);
+ GET_REPORT_OPTIONS(false);
handleInvalidBuiltin(Data, Opts);
}
void __ubsan::__ubsan_handle_invalid_builtin_abort(InvalidBuiltinData *Data) {
@@ -894,21 +904,6 @@ void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable,
} // namespace __ubsan
-void __ubsan::__ubsan_handle_cfi_bad_icall(CFIBadIcallData *CallData,
- ValueHandle Function) {
- GET_REPORT_OPTIONS(false);
- CFICheckFailData Data = {CFITCK_ICall, CallData->Loc, CallData->Type};
- handleCFIBadIcall(&Data, Function, Opts);
-}
-
-void __ubsan::__ubsan_handle_cfi_bad_icall_abort(CFIBadIcallData *CallData,
- ValueHandle Function) {
- GET_REPORT_OPTIONS(true);
- CFICheckFailData Data = {CFITCK_ICall, CallData->Loc, CallData->Type};
- handleCFIBadIcall(&Data, Function, Opts);
- Die();
-}
-
void __ubsan::__ubsan_handle_cfi_check_fail(CFICheckFailData *Data,
ValueHandle Value,
uptr ValidVtable) {
@@ -147,6 +147,7 @@ struct ImplicitConversionData {
const TypeDescriptor &FromType;
const TypeDescriptor &ToType;
/* ImplicitConversionCheckKind */ unsigned char Kind;
+ unsigned int BitfieldBits;
};
/// \brief Implict conversion that changed the value.
@@ -158,6 +159,7 @@ RECOVERABLE(implicit_conversion, ImplicitConversionData *Data, ValueHandle Src,
enum BuiltinCheckKind : unsigned char {
BCK_CTZPassedZero,
BCK_CLZPassedZero,
+ BCK_AssumePassedFalse,
};
struct InvalidBuiltinData {
@@ -215,20 +217,12 @@ enum CFITypeCheckKind : unsigned char {
CFITCK_VMFCall,
};
-struct CFIBadIcallData {
- SourceLocation Loc;
- const TypeDescriptor &Type;
-};
-
struct CFICheckFailData {
CFITypeCheckKind CheckKind;
SourceLocation Loc;
const TypeDescriptor &Type;
};
-/// \brief Handle control flow integrity failure for indirect function calls.
-RECOVERABLE(cfi_bad_icall, CFIBadIcallData *Data, ValueHandle Function)
-
/// \brief Handle control flow integrity failures.
RECOVERABLE(cfi_check_fail, CFICheckFailData *Data, ValueHandle Function,
uptr VtableIsValid)
@@ -156,50 +156,6 @@ void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable,
Diag(Loc, DL_Note, ET, "check failed in %0, vtable located in %1")
<< SrcModule << DstModule;
}
-
-static bool handleFunctionTypeMismatch(FunctionTypeMismatchData *Data,
- ValueHandle Function,
- ValueHandle calleeRTTI,
- ValueHandle fnRTTI, ReportOptions Opts) {
- if (checkTypeInfoEquality(reinterpret_cast<void *>(calleeRTTI),
- reinterpret_cast<void *>(fnRTTI)))
- return false;
-
- SourceLocation CallLoc = Data->Loc.acquire();
- ErrorType ET = ErrorType::FunctionTypeMismatch;
-
- if (ignoreReport(CallLoc, Opts, ET))
- return true;
-
- ScopedReport R(Opts, CallLoc, ET);
-
- SymbolizedStackHolder FLoc(getSymbolizedLocation(Function));
- const char *FName = FLoc.get()->info.function;
- if (!FName)
- FName = "(unknown)";
-
- Diag(CallLoc, DL_Error, ET,
- "call to function %0 through pointer to incorrect function type %1")
- << FName << Data->Type;
- Diag(FLoc, DL_Note, ET, "%0 defined here") << FName;
- return true;
-}
-
-void __ubsan_handle_function_type_mismatch_v1(FunctionTypeMismatchData *Data,
- ValueHandle Function,
- ValueHandle calleeRTTI,
- ValueHandle fnRTTI) {
- GET_REPORT_OPTIONS(false);
- handleFunctionTypeMismatch(Data, Function, calleeRTTI, fnRTTI, Opts);
-}
-
-void __ubsan_handle_function_type_mismatch_v1_abort(
- FunctionTypeMismatchData *Data, ValueHandle Function,
- ValueHandle calleeRTTI, ValueHandle fnRTTI) {
- GET_REPORT_OPTIONS(true);
- if (handleFunctionTypeMismatch(Data, Function, calleeRTTI, fnRTTI, Opts))
- Die();
-}
} // namespace __ubsan
#endif // CAN_SANITIZE_UB
@@ -33,19 +33,6 @@ void __ubsan_handle_dynamic_type_cache_miss(
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
void __ubsan_handle_dynamic_type_cache_miss_abort(
DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash);
-
-struct FunctionTypeMismatchData;
-
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
-__ubsan_handle_function_type_mismatch_v1(FunctionTypeMismatchData *Data,
- ValueHandle Val,
- ValueHandle calleeRTTI,
- ValueHandle fnRTTI);
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
-__ubsan_handle_function_type_mismatch_v1_abort(FunctionTypeMismatchData *Data,
- ValueHandle Val,
- ValueHandle calleeRTTI,
- ValueHandle fnRTTI);
}
#endif // UBSAN_HANDLERS_CXX_H
@@ -43,8 +43,8 @@ static void CommonStandaloneInit() {
SanitizerToolName = GetSanititizerToolName();
CacheBinaryName();
InitializeFlags();
- __sanitizer::InitializePlatformEarly();
__sanitizer_set_report_path(common_flags()->log_path);
+ __sanitizer::InitializePlatformEarly();
AndroidLogInit();
InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
CommonInit();
@@ -30,6 +30,6 @@ static void PreInitAsStandalone() {
} // namespace __ubsan
-__attribute__((section(".preinit_array"), used)) void (*__local_ubsan_preinit)(
- void) = __ubsan::PreInitAsStandalone;
+__attribute__((section(".preinit_array"), used)) static auto preinit =
+ __ubsan::PreInitAsStandalone;
#endif // SANITIZER_CAN_USE_PREINIT_ARRAY
@@ -21,8 +21,6 @@ INTERFACE_FUNCTION(__ubsan_handle_dynamic_type_cache_miss)
INTERFACE_FUNCTION(__ubsan_handle_dynamic_type_cache_miss_abort)
INTERFACE_FUNCTION(__ubsan_handle_float_cast_overflow)
INTERFACE_FUNCTION(__ubsan_handle_float_cast_overflow_abort)
-INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch_v1)
-INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch_v1_abort)
INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch)
INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch_abort)
INTERFACE_FUNCTION(__ubsan_handle_implicit_conversion)
@@ -12,7 +12,6 @@
#ifndef UBSAN_PLATFORM_H
#define UBSAN_PLATFORM_H
-#ifndef CAN_SANITIZE_UB
// Other platforms should be easy to add, and probably work as-is.
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || \
defined(__NetBSD__) || defined(__DragonFly__) || \
@@ -22,6 +21,5 @@
#else
# define CAN_SANITIZE_UB 0
#endif
-#endif //CAN_SANITIZE_UB
#endif
@@ -66,6 +66,11 @@ void InitializeDeadlySignals() {
return;
is_initialized = true;
InitializeSignalInterceptors();
+#if SANITIZER_INTERCEPT_SIGNAL_AND_SIGACTION
+ // REAL(sigaction_symname) is nullptr in a static link. Bail out.
+ if (!REAL(sigaction_symname))
+ return;
+#endif
InstallDeadlySignalHandlers(&UBsanOnDeadlySignal);
}
@@ -207,7 +207,7 @@ struct VtablePrefix {
std::type_info *TypeInfo;
};
VtablePrefix *getVtablePrefix(void *Vtable) {
- Vtable = ptrauth_auth_data(Vtable, ptrauth_key_cxx_vtable_pointer, 0);
+ Vtable = ptrauth_strip(Vtable, ptrauth_key_cxx_vtable_pointer);
VtablePrefix *Vptr = reinterpret_cast<VtablePrefix*>(Vtable);
VtablePrefix *Prefix = Vptr - 1;
if (!IsAccessibleMemoryRange((uptr)Prefix, sizeof(VtablePrefix)))
@@ -67,18 +67,21 @@ const char *__ubsan::getObjCClassName(ValueHandle Pointer) {
SIntMax Value::getSIntValue() const {
CHECK(getType().isSignedIntegerTy());
+ // Val was zero-extended to ValueHandle. Sign-extend from original width
+ // to SIntMax.
+ const unsigned ExtraBits =
+ sizeof(SIntMax) * 8 - getType().getIntegerBitCount();
if (isInlineInt()) {
- // Val was zero-extended to ValueHandle. Sign-extend from original width
- // to SIntMax.
- const unsigned ExtraBits =
- sizeof(SIntMax) * 8 - getType().getIntegerBitWidth();
return SIntMax(UIntMax(Val) << ExtraBits) >> ExtraBits;
}
- if (getType().getIntegerBitWidth() == 64)
- return *reinterpret_cast<s64*>(Val);
+ if (getType().getIntegerBitWidth() == 64) {
+ return SIntMax(UIntMax(*reinterpret_cast<s64 *>(Val)) << ExtraBits) >>
+ ExtraBits;
+ }
#if HAVE_INT128_T
if (getType().getIntegerBitWidth() == 128)
- return *reinterpret_cast<s128*>(Val);
+ return SIntMax(UIntMax(*reinterpret_cast<s128 *>(Val)) << ExtraBits) >>
+ ExtraBits;
#else
if (getType().getIntegerBitWidth() == 128)
UNREACHABLE("libclang_rt.ubsan was built without __int128 support");
@@ -103,6 +103,13 @@ public:
/// representation is that of bitcasting the floating-point value to an
/// integer type.
TK_Float = 0x0001,
+ /// An _BitInt(N) type. Lowest bit is 1 for a signed value, 0 for an
+ /// unsigned value. Remaining bits are log_2(bit_width). The value
+ /// representation is the integer itself if it fits into a ValueHandle, and
+ /// a pointer to the integer otherwise. TypeName contains the true width
+ /// of the type for the signed _BitInt(N) type stored after zero bit after
+ /// TypeName as 32-bit unsigned integer.
+ TK_BitInt = 0x0002,
/// Any other type. The value representation is unspecified.
TK_Unknown = 0xffff
};
@@ -113,10 +120,15 @@ public:
return static_cast<Kind>(TypeKind);
}
- bool isIntegerTy() const { return getKind() == TK_Integer; }
+ bool isIntegerTy() const {
+ return getKind() == TK_Integer || getKind() == TK_BitInt;
+ }
+ bool isBitIntTy() const { return getKind() == TK_BitInt; }
+
bool isSignedIntegerTy() const {
return isIntegerTy() && (TypeInfo & 1);
}
+ bool isSignedBitIntTy() const { return isBitIntTy() && (TypeInfo & 1); }
bool isUnsignedIntegerTy() const {
return isIntegerTy() && !(TypeInfo & 1);
}
@@ -125,6 +137,25 @@ public:
return 1 << (TypeInfo >> 1);
}
+ const char *getBitIntBitCountPointer() const {
+ DCHECK(isBitIntTy());
+ DCHECK(isSignedBitIntTy());
+ // Scan Name for zero and return the next address
+ const char *p = getTypeName();
+ while (*p != '\0')
+ ++p;
+ // Return the next address
+ return p + 1;
+ }
+
+ unsigned getIntegerBitCount() const {
+ DCHECK(isIntegerTy());
+ if (isSignedBitIntTy())
+ return *reinterpret_cast<const u32 *>(getBitIntBitCountPointer());
+ else
+ return getIntegerBitWidth();
+ }
+
bool isFloatTy() const { return getKind() == TK_Float; }
unsigned getFloatBitWidth() const {
CHECK(isFloatTy());
deleted file mode 100644
@@ -1,20 +0,0 @@
-//===-- ubsan_win_dll_thunk.cpp -------------------------------------------===//
-//
-// 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 defines a family of thunks that should be statically linked into
-// the DLLs that have instrumentation in order to delegate the calls to the
-// shared runtime that lives in the main binary.
-// See https://github.com/google/sanitizers/issues/209 for the details.
-//===----------------------------------------------------------------------===//
-#ifdef SANITIZER_DLL_THUNK
-#include "sanitizer_common/sanitizer_win_dll_thunk.h"
-// Ubsan interface functions.
-#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
-#include "ubsan_interface.inc"
-#endif // SANITIZER_DLL_THUNK
similarity index 62%
rename from libsanitizer/ubsan/ubsan_win_dynamic_runtime_thunk.cpp
rename to libsanitizer/ubsan/ubsan_win_runtime_thunk.cpp
@@ -1,4 +1,4 @@
-//===-- ubsan_win_dynamic_runtime_thunk.cpp -------------------------------===//
+//===-- ubsan_win_runtime_thunk.cpp ----------------------------- --===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -10,11 +10,14 @@
// to interact with Ubsan, when it is included in a dll.
//
//===----------------------------------------------------------------------===//
-#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK
+#if defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) || \
+ defined(SANITIZER_STATIC_RUNTIME_THUNK)
#define SANITIZER_IMPORT_INTERFACE 1
#include "sanitizer_common/sanitizer_win_defs.h"
+#include "sanitizer_common/sanitizer_win_thunk_interception.h"
// Define weak alias for all weak functions imported from ubsan.
#define INTERFACE_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name)
+#define INTERFACE_WEAK_FUNCTION(Name) REGISTER_WEAK_FUNCTION(Name)
#include "ubsan_interface.inc"
-#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK
+#endif // defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) ||
+ // defined(SANITIZER_STATIC_RUNTIME_THUNK)
deleted file mode 100644
@@ -1,23 +0,0 @@
-//===-- ubsan_win_weak_interception.cpp -----------------------------------===//
-//
-// 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 module should be included in Ubsan when it is implemented as a shared
-// library on Windows (dll), in order to delegate the calls of weak functions to
-// the implementation in the main executable when a strong definition is
-// provided.
-//===----------------------------------------------------------------------===//
-#ifdef SANITIZER_DYNAMIC
-#include "sanitizer_common/sanitizer_win_weak_interception.h"
-#include "ubsan_flags.h"
-#include "ubsan_monitor.h"
-// Check if strong definitions for weak functions are present in the main
-// executable. If that is the case, override dll functions to point to strong
-// implementations.
-#define INTERFACE_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
-#include "ubsan_interface.inc"
-#endif // SANITIZER_DYNAMIC