This is an archive of the discontinued LLVM Phabricator instance.

[sanitizer] Don't call dlerror() after swift_demangle lookup through dlsym
ClosedPublic

Authored by serge-sans-paille on Jul 1 2022, 7:05 AM.

Details

Summary

Because the call to dlerror() may actually want to print something, which turns into a deadlock
as showcased in #49223.

Instead rely on further call to dlsym to clear dlerror internal state if they
need to check the return status.

Diff Detail

Event Timeline

Herald added a project: Restricted Project. · View Herald TranscriptJul 1 2022, 7:05 AM
Herald added a subscriber: Enna1. · View Herald Transcript
serge-sans-paille requested review of this revision.Jul 1 2022, 7:05 AM
Herald added a project: Restricted Project. · View Herald TranscriptJul 1 2022, 7:05 AM
Herald added a subscriber: Restricted Project. · View Herald Transcript
vitalybuka edited reviewers, added: vitalybuka; removed: aizatsky.Jul 7 2022, 3:46 PM
vitalybuka added a subscriber: vitalybuka.
vitalybuka added inline comments.
compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp
61–63

Unrelated to the patch, but this is used only in sanitizer_symbolizer_mac.cpp, so it's better to be moved there.

79

Maybe better just make
SANITIZER_WEAK_ATTRIBUTE DemangleSwift()
and remove swift_demangle_ft related stuff?

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp
79

The idea of this patch is to delegate detection of the swift_demangle symbol from dlsym to weak/strong symbol. I don't see how making DemangleSwift weak achieves that goal?

vitalybuka added inline comments.Jul 10 2022, 9:02 PM
compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp
61–63

My mistake, it's not just mac

66

shouldn't this be like:

extern "C" SANITIZER_WEAK_ATTRIBUTE swift_demangle(
   const char *mangledName,
   size_t mangledNameLength, char *outputBuffer,
   size_t *outputBufferSize, uint32_t flags);
76

why do we need InitializeSwiftDemangler here at all
can we just check if (swift_demangle) in DemangleSwift?

Follow reviewer's advice

serge-sans-paille marked 4 inline comments as done.Jul 11 2022, 1:55 AM
serge-sans-paille added inline comments.
compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp
66

I don't think so, because in that case if the symbol is not available externally we get a link error.

76

Correct!

vitalybuka added inline comments.Jul 11 2022, 10:48 AM
compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp
66

I believe it should be

// As of now, there are no headers for the Swift runtime. Provide a weak version
// of that symbol and rely on weak symbol resolution to pick the right one.
extern "C" char *SANITIZER_WEAK_ATTRIBUTE
swift_demangle(const char *mangledName, size_t mangledNameLength,
               char *outputBuffer, size_t *outputBufferSize, uint32_t flags);

// Attempts to demangle a Swift name. The demangler will return nullptr if a
// non-Swift name is passed in.
const char *DemangleSwift(const char *name) {
  if (&swift_demangle)
    return swift_demangle(name, internal_strlen(name), 0, 0, 0);
  return nullptr;
}

SANITIZER_WEAK_ATTRIBUTE makes it null if it's not linked, not linking error

"swift_demangle" symbol is a function, not function ptr, not sure how patch as is can work at all
Have you tried to run this with swift_demangle linked into the binary?

Maybe we should have a test with a fake version of the function
https://github.com/apple/swift/blob/de16c9a7385e61b6bf6dd604e832c64a02358d84/stdlib/public/runtime/Demangle.cpp#L721

505–506

does not compile with this line

yln added a comment.Jul 11 2022, 2:10 PM

For the technicalities, I agree with Vitaly.

However, @rsundahl has discovered that the whole "strong overrides weak symbol across dylibs" pattern isn't reliably anymore. See here: https://reviews.llvm.org/D127929
Roy, can you confirm that we are running into this case here>

Also: does this still work right now? Should depend on the swift runtime dylib defining a weak symbol/being marked as in participating in weak symbol lookup. Can we promise that the Swift runtime dylib will always define a weak symbol?

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp
66

I agree that this is what we would want if we take this path.

rsundahl added inline comments.Jul 11 2022, 2:34 PM
compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp
505–506

What is the compiler complaining about in this case @vitalybuka?

vitalybuka added inline comments.Jul 11 2022, 3:04 PM
compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp
505–506

it's trivial. @serge-sans-paille removed InitializeSwiftDemangler as I asked, but forgot to remove the call.

There are very significant differences between Linux, Windows and Darwin w.r.t. how weak symbols are (possibly) overridden by so-called "strong" symbols. The discussion with @MaskRay in https://reviews.llvm.org/D127929 is worth a look for context but I would encourage moving in the other direction, completely away from weak symbols and/or any expectation that the loader will "do what we want it to do".

There are very significant differences between Linux, Windows and Darwin w.r.t. how weak symbols are (possibly) overridden by so-called "strong" symbols. The discussion with @MaskRay in https://reviews.llvm.org/D127929 is worth a look for context but I would encourage moving in the other direction, completely away from weak symbols and/or any expectation that the loader will "do what we want it to do".

The file handles ELF systems and macOS. It doesn't deal with Windows.
Again, this is a use case which unnecessarily regressed on macOS with the latest dyld :(

serge-sans-paille marked an inline comment as done.

Another approach: make sure swift_demangle is setup before or after symbol lookup, but not during interception.
It solves the issue because it prevents calling the intiialization routine as a consequence of lazy initialization when hooking the malloc call from dlerror

Please update description, as this approach does not match, it still uses dlsym.

Can you show a pseudo-stack trace of the case you are trying to solve?

serge-sans-paille edited the summary of this revision. (Show Details)Jul 13 2022, 8:03 AM

title is still "Don't lookup swift_demangle through dlsym"

Because it can fail if the symbol is not there, the subsequent (and legit) call to dlerror()

legit but unnecessary. Most code in sanitizer does not call dlerror after dlsym.
Doc says: https://linux.die.net/man/3/dlerror

the correct way to test for an error is to call dlerror() to clear any old error conditions, then call dlsym(), and then call dlerror() again, saving its return value into a variable, and check whether this saved value is not NULL.

We don't check dlerror here, so it's up to the next caller to clear it before next dlsym, and only if they care about dlerror.

compiler-rt/lib/interception/interception_linux.cpp is the main and very early origin of dlsym calls for sanitizers

serge-sans-paille retitled this revision from [sanitizer] Don't lookup swift_demangle through dlsym to [sanitizer] Don't call dlerror() after swift_demangle lookup through dlsym.
serge-sans-paille edited the summary of this revision. (Show Details)
yln added inline comments.Jul 18 2022, 11:57 AM
compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp
75

I like that we settled on this very simple (and reasonable) change to solve the problem.

LGTM, but deferring to Vitaly since he suggested the change! :)

vitalybuka accepted this revision.Jul 18 2022, 2:09 PM
This revision is now accepted and ready to land.Jul 18 2022, 2:09 PM
MaskRay accepted this revision.Jul 18 2022, 2:26 PM