Repository navigation
rocr: add AddressSanitizer build flags and an instrumentation consistency check - #14925
shwetagkhatri wants to merge 1 commit into
Conversation
✅ All Policy Checks Passed
📖 Need help? See the Policy FAQ for details on every check and how to fix failures. |
|
🚫 Please fix the failed policies before requesting reviews. The following policy checks failed:
The |
There was a problem hiding this comment.
🟡 Changes recommended
The ASan build changes omit required sanitizer linkage for static-library consumers and force a shared ASan runtime that conflicts with applications using Clang’s default static runtime.
2 open issues
What changed in this PR
This PR adds explicit AddressSanitizer instrumentation to hsa-runtime64 and a compile-time check to catch requested instrumentation being lost before compilation.
Changes:
- Add ASan compile and link flags when requested through
ADDRESS_SANITIZERorTHEROCK_SANITIZER. - Define
HSA_ASAN_REQUESTEDfor Clang builds and reject compilation when ASan-specific memory handling is inactive. - Warn when ASan is requested with a non-Clang compiler.
| File | Description |
|---|---|
projects/rocr-runtime/runtime/hsa-runtime/core/inc/runtime.h |
Check requested ASan instrumentation against compiler-derived support. |
projects/rocr-runtime/runtime/hsa-runtime/CMakeLists.txt |
Add sanitizer flags, the consistency-check definition, and a compiler warning. |
Review effort: Balanced
| set ( HSA_SHARED_LINK_FLAGS "${HSA_SHARED_LINK_FLAGS} -fsanitize=address" ) | ||
| if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) | ||
| if ( BUILD_SHARED_LIBS ) | ||
| set ( HSA_SHARED_LINK_FLAGS "${HSA_SHARED_LINK_FLAGS} -shared-libsan" ) |
There was a problem hiding this comment.
Seems like libhsakmt does the same, so it should be okay.
| if ( BUILD_SHARED_LIBS ) | ||
| set ( HSA_SHARED_LINK_FLAGS "${HSA_SHARED_LINK_FLAGS} -shared-libsan" ) | ||
| else() | ||
| set ( HSA_SHARED_LINK_FLAGS "${HSA_SHARED_LINK_FLAGS} -static-libsan" ) |
There was a problem hiding this comment.
This seems like a valid concern, @shwetagkhatri. Look at my comment below for target_link_options.
29363ab to
343cf9a
Compare
…ency check Add -fsanitize=address and the matching link flags to the hsa-runtime64 target when AddressSanitizer is requested, mirroring the existing block in libhsakmt. Select the shared sanitizer runtime for the shared library only when the caller has not already selected one, and carry -fsanitize=address on the static target's interface link requirements, which LINK_FLAGS does not reach. Define HSA_ASAN_REQUESTED when AddressSanitizer is requested and fail the compile if it disagrees with SANITIZER_AMDGPU, so a build that asked for AddressSanitizer but was not instrumented fails instead of silently omitting the sanitizer-specific allocation handling. Builds that do not request AddressSanitizer are unaffected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
| add_dependencies( ${CORE_RUNTIME_NAME} ${CORE_RUNTIME_TARGET} ) | ||
|
|
||
| ## Add external link requirements. | ||
| target_link_libraries ( ${CORE_RUNTIME_NAME} INTERFACE hsakmt-staticdrm::hsakmt-staticdrm ) |
There was a problem hiding this comment.
To address the issue pointed out by Copilot, we can add here:
if ( HSA_ASAN_REQUESTED )
target_link_options ( ${CORE_RUNTIME_NAME} INTERFACE -fsanitize=address )
endif()
ApurvMishra-amd
left a comment
There was a problem hiding this comment.
Look at the comments, it looks good to me except for the second comment from Copilot.
343cf9a to
1af6005
Compare
cfreeamd
left a comment
There was a problem hiding this comment.
🤖 AI-assisted pre-review (for @cfreeamd) — CMakeLists.txt and runtime.h changes look structurally sound; the runtime.h consistency check is a good paranoid-check pattern. Two issues with the CMake link-flag logic flagged inline. [Required] PR title should follow type(component): format — suggest: build(rocr): add AddressSanitizer flags and instrumentation consistency check
| set ( HSA_SHARED_LINK_FLAGS "${HSA_SHARED_LINK_FLAGS} -fsanitize=address" ) | ||
| ## A shared library cannot carry the static sanitizer runtime, so select the shared | ||
| ## one -- but only when the caller has not already made that choice, so that a build | ||
| ## selecting its own sanitizer runtime is not overridden into an incompatible pair. |
There was a problem hiding this comment.
🤖 AI-assisted pre-review (for @cfreeamd)
[Important] Linking ROCr's shared library with -shared-libsan bakes the shared ASan runtime into libhsa-runtime64.so. Any application using Clang's default static ASan runtime (-fsanitize=address without -shared-libsan) will abort on startup with "Your application is linked against incompatible ASan runtimes". For example, rocrtst built with CMAKE_CXX_FLAGS=-fsanitize=address uses static ASan by default and will fail.
The more composable approach for a shared library: omit the runtime-variant flag (-shared-libsan/-static-libsan) from the library's own link step and let consumers control their runtime selection. If you need to enforce shared-runtime linkage, propagate it via target_link_options(... INTERFACE -shared-libsan) and document the requirement explicitly.
| ## one -- but only when the caller has not already made that choice, so that a build | ||
| ## selecting its own sanitizer runtime is not overridden into an incompatible pair. | ||
| if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND BUILD_SHARED_LIBS ) | ||
| if ( NOT "${CMAKE_CXX_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}" MATCHES "-(shared|static)-libsan" ) |
There was a problem hiding this comment.
🤖 AI-assisted pre-review (for @cfreeamd)
[Important] LINK_FLAGS has no effect on static archives — it is silently ignored when BUILD_SHARED_LIBS=OFF. Consumers linking the static ROCr library will get instrumented object code but no ASan link requirement, resulting in unresolved __asan_* symbols at link time unless they independently add -fsanitize=address.
Fix: in the BUILD_SHARED_LIBS=OFF + HSA_ASAN_REQUESTED branch, add:
target_link_options(${CORE_RUNTIME_TARGET} INTERFACE -fsanitize=address)(CMake 3.13+ required — already asserted near line 251. Clang-only guard applies here too.)

What this changes
hsa-runtime64decides whether to compile its ASan-specific device-memoryhandling by asking the compiler, not the build system:
SANITIZER_AMDGPUgatesAllocateAsanon GPU pools, which is what driveshsaKmtReplaceAsanHeaderPage. That call swaps the first page of a VRAMallocation for ordinary host anonymous memory. If the instrumentation flag never
reaches the compiler,
SANITIZER_AMDGPUis quietly never defined, the build staysgreen, and the failure surfaces much later as a bogus free diagnostic that looks
like an application bug.
Two changes:
runtime/hsa-runtime/CMakeLists.txt— add-fsanitize=addressand thematching link flags to the
hsa-runtime64target directly when ASan isrequested, mirroring the block that already exists in
libhsakmt/CMakeLists.txt. Today the thunk opts itself in and the runtime doesnot, so the two halves of one allocator can disagree.
runtime/hsa-runtime/core/inc/runtime.h— defineHSA_ASAN_REQUESTEDfromthe build when ASan was asked for, and
#errorif it disagrees withSANITIZER_AMDGPU.Two details in the CMake half, both raised in review:
shared library cannot carry the static ASan runtime, so a Clang
BUILD_SHARED_LIBSbuild adds
-shared-libsan— but only ifCMAKE_CXX_FLAGS,CMAKE_SHARED_LINKER_FLAGSandCMAKE_EXE_LINKER_FLAGScontain no-shared-libsan/-static-libsanalready.A build that picked its own runtime keeps it, rather than being forced into the
"linked against incompatible ASan runtimes" pair.
LINK_FLAGSare notapplied to a static archive — the step is an
arinvocation, not a link — and arenot exported, so the archive's objects were instrumented while consumers got
nothing and would fail on unresolved
__asan_*.-fsanitize=addressis nowcarried on the
INTERFACElink requirements of the consumer-facinghsa-runtime64target that wraps the archive, alongside the existing ones.
__has_featureremains the single source of truth forSANITIZER_AMDGPU. The newmacro only cross-checks it. Defining
SANITIZER_AMDGPUfrom CMake directly wouldtrade a silent miss for a silent false positive — ROCr remapping head pages with no
ASan runtime present — which is strictly worse.
Why the check is at compile time
A
check_cxx_source_compilesprobe does not see this class of failure by default,because
try_compiledoes not inheritCMAKE_CXX_COMPILER_LAUNCHER. Measured onCMake 3.29.6 with a logging script as the launcher: zero launcher invocations
during configure, one during the build — the probe reported success while the real
compile line was the thing at issue.
This is a default, not an absolute: setting
CMAKE_TRY_COMPILE_PLATFORM_VARIABLESto includeCMAKE_CXX_COMPILER_LAUNCHERbefore the probe does propagate it, and the behaviour is CMake-version sensitive. A
#erroron the real compile line avoids having to get that opt-in right.Relationship to #14327
#14327 (open, by @dileepr1, who also authored ROCm/TheRock#9511) solves the same
problem class for
projects/clrby a different mechanism: a post-buildcheck_asan_instrumentation.cmakethat runsnm -Don the linked library and failsif
__asan_initis absent.The two are worth comparing deliberately rather than landing side by side by
accident:
.soruntime.hnmper linkNeither dominates the other — they fail in different places.
nm -Dsees every TU butonly asks whether any of them was instrumented (
__asan_initis emitted as soon asone object is), and it returns early unless ASan is enabled and
BUILD_SHARED_LIBS AND UNIX. A#errorasks per TU, but only in TUs that include the header. Reviewers should say whether
rocm-systems wants one mechanism
across both components — if so, adopting #14327's post-build check for
hsa-runtime64too is the more consistent outcome, and the CMake half of this PR(the missing ASan flags) still stands on its own.
#14327 names the same root cause as ROCm/TheRock#9511 — "ccache drops
-Xarch_host -fsanitize=addressunless the compile also passes-arch, so clangnever saw the sanitizer flag" — and says explicitly that the
hipFreebad-free"also needs the ROCr runtime compiled with that same plain flag, so
SANITIZER_AMDGPUis defined". #14327 does that forprojects/clr; this PR does itfor
projects/rocr-runtime, which is the component #14327 points at.Relationship to ROCm/TheRock#9511
TheRock PR 9511 adds a
pre_hook_ROCR-Runtime.cmakethat re-adds plain host ASanflags to this subproject, because ccache reportedly drops
-Xarch_*when thecompile has no
-arch.This PR is independent of 9511 and can merge before it, after it, or without it.
Different repositories, no shared files, no textual conflict.
__has_featureis true; check passes. Fixed from the ROCr side.-fsanitize=address. Measured idempotent — doubled flag compiles and links, exit 0.9511 fixes the flag at the TheRock layer; this fixes the inference at the ROCr layer,
and additionally covers ROCr builds that never go through TheRock — distro packaging,
Spack, rocm-systems CI, or a plain
cmakeinvocation.Testing
All figures in this section were taken on this branch's single commit, on one Linux host with
AMD Clang 18 and CMake 3.29.6. They are configure-time and compile-time observations,
not a device run.
-DADDRESS_SANITIZER=ON:hsa-runtime64: AddressSanitizer (ASAN) enabledemitted.-fsanitize=address,-fno-omit-frame-pointerandHSA_ASAN_REQUESTED=1allpresent on the generated compile line;
-fsanitize=addressand-shared-libsanon the link line, once each.
core/runtime/runtime.cppcompiles clean (pre-existing warnings only).-fsanitize=address— what alauncher would leave behind — fails with the new
#error, exit 1.Sanitizer-runtime and static-interface behaviour, measured across three configures:
-shared-libsanon the link line, onceCMAKE_CXX_FLAGS=-static-libsan-shared-libsanadded; caller's choice keptBUILD_SHARED_LIBS=OFF+ ASanhsa-runtime64Targets.cmakecarries-fsanitize=address(was absent)Default build, no ASan requested: no status message, no
-fsanitize=address, noHSA_ASAN_REQUESTED; same TU builds with zero errors. Unaffected.Preprocessor logic verified across all four states: no-ASan/no-define compiles,
requested+instrumented compiles, requested+stripped fails, instrumented-without-define
compiles (no false alarm).
Coverage of the check
The check fires only in translation units that include
core/inc/runtime.h, which isa subset of this target's sources — so it is not a whole-target guarantee. Nor is
#14327's post-build
nm -Dcheck, which passes as soon as a single object in thelibrary was instrumented. What this check does guarantee is the part that
matters: both TUs in this target that consume
SANITIZER_AMDGPU—core/runtime/runtime.cppandcore/runtime/hsa_ext_amd.cpp— include that header,so every site whose behaviour depends on the macro is covered.
Not covered / known limits
unrelated, pre-existing toolchain reason — the
.hsacosteps pick up a systemclang that rejects the gfx950 and gfx1250 target IDs and cannot find the ROCm
device library. Those are not host C++ compiles and this change does not touch
them. The host TU was built directly instead. CI exercises the full path.
hipMalloc/hipFreeASan scenario was executed on a device.behaviour was demonstrated with a stand-in script, which shows the
try_compileblindness but not ccache's specific
-Xarch_*handling.g++ -DADDRESS_SANITIZER=ON,hsa-runtime64previously received-fsanitize=addresson neither its compile nor its link line; with this patch itreceives both, matching
libhsakmt. What is pre-existing is only thatSANITIZER_AMDGPUis__clang__-gated, so ROCr's ASan-specific handling stilldoes not activate under GCC. The consistency check is therefore withheld under GCC
— it could only ever fire — and CMake emits a warning instead. Reviewers may
reasonably prefer that GCC get no flags at all; say so if you do.
-DCMAKE_CXX_FLAGS=-fsanitize=addressthrough a launcher that eats it still getsno warning, because nothing told the build ASan was requested. The check covers
builds requesting ASan via
ADDRESS_SANITIZERorTHEROCK_SANITIZER.build-system assertion: its entire behaviour is whether a configure emits certain
flags and whether a compile fails when they are missing. There is no runtime entry
point for a
test_*.cppto call. The equivalent verification is the configure andcompile matrix in Testing above. Happy to add one if a reviewer sees a shape for it.
libhsakmt's rather than the UBSAN block's,which also consults environment variables. Worth a reviewer opinion on whether the
two should converge.
Provenance of the allocator description
The reason the head-page swap matters at all is that, when the application is
instrumented, ASan's interceptor serves
hsa_amd_memory_pool_allocatefrom its own4096-aligned device allocator and places the 16-byte
AsanChunkbookkeeping headerin the last 16 bytes of that first page, written by the host CPU. If that page is
still device memory,
freere-reads the header, does not findCHUNK_ALLOCATED, andreports
attempting free on address which was not malloc()-ed.That description was read from a shallow checkout of an AMD
llvm-projectforkat commit
7c0e3b4170de, dated 2026-03-30, not from upstream LLVM at any releasedtag. The constants (
kChunkHeaderSize == 16,CHUNK_ALLOCATED = 2,kPageSize_ = 4096) hold in that tree. Anyone relying on this reasoning shouldre-confirm against the compiler-rt actually shipping with their ROCm build.
🤖 Generated with Claude Code