From a1d186990d3624b8ae52cc1d1d989aeaff2aae4f Mon Sep 17 00:00:00 2001 From: Roman Kennke Date: Thu, 9 Jul 2026 15:29:40 +0000 Subject: [PATCH] build-logic: link sanitizer runtime for the fuzzer shared lib commonLinuxLinkerArgs() carries -Wl,-z,defs, which forbids undefined symbols in the output. The fuzzer objects are instrumented with -fsanitize=address,undefined and therefore reference __asan_*/__ubsan_*, but -fsanitize=address only links the sanitizer runtime into *executables*, not shared libraries. So the fuzzer .so ends up with undefined __asan_*/__ubsan_* and the link fails under -z defs on any box where the fuzzer config is active (clang with libFuzzer, non-musl). The asan config already solves this by linking the matching runtime explicitly via locateLibasan (-lclang_rt.asan- on clang, which also provides UBSan symbols; -lasan + -lubsan on gcc). Apply the same, already-proven treatment to configureFuzzer: thread the detected compiler through and add the runtime to the Linux linker args. CI never hit this because its publish/build path passes --exclude-task compileFuzzer and only assembles the release jar, so the fuzzer is never linked there. It only bites a local build that links all active configs (e.g. publishToMavenLocal on a clang box). The macOS branch is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../native/config/ConfigurationPresets.kt | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/build-logic/conventions/src/main/kotlin/com/datadoghq/native/config/ConfigurationPresets.kt b/build-logic/conventions/src/main/kotlin/com/datadoghq/native/config/ConfigurationPresets.kt index 3f91ca36c2..681f76a1bc 100644 --- a/build-logic/conventions/src/main/kotlin/com/datadoghq/native/config/ConfigurationPresets.kt +++ b/build-logic/conventions/src/main/kotlin/com/datadoghq/native/config/ConfigurationPresets.kt @@ -51,7 +51,7 @@ object ConfigurationPresets { configureTsan(this, currentPlatform, currentArch, version, rootDir, compiler) } register("fuzzer") { - configureFuzzer(this, currentPlatform, currentArch, version, rootDir) + configureFuzzer(this, currentPlatform, currentArch, version, rootDir, compiler) } } @@ -313,7 +313,8 @@ object ConfigurationPresets { platform: Platform, architecture: Architecture, version: String, - rootDir: File + rootDir: File, + compiler: String = "gcc" ) { config.platform.set(platform) config.architecture.set(architecture) @@ -339,7 +340,28 @@ object ConfigurationPresets { when (platform) { Platform.LINUX -> { config.compilerArgs.set(fuzzerCompilerArgs + commonLinuxCompilerArgs(version)) - config.linkerArgs.set(commonLinuxLinkerArgs() + fuzzerLinkerArgs) + + // commonLinuxLinkerArgs() carries -Wl,-z,defs, which forbids the + // undefined __asan_*/__ubsan_* symbols the instrumented objects + // reference. -fsanitize=address only links the runtime into + // executables, not shared libraries, so the fuzzer .so needs the + // runtime linked explicitly — same treatment (and rationale) as the + // asan config. On clang this resolves both __asan_* and __ubsan_* + // from one clang_rt.asan runtime; on gcc it adds -lubsan. + val libasan = PlatformUtils.locateLibasan(compiler) + val fuzzerRuntimeArgs = if (libasan != null) { + val asanLibDir = File(libasan).parent + val asanLibName = File(libasan).nameWithoutExtension.removePrefix("lib") + val ubsanLibs = if (asanLibName.startsWith("clang_rt")) emptyList() + else listOf("-lubsan") + listOf("-L$asanLibDir", "-l$asanLibName", + "-Wl,-rpath,$asanLibDir") + + ubsanLibs + + fuzzerLinkerArgs + } else { + fuzzerLinkerArgs + } + config.linkerArgs.set(commonLinuxLinkerArgs() + fuzzerRuntimeArgs) config.testEnvironment.apply { put("ASAN_OPTIONS", "allocator_may_return_null=1:detect_stack_use_after_return=0:handle_segv=0:abort_on_error=1:symbolize=1:suppressions=$rootDir/gradle/sanitizers/asan.supp")