Skip to content

Sanitizers for the D test runner

Whether — and how — sparkles:test-runner can drive a memory or threading sanitizer over a D unittest suite and surface each finding against the test that produced it. The survey grounds the LLVM compiler-rt stack (asan/lsan/tsan/msan/ubsan/hwasan/gwp_asan) and the Valgrind family on the D toolchain (LDC/GDC/DMD, druntime, dub), surveys how the field's runners attribute a finding per test, maps the story onto macOS/Windows and the hardware-assisted frontier, audits today's runner, and lands in a capability matrix and a milestoned --sanitize/--valgrind/--isolate proposal. Every page is grounded twice over: claims carry pinned repo@commit path:line locators or recorded experiments, and locally-demonstrable behavior is backed by runnable probes CI compiles and runs.

Last reviewed: July 11, 2026

This survey answers thirteen questions:

  1. Can LDC actually sanitize D — which -fsanitize= kinds, and which runtime really links? → d-toolchain
  2. Which of the three D compilers reaches which tool? → d-toolchain
  3. What does D's garbage collector hide from every memory tool? → the GC blind spot · asan · d-toolchain
  4. Is UndefinedBehaviorSanitizer reachable from D at all? → ubsan
  5. What does AddressSanitizer catch, and what are its D-specific blind spots? → asan
  6. Can a test runner attribute a race/leak/fault to the test that caused it? → runner-integrations + tsan
  7. What deterministically livelocks under a sanitizer, and why? → tsan
  8. Is MemorySanitizer usable for D? → d-toolchain § MSan
  9. What is the no-recompile path a DMD-built binary can use? → valgrind
  10. How does the field's tooling surface a sanitizer finding per test? → runner-integrations
  11. What happens today when a sparkles test crashes mid-run? → baseline
  12. What changes when the runner leaves Linux for macOS or Windows? → macos-windows
  13. What is the hardware-assisted / post-ASan story for D (HWASan, MTE, GWP-ASan, RTSan, TySan)? → hardware-assisted

Where does sparkles stand against all of it, and what should it build? → baseline + the delta tableproposal.


The seven concerns

Every tool in the tree is analyzed against the same seven-concern spine, in fixed order — and where a concern does not apply (UBSan has no instrumentation to control; MTE has no runner-facing option surface), the page says so, because the absence of a capability is itself a finding:

  1. Defect classes and blind spots — what it catches, and what it structurally cannot (the GC memory blind spot, redzone limits, definedness vs addressability).
  2. Instrumentation model and recompile scope — the instrumentation locus and whether user code, the whole instrumented world, or nothing must be rebuilt.
  3. D and druntime interaction — the GC, fibers, and the signal-based stop-the-world scan against a tool built for malloc/free C.
  4. Runtime control and report capturehalt vs recover, the weak-hook surface, and Valgrind's client requests.
  5. Symbolization and suppressions — who demangles D (almost nobody), and the two suppression formats.
  6. Test-runner integration semantics — the three attribution designs (windowing / process-per-test / wrapper-and-parse).
  7. Platform, toolchain, and overhead — LDC/GDC/DMD reach, off-Linux columns, and the measured cost.

Master catalog

SubjectWhat it isConcern focusVerification bedLink
Conceptsthe tree's shared vocabularyalltool sourcesconcepts.md
AddressSanitizer + LSanthe memory-error workhorse (shadow + redzones)1, 3, 4x86_64-linux hwasan.md
UBSana documented absence — unreachable from D2source + one GDC hw datumubsan.md
ThreadSanitizerthe happens-before data-race detector3, 6x86_64-linux hwtsan.md
The D toolchainLDC/GDC/DMD × druntime × dub; owns the MSan story2, 3, 7x86_64-linux hwd-toolchain.md
Valgrindmemcheck/helgrind/DRD — no recompile, DMD's path1, 2, 6x86_64-linux hw (LDC + DMD)valgrind.md
Runner integrationshow Go/Rust/Swift/Zig/CTest/Bazel/pytest attribute6source; one Go hw batteryrunner-integrations.md
macOS & Windowsthe two off-Linux columns7source/lit; one aarch64-darwin datummacos-windows.md
Hardware-assistedHWASan · MTE · GWP-ASan · RTSan · TySan1, 2, 7x86_64-linux hw (3 of 5); MTE source/lithardware-assisted.md
sparkles baselinetoday's runner, as observed under auditaudit targetin-repo (private clone)sparkles-baseline.md
Integration proposalmilestoned --sanitize/--valgrind/--isolatedesignintegration-proposal.md
Comparisoncapability matrix · trade-offs · delta tablesynthesiscomparison.md

Taxonomies

By instrumentation locus

The axis that decides which D compiler can reach a tool at all (concepts § instrumentation locus).

LocusToolsD-reachabilityPages
LLVM IR passASan, LSan, TSan, MSan, HWASan, RTSaninherited by LDC once the flag is plumbedasan, tsan, d-toolchain, hardware-assisted
clang CodeGen onlyUBSan, TySanunreachable — no IR pass for a non-clang frontendubsan, hardware-assisted
Dynamic binary translationValgrind memcheck/helgrind/DRD, Dr. Memoryevery compiler incl. DMD, no recompilevalgrind, macos-windows
Hardware tag checkArm MTEa deployment, needs MTE siliconhardware-assisted
Sampling allocatorGWP-ASanC-heap only; a production, not test, toolhardware-assisted

By defect class

Which tool catches what — and the D caveat on each.

Defect classCaught byNot caught by / D caveat
Heap/stack/global overflow, use-after-freeASan, HWASan, memcheck, GWP-ASan (sampled)all blind to GC-pool memory; memcheck has no redzones
Stack-use-after-returnASan (fake stack) — the flagship fiber catchruntime-gated by detect_stack_use_after_return
Uninitialized-value read (definedness)memcheck, MSanASan structurally cannot; MSan needs an instrumented world
Data raceTSan, helgrind/DRDhelgrind/DRD blind to core.atomic/SpinLock; a serialized schedule hides it
Memory leakLSan, memcheckGC-referenced malloc = LSan false positive; a dropped GC block is invisible
Undefined behaviour (shifts, int.min/-1)UBSan (C/C++ only)unreachable from every D compiler
Real-time-safety violationRTSanLDC blocked on an LLVM ≥ 20 bump
Strict-aliasing (TBAA)TySannever for D — no !tbaa, no aliasing rules

By toolchain

CompilerReachesVia
LDCASan, LSan, TSan, MSan (-conf=/tarball), Valgrind-fsanitize= IR passes + the gcc-runtime fallback; Valgrind no-recompile
GDC 11.5ASan (--param asan-globals=0), LSan, TSan, Valgrind; UBSan check-emptyGCC libsanitizer + a -B/-L workaround
DMDValgrind onlyno -fsanitize at all — DBI is its sole path

By verification level

LevelWhat carries it
[hw-verified: x86_64-linux]ASan/LSan/TSan/MSan(-conf=)/HWASan(aliasing)/GWP-ASan/RTSan/TySan catches, the dub-channel + druntime experiments, and the eleven probes — the primary bed
[hw-verified: aarch64-darwin]one datum: the recon Apple-clang ASan smoke test; the D-on-darwin runs were blocked (rerun kit staged)
[source-verified]LDC/GDC/DMD flag sets, compiler-rt/Valgrind/dub source, the Windows link branches, the MTE kernel ABI
[literature]vendor docs (MS Learn, Apple MIE, Android), the overhead headline figures, the saved papers

IMPORTANT

The honest boundary: the x86_64-linux box is the only full bed. macOS is mechanism-verified, not run-verified — the mac-bsn transcripts were blocked and one Apple-clang C datum stands in; Windows has no hardware at all; and no MTE silicon is in reach (the project's only aarch64 box, an Apple M4, has no MTE). Every such gap is tagged in place and carried into comparison § open questions.

Milestones

When the field's tools landed. Coarse dates are [literature]; the Valgrind design papers are [source-verified] (archived locally).

WhenWhat
2002Valgrind's first public release (DBI memory checking) [literature]
2007Valgrind's design papers — the framework at PLDI 2007, "How to Shadow Every Byte of Memory" at VEE 2007 [source-verified]
2011AddressSanitizer lands in LLVM (paper USENIX ATC 2012) [literature]
2011–2012ThreadSanitizer v2 (the v3 shadow rewrite follows in 2021) [literature]
~2013LeakSanitizer ships alongside ASan [literature]
2015MemorySanitizer paper at CGO 2015 [literature]
2018Arm MTE announced (arXiv 1802.09517) [literature]
2019HWASan becomes Android 10's production memory-safety tool; GWP-ASan enters production fleets [literature]
2021MSVC /fsanitize=address reaches GA in Visual Studio 16.9 [literature]
2023-11first MTE handset: Google Pixel 8 / Tensor G3; the GWP-ASan paper (arXiv 2311.09394) [literature]
2024first MTE datacenter CPU: AmpereOne [literature]
2025-03RTSan and experimental TySan ship in LLVM/Clang 20 [literature]
2025-09Apple Memory Integrity Enforcement (EMTE) debuts on the A19 / iPhone 17 — no M-series [literature]

The runnable probes

Standalone dub single-file programs under examples/; CI compiles and runs each, and every one SKIP:s cleanly on a host or toolchain that lacks the capability (an uninstrumented or DMD build). Environment: Linux 6.18.26, AMD Ryzen 9 7940HX, LDC 1.41.0, against GCC 15.2's libasan/libtsan via LDC's gcc link fallback.

ProbeDemonstratesBacks
asan-heap-uaf.dheap use-after-free caught, self-symbolized, exit 1asan concern 1
asan-stack-uar.dstack-use-after-return + the detect_stack_use_after_return gateasan concern 1
asan-global-overflow.dglobal-buffer-overflow via .ptr past a __gshared arrayasan concern 1
asan-report-capture.dlog_path routing + report callback (the attribution material)asan concern 4
lsan-gc-interplay.dLSan × GC four quadrants; detect_leaks gates the manual checkasan § LSan
tsan-data-race.drace caught, core.atomic silent; halt_on_error=false, exit 66tsan concerns 3/4
gc-uaf-blindspot.dGC-pool UAF invisible while the malloc twin is caughtd-toolchain § GC blind spot
fiber-asan.dfiber stack-use-after-return (the event-horizon bug), stock druntimed-toolchain § fibers
valgrind-memcheck-catch.dno-recompile XML + exit-code pipeline (DMD too)valgrind concern 4
valgrind-client-requests.detc.valgrind driving memcheck's A/V bits from Dvalgrind concern 3
valgrind-attribution.dVALGRIND_PRINTF marker-window per-test attributionvalgrind § runner integration

All eleven are platforms "linux" by design (macos-windows explains why a darwin port is a new file, not a platforms toggle); the ci helper -skips them on the macOS CI row. Run them with:

bash
dub run :ci -- -x --files 'docs/research/sanitizers/examples/*.d'

Quick navigation

Sources

Per-page Sources sections carry the primary references; repos are pinned by SHA there. The reads behind the survey are LLVM compiler-rt/clang/llvm at 73802c2e, Valgrind at 218cee2f (tag VALGRIND_3_26_0), LDC v1.41.0, DMD e6baf474, dub 5efed360, and the Linux kernel at e43ffb69 (v7.1-rc6) for the MTE ABI; the runner-integration survey adds Go, Rust/cargo-nextest, SwiftPM, Zig, googletest, CMake, and pytest-valgrind at their pinned SHAs. The sanitizer papers (ASan, TSan, MSan, the two Valgrind papers, GWP-ASan, and the MTE whitepaper) are archived locally under papers/sanitizers/. The survey's own experimental evidence is the eleven runnable probes plus the recon mac-bsn Apple-clang smoke test, with every experiment's environment recorded alongside its output.