[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