<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/96099>96099</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            libc++ asan annotations for short string inline storage is incompatible with apple blocks runtime
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            libc++
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          nico
      </td>
    </tr>
</table>

<pre>
    Consider this program:

```
% cat repro.mm
#include <string>

[[gnu::noinline]] void use_string(const std::string&) {}

typedef void(^B)();
[[gnu::noinline]] void use_block(B&& b) {}

int main() {
  std::string request_id("123");
 use_block(^() {
    use_string(request_id);
  });
}
```

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:

```
% third_party/llvm-build/Release+Asserts/bin/clang repro.mm -isystem third_party/libc++/src/include/ -isystem buildtools/third_party/libc++/ -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE  -nostdinc++ -fobjc-arc -fno-exceptions -fno-rtti -fblocks -fsanitize=address out/gn/obj/buildtools/third_party/libc++/libc++/*.o out/gn/obj/buildtools/third_party/libc++abi/libc++abi/*.o -I. -isysroot $(xcrun -show-sdk-path)
```

This triggers an asan report from the blocks runtime:

```
% MallocNanoZone=0 ./a.out
=================================================================
==8812==ERROR: AddressSanitizer: container-overflow on address 0x00016bd6f3e4 at pc 0x0001049b74dc bp 0x00016bd6f2d0 sp 0x00016bd6ea80
READ of size 56 at 0x00016bd6f3e4 thread T0
    #0 0x1049b74d8 (libclang_rt.asan_osx_dynamic.dylib:arm64+0x4f4d8)
    #1 0x194932b48 in _Block_copy+0x58 (libsystem_blocks.dylib:arm64+0x1b48)
    #2 0x630f80010409287c  (<unknown module>)
    #3 0x1948ae0dc  (<unknown module>)
    #4 0x86397ffffffffffc  (<unknown module>)
```

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:

```
...
    // Its a stack block. Make a copy.
    if (!isGC) {
        struct Block_layout *result = malloc(aBlock->descriptor->size);
        if (!result) return (void *)0;
        memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
        // reset refcount
        result->flags &= ~(BLOCK_REFCOUNT_MASK);    // XXX not needed
        result->flags |= BLOCK_NEEDS_FREE | 1;
        result->isa = _NSConcreteMallocBlock;
 if (result->flags & BLOCK_HAS_COPY_DISPOSE) {
 //printf("calling block copy helper %p(%p, %p)...\n", aBlock->descriptor->copy, result, aBlock);
 (*aBlock->descriptor->copy)(result, aBlock); // do fixup
        }
 return result;
    }
...
```

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 that `memmove()` 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.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzUWM1y2zgSfhr40iUVBYqUePBBv4lqJnbKztTM7kUFkk0RCQlwAdC25rDPvtUAZUlOnJnd26pUtkiif9D9dfcHCmvlQSHesmTJkvWN6F2tza2Shb7JdXm8XWllZYkGXC0tdEYfjGhZvGDRmkWnv2k0fMMlT6AQDgx2Ro_b9nQ3lqpo-hKBxSvrjFQHFm-u9JAPy4PqSX-8UFqqRipkyZola3jSsoTe4n6Q5fNCK-vAujKsP91PGc-AzZZstr7U7o4dllh5PYzPWbJZMp7RL56xePlfuZA3uvjG-HzpraWQ_9ikVA5aIVUw4hf4-_DWaTD4rx6t2wfXOJ_wmHF-6dmVXZZsvtcJ19G5VHmhBsjHyx2_uvwmi_7vDkqtGJ85qMUTgoBCt12DDpsjWCdUKRqtMKQajugYX0HeO8iFxRK0gvYIRW8MKge9KtF4Ie_gCqRjfGah09bKvEFwGlrxjawEfa4WjvZk4WtvHbgaoWme2lFn9FcsPMD0GLbaXFrxoqR9x_ishd5SeFe10a3sW2_Pad0UtZAKGvkNweq_hrOrpSn3nTDuyPjWO5H3sikZ3z5gg8Ii48uFtWicZXybU8q3RSN8ZkMVwEjao3XYvlUm84Lxpf9urSkY3w51wvj2LOTNkeek_ycaYLTe_7pbrj5_3n9cPKw3d7u7D_tP9-sNi995sL-7v9sAjJS2rpRqUAWjSudfi5EwBYwqpUf4UmDnpFY2XBvnJIwqj0i6ZYWSTv6JLF6LsjRoLejeMb49UCx0_pXi8jc3cXXB-GKs_0ddIpc_uA4aR7txCK_R2gHjU8bnL4XpFYxsrZ9Htvw26oSrqVjeL5Av1BadkYcDGgtCgbBCeWQaB5XRrYftECXTKydb_Gu8fRJNo4s7ofQ_taKQRjBmfCvGFIawKl7_338vNjKfT3j4tXl4uH9g8QIWAUWPA64M3Su0ckIqNCP9hKZq9DP1mBPeopcoiiZpXqZVjFMQDrpiuBlNs3w2LQvIu8tlvIzAXt5BMR_S8LBZrEFXYOWfCElK2t7od7VBUcKX6NyAGY8jiF5O5ubA-JzQR51gb9yYwLHX9mVfHpVoZTEuj43MWbwQpk2njC-jl2k1LeevmBuUTkhpNs1ink_nIBXsl4SofaG7o5dKTqZCuwiDwv5A_SSffqedQ_SSxlE194GKMj6fFfRgzuJVr74p_ayg1WXfIA3sN8JxcG0uMCr_vtQUopd5Gmez6vXzN4R_WIEf0aDv68_18U1l_V6jgsKgcDQERChDKBpte5JZQWjRz7JpgEIJq6H56ZwGjAWpnPYFPMgM48TVKE2Q8BzE9IXThhRaxKA0tB3Gt6sPy5CLoutog1ZDJQxYDQetaTSPLz2-dIj8RihE09DMApZG1JH3BqkEvFKWRmGqwpMwUveW3Cml79LveKNL_IDK-3Wff12RV2O4Vwi2L-qzOBDZI3J4ETenQUDVq8IvcOKbf6jAPImmJw5QoUFVIDxT2BcPK1KCSuQNlrRz4gWuRoMgDIKmn1f-Xkfine16JhBiwtLosgzSiIzUznWWYEBtfnuQru7zcaFbmhsdKqt7U-BIdF2DYZQUUxomjaboFGWCKRdJks1QzEQ8KYsCq2iexZOZyPNYpOmkmCQkcWrlfHv3eO_RQkyXx79yPnlDa3fVeQacOph9CzVPnKQDnlB5n9eXaAsjO6cNRdOiG4MfObRWWh8Jim4aLX-9X_2y_7h43K_uP_9jv949fr5_3FBUpIJcu3oAu1AlBEQ-hC2M4QqiB1RohEPryV53hBqbDo2vzTT6MDxd6e740T_YDoCgquRZ4Gxn9zstlSP4Bp72fX1dVlfghCAOxM1CjkONOW3sW3C8Sb20UGIlFZaklFiqbNCMjHuF_tWmzxkcF2PYDURUGEctn1waaFd5bMrX4n-uZVGDrWXnPW9Fcf9IwlBqtD8f6uPx-LL_ETZh5yjG1oniW4jWGD4F-kubulgvKx99PpH2w-o70k-f0IJCXveNOOqeGM3CoO0bByxeQ-sZBeNz4ReNWLw5Q4uuaM5dHxTC59V4UEbmDbreKLrtD0SMLxjPou8kW2xb_YT-KBJEVxCsn3_9zI9TnHLpPAwqaay7NjGsMGiRmH9V6F69WRJsk-aqEQcLdGKL1_BvOr35knnYbFf3v9192X9aPP4yWD6r_uOPP0BpBwqxxPIvVM9WpDqovdts1o_77cNmQ_dh8l14ztLSCp-j_d3jSqvCoMNAAEOwXgVDKn6wIXin-K-gEjbUGalcFQ6ZVGLUw0-d6bLYk86voX-r4TIjECcr5Q-m7ycwcJIVfJ_0C3B55Yufq8h-iJxLZJQaKvnSd29AcTrRnoA6KLlMwOua18J8h90LarNDY_IA9EfU1-aoL3q77yGnXkEDmSafHSgDS6NzPdAu0si3YucpSpjx1AkvkuD87ui4qA4W-u6qA05Elk5mWZKlUZblcxHP4yxKojKvqojnFZbZNIk4Jq8D2J9LpCcrLSon_BCvtAndrqbjyvAuIrzyAOu0EQccwy406deDtRcYlvqRNLCT82sL-r2CFoWiRVa3SIGSzl4bGiyAtOEVgySmLxr5J5Y05cQp1n6m_CiAV5Ny6OdQCdlcheqDfEJ1GkzDzCTPpQIRmvip2zcyN8J49FbyJTA9aQMd64zORd4cIUeQ6kk3T1hezyQ-95NZV1Do3lgErZoj1KIj3uGLN41OR_Dd3eOXh98-be6-bNb733dfPu4Xj4u7YZANKRufee-ZGzK-LXVhT_NstFjuRgtiM2PCZiijHAOYnmvdvJ49F8sdhcY0R3jGphnflLdxmcWZuMHbyWwyn0-yOZ_c1LeY52U-nSbFpExnlYjErJzFyaxMomlaZFV0I295xKdROskms2QWTcZYxpMkybMoLguBWcqmEbaUhaZ5asfaHG6ktT3eeqzeNCLHxvoXjpxfnvQ5S9Y35nZ4u3KwbBo10jp7VuOka_D2LBNALZTSAc_WA_onYA5pJ3YgnH_l9CxdDZ4Nvjmj3_Smuf0JmSSfTq-ChvdRjG_9Pik5YatPt_w_AQAA___4a4Xo">