Open nico opened 2 weeks ago
Triggering this is as easy as:
std::string
objc_retainBlock
on such a block
This is likely all true in most larger Objective-C++ code bases.
Won't this happen with any code which uses asan annotations for their memory in a structure that gets copied for the apple blocks? If so shouldn't this be fixed in the block runtime instead?
Won't this happen with any code which uses asan annotations for their memory in a structure that gets copied for the apple blocks?
Yes, but you'd only use asan annotations for things with inline storage. And even then, it seems to be very rare in practice. I've only seen this happen with std::string so far.
If so shouldn't this be fixed in the block runtime instead?
That seems like the right fix to me too, but:
So we'll need something else in the meantime as well.
It seems to be Apple-specific issue and I agree that it should be fixed in the block logic. However, if it's impossible to do it in time, we can temporarily turn off short string annotations for macOS only.
But why it's using memmove
instead of std::basic_string
constructor?
There are a few other possible solutions for that, but using the constructor is probably the best one. Temporarily, we can turn off short string annotations for affected systems, unless someone has a better idea.
I don't have macOS, so unfortunately I cannot test anything myself.
But why it's using memmove instead of std::basic_string constructor?
A block closure is a block of bytes. It doesn't know about the types of data therein. See clang/docs/Block-ABI-Apple.rst
Thank you for pointing me into the explanation. I'm not really a fan of turning off short string annotations, but in case it's the only option I opened a new PR: https://github.com/llvm/llvm-project/pull/96269
Other solutions I see right now are:
_Block_copy
- that may not work based on blocks implementation, which I don't know.memmove
- that should resolve all issues.
Consider this program:
I don't have a completely standalone repro yet, but based on my current understanding, it's possible to make a repro that uses just the llvm-project repo. For my current repro, I'm using Chromium's toolchain like so:
This triggers an asan report from the blocks runtime:
Here's why:
When creating a block closure, clang will copy C++ objects into the closure using their copy constructor, see clang/lib/CGBlocks.cpp (so far so good).
clang will create calls to
objc_retainBlock
under various conditions, see clang/lib/CodeGen/CGObjC.cpp. One such condition is passing a block to a function taking an rvalue reference when ARC is enabled (but there are other conditions).objc_retainBlock
just calls_Block_copy
(https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/NSObject.mm#L221>If the block contains C++ objects, bit 25 in the block descriptor is set. This bit is called
BLOCK_HAS_COPY_DISPOSE
in both clang and BlocksRuntime. CGBlocks.cpp generates a copy helper (GenerateCopyHelperFunction
) that the block points to. For C++ objects in the closure, it again calls copy ctors._Block_copy
is defined in compiler-rt/lib/BlocksRuntime/runtime.c. It's part of the system dyld closure which ships in macOS. It does:That is, it first makes a copy of the block's closure variables using
memmove()
and then calls the copy helper to fix things up.1a96179596099b8a3839050dbff02bfed94502e5 enabled asan instrumentation for the short string inline storage. In the repro, the string is set to
"123"
, meaning some of its short string storage isn't initialized. That makes thatmemmove()
in the block runtime fail.Given that this call is in a macOS system library, fixing this will probably be involved.
(This of course only happens if
_LIBCPP_INSTRUMENTED_WITH_ASAN
is enabled.)clang/docs/Block-ABI-Apple.rst describes the whole blocks ABI fairly well.