Repository navigation
Description
Disclaimer: I tried to create a testcase from scratch, but for some reason I wasn't able to find a way to trigger the use of __asan_register_elf_globals instead of __asan_globals_register.
STR:
- Clone https://github.com/glandium/nss-builtins
cd nss-builtinsRUSTFLAGS="-Zsanitizer=address" CARGO_PROFILE_RELEASE_LTO=true cargo +nightly build --releaseobjdump -t target/release/libbuiltins_static.a | grep asan_globals_registered
Actual output:
0000000000000000 l O .bss.___asan_globals_registered 0000000000000008 ___asan_globals_registered
0000000000000000 l d .bss.___asan_globals_registered 0000000000000000 .bss.___asan_globals_registered
Expected output:
Something like:
0000000000000008 O *COM* 0000000000000008 .hidden ___asan_globals_registered
This doesn't happen without LTO.
The unfortunate consequence is that when the resulting static library is linked with C or C++ code compiled with clang with -fsanitize=address -fsanitize-address-globals-dead-stripping (that latter flag is now default in clang trunk), which also uses __asan_register_elf_globals/__asan_globals_registered, ODR violation detection kick in complaining about globals defined multiple times, because both the clang-side asan constructor and the rust asan constructor register all the globals. Normally, what happens is that they both use the same __asan_globals_registered (thus it normally being *COM*), and set its value, so that only one constructor registers the globals. With the LTOed staticlib, what happens is that there are two distinct __asan_globals_registered, so both constructors go through.
rustc +nightly --version --verbose:
rustc 1.72.0-nightly (d9c13cd45 2023-07-05)
binary: rustc
commit-hash: d9c13cd4531649c2028a8384cb4d4e54f985380e
commit-date: 2023-07-05
host: x86_64-unknown-linux-gnu
release: 1.72.0-nightly
LLVM version: 16.0.5
(Edit: fixed typos, changed the peculiar setup with -Clto and -Cembed-bitcode=yes to the more normal LTO, which shows the problem too)
Activity
danakj commentedon Jul 12, 2023
https://bugs.chromium.org/p/chromium/issues/detail?id=1459233 is tracking this for Chromium as it causes our asan bots to fail.
rnk commentedon Jul 12, 2023
Here is the code which creates the
__asan_globals_registeredglobal:https://github.com/llvm/llvm-project/blob/main/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp#L2216
It's interesting that it uses
commonlinkage (*COM*as you said). Common linkage should work, but to me it is a surprising choice. I would've expected this global to uselinkonce_odrlinkage and be markedcomdat, which is what you would get for a C++17 inline global.I don't know how Rust is producing LTOed static libraries, but maybe somewhere along the way there is a bug in how LTO is handling common linkage globals.
One possible summary of this issue is that the ODR violation detector is suffering from an ODR violation, we have two flags when we should have one.
glandium commentedon Jul 12, 2023
I like this take :)
MaskRay commentedon Jul 12, 2023
I consider myself quite familiar with asan, LTO, Clang Driver, but I know very little about Rust.
gives me this command (after removing
--error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat):Most defined symbols are localized. I do not know why the 4 symbols are special and rustc lto doesn't localize them.
I don't know how to rerun the rustc with a locally built (
./x.py buildwithconfig.tomlcontaining[rust]\ndebug=true).I suspect that preventing
___asan_globals_registeredfrom being localized will fix this bug.MaskRay commentedon Jul 12, 2023
-fcommonis consider bad nowadays but the___asan_globals_registeredCOMMON symbol use case is fine. It is like a COMDAT group containing just a variable. COMMON is more size efficient than using a COMDAT group (there is a size overhead due to a 64 byteElf64_Shdrheader).If we use a COMDAT for
___asan_globals_registeredbut rustccompiler/rustc_codegen_llvm/src/back/lto.rsstill localizes the symbol, then we'd still have this bug.[Fuchsia] Add ASAN_OPTIONS in asan build
[Fuchsia] Add ASAN_OPTIONS in asan build
anforowicz commentedon Jul 27, 2023
Let me try to help move this bug forward by attempting to answer some of the questions above. I have limited experience with linkers, asan, and compilers, so please shout if there are any mistakes below.
RE: @MaskRay: I don't know how to rerun the rustc with a locally built (./x.py build)
I tweaked the original
cargocmdline (from the first comment/report on this bug) by addingRUSTC=<path to locally built rustc>and adding-Zbuild-std --target x86_64-unknown-linux-gnu(and other than that I've rerun the original repro steps + debugging steps from your earlier comment at #113404 (comment)):(Note that this changes the path where the build artifacts are - e.g.
target/x86_64-unknown-linux-gnu/release/deps/libbuiltins_static-9bce9c00959dd948.ainstead oftarget/release/deps/libbuiltins_static-9ef9aaa79a14308e.a)RE: @MaskRay: I do not know why the 4 symbols are special and rustc lto doesn't localize them.
I am not sure if these are the reasons, but this is what I've found for some of the symbols emitted by
llvm-nm -gU ...:I see that
rust/compiler/rustc_codegen_llvm/src/context.rsmarks the EH personality withllvm::UnnamedAddr::GlobalAFAIU
_ZN3std3sys4unix4args3imp15ARGV_INIT_ARRAY17h244e25de9c1d3c88Ecorresponds to the static initializer inrust/library/std/src/sys/unix/args.rswhich is marked as#[used]I see that
BUILTINSC_GetFunctionListcomes from the repro and is declared as#[no_mangle]which AFAIU tellsrustcto export the function.rust/compiler/rustc_codegen_ssa/src/back/symbol_export.rsspecial-cases "rust_eh_personality" (see here), but changing this to cover names with "asan" substring didn't seem to have an effect on thellvm-nm -gU ...output)RE: @MaskRay: I suspect that preventing ___asan_globals_registered from being localized will fix this bug.
Can you please elaborate on that? From your #113404 (comment), it seems that you are saying that a change in
rustcis needed - did I get that right?OTOH, it seems that
___asan_globals_registeredcomes from outside ofrustcsources - it comes fromllvm-project/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cppand therefore it seems that maybe we need to change how it is marked for linking by LLVM? Interestingly the comment there mentions "a local symbol" - not sure if this is relevant:anforowicz commentedon Jul 27, 2023
/cc @eugenis who AFAICT added the
kAsanGlobalsRegisteredFlagNamecomment above in llvm/llvm-project@964f466#diff-74cc02fff013e3ab08aefcad70e555b1177549f8403d03395c856c3f8c5b587531 remaining items
anforowicz commentedon Aug 17, 2023
Hmmm... I now think that it still makes sense to review and attempt to land the
rustcPR at https://github.com/rust-lang/rust/pull/114946I even if we (eventually) preserve these ASAN symbols via a separate LLVM PR:rustcPR at Preserve ASAN-related symbols during LTO. #114946 is desirable in the long-term (i.e. even once LLVM changes are made). Specifically, we want to land the changes that makesymbol_export.rsunaware of__llvm_profile_raw_version,__llvm_profile_filename,__msan_keep_going,__msan_track_originsnames (i.e. keep the removal of code in 2c75640#diff-6d46444b86c506127f8dd251a0c949845de78baec01de25b7524b3b10d90efd7).rustcto work correctly even if built with older LLVM versionsrustcPR?)RE: @rnk: #113404 (comment): Is it possible to emulate what Rust is doing using
opt -internalize?Maybe. I don't know how to check. Sorry.
FWIW, I see that LLVM-level tests that use
opt -internalizetake LLVM-IR as input. This probably means that___asan_globals_registeredwould have to be hardcoded/simulated in the test input (rather than generated by the real ASAN). Maybe this is ok.RE: @rnk: #113404 (comment): Putting this list of symbols in LLVM sounds practical to me.
The
llvm-project/llvm/lib/Transforms/IPO/Internalize.cppsource file has quite elaborate code for preserving some symbols. I wonder if ASAN / MSAN / etc symbols should be protected by any of the existing mechanisms:___asan_globals_registered,__msan_track_origins, etc. intoInteranlizePass::AlwaysPreserved(e.g. somewhere here)Usedvariables should kick-in (but for some reason doesn't work for ASAN/MSAN/etc)?InternalizePass::shouldPreserveGVshould kick-in (but for some readon don't work for ASAN/MSAN/etc). Or maybe these checks need to be extended in a generic way (rather than teachingInternalize.cppabout__asanand/or__msanprefixes).anforowicz commentedon Aug 25, 2023
Status update / summary (I edited this comment on 2023-08-25 at 10:47 PST to add one other potential next step at the very end of the comment):
@___asan_globals_registered = common hidden globalvs... = internal global). This translates nicely into a newtests/codegen/sanitizer/address-sanitizer-globals-tracking.rstest.nmlevel (see __asan_globals_registered is not comdat when building a staticlib with LTO #113404 (comment)). This was checked manually before and after the fix in __asan_globals_registered is not comdat when building a staticlib with LTO #113404 (comment)rustcinvocations + maybeclanginvocations => incorrect ASAN report about an ODR violation)___asan_globals_registeredas an exported symbol for LTO #114642): Tweakingrust/compiler/rustc_codegen_ssa/src/back/symbol_export.rs__asan_globals_registeredfor ELF and MachO targets (see __asan_globals_registered is not comdat when building a staticlib with LTO #113404 (comment), __asan_globals_registered is not comdat when building a staticlib with LTO #113404 (comment), and __asan_globals_registered is not comdat when building a staticlib with LTO #113404 (comment))rust/compiler/rustc_codegen_ssa/src/back/symbol_export.rsand instead controlling LTO internalization incompiler/rustc_llvm/llvm-wrapper/PassWrapper.cppviaPreserveFunctionslambda (using symbol name prefixes like__msaninstead of specific names like__msan_track_origins).tests/codegentests/ui(run-pass,check-run-results) cannot provide test coverage (AFAIUtests/uican only invoke `rustc).rustcandclangviatests/run-make, but this requires actually having an end-to-end repro. I spent some time trying to create a repro yesterday but failed.tests/codegen/sanitizer/address-sanitizer-globals-tracking.rsmimics and covers similar scenarios as (already existing)tests/codegen/sanitizer/sanitizer-recover.rsandtests/codegen/sanitizer/memory-track-origins.rscompiler/rustc_codegen_llvm/src/back/lto.rsdoes indeed callLLVMRustRunRestrictionPassonly fromfat_lto. In other words, Option 2 stops special-handling__msan_track_origins,__msan_keep_going,__llvm_profile_raw_version, and__llvm_profile_filenameduring ThinLTO. OTOH, the problem with these symbols was internalization which happens during fat LTO, but I am not sure if happens during ThinLTO (definitely not the cross-module kind).Can we discuss the next steps?:
tests/run-make/cross-lang-lto-clangortests/run-make/cross-lang-lto-pgo-smoketestmight be good role models. (Let me ask in https://crbug.com/1459233#c21 if anyone else from Chromium might be able to help).opt -internalize(whatever that means in the world of LLVM tests :-)).anforowicz commentedon Aug 25, 2023
@bjorn3, in #114946 (comment) you've suggested that there might be issue with Option 2 of the fix. Let me partially reply here, because I have some questions that are not related to Option 2.
Do you think we should proceed with Option 1 (or is there another approach that you'd suggest)? If so, then I assume that you agree that to avoid the x86_64-apple-1 test failures we should only inject
__asan_globals_registeredinexported_symbols_provider_localwhen the symbol is actually present (i.e. whenModuleAddressSanitizer::InstrumentGlobalsELFand/orModuleAddressSanitizer::InstrumentGlobalsMachOinject the symbol). Could you please help me understand how to tweak #114642 to do this? How canexported_symbols_provider_localdetect ELF and/or MachO targets? Do you think it would be okay to copy-and-pastelet is_like_elf = ...fromcodegen_attrs.rs? What would you suggest for detecting MachO targets? (I don't see code for detecting MachO targets outside ofcompiler/rustc_codegen_cranelift/src/lib.rswhich I assume can't be used outside of cranelist.)bjorn3 commentedon Aug 26, 2023
I think so, but I may be misunderstanding what exactly address sanitizer needs in terms of linkage.
Like this:
rust/compiler/rustc_codegen_ssa/src/back/metadata.rs
Lines 217 to 225 in 22d41ae
Maybe extracting this into a function to ensure it is kept in sync makes sense.
Mark `___asan_globals_registered` as an exported symbol for LTO
anforowicz commentedon Aug 28, 2023
Thanks! That helps :-). Not sure how I managed to miss this piece of code when greepping the source code for
BinaryFormat... :-/FWIW I've applied the changes suggested above to the newly pushed version of #114642 (e.g. see d9abc46#diff-aa810a3be0834da891b171a8b04b09d0d3bbb76ac27c757cd232235099e062d2)
Preserve `___asan_globals_registered` symbol during LTO.
Auto merge of rust-lang#114946 - anforowicz:generic-fix-for-asan-lto,…
Auto merge of #114946 - anforowicz:generic-fix-for-asan-lto, r=tmiasko