diff --git a/.github/workflows/build-reusable.yml b/.github/workflows/build-reusable.yml index 1ff4c62345c8..2faffe9e435c 100644 --- a/.github/workflows/build-reusable.yml +++ b/.github/workflows/build-reusable.yml @@ -236,6 +236,7 @@ jobs: windows: strategy: + fail-fast: false matrix: include: - runner: windows @@ -258,6 +259,20 @@ jobs: package_json_arch: "arm64" label: bun-webkit-windows-arm64-debug platform: ARM64 + - runner: windows + build-type: Release + package_json_arch: "x64" + label: bun-webkit-windows-amd64-asan + platform: x64 + ENABLE_SANITIZERS: "address" + - runner: windows + build-type: Debug + package_json_arch: "x64" + label: bun-webkit-windows-amd64-debug-asan + platform: x64 + ENABLE_SANITIZERS: "address" + # Note: ARM64 Windows ASAN is not available — LLVM does not ship + # clang_rt.asan libraries for Windows on ARM64 (woa64) yet. runs-on: ${{ matrix.runner }} timeout-minutes: 90 steps: @@ -312,6 +327,7 @@ jobs: $env:GITHUB_SHA = "${{ inputs.build_ref }}" $env:PACKAGE_JSON_ARCH = "${{matrix.package_json_arch}}" $env:GITHUB_REPOSITORY = "${{ github.repository }}" + $env:ENABLE_SANITIZERS = "${{matrix.ENABLE_SANITIZERS}}" ./windows-release.ps1 -Platform ${{matrix.platform}} - uses: actions/upload-artifact@v4 with: @@ -493,6 +509,14 @@ jobs: with: name: bun-webkit-windows-arm64-debug path: ${{runner.temp}}/bun-webkit-windows-arm64-debug + - uses: actions/download-artifact@v4 + with: + name: bun-webkit-windows-amd64-asan + path: ${{runner.temp}}/bun-webkit-windows-amd64-asan + - uses: actions/download-artifact@v4 + with: + name: bun-webkit-windows-amd64-debug-asan + path: ${{runner.temp}}/bun-webkit-windows-amd64-debug-asan - uses: actions/download-artifact@v4 with: name: bun-webkit-linux-amd64-musl @@ -547,6 +571,8 @@ jobs: mv ${{runner.temp}}/bun-webkit-windows-amd64-debug/bun-webkit.tar.gz ./out/bun-webkit-windows-amd64-debug.tar.gz mv ${{runner.temp}}/bun-webkit-windows-arm64/bun-webkit.tar.gz ./out/bun-webkit-windows-arm64.tar.gz mv ${{runner.temp}}/bun-webkit-windows-arm64-debug/bun-webkit.tar.gz ./out/bun-webkit-windows-arm64-debug.tar.gz + mv ${{runner.temp}}/bun-webkit-windows-amd64-asan/bun-webkit.tar.gz ./out/bun-webkit-windows-amd64-asan.tar.gz + mv ${{runner.temp}}/bun-webkit-windows-amd64-debug-asan/bun-webkit.tar.gz ./out/bun-webkit-windows-amd64-debug-asan.tar.gz - name: Set release info id: release_info @@ -605,3 +631,5 @@ jobs: ./out/bun-webkit-windows-amd64-debug.tar.gz ./out/bun-webkit-windows-arm64.tar.gz ./out/bun-webkit-windows-arm64-debug.tar.gz + ./out/bun-webkit-windows-amd64-asan.tar.gz + ./out/bun-webkit-windows-amd64-debug-asan.tar.gz diff --git a/Source/WTF/wtf/Threading.cpp b/Source/WTF/wtf/Threading.cpp index 825ece6195b4..04e66ad580ba 100644 --- a/Source/WTF/wtf/Threading.cpp +++ b/Source/WTF/wtf/Threading.cpp @@ -109,7 +109,7 @@ static std::optional stackSize(ThreadType threadType) #if PLATFORM(PLAYSTATION) if (threadType == ThreadType::JavaScript) return 512 * KB; -#elif OS(DARWIN) && (ASAN_ENABLED || ASSERT_ENABLED) +#elif (OS(DARWIN) || OS(WINDOWS)) && (ASAN_ENABLED || ASSERT_ENABLED) if (threadType == ThreadType::Compiler) return 1 * MB; // ASan / Debug build needs more stack space. #elif OS(WINDOWS) diff --git a/Source/cmake/OptionsJSCOnly.cmake b/Source/cmake/OptionsJSCOnly.cmake index dc25e8945abb..3c020b396790 100644 --- a/Source/cmake/OptionsJSCOnly.cmake +++ b/Source/cmake/OptionsJSCOnly.cmake @@ -207,6 +207,30 @@ if (DEFINED ENV{ICU_ROOT}) set(ICU_ROOT "$ENV{ICU_ROOT}" CACHE PATH "" FORCE) endif () +# On Windows with ICU_ROOT set, pre-populate FindICU library cache variables +# to ensure our static ICU build is used instead of a system-installed dynamic ICU. +# Without this, FindICU may find dynamic ICU libraries from the system PATH, +# causing jsc.exe to depend on ICU DLLs that aren't shipped in the package. +if (WIN32 AND DEFINED ICU_ROOT AND EXISTS "${ICU_ROOT}/lib") + set(_icu_lib_dir "${ICU_ROOT}/lib") + if (NOT ICU_DATA_LIBRARY_RELEASE) + find_library(ICU_DATA_LIBRARY_RELEASE NAMES sicudt icudt PATHS "${_icu_lib_dir}" NO_DEFAULT_PATH) + endif () + if (NOT ICU_I18N_LIBRARY_RELEASE) + find_library(ICU_I18N_LIBRARY_RELEASE NAMES icuin sicuin PATHS "${_icu_lib_dir}" NO_DEFAULT_PATH) + endif () + if (NOT ICU_UC_LIBRARY_RELEASE) + find_library(ICU_UC_LIBRARY_RELEASE NAMES icuuc sicuuc PATHS "${_icu_lib_dir}" NO_DEFAULT_PATH) + endif () + if (ICU_DATA_LIBRARY_RELEASE AND ICU_I18N_LIBRARY_RELEASE AND ICU_UC_LIBRARY_RELEASE) + message(STATUS "Pre-set static ICU libraries from ICU_ROOT=${ICU_ROOT}:") + message(STATUS " ICU data: ${ICU_DATA_LIBRARY_RELEASE}") + message(STATUS " ICU i18n: ${ICU_I18N_LIBRARY_RELEASE}") + message(STATUS " ICU uc: ${ICU_UC_LIBRARY_RELEASE}") + endif () + unset(_icu_lib_dir) +endif () + find_package(ICU 70.1 REQUIRED COMPONENTS data i18n uc) if (APPLE) diff --git a/Source/cmake/WebKitCompilerFlags.cmake b/Source/cmake/WebKitCompilerFlags.cmake index c94cb1924ac5..17acd2338283 100644 --- a/Source/cmake/WebKitCompilerFlags.cmake +++ b/Source/cmake/WebKitCompilerFlags.cmake @@ -325,10 +325,39 @@ if (COMPILER_IS_GCC_OR_CLANG) # -fsanitize=* flags, because check_cxx_compiler_flag will report it's # unsupported, because it causes the build to fail if not used when linking. if (ENABLE_SANITIZERS) - if (MSVC AND WTF_CPU_X86_64) - find_library(CLANG_ASAN_LIBRARY clang_rt.asan_dynamic_runtime_thunk-x86_64 ${CLANG_LIB_PATH}) - find_library(CLANG_ASAN_RT_LIBRARY clang_rt.asan_dynamic-x86_64 PATHS ${CLANG_LIB_PATH}) - set(SANITIZER_LINK_FLAGS "\"${CLANG_ASAN_LIBRARY}\" \"${CLANG_ASAN_RT_LIBRARY}\"") + if (MSVC) + # Auto-discover CLANG_LIB_PATH from the compiler if not explicitly set + if (NOT CLANG_LIB_PATH OR CLANG_LIB_PATH STREQUAL "") + execute_process( + COMMAND ${CMAKE_CXX_COMPILER} /clang:--print-resource-dir + OUTPUT_VARIABLE _CLANG_RESOURCE_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + ) + if (_CLANG_RESOURCE_DIR) + set(CLANG_LIB_PATH "${_CLANG_RESOURCE_DIR}/lib/windows") + endif () + endif () + + if (WTF_CPU_X86_64) + find_library(CLANG_ASAN_LIBRARY clang_rt.asan_dynamic_runtime_thunk-x86_64 PATHS ${CLANG_LIB_PATH}) + find_library(CLANG_ASAN_RT_LIBRARY clang_rt.asan_dynamic-x86_64 PATHS ${CLANG_LIB_PATH}) + elseif (WTF_CPU_ARM64) + find_library(CLANG_ASAN_LIBRARY clang_rt.asan_dynamic_runtime_thunk-aarch64 PATHS ${CLANG_LIB_PATH}) + find_library(CLANG_ASAN_RT_LIBRARY clang_rt.asan_dynamic-aarch64 PATHS ${CLANG_LIB_PATH}) + endif () + + if (CLANG_ASAN_LIBRARY AND CLANG_ASAN_RT_LIBRARY) + # The thunk library must be linked with /WHOLEARCHIVE to force inclusion of + # all its symbols — particularly operator new/delete replacements and weak + # function stubs (__asan_new, __asan_delete, __sanitizer_register_weak_function, + # etc.) that would otherwise be dropped by the linker as unreferenced. + set(SANITIZER_LINK_FLAGS "/WHOLEARCHIVE:\"${CLANG_ASAN_LIBRARY}\" \"${CLANG_ASAN_RT_LIBRARY}\"") + message(STATUS "Found ASAN libraries: ${CLANG_ASAN_LIBRARY} ${CLANG_ASAN_RT_LIBRARY}") + else () + message(FATAL_ERROR "ASAN libraries not found at CLANG_LIB_PATH=${CLANG_LIB_PATH}. " + "Ensure LLVM is installed with ASAN runtime libraries for this architecture.") + endif () else () set(SANITIZER_LINK_FLAGS "-lpthread") endif () @@ -337,14 +366,28 @@ if (COMPILER_IS_GCC_OR_CLANG) if (${SANITIZER} MATCHES "address") WEBKIT_PREPEND_GLOBAL_COMPILER_FLAGS("-fno-omit-frame-pointer -fno-optimize-sibling-calls") set(SANITIZER_COMPILER_FLAGS "-fsanitize=address ${SANITIZER_COMPILER_FLAGS}") - set(SANITIZER_LINK_FLAGS "-fsanitize=address ${SANITIZER_LINK_FLAGS}") + if (MSVC) + # Disable MSVC STL container annotations to avoid /failifmismatch errors + # when linking against static libraries not compiled with ASAN (e.g., ICU). + # The STL emits annotate_string/annotate_vector pragmas when __SANITIZE_ADDRESS__ + # is defined, causing mismatches with non-ASAN object files. + WEBKIT_PREPEND_GLOBAL_COMPILER_FLAGS("-D_DISABLE_STRING_ANNOTATION -D_DISABLE_VECTOR_ANNOTATION") + endif () + # On MSVC (clang-cl), CMake passes linker flags directly to lld-link which + # doesn't understand -fsanitize=. The ASAN .lib files are already in + # SANITIZER_LINK_FLAGS from the find_library block above. + if (NOT MSVC) + set(SANITIZER_LINK_FLAGS "-fsanitize=address ${SANITIZER_LINK_FLAGS}") + endif () elseif (${SANITIZER} MATCHES "undefined") # Please keep these options synchronized with Tools/sanitizer/ubsan.xcconfig WEBKIT_PREPEND_GLOBAL_COMPILER_FLAGS("-fno-omit-frame-pointer -fno-delete-null-pointer-checks -fno-optimize-sibling-calls") # -fsanitize=vptr is disabled because incompatible with -fno-rtti set(SANITIZER_COMPILER_FLAGS "-fsanitize=undefined -fno-sanitize=vptr ${SANITIZER_COMPILER_FLAGS}") - set(SANITIZER_LINK_FLAGS "-fsanitize=undefined ${SANITIZER_LINK_FLAGS}") + if (NOT MSVC) + set(SANITIZER_LINK_FLAGS "-fsanitize=undefined ${SANITIZER_LINK_FLAGS}") + endif () elseif (${SANITIZER} MATCHES "thread" AND NOT MSVC) set(SANITIZER_COMPILER_FLAGS "-fsanitize=thread ${SANITIZER_COMPILER_FLAGS}") diff --git a/build.ts b/build.ts index d7e16755b34b..988f3c5404b7 100755 --- a/build.ts +++ b/build.ts @@ -177,8 +177,8 @@ const getBuildFlags = (config: BuildConfig) => { "-DUSE_VISIBILITY_ATTRIBUTE=1" ); - if (IS_MAC || IS_LINUX) { - // Enable address sanitizer by default on Mac/Linux debug builds + if (IS_MAC || IS_LINUX || IS_WINDOWS) { + // Enable address sanitizer by default on debug builds (all platforms) flags.push("-DENABLE_SANITIZERS=address"); // To disable asan, comment the line above and uncomment: // flags.push("-DENABLE_MALLOC_HEAP_BREAKDOWN=ON"); diff --git a/windows-release.ps1 b/windows-release.ps1 index fab4c9b7d8b1..a83ad1a6f9f9 100644 --- a/windows-release.ps1 +++ b/windows-release.ps1 @@ -45,6 +45,15 @@ $output = if ($env:WEBKIT_OUTPUT_DIR) { $env:WEBKIT_OUTPUT_DIR } else { "bun-web $WebKitBuild = if ($env:WEBKIT_BUILD_DIR) { $env:WEBKIT_BUILD_DIR } else { "WebKitBuild" } $CMAKE_BUILD_TYPE = if ($env:CMAKE_BUILD_TYPE) { $env:CMAKE_BUILD_TYPE } else { "Release" } $BUN_WEBKIT_VERSION = if ($env:BUN_WEBKIT_VERSION) { $env:BUN_WEBKIT_VERSION } else { $(git rev-parse HEAD) } +$ENABLE_SANITIZERS = if ($env:ENABLE_SANITIZERS) { $env:ENABLE_SANITIZERS } else { "" } + +# When sanitizers are enabled, force asserts on. Otherwise leave as AUTO (CMake default). +if ($ENABLE_SANITIZERS) { + Write-Host ":: Sanitizers enabled: $ENABLE_SANITIZERS" + $ENABLE_ASSERTS = "ON" +} else { + $ENABLE_ASSERTS = "AUTO" +} # Build ICU statically (idempotent - skips if already built) $ICU_STATIC_ROOT = Join-Path $WebKitBuild "icu" @@ -62,9 +71,15 @@ $env:PATH = $PathWithPerl $env:CFLAGS = "/Zi" $env:CXXFLAGS = "/Zi" -$CmakeMsvcRuntimeLibrary = "MultiThreaded" -if ($CMAKE_BUILD_TYPE -eq "Debug") { - $CmakeMsvcRuntimeLibrary = "MultiThreadedDebug" +# clang-cl's ASAN does not support debug CRT (/MTd, /MDd) — only release CRT (/MT, /MD). +# Always use static release CRT (/MT) for ASAN builds regardless of build type. +if ($ENABLE_SANITIZERS) { + $CmakeMsvcRuntimeLibrary = "MultiThreaded" +} else { + $CmakeMsvcRuntimeLibrary = "MultiThreaded" + if ($CMAKE_BUILD_TYPE -eq "Debug") { + $CmakeMsvcRuntimeLibrary = "MultiThreadedDebug" + } } $NoWebassembly = if ($env:NO_WEBASSEMBLY) { $env:NO_WEBASSEMBLY } else { $false } @@ -75,6 +90,7 @@ $WebAssemblyState = if ($NoWebassembly) { "OFF" } else { "ON" } if ($Platform -eq "ARM64") { $ClangPath = "C:/LLVM/bin/clang-cl.exe" $LldLinkPath = "C:/LLVM/bin/lld-link.exe" + $ClangLibPath = "C:/LLVM/lib/clang" # Note: The LLVM SEH unwind bug on Windows ARM64 (llvm/llvm-project#47432) is worked # around by disabling the probe functionality in MacroAssemblerARM64.cpp rather than # using compiler flags. @@ -84,38 +100,68 @@ if ($Platform -eq "ARM64") { $ClangPath = "clang-cl" $LldLinkPath = "lld-link" $ARM64SehWorkaround = "" + # Discover the LLVM lib path from the scoop-installed clang-cl + $ClangFullPath = (Get-Command clang-cl -ErrorAction SilentlyContinue).Source + if ($ClangFullPath) { + $ClangLibPath = Join-Path (Split-Path (Split-Path $ClangFullPath)) "lib/clang" + } else { + $ClangLibPath = "" + } } -cmake -S . -B $WebKitBuild ` - -DPORT="JSCOnly" ` - -DENABLE_STATIC_JSC=ON ` - -DALLOW_LINE_AND_COLUMN_NUMBER_IN_BUILTINS=ON ` - "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" ` - -DUSE_THIN_ARCHIVES=OFF ` - -DENABLE_JIT=ON ` - -DENABLE_DFG_JIT=ON ` - -DENABLE_FTL_JIT=ON ` - -DENABLE_WEBASSEMBLY_BBQJIT=ON ` - -DENABLE_WEBASSEMBLY_OMGJIT=ON ` - -DENABLE_SAMPLING_PROFILER=ON ` - "-DENABLE_WEBASSEMBLY=${WebAssemblyState}" ` - -DUSE_BUN_JSC_ADDITIONS=ON ` - -DUSE_BUN_EVENT_LOOP=ON ` - -DENABLE_BUN_SKIP_FAILING_ASSERTIONS=ON ` - "-DICU_ROOT=${ICU_STATIC_ROOT}" ` - "-DICU_LIBRARY=${ICU_STATIC_LIBRARY}" ` - "-DICU_INCLUDE_DIR=${ICU_STATIC_INCLUDE_DIR}" ` - "-DCMAKE_C_COMPILER=${ClangPath}" ` - "-DCMAKE_CXX_COMPILER=${ClangPath}" ` - "-DCMAKE_LINKER=${LldLinkPath}" ` - "-DCMAKE_C_FLAGS_RELEASE=/Zi /O2 /Ob2 /DNDEBUG /DU_STATIC_IMPLEMENTATION ${ARM64SehWorkaround}" ` - "-DCMAKE_CXX_FLAGS_RELEASE=/Zi /O2 /Ob2 /DNDEBUG /DU_STATIC_IMPLEMENTATION /clang:-fno-c++-static-destructors ${ARM64SehWorkaround}" ` - "-DCMAKE_C_FLAGS_DEBUG=/Zi /FS /O0 /Ob0 /DU_STATIC_IMPLEMENTATION ${ARM64SehWorkaround}" ` - "-DCMAKE_CXX_FLAGS_DEBUG=/Zi /FS /O0 /Ob0 /DU_STATIC_IMPLEMENTATION /clang:-fno-c++-static-destructors ${ARM64SehWorkaround}" ` - -DENABLE_REMOTE_INSPECTOR=ON ` - "-DCMAKE_MSVC_RUNTIME_LIBRARY=${CmakeMsvcRuntimeLibrary}" ` - -G Ninja -# TODO: "-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded" ` +# Discover the ASAN runtime library path for CMake +$ClangRtLibPath = "" +if ($ENABLE_SANITIZERS -and $ClangLibPath) { + # Find the versioned directory (e.g., lib/clang/21/lib/windows) + $versionDirs = Get-ChildItem -Path $ClangLibPath -Directory -ErrorAction SilentlyContinue | Sort-Object Name -Descending + foreach ($vdir in $versionDirs) { + $candidate = Join-Path $vdir.FullName "lib/windows" + if (Test-Path $candidate) { + $ClangRtLibPath = $candidate + Write-Host ":: Found Clang runtime lib path: $ClangRtLibPath" + break + } + } +} + +$cmakeArgs = @( + "-S", ".", + "-B", $WebKitBuild, + "-DPORT=JSCOnly", + "-DENABLE_STATIC_JSC=ON", + "-DALLOW_LINE_AND_COLUMN_NUMBER_IN_BUILTINS=ON", + "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}", + "-DUSE_THIN_ARCHIVES=OFF", + "-DENABLE_JIT=ON", + "-DENABLE_DFG_JIT=ON", + "-DENABLE_FTL_JIT=ON", + "-DENABLE_WEBASSEMBLY_BBQJIT=ON", + "-DENABLE_WEBASSEMBLY_OMGJIT=ON", + "-DENABLE_SAMPLING_PROFILER=ON", + "-DENABLE_WEBASSEMBLY=${WebAssemblyState}", + "-DUSE_BUN_JSC_ADDITIONS=ON", + "-DUSE_BUN_EVENT_LOOP=ON", + "-DENABLE_BUN_SKIP_FAILING_ASSERTIONS=ON", + "-DICU_ROOT=${ICU_STATIC_ROOT}", + "-DICU_LIBRARY=${ICU_STATIC_LIBRARY}", + "-DICU_INCLUDE_DIR=${ICU_STATIC_INCLUDE_DIR}", + "-DCMAKE_C_COMPILER=${ClangPath}", + "-DCMAKE_CXX_COMPILER=${ClangPath}", + "-DCMAKE_LINKER=${LldLinkPath}", + "-DCMAKE_C_FLAGS_RELEASE=/Zi /O2 /Ob2 /DNDEBUG /DU_STATIC_IMPLEMENTATION ${ARM64SehWorkaround}", + "-DCMAKE_CXX_FLAGS_RELEASE=/Zi /O2 /Ob2 /DNDEBUG /DU_STATIC_IMPLEMENTATION /clang:-fno-c++-static-destructors ${ARM64SehWorkaround}", + "-DCMAKE_C_FLAGS_DEBUG=/Zi /FS /O0 /Ob0 /DU_STATIC_IMPLEMENTATION ${ARM64SehWorkaround}", + "-DCMAKE_CXX_FLAGS_DEBUG=/Zi /FS /O0 /Ob0 /DU_STATIC_IMPLEMENTATION /clang:-fno-c++-static-destructors ${ARM64SehWorkaround}", + "-DENABLE_REMOTE_INSPECTOR=ON", + "-DENABLE_SANITIZERS=${ENABLE_SANITIZERS}", + "-DENABLE_ASSERTS=${ENABLE_ASSERTS}", + "-DCLANG_LIB_PATH=${ClangRtLibPath}", + "-G", "Ninja" +) + +$cmakeArgs += "-DCMAKE_MSVC_RUNTIME_LIBRARY=${CmakeMsvcRuntimeLibrary}" + +cmake @cmakeArgs if ($LASTEXITCODE -ne 0) { throw "cmake failed with exit code $LASTEXITCODE" } # Workaround for what is probably a CMake bug @@ -168,6 +214,19 @@ if ($CMAKE_BUILD_TYPE -eq "Release") { Copy-Item "$ICU_STATIC_LIBRARY/icuuc.lib" "$output/lib/sicuucd.lib" -Force } +# Copy ASAN runtime DLLs to output when sanitizers are enabled +if ($ENABLE_SANITIZERS -and $ClangRtLibPath) { + $asanArch = if ($Platform -eq "ARM64") { "aarch64" } else { "x86_64" } + $asanDll = "clang_rt.asan_dynamic-${asanArch}.dll" + $asanDllPath = Join-Path $ClangRtLibPath $asanDll + if (Test-Path $asanDllPath) { + Copy-Item $asanDllPath "$output/bin/$asanDll" -Force + Write-Host ":: Copied ASAN runtime DLL: $asanDll" + } else { + throw "ASAN runtime DLL not found at $asanDllPath" + } +} + Add-Content -Path $output/include/cmakeconfig.h -Value "`#define BUN_WEBKIT_VERSION `"$BUN_WEBKIT_VERSION`"" Copy-Item -r -Force $WebKitBuild/JavaScriptCore/DerivedSources/* $output/include/JavaScriptCore