[llvm-branch-commits] [clang] [compiler-rt] [compiler-rt] Add 'csan' library for the concurrency sanitizer (PR #225782)
Vitaly Buka via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Oct 1 17:47:32 PDT 2026
================
@@ -0,0 +1,330 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Watchpoint-based host data race detector inspired by KCSAN. The host and GPU
+/// runtimes share a configurable packed-watchpoint implementation.
+///
+//===----------------------------------------------------------------------===//
+
+#include "csan.h"
+#include "csan_watch.h"
+
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_mutex.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+
+using namespace __sanitizer;
+
+#define INTERFACE extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+
+static constexpr u32 kHostWatchpointEntries = 64;
+static constexpr u32 kHostCheckAdjacent = 1;
+static constexpr uptr kHostSlotRange = 4096;
+static constexpr uptr kHostMaxAccessSize =
+ kHostSlotRange * (1 + kHostCheckAdjacent);
+static_assert(__atomic_always_lock_free(sizeof(u64), nullptr),
+ "host watchpoints must be lock-free");
+
+using HostWatchpointTable =
+ __csan::WatchpointTable<kHostMaxAccessSize, kHostCheckAdjacent>;
+static u64 HostWatchpoints[kHostWatchpointEntries +
+ HostWatchpointTable::OverflowEntries];
+static_assert(HostWatchpointTable::AddressBits == 48,
+ "host watchpoints require 48-bit pointers");
+
+static HostWatchpointTable GetHostWatchpoints() {
+ return HostWatchpointTable(HostWatchpoints);
+}
+
+static u32 HostWatchpointSlot(uptr Address) {
+ return Address / kHostSlotRange % kHostWatchpointEntries;
+}
+
+namespace __csan {
+
+Flags flags_data;
+
+static void Initialize();
+
+static atomic_uint64_t NumDataRaces;
+static THREADLOCAL u32 DisableCount;
+static THREADLOCAL s32 Skip;
+static THREADLOCAL u32 RandState;
+
+namespace {
+struct ScopedDisable {
+ ScopedDisable() { ++DisableCount; }
+ ~ScopedDisable() { --DisableCount; }
+};
+} // namespace
+
+void RecordDataRace() {
+ atomic_fetch_add(&NumDataRaces, 1, memory_order_relaxed);
+}
+
+void Flags::SetDefaults() {
+#define CSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "csan_flags.inc"
+#undef CSAN_FLAG
+}
+
+static void RegisterCsanFlags(FlagParser *Parser, Flags *F) {
+#define CSAN_FLAG(Type, Name, DefaultValue, Description) \
+ RegisterFlag(Parser, #Name, Description, &F->Name);
+#include "csan_flags.inc"
+#undef CSAN_FLAG
+}
+
+void InitializeFlags() {
+ SetCommonFlagsDefaults();
+ {
+ CommonFlags CF;
+ CF.CopyFrom(*common_flags());
+ CF.external_symbolizer_path = GetEnv("CSAN_SYMBOLIZER_PATH");
+ OverrideCommonFlags(CF);
+ }
+
+ flags()->SetDefaults();
+
+ FlagParser Parser;
+ RegisterCommonFlags(&Parser);
+ RegisterCsanFlags(&Parser, flags());
+ Parser.ParseString(__csan_default_options());
+ Parser.ParseStringFromEnv("CSAN_OPTIONS");
+ InitializeCommonFlags();
+ if (Verbosity())
+ ReportUnrecognizedFlags();
+ if (common_flags()->help)
+ Parser.PrintFlagDescriptions();
+}
+
+static u32 Random(u32 EpRo) {
+ if (EpRo <= 1)
+ return 0;
+ u32 State = RandState;
+ if (!State)
+ State = (u32)__builtin_readcyclecounter() | 1u;
+ State = 1664525u * State + 1013904223u;
+ RandState = State;
+ return State % EpRo;
+}
+
+static void ResetSkip() {
+ s32 Count = flags()->skip_watch;
+ if (Count < 0)
+ Count = 0;
+ if (Count)
+ Count -= (s32)Random((u32)Count);
+ Skip = Count;
+}
+
+static bool ShouldWatch(int Type) {
+ if (Type & CSAN_ACCESS_ATOMIC)
+ return false;
+ if (--Skip >= 0)
+ return false;
+ return true;
+}
+
+static void DelayAccess(int Type) {
+ s32 Delay = flags()->udelay;
+ if (Delay < 0)
+ Delay = 0;
+ if (Delay) {
+ u32 Skew = (Type & CSAN_ACCESS_COMPOUND) ? 1u : 0u;
+ u32 Span = (u32)Delay >> Skew;
+ if (!Span)
+ Span = (u32)Delay;
+ Delay -= (s32)Random(Span);
+ }
+ if (Delay)
+ internal_usleep((u64)Delay);
+}
+
+static u64 ReadRange(const volatile u8 *Bytes, uptr Size) {
+ u64 Sum = 0xcbf29ce484222325ull;
+ uptr I = 0;
+ for (; I < Size && ((uptr)(Bytes + I) & 7u); ++I)
+ Sum = (Sum ^ Bytes[I]) * 0x100000001b3ull;
+ for (; I + 8 <= Size; I += 8)
+ Sum = (Sum ^ *(const volatile u64 *)(Bytes + I)) * 0x100000001b3ull;
+ for (; I < Size; ++I)
+ Sum = (Sum ^ Bytes[I]) * 0x100000001b3ull;
+ return Sum;
+}
+
+static u64 ReadInstrumented(const volatile void *Ptr, uptr Size) {
+ switch (Size) {
+ case 1:
+ return *(const volatile u8 *)Ptr;
----------------
vitalybuka wrote:
it looks like it used for __csan_unaligned_read* which make here unaligned load
https://github.com/llvm/llvm-project/pull/225782
More information about the llvm-branch-commits
mailing list