<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/78591>78591</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[LLD] Use-after-free with COFF directives from LTO objects
</td>
</tr>
<tr>
<th>Labels</th>
<td>
lld:COFF,
platform:windows
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
mstorsjo
</td>
</tr>
</table>
<pre>
When linking LTO objects that contain COFF directives (in particular, dllexports), use of those directives can end up with use-after-free.
This seems to be triggerable when the LTO object has been compiled with an older LLVM version of the compiler, than the version used in LTO.
To reproduce:
```console
$ cat main.c
void __declspec(dllexport) entry(void) { }
$ ~/clang-16.0/bin/clang -target x86_64-windows-msvc -c main.c -flto
$ lld-link main.o -entry:entry -out:main.exe -subsystem:console
=================================================================
==627895==ERROR: AddressSanitizer: heap-use-after-free on address 0xffff90f0385f at pc 0x000000b7330c bp 0xffffe1d1f5c0 sp 0xffffe1d1f5b8
READ of size 4 at 0xffff90f0385f thread T0
[...]
#11 0xd695dc in insert /home/martin/code/llvm-project/llvm/include/llvm/ADT/DenseMap.h:228:12
#12 0xd695dc in lld::coff::LinkerDriver::fixupExports() /home/martin/code/llvm-project/llvm/tools/lld/COFF/DriverUtils.cpp:708:21
#13 0xd1cd14 in lld::coff::LinkerDriver::linkerMain(llvm::ArrayRef<char const*>) /home/martin/code/llvm-project/llvm/tools/lld/COFF/Driver.cpp:2547:5
[...]
0xffff90f0385f is located 63 bytes inside of 68-byte region [0xffff90f03820,0xffff90f03864)
freed by thread T0 here:
#0 0xab0090 in free (/home/martin/code/llvm-project/llvm/build-asan/bin/lld+0xab0090)
#1 0x3c9bf7c in llvm::lto::InputFile::~InputFile() /home/martin/code/llvm-project/llvm/lib/LTO/LTO.cpp:538:23
#2 0xed86fc in operator() /usr/bin/../lib/gcc/aarch64-linux-gnu/9/../../../../include/c++/9/bits/unique_ptr.h:81:2
#3 0xed86fc in ~unique_ptr /usr/bin/../lib/gcc/aarch64-linux-gnu/9/../../../../include/c++/9/bits/unique_ptr.h:292:4
#4 0xed86fc in lld::coff::BitcodeCompiler::add(lld::coff::BitcodeFile&) /home/martin/code/llvm-project/llvm/tools/lld/COFF/LTO.cpp:167:3
[...]
previously allocated by thread T0 here:
#0 0xab02fc in malloc (/home/martin/code/llvm-project/llvm/build-asan/bin/lld+0xab02fc)
#1 0xb58874 in safe_malloc /home/martin/code/llvm-project/llvm/include/llvm/Support/MemAlloc.h:26:18
#2 0xb58874 in llvm::SmallVectorBase<unsigned long>::grow_pod(void*, unsigned long, unsigned long) /home/martin/code/llvm-project/llvm/lib/Support/SmallVector.cpp:143:15
#3 0xb50908 in grow_pod /home/martin/code/llvm-project/llvm/include/llvm/ADT/SmallVector.h:141:11
#4 0xb50908 in grow /home/martin/code/llvm-project/llvm/include/llvm/ADT/SmallVector.h:529:41
#5 0xb50908 in reserve /home/martin/code/llvm-project/llvm/include/llvm/ADT/SmallVector.h:669:13
#6 0xb50908 in void llvm::SmallVectorImpl<char>::resizeImpl<false>(unsigned long) /home/martin/code/llvm-project/llvm/include/llvm/ADT/SmallVector.h:632:11
#7 0x79045d4 in resize /home/martin/code/llvm-project/llvm/include/llvm/ADT/SmallVector.h:642:30
#8 0x79045d4 in upgrade(llvm::ArrayRef<llvm::BitcodeModule>) /home/martin/code/llvm-project/llvm/lib/Object/IRSymtab.cpp:403:13
#9 0x7903aac in llvm::irsymtab::readBitcode(llvm::BitcodeFileContents const&) /home/martin/code/llvm-project/llvm/lib/Object/IRSymtab.cpp
#10 0x78fcd8c in llvm::object::readIRSymtab(llvm::MemoryBufferRef) /home/martin/code/llvm-project/llvm/lib/Object/IRObjectFile.cpp:146:46
#11 0x3c9c23c in llvm::lto::InputFile::create(llvm::MemoryBufferRef) /home/martin/code/llvm-project/llvm/lib/LTO/LTO.cpp:543:35
[...]
```
The reason is that the export directives are stored as `StringRef` pointing at the source memory. In the case of LTO objects that have been upgraded, this is memory owned by `SmallVector<char, 0> Strtab` here: https://github.com/llvm/llvm-project/blob/llvmorg-17.0.6/llvm/include/llvm/LTO/LTO.h#L119-L125 After compiling LTO, this object is destructed, and the `StringRef` is left with a dangling pointer. For LTO objects that didn't need to be upgraded (see https://github.com/llvm/llvm-project/blob/llvmorg-17.0.6/llvm/lib/Object/IRSymtab.cpp#L404-L438), the `StringRef` seems to point into memory elsewhere (maybe part of a memory map?) which isn't destructed at that time.
This issue is downstream issue https://github.com/mstorsjo/llvm-mingw/issues/392.
CC @rnk @MaskRay
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzUWFtvpLgS_jXOS6lbYC4ND3noXFoaqaORktk9j5HBReMdsDm2Sbr3YX_7kQ00TSZnNDub0dGJWgQK4_rq8hVFMWPEQSJek-SGJHdXrLe10tetsUqbP9RVofjp-l81SmiE_CrkAfZfPoMq_sDSGrA1s1AqaZmQcPt5twMuNJZWvKABQjMhoWPairJvmCb0FnjT4LFT2hpCcyfoDYKqwNbK4OXDJZOAkkPfwauwtVu3YpVFvao04poEdyTYDscvtTBgEFsDVkGBYLU4HFCzokF4ddBtjRewoWYGCkQJpWo70SAfVDAJquGoYb___QFeUBuh5AAOp6XeCFuzYc9pTW-Qg5BOxxKZAo2dVrwvkUSjkKTB8CuVNKrBUUpjKJmFlgm5LmEQvijB4fmZY9mYDktCs7P_CM0BpdUnQjO3zF2TzQ2Qzd284V-E7sqGycMqTNcBobtCyEkEK8v0AS0cs_Q5jVevQnL1alateSlhVU5AVlVj1bxj0_CVS4ThtoLVgCHa-v-wUr0l0dbfxCPCyvSFORmLLYm2S3uju__734UhKd1keTKc3z8-fn4k0Ra2nGs05olJYcWfqJ2sRtatlskMSgIblkJwrKqqyoMqiLKkAmahKyE4Bv6v2ERRUELRjcsw5GGVlAGYpaTIBmiP99s7l79G_IkQu83ebG9rjYzDl2A0JblZr9ckGS0DACA0CkMIjjzNE166HBfSoLZA6K5WLRK6ax3BfVop7q6b5qVddVo5qo2XhO6ELJv-fJ_Q3fbuC6G7O5QGH1i3rkm0pTQj0Tako3qnmy50Nw13NHKpVFXD2V7Ir6jvtHjx_iXRthLHvrufikzmifE3wVqlGuMvOaE7V9ccVK_jNysasy67jkTbTeDw0vACb-TwhiUP4x_G23jJA3OwMg_Ai7das9MjViS6LWumXZU1ltAtie4_2KTRGprEGxJtk3dz4U3iCAONKplFDmkExcmicYkhuC_mabZyItB4cNWRJDeXj9OA0NtLQRq7d4FX4-jAoTjNmQk16rl2jikZQHBkRRDkgfOy55CP9N9zSdGLhq-YYfJcGb13bqbNz7AmJkBwjMq8qDZjNk6xciXSn3ySXW93osHh8q_5-ucSsREFobv9l8_DcYxUEvm8ixbgHFOQZ2nlsakONbNKz3p7o89mrtfnvQ9lSeiOMV3WaexKe39cHWRP6C6fFr45zEwuCb3xP7-2EI5vu16Kf_f43FntOZ2FDurMkGgB86959f8QI80pibbxDDJegHyHxTfCurjdTk2BFzLOPYP_2-ohD9IPY--cEGHqmBu9y9xO44tQvWlOwJqJtD_KMDp4oPVPfjzHaFW-x7EiybKNL6CGVfh81v4P3zhPfTd0TrsHbLduzyH6qfNgNkefLiDMLH9yQH7H0ip9wwyS6LaXvnnm0Ch5cIXZrzto9frcKX7uzLa-0V2s_Vbws8VhtuoC35QXceSOyZJ-RRLkQeZsm5B-1Nv8EkHt9Tv2h-GSWUv9v0p3QnNH6gvdyUK3RoP6BX-V-jR16sNoVp8u1PvW_t3c-tR2zfjSP6eURtfDjXcq1rjsuyc0-8c59KPWRPRNIDcQHDd5ECc8Hr3pmsxfpT526qNgVp8t1ffdQTO3z_sN1CwcS_GD4n2DP9lKDbz7XIzST49Pp9ayYuRcHESXgR-qWj7AjRh70zkIbfzDU5gZHxEuTLl4gdwqaVFaM_WDP_U6-Z4Fi2LsXgObrCp59gb38CU9o562WMB-wFbp001fVahdID4C6XDqPHGuca5-x-lFEz72aSWNfrBPKzUyi78G-zfNm6_J0ftt9nk2sJxvuFaaGSVd3-1HLrZGGOYAlzMTphGMVRo5MAMkDZ6sFvLg4KcBdEpIK-QBxueN6nWJ0HpD1_BpGGmUbBjIfDPlqdkLDlOTkWx8GIUI41ANu4B6lUN34ZTPJJ7KGb2FgET38GS1y5X03HtAbW1nnOep62wOwtZ9sS5Ve-HNpY-LRhWjVOnDKtysg3X6ncIyh6EmNNqHYb7ahzSBrfsMH6c743TrbNc4LhIGOBqr-9IORjPJva_eeth9FWFlx2kScCYPfk_vedRr2Cn9rWO54JLQjQXpvn2GCdbkYtdyGcQPd8_3CgCN9nEQr_ZxlI0zuvdsPc_bvHEgpFVTEmBj8NUF1qFv2alAPwR0WcWmNS3rSLRzrHqtRVmDMIMPZkcPaeoOon1n3ieM6dGHRr1KYzWydpR9x1fTVHPyVyvk4dVli3vQ9ddRTheqbm-BxIGWX92_B2a-PrITXPHriOdRzq7wOtwESZLlSRhf1ddZEESbNM4qHuTIQ5ZgnhTZpsqqIqBlGl-JaxrQOAjDLIyTMInXYcUwy1mZY0CzqshIHGDLRLN28NZKH648tOtNluThVcMKbIyf1FI6fGcMnwKU0FtCadcwWyntKtg4zXO3krsrfe3NLfqDIXHQCGPNrMEK2_jx735_R5I7-G05nPLJ_Ha0W2nVXmbyVa-b67-dpGe3e_P-EwAA___yPvDf">