Cheng Shao pushed to branch wip/terrorjack/asan at Glasgow Haskell Compiler / GHC Commits: a160554c by Cheng Shao at 2026-06-23T08:37:24+00:00 hadrian: add support for building with AddressSanitizer This patch adds a +asan flavour transformer to hadrian to build all stage1+ C/C++ code with AddressBehaviorSanitizer. This is particularly useful to catch potential out-of-bounds and use-after-free bugs in the RTS codebase. - - - - - 1ee9e876 by Cheng Shao at 2026-06-23T08:37:34+00:00 ci: add ubsan+asan job We now have a `x86_64-linux-fedora43-quick-validate+debug_info+ubsan+asan` validate/nightly job with both UBSan/ASan enabled. This is enabled in full-ci pipelines as well as MR pipelines with `test-sanitizer` label. - - - - - 12b1c4e1 by Cheng Shao at 2026-06-23T08:37:34+00:00 rts: add ASAN instrumentation to mblock allocator - - - - - d69db72c by Cheng Shao at 2026-06-23T08:37:34+00:00 rts: add ASAN instrumentation to mgroup allocator - - - - - a9ba6486 by Cheng Shao at 2026-06-23T08:37:34+00:00 rts: add ASAN instrumentation to block allocator - - - - - a1c625f1 by Cheng Shao at 2026-06-23T08:37:34+00:00 rts: add ASAN instrumentation to cap->pinned_object_empty - - - - - 22a39627 by Cheng Shao at 2026-06-23T08:37:34+00:00 rts: add ASAN instrumentation to gc_thread->free_blocks - - - - - 65d3c885 by Cheng Shao at 2026-06-23T08:37:34+00:00 rts: add ASAN instrumentation to hash table free list - - - - - 73c0b897 by Cheng Shao at 2026-06-23T08:37:34+00:00 rts: add ASAN instrumentation to per-Task InCall free list - - - - - 18 changed files: - .gitlab/generate-ci/gen_ci.hs - .gitlab/jobs.yaml - hadrian/doc/flavours.md - hadrian/src/Flavour.hs - rts/Hash.c - rts/Task.c - rts/include/Stg.h - + rts/include/rts/ASANUtils.h - rts/rts.cabal - rts/sm/BlockAlloc.c - rts/sm/GCUtils.c - rts/sm/MBlock.c - rts/sm/Storage.c - testsuite/driver/testglobals.py - testsuite/driver/testlib.py - testsuite/tests/ffi/should_run/all.T - testsuite/tests/rts/T18623/all.T - testsuite/tests/rts/all.T Changes: ===================================== .gitlab/generate-ci/gen_ci.hs ===================================== @@ -167,6 +167,7 @@ data BuildConfig , tablesNextToCode :: Bool , threadSanitiser :: Bool , ubsan :: Bool + , asan :: Bool , noSplitSections :: Bool , validateNonmovingGc :: Bool , textWithSIMDUTF :: Bool @@ -178,7 +179,7 @@ configureArgsStr :: BuildConfig -> String configureArgsStr bc = unwords $ ["--enable-unregisterised"| unregisterised bc ] ++ ["--disable-tables-next-to-code" | not (tablesNextToCode bc) ] - ++ ["--with-intree-gmp" | Just _ <- [crossTarget bc] ] + ++ ["--with-intree-gmp" | isJust (crossTarget bc) || ubsan bc || asan bc ] ++ ["--with-system-libffi" | crossTarget bc == Just "wasm32-wasi" ] ++ ["--enable-ipe-data-compression" | withZstd bc ] ++ ["--enable-strict-ghc-toolchain-check"] @@ -193,6 +194,7 @@ mkJobFlavour BuildConfig{..} = Flavour buildFlavour opts [HostFullyStatic | hostFullyStatic] ++ [ThreadSanitiser | threadSanitiser] ++ [UBSan | ubsan] ++ + [ASan | asan] ++ [NoSplitSections | noSplitSections, buildFlavour == Release ] ++ [BootNonmovingGc | validateNonmovingGc ] ++ [TextWithSIMDUTF | textWithSIMDUTF] @@ -206,11 +208,12 @@ data FlavourTrans = | HostFullyStatic | ThreadSanitiser | UBSan + | ASan | NoSplitSections | BootNonmovingGc | TextWithSIMDUTF -data BaseFlavour = Release | Validate | SlowValidate deriving Eq +data BaseFlavour = Release | QuickValidate | Validate | SlowValidate deriving Eq ----------------------------------------------------------------------------- -- Build Configurations @@ -236,6 +239,7 @@ vanilla = BuildConfig , tablesNextToCode = True , threadSanitiser = False , ubsan = False + , asan = False , noSplitSections = False , validateNonmovingGc = False , textWithSIMDUTF = False @@ -290,8 +294,14 @@ llvm = vanilla { llvmBootstrap = True } tsan :: BuildConfig tsan = vanilla { threadSanitiser = True } -enableUBSan :: BuildConfig -enableUBSan = vanilla { withDwarf = True, ubsan = True } +sanitizers :: BuildConfig +sanitizers = + vanilla + { buildFlavour = QuickValidate, + withDwarf = True, + ubsan = True, + asan = True + } noTntc :: BuildConfig noTntc = vanilla { tablesNextToCode = False } @@ -376,6 +386,7 @@ flavourString :: Flavour -> String flavourString (Flavour base trans) = base_string base ++ concatMap (("+" ++) . flavour_string) trans where base_string Release = "release" + base_string QuickValidate = "quick-validate" base_string Validate = "validate" base_string SlowValidate = "slow-validate" @@ -385,6 +396,7 @@ flavourString (Flavour base trans) = base_string base ++ concatMap (("+" ++) . f flavour_string HostFullyStatic = "host_fully_static" flavour_string ThreadSanitiser = "thread_sanitizer_cmm" flavour_string UBSan = "ubsan" + flavour_string ASan = "asan" flavour_string NoSplitSections = "no_split_sections" flavour_string BootNonmovingGc = "boot_nonmoving_gc" flavour_string TextWithSIMDUTF = "text_simdutf" @@ -723,6 +735,7 @@ data ValidateRule | WasmBackend -- ^ Run this job when the "wasm" label is present | FreeBSDLabel -- ^ Run this job when the "FreeBSD" label is set. | NonmovingGc -- ^ Run this job when the "non-moving GC" label is set. + | Sanitizers -- ^ Run this job when the "test-sanitizers" label is set. | IpeData -- ^ Run this job when the "IPE" label is set | TestPrimops -- ^ Run this job when "test-primops" label is set | I386Backend -- ^ Run this job when the "i386" label is set @@ -770,6 +783,7 @@ validateRuleString RiscV = labelString "RISC-V" validateRuleString WasmBackend = labelString "wasm" validateRuleString FreeBSDLabel = labelString "FreeBSD" validateRuleString NonmovingGc = labelString "non-moving GC" +validateRuleString Sanitizers = labelString "test-sanitizers" validateRuleString IpeData = labelString "IPE" validateRuleString TestPrimops = labelString "test-primops" validateRuleString I386Backend = labelString "i386" @@ -1231,15 +1245,25 @@ fedora_x86 = , hackage_doc_job (disableValidate (standardBuildsWithConfig Amd64 (Linux Fedora43) releaseConfig)) , disableValidate (standardBuildsWithConfig Amd64 (Linux Fedora43) dwarf) , disableValidate (standardBuilds Amd64 (Linux Fedora43)) - -- For UBSan jobs, only enable for validate/nightly pipelines. - -- Also disable docs since it's not the point for UBSan jobs. + -- For UBSan/ASan jobs, only enable for validate/nightly + -- pipelines. Disable docs and use quick-validate to skip + -- linting/assertion to avoid unnecessary overhead. + -- + -- See + -- https://github.com/llvm/llvm-project/blob/llvmorg-21.1.8/compiler-rt/lib/san... + -- for ASAN options help, for now these are required to pass the + -- testsuite , modifyJobs ( setVariable "HADRIAN_ARGS" "--docs=none" . addVariable "UBSAN_OPTIONS" "suppressions=$CI_PROJECT_DIR/rts/.ubsan-suppressions" + . addVariable + "ASAN_OPTIONS" + "detect_leaks=false:handle_segv=0:handle_sigfpe=0:verify_asan_link_order=false" ) - $ validateBuilds Amd64 (Linux Fedora43) enableUBSan + $ addValidateRule Sanitizers + $ validateBuilds Amd64 (Linux Fedora43) sanitizers ] where hackage_doc_job = rename (<> "-hackage") . modifyJobs (addVariable "HADRIAN_ARGS" "--haddock-for-hackage") ===================================== .gitlab/jobs.yaml ===================================== @@ -3142,7 +3142,7 @@ "XZ_OPT": "-9" } }, - "nightly-x86_64-linux-fedora43-release": { + "nightly-x86_64-linux-fedora43-quick-validate+debug_info+ubsan+asan": { "after_script": [ ".gitlab/ci.sh save_cache", ".gitlab/ci.sh save_test_output", @@ -3153,7 +3153,7 @@ "artifacts": { "expire_in": "8 weeks", "paths": [ - "ghc-x86_64-linux-fedora43-release.tar.xz", + "ghc-x86_64-linux-fedora43-quick-validate+debug_info+ubsan+asan.tar.xz", "junit.xml", "unexpected-test-output.tar.gz" ], @@ -3195,17 +3195,20 @@ "x86_64-linux" ], "variables": { + "ASAN_OPTIONS": "detect_leaks=false:handle_segv=0:handle_sigfpe=0:verify_asan_link_order=false", "BIGNUM_BACKEND": "gmp", - "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-release", - "BUILD_FLAVOUR": "release", - "CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", + "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-quick-validate+debug_info+ubsan+asan", + "BUILD_FLAVOUR": "quick-validate+debug_info+ubsan+asan", + "CONFIGURE_ARGS": "--with-intree-gmp --enable-strict-ghc-toolchain-check", + "HADRIAN_ARGS": "--docs=none", "INSTALL_CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", "RUNTEST_ARGS": "", - "TEST_ENV": "x86_64-linux-fedora43-release", + "TEST_ENV": "x86_64-linux-fedora43-quick-validate+debug_info+ubsan+asan", + "UBSAN_OPTIONS": "suppressions=$CI_PROJECT_DIR/rts/.ubsan-suppressions", "XZ_OPT": "-9" } }, - "nightly-x86_64-linux-fedora43-release-hackage": { + "nightly-x86_64-linux-fedora43-release": { "after_script": [ ".gitlab/ci.sh save_cache", ".gitlab/ci.sh save_test_output", @@ -3262,14 +3265,13 @@ "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-release", "BUILD_FLAVOUR": "release", "CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", - "HADRIAN_ARGS": "--haddock-for-hackage", "INSTALL_CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", "RUNTEST_ARGS": "", "TEST_ENV": "x86_64-linux-fedora43-release", "XZ_OPT": "-9" } }, - "nightly-x86_64-linux-fedora43-validate": { + "nightly-x86_64-linux-fedora43-release-hackage": { "after_script": [ ".gitlab/ci.sh save_cache", ".gitlab/ci.sh save_test_output", @@ -3280,7 +3282,7 @@ "artifacts": { "expire_in": "8 weeks", "paths": [ - "ghc-x86_64-linux-fedora43-validate.tar.xz", + "ghc-x86_64-linux-fedora43-release.tar.xz", "junit.xml", "unexpected-test-output.tar.gz" ], @@ -3323,16 +3325,17 @@ ], "variables": { "BIGNUM_BACKEND": "gmp", - "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-validate", - "BUILD_FLAVOUR": "validate", + "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-release", + "BUILD_FLAVOUR": "release", "CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", + "HADRIAN_ARGS": "--haddock-for-hackage", "INSTALL_CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", "RUNTEST_ARGS": "", - "TEST_ENV": "x86_64-linux-fedora43-validate", + "TEST_ENV": "x86_64-linux-fedora43-release", "XZ_OPT": "-9" } }, - "nightly-x86_64-linux-fedora43-validate+debug_info": { + "nightly-x86_64-linux-fedora43-validate": { "after_script": [ ".gitlab/ci.sh save_cache", ".gitlab/ci.sh save_test_output", @@ -3343,7 +3346,7 @@ "artifacts": { "expire_in": "8 weeks", "paths": [ - "ghc-x86_64-linux-fedora43-validate+debug_info.tar.xz", + "ghc-x86_64-linux-fedora43-validate.tar.xz", "junit.xml", "unexpected-test-output.tar.gz" ], @@ -3386,16 +3389,16 @@ ], "variables": { "BIGNUM_BACKEND": "gmp", - "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-validate+debug_info", - "BUILD_FLAVOUR": "validate+debug_info", + "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-validate", + "BUILD_FLAVOUR": "validate", "CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", "INSTALL_CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", "RUNTEST_ARGS": "", - "TEST_ENV": "x86_64-linux-fedora43-validate+debug_info", + "TEST_ENV": "x86_64-linux-fedora43-validate", "XZ_OPT": "-9" } }, - "nightly-x86_64-linux-fedora43-validate+debug_info+ubsan": { + "nightly-x86_64-linux-fedora43-validate+debug_info": { "after_script": [ ".gitlab/ci.sh save_cache", ".gitlab/ci.sh save_test_output", @@ -3406,7 +3409,7 @@ "artifacts": { "expire_in": "8 weeks", "paths": [ - "ghc-x86_64-linux-fedora43-validate+debug_info+ubsan.tar.xz", + "ghc-x86_64-linux-fedora43-validate+debug_info.tar.xz", "junit.xml", "unexpected-test-output.tar.gz" ], @@ -3449,14 +3452,12 @@ ], "variables": { "BIGNUM_BACKEND": "gmp", - "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-validate+debug_info+ubsan", - "BUILD_FLAVOUR": "validate+debug_info+ubsan", + "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-validate+debug_info", + "BUILD_FLAVOUR": "validate+debug_info", "CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", - "HADRIAN_ARGS": "--docs=none", "INSTALL_CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", "RUNTEST_ARGS": "", - "TEST_ENV": "x86_64-linux-fedora43-validate+debug_info+ubsan", - "UBSAN_OPTIONS": "suppressions=$CI_PROJECT_DIR/rts/.ubsan-suppressions", + "TEST_ENV": "x86_64-linux-fedora43-validate+debug_info", "XZ_OPT": "-9" } }, @@ -7051,7 +7052,7 @@ "TEST_ENV": "x86_64-linux-deb13-zstd-validate" } }, - "x86_64-linux-fedora43-release": { + "x86_64-linux-fedora43-quick-validate+debug_info+ubsan+asan": { "after_script": [ ".gitlab/ci.sh save_cache", ".gitlab/ci.sh save_test_output", @@ -7062,7 +7063,7 @@ "artifacts": { "expire_in": "2 weeks", "paths": [ - "ghc-x86_64-linux-fedora43-release.tar.xz", + "ghc-x86_64-linux-fedora43-quick-validate+debug_info+ubsan+asan.tar.xz", "junit.xml", "unexpected-test-output.tar.gz" ], @@ -7088,7 +7089,7 @@ ], "rules": [ { - "if": "((($ONLY_JOBS) && ($ONLY_JOBS =~ /.*\\bx86_64-linux-fedora43-release(\\s|$).*/)) || (($ONLY_JOBS == null) && ((\"true\" == \"true\")))) && ($RELEASE_JOB != \"yes\") && ($NIGHTLY == null)", + "if": "((($ONLY_JOBS) && ($ONLY_JOBS =~ /.*\\bx86_64-linux-fedora43-quick-validate\\+debug_info\\+ubsan\\+asan(\\s|$).*/)) || (($ONLY_JOBS == null) && ((($CI_MERGE_REQUEST_LABELS =~ /.*full-ci.*/) || ($CI_MERGE_REQUEST_LABELS =~ /.*marge_bot_batch_merge_job.*/) || ($CI_COMMIT_BRANCH == \"master\") || ($CI_COMMIT_BRANCH =~ /ghc-[0-9]+\\.[0-9]+/)) || ($CI_MERGE_REQUEST_LABELS =~ /.*test-sanitizers.*/)))) && ($RELEASE_JOB != \"yes\") && ($NIGHTLY == null)", "when": "on_success" } ], @@ -7104,16 +7105,19 @@ "x86_64-linux" ], "variables": { + "ASAN_OPTIONS": "detect_leaks=false:handle_segv=0:handle_sigfpe=0:verify_asan_link_order=false", "BIGNUM_BACKEND": "gmp", - "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-release", - "BUILD_FLAVOUR": "release", - "CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", + "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-quick-validate+debug_info+ubsan+asan", + "BUILD_FLAVOUR": "quick-validate+debug_info+ubsan+asan", + "CONFIGURE_ARGS": "--with-intree-gmp --enable-strict-ghc-toolchain-check", + "HADRIAN_ARGS": "--docs=none", "INSTALL_CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", "RUNTEST_ARGS": "", - "TEST_ENV": "x86_64-linux-fedora43-release" + "TEST_ENV": "x86_64-linux-fedora43-quick-validate+debug_info+ubsan+asan", + "UBSAN_OPTIONS": "suppressions=$CI_PROJECT_DIR/rts/.ubsan-suppressions" } }, - "x86_64-linux-fedora43-release-hackage": { + "x86_64-linux-fedora43-release": { "after_script": [ ".gitlab/ci.sh save_cache", ".gitlab/ci.sh save_test_output", @@ -7150,7 +7154,7 @@ ], "rules": [ { - "if": "((($ONLY_JOBS) && ($ONLY_JOBS =~ /.*\\bx86_64-linux-fedora43-release(\\s|$).*/)) || (($ONLY_JOBS == null) && (\"disabled\" != \"disabled\"))) && ($RELEASE_JOB != \"yes\") && ($NIGHTLY == null)", + "if": "((($ONLY_JOBS) && ($ONLY_JOBS =~ /.*\\bx86_64-linux-fedora43-release(\\s|$).*/)) || (($ONLY_JOBS == null) && ((\"true\" == \"true\")))) && ($RELEASE_JOB != \"yes\") && ($NIGHTLY == null)", "when": "on_success" } ], @@ -7170,13 +7174,12 @@ "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-release", "BUILD_FLAVOUR": "release", "CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", - "HADRIAN_ARGS": "--haddock-for-hackage", "INSTALL_CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", "RUNTEST_ARGS": "", "TEST_ENV": "x86_64-linux-fedora43-release" } }, - "x86_64-linux-fedora43-validate": { + "x86_64-linux-fedora43-release-hackage": { "after_script": [ ".gitlab/ci.sh save_cache", ".gitlab/ci.sh save_test_output", @@ -7187,7 +7190,7 @@ "artifacts": { "expire_in": "2 weeks", "paths": [ - "ghc-x86_64-linux-fedora43-validate.tar.xz", + "ghc-x86_64-linux-fedora43-release.tar.xz", "junit.xml", "unexpected-test-output.tar.gz" ], @@ -7213,7 +7216,7 @@ ], "rules": [ { - "if": "((($ONLY_JOBS) && ($ONLY_JOBS =~ /.*\\bx86_64-linux-fedora43-validate(\\s|$).*/)) || (($ONLY_JOBS == null) && (\"disabled\" != \"disabled\"))) && ($RELEASE_JOB != \"yes\") && ($NIGHTLY == null)", + "if": "((($ONLY_JOBS) && ($ONLY_JOBS =~ /.*\\bx86_64-linux-fedora43-release(\\s|$).*/)) || (($ONLY_JOBS == null) && (\"disabled\" != \"disabled\"))) && ($RELEASE_JOB != \"yes\") && ($NIGHTLY == null)", "when": "on_success" } ], @@ -7230,15 +7233,16 @@ ], "variables": { "BIGNUM_BACKEND": "gmp", - "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-validate", - "BUILD_FLAVOUR": "validate", + "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-release", + "BUILD_FLAVOUR": "release", "CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", + "HADRIAN_ARGS": "--haddock-for-hackage", "INSTALL_CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", "RUNTEST_ARGS": "", - "TEST_ENV": "x86_64-linux-fedora43-validate" + "TEST_ENV": "x86_64-linux-fedora43-release" } }, - "x86_64-linux-fedora43-validate+debug_info": { + "x86_64-linux-fedora43-validate": { "after_script": [ ".gitlab/ci.sh save_cache", ".gitlab/ci.sh save_test_output", @@ -7249,7 +7253,7 @@ "artifacts": { "expire_in": "2 weeks", "paths": [ - "ghc-x86_64-linux-fedora43-validate+debug_info.tar.xz", + "ghc-x86_64-linux-fedora43-validate.tar.xz", "junit.xml", "unexpected-test-output.tar.gz" ], @@ -7275,7 +7279,7 @@ ], "rules": [ { - "if": "((($ONLY_JOBS) && ($ONLY_JOBS =~ /.*\\bx86_64-linux-fedora43-validate\\+debug_info(\\s|$).*/)) || (($ONLY_JOBS == null) && (\"disabled\" != \"disabled\"))) && ($RELEASE_JOB != \"yes\") && ($NIGHTLY == null)", + "if": "((($ONLY_JOBS) && ($ONLY_JOBS =~ /.*\\bx86_64-linux-fedora43-validate(\\s|$).*/)) || (($ONLY_JOBS == null) && (\"disabled\" != \"disabled\"))) && ($RELEASE_JOB != \"yes\") && ($NIGHTLY == null)", "when": "on_success" } ], @@ -7292,15 +7296,15 @@ ], "variables": { "BIGNUM_BACKEND": "gmp", - "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-validate+debug_info", - "BUILD_FLAVOUR": "validate+debug_info", + "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-validate", + "BUILD_FLAVOUR": "validate", "CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", "INSTALL_CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", "RUNTEST_ARGS": "", - "TEST_ENV": "x86_64-linux-fedora43-validate+debug_info" + "TEST_ENV": "x86_64-linux-fedora43-validate" } }, - "x86_64-linux-fedora43-validate+debug_info+ubsan": { + "x86_64-linux-fedora43-validate+debug_info": { "after_script": [ ".gitlab/ci.sh save_cache", ".gitlab/ci.sh save_test_output", @@ -7311,7 +7315,7 @@ "artifacts": { "expire_in": "2 weeks", "paths": [ - "ghc-x86_64-linux-fedora43-validate+debug_info+ubsan.tar.xz", + "ghc-x86_64-linux-fedora43-validate+debug_info.tar.xz", "junit.xml", "unexpected-test-output.tar.gz" ], @@ -7337,7 +7341,7 @@ ], "rules": [ { - "if": "((($ONLY_JOBS) && ($ONLY_JOBS =~ /.*\\bx86_64-linux-fedora43-validate\\+debug_info\\+ubsan(\\s|$).*/)) || (($ONLY_JOBS == null) && ((($CI_MERGE_REQUEST_LABELS =~ /.*full-ci.*/) || ($CI_MERGE_REQUEST_LABELS =~ /.*marge_bot_batch_merge_job.*/) || ($CI_COMMIT_BRANCH == \"master\") || ($CI_COMMIT_BRANCH =~ /ghc-[0-9]+\\.[0-9]+/))))) && ($RELEASE_JOB != \"yes\") && ($NIGHTLY == null)", + "if": "((($ONLY_JOBS) && ($ONLY_JOBS =~ /.*\\bx86_64-linux-fedora43-validate\\+debug_info(\\s|$).*/)) || (($ONLY_JOBS == null) && (\"disabled\" != \"disabled\"))) && ($RELEASE_JOB != \"yes\") && ($NIGHTLY == null)", "when": "on_success" } ], @@ -7354,14 +7358,12 @@ ], "variables": { "BIGNUM_BACKEND": "gmp", - "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-validate+debug_info+ubsan", - "BUILD_FLAVOUR": "validate+debug_info+ubsan", + "BIN_DIST_NAME": "ghc-x86_64-linux-fedora43-validate+debug_info", + "BUILD_FLAVOUR": "validate+debug_info", "CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", - "HADRIAN_ARGS": "--docs=none", "INSTALL_CONFIGURE_ARGS": "--enable-strict-ghc-toolchain-check", "RUNTEST_ARGS": "", - "TEST_ENV": "x86_64-linux-fedora43-validate+debug_info+ubsan", - "UBSAN_OPTIONS": "suppressions=$CI_PROJECT_DIR/rts/.ubsan-suppressions" + "TEST_ENV": "x86_64-linux-fedora43-validate+debug_info" } }, "x86_64-linux-rocky8-validate": { ===================================== hadrian/doc/flavours.md ===================================== @@ -218,6 +218,10 @@ The supported transformers are listed below: <td><code>ubsan</code></td> <td>Build all stage1+ C/C++ code with UndefinedBehaviorSanitizer support</td> </tr> + <tr> + <td><code>asan</code></td> + <td>Build all stage1+ C/C++ code with AddressSanitizer support</td> + </tr> <tr> <td><code>llvm</code></td> <td>Use GHC's LLVM backend (`-fllvm`) for all stage1+ compilation.</td> ===================================== hadrian/src/Flavour.hs ===================================== @@ -8,6 +8,7 @@ module Flavour , splitSections , enableThreadSanitizer , enableUBSan + , enableASan , enableLateCCS , enableHashUnitIds , enableDebugInfo, enableTickyGhc @@ -58,6 +59,7 @@ flavourTransformers = M.fromList , "thread_sanitizer" =: enableThreadSanitizer False , "thread_sanitizer_cmm" =: enableThreadSanitizer True , "ubsan" =: enableUBSan + , "asan" =: enableASan , "llvm" =: viaLlvmBackend , "profiled_ghc" =: enableProfiledGhc , "no_dynamic_ghc" =: disableDynamicGhcPrograms @@ -304,6 +306,28 @@ enableUBSan = builder Testsuite ? arg "--config=have_ubsan=True" ] +-- | Build all stage1+ C/C++ code with AddressSanitizer support: +-- https://clang.llvm.org/docs/AddressSanitizer.html +enableASan :: Flavour -> Flavour +enableASan = + addArgs $ + notStage0 + ? mconcat + [ package rts + ? builder (Cabal Flags) + ? arg "+asan" + <> (staged needSharedLibSAN ? arg "+shared-libsan"), + builder (Ghc CompileHs) ? arg "-optc-fsanitize=address", + builder (Ghc CompileCWithGhc) ? arg "-optc-fsanitize=address", + builder (Ghc CompileCppWithGhc) ? arg "-optcxx-fsanitize=address", + builder (Ghc LinkHs) + ? pure ["-optc-fsanitize=address", "-optl-fsanitize=address"] + <> (staged needSharedLibSAN ? arg "-optl-shared-libsan"), + builder (Cc CompileC) ? arg "-fsanitize=address", + builder Testsuite + ? pure ["--config=have_asan=True", "-e", "config.timeout=1800"] + ] + -- | Use the LLVM backend in target stages viaLlvmBackend :: Flavour -> Flavour viaLlvmBackend = addArgs $ staged buildingForTarget ? builder Ghc ? arg "-fllvm" ===================================== rts/Hash.c ===================================== @@ -283,6 +283,7 @@ allocHashList (HashTable *table) if (table->freeList != NULL) { HashList *hl = table->freeList; table->freeList = hl->next; + __ghc_asan_unpoison_memory_region(hl, offsetof(HashList, next)); return hl; } else { /* We allocate one block of memory which contains: @@ -302,8 +303,11 @@ allocHashList (HashTable *table) table->freeList = hl + 1; HashList *p = table->freeList; - for (; p < hl + HCHUNK - 1; p++) + for (; p < hl + HCHUNK - 1; p++) { + __ghc_asan_poison_memory_region(p, offsetof(HashList, next)); p->next = p + 1; + } + __ghc_asan_poison_memory_region(p, offsetof(HashList, next)); p->next = NULL; return hl; } @@ -318,6 +322,7 @@ freeHashList (HashTable *table, HashList *hl) // HashListChunks. hl->next = table->freeList; table->freeList = hl; + __ghc_asan_poison_memory_region(hl, offsetof(HashList, next)); } STATIC_INLINE void @@ -388,9 +393,10 @@ removeHashTable_inlined(HashTable *table, StgWord key, const void *data, table->dir[segment][index] = hl->next; else prev->next = hl->next; + void *hl_data = (void*)hl->data; freeHashList(table,hl); table->kcount--; - return (void *) hl->data; + return hl_data; } prev = hl; } ===================================== rts/Task.c ===================================== @@ -183,6 +183,7 @@ freeTask (Task *task) stgFree(incall); } for (incall = task->spare_incalls; incall != NULL; incall = next) { + __ghc_asan_unpoison_memory_region(incall, sizeof(InCall)); next = incall->next; stgFree(incall); } @@ -252,6 +253,7 @@ newInCall (Task *task) if (task->spare_incalls != NULL) { incall = task->spare_incalls; + __ghc_asan_unpoison_memory_region(incall, sizeof(InCall)); task->spare_incalls = incall->next; task->n_spare_incalls--; } else { @@ -283,6 +285,7 @@ endInCall (Task *task) stgFree(incall); } else { incall->next = task->spare_incalls; + __ghc_asan_poison_memory_region(incall, sizeof(InCall)); task->spare_incalls = incall; task->n_spare_incalls++; } ===================================== rts/include/Stg.h ===================================== @@ -331,6 +331,7 @@ external prototype return neither of these types to workaround #11395. #include "stg/MachRegsForHost.h" #include "stg/Regs.h" #include "stg/Ticky.h" +#include "rts/ASANUtils.h" #include "rts/TSANUtils.h" #if IN_STG_CODE ===================================== rts/include/rts/ASANUtils.h ===================================== @@ -0,0 +1,33 @@ +#pragma once + +#if defined(__SANITIZE_ADDRESS__) +#define ASAN_ENABLED +#elif defined(__has_feature) +#if __has_feature(address_sanitizer) +#define ASAN_ENABLED +#endif +#endif + +#if defined(ASAN_ENABLED) +#include <sanitizer/asan_interface.h> +#define USED_IF_ASAN +#else +#include <stdlib.h> +#define USED_IF_ASAN __attribute__((unused)) +#endif + +static inline void +__ghc_asan_poison_memory_region(void const volatile *addr USED_IF_ASAN, + size_t size USED_IF_ASAN) { +#if defined(ASAN_ENABLED) + __asan_poison_memory_region(addr, size); +#endif +} + +static inline void +__ghc_asan_unpoison_memory_region(void const volatile *addr USED_IF_ASAN, + size_t size USED_IF_ASAN) { +#if defined(ASAN_ENABLED) + __asan_unpoison_memory_region(addr, size); +#endif +} ===================================== rts/rts.cabal ===================================== @@ -97,6 +97,12 @@ flag ubsan UndefinedBehaviorSanitizer. default: False manual: True +flag asan + description: + Link with -fsanitize=address, to be enabled when building with + AddressSanitizer. + default: False + manual: True flag shared-libsan description: Link with -shared-libsan, to guarantee only one copy of the @@ -205,6 +211,9 @@ library if flag(ubsan) ld-options: -fsanitize=undefined + if flag(asan) + ld-options: -fsanitize=address + if flag(shared-libsan) ld-options: -shared-libsan @@ -283,6 +292,7 @@ library -- ^ generated rts/ghc_ffi.h rts/Adjustor.h + rts/ASANUtils.h rts/ExecPage.h rts/BlockSignals.h rts/Bytecodes.h ===================================== rts/sm/BlockAlloc.c ===================================== @@ -261,6 +261,8 @@ initGroup(bdescr *head) head[i].flags = 0; } #endif + + __ghc_asan_unpoison_memory_region(head->start, (W_)head->blocks * BLOCK_SIZE); } #if SIZEOF_VOID_P == SIZEOF_LONG @@ -474,6 +476,7 @@ alloc_mega_group (uint32_t node, StgWord mblocks) bd = alloc_mega_group_from_free_list(&deferred_free_mblock_list[node], n, &best); if(bd) { + __ghc_asan_unpoison_memory_region(bd->start, (W_)bd->blocks * BLOCK_SIZE); return bd; } else if(!best) @@ -490,6 +493,7 @@ alloc_mega_group (uint32_t node, StgWord mblocks) if (bd) { + __ghc_asan_unpoison_memory_region(bd->start, (W_)bd->blocks * BLOCK_SIZE); return bd; } else if (best) @@ -500,6 +504,7 @@ alloc_mega_group (uint32_t node, StgWord mblocks) (best_mblocks-mblocks)*MBLOCK_SIZE); best->blocks = MBLOCK_GROUP_BLOCKS(best_mblocks - mblocks); + __ghc_asan_unpoison_memory_region(MBLOCK_ROUND_DOWN(bd), mblocks * MBLOCK_SIZE); initMBlock(MBLOCK_ROUND_DOWN(bd), node); } else @@ -880,6 +885,8 @@ free_mega_group (bdescr *mg) IF_DEBUG(sanity, checkFreeListSanity()); } + + __ghc_asan_poison_memory_region(mg->start, (W_)mg->blocks * BLOCK_SIZE); } static void @@ -927,6 +934,8 @@ free_deferred_mega_groups (uint32_t node) // coalesce forwards coalesce_mblocks(mg); + __ghc_asan_poison_memory_region(mg->start, (W_)mg->blocks * BLOCK_SIZE); + // initialize search for next round prev = mg; bd = prev->link; @@ -1050,6 +1059,8 @@ freeGroup(bdescr *p) setup_tail(p); free_list_insert(node,p); + __ghc_asan_poison_memory_region(p->start, (W_)p->blocks * BLOCK_SIZE); + IF_DEBUG(sanity, checkFreeListSanity()); } ===================================== rts/sm/GCUtils.c ===================================== @@ -348,6 +348,7 @@ alloc_todo_block (gen_workspace *ws, uint32_t size) } else { if (gct->free_blocks) { bd = gct->free_blocks; + __ghc_asan_unpoison_memory_region(bd->start, (W_)bd->blocks * BLOCK_SIZE); gct->free_blocks = bd->link; } else { // We allocate in chunks of at most 16 blocks, use one @@ -357,6 +358,9 @@ alloc_todo_block (gen_workspace *ws, uint32_t size) StgWord n_blocks = stg_min(chunk_size, 1 << (MBLOCK_SHIFT - BLOCK_SHIFT - 1)); allocBlocks_sync(n_blocks, &bd); gct->free_blocks = bd->link; + for (bdescr *bd = gct->free_blocks; bd; bd = bd->link) { + __ghc_asan_poison_memory_region(bd->start, (W_)bd->blocks * BLOCK_SIZE); + } } } initBdescr(bd, ws->gen, ws->gen->to); ===================================== rts/sm/MBlock.c ===================================== @@ -641,6 +641,8 @@ getMBlocks(uint32_t n) ret = getCommittedMBlocks(n); + __ghc_asan_unpoison_memory_region(ret, (W_)n * MBLOCK_SIZE); + debugTrace(DEBUG_gc, "allocated %d megablock(s) at %p",n,ret); mblocks_allocated += n; @@ -673,6 +675,8 @@ freeMBlocks(void *addr, uint32_t n) mblocks_allocated -= n; + __ghc_asan_poison_memory_region(addr, (W_)n * MBLOCK_SIZE); + decommitMBlocks(addr, n); } ===================================== rts/sm/Storage.c ===================================== @@ -1242,6 +1242,10 @@ start_new_pinned_block(Capability *cap) ACQUIRE_SM_LOCK; bd = allocNursery(cap->node, NULL, PINNED_EMPTY_SIZE); RELEASE_SM_LOCK; + + for (bdescr *pbd = bd; pbd; pbd = pbd->link) { + __ghc_asan_poison_memory_region(pbd->start, (W_)pbd->blocks * BLOCK_SIZE); + } } // Bump up the nursery pointer to avoid the pathological situation @@ -1267,6 +1271,7 @@ start_new_pinned_block(Capability *cap) } cap->pinned_object_empty = bd->link; + __ghc_asan_unpoison_memory_region(bd->start, (W_)bd->blocks * BLOCK_SIZE); newNurseryBlock(bd); if (bd->link != NULL) { bd->link->u.back = cap->pinned_object_empty; ===================================== testsuite/driver/testglobals.py ===================================== @@ -197,6 +197,9 @@ class TestConfig: # Are we running with UndefinedBehaviorSanitizer enabled? self.have_ubsan = False + # Are we running with AddressSanitizer enabled? + self.have_asan = False + # Do symbols use leading underscores? self.leading_underscore = False ===================================== testsuite/driver/testlib.py ===================================== @@ -1118,6 +1118,9 @@ def have_thread_sanitizer( ) -> bool: def have_ubsan( ) -> bool: return config.have_ubsan +def have_asan( ) -> bool: + return config.have_asan + def gcc_as_cmmp() -> bool: return config.cmm_cpp_is_gcc ===================================== testsuite/tests/ffi/should_run/all.T ===================================== @@ -192,6 +192,9 @@ test('rts_clearMemory', [ extra_ways(['g1', 'nursery_chunks', 'nonmoving', 'compacting_gc', 'sanity']), # On windows, nonmoving way fails with bad exit code (2816) when(opsys('mingw32'), fragile(23091)), + # For simplicity, ASAN poisoning/unpoisoning logic is omitted + # from rts_clearMemory implementation + when(have_asan(), skip), req_c, pre_cmd('$MAKE -s --no-print-directory rts_clearMemory_setup') ], # Same hack as ffi023 ===================================== testsuite/tests/rts/T18623/all.T ===================================== @@ -8,6 +8,8 @@ test('T18623', # Recent versions of osx report an error when running `ulimit -v` when(opsys('darwin'), skip), when(arch('powerpc64le'), skip), + # ASan can't allocate shadow memory + when(have_asan(), skip), cmd_prefix('ulimit -v ' + str(8 * 1024 ** 2) + ' && '), ignore_stdout], run_command, ===================================== testsuite/tests/rts/all.T ===================================== @@ -105,6 +105,8 @@ def remove_parenthesis(s): return re.sub(r'\s+\([^)]*\)', '', s) test('outofmem', [ when(opsys('darwin'), skip), + # ASan shadow memory allocation blows up + when(have_asan(), skip), # this is believed to cause other processes to die # that happen concurrently while the outofmem test # runs in CI. As such we'll need to disable it on View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/68053a558b154c0220acce9b5e5429d... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/68053a558b154c0220acce9b5e5429d... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
Cheng Shao (@TerrorJack)