<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/62431>62431</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
Binaries linked with shared ASAN behave different on various distributions
</td>
</tr>
<tr>
<th>Labels</th>
<td>
new issue
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
maxammann
</td>
</tr>
</table>
<pre>
It is possible to link ASAN statically or dynamically. The default in Clang is static, and dynamic is to a certain degree experimental. On GCC (which uses compiler-rt) the default is dynamic and it is a stable feature.
While fuzzing with AFLplusplus I discovered a discrepancy on how it behaves on Ubuntu vs. Debian/Fedora/Nix. I raised an [issue](https://github.com/AFLplusplus/AFLplusplus/issues/1716) in AFL++, but we are unsure what and where the proper fix should be implemented.
A very similar issue exists with (__asan_default_options](https://www.mail-archive.com/ubuntu-bugs@lists.ubuntu.com/msg6005262.html), though no fix was proposed.
## Reproduction
The following experiments are performed on Debian and NOT on Ubuntu. See below for more information.
Suppose the following harness, which copies from outside an allocated region to some other buffer (y).
```c
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
char *x = (char*)malloc(5 * sizeof(char));
memset(x, 0, 5);
char *y = (char*)malloc(10 * sizeof(char));
memcpy(y, x, 10);
free(x);
free(y);
return 0;
}
__attribute__((weak)) void *__asan_region_is_poisoned(void *beg, size_t size) {
(void)beg;
(void)size;
return NULL;
}
```
(`__asan_region_is_poisoned` is for instance defined in AFL++ and other tools. See [issue](https://github.com/AFLplusplus/AFLplusplus/issues/1716) on how.)
Compiling and executing this with ASAN should crash. And it is true in the default configuration.
| Command | Corruption detected? |
| ----------- | ----------- |
| `clang harness.c -o harness -O0 -fsanitize=address && ./harness` | ✅ |
| Command | Corruption detected? |
| ----------- | ----------- |
| `clang harness.c -o harness -O0 -fsanitize=address -shared-libasan && LD_PRELOAD=$(clang -print-file-name=libclang_rt.asan-x86_64.so) ./harness` | ❌ |
The reason for this is that `libclang_rt.asan-x86_64.so` exports `__asan_region_is_poisoned` and makes it relocatable on Debian. On Ubuntu the symbol is exported but not relocatable, and therefore the correct implementation from `libclang_rt.asan-x86_64.so` will be chosen.
With GCC shared ASAN is the default. And it works with static and shared ASAN.
| Command | Corruption detected? |
| ----------- | ----------- |
| `gcc harness.c -o harness -O0 -fsanitize=address && ./harness` | ❌ |
| `gcc harness.c -o harness -O0 -fsanitize=address -static-libasan && ./harness` | ✅ |
## Why is shared ASAN + Clang not working on Debian?
On Debian the `libclang_rt.asan-x86_64.so` exports `__asan_region_is_poisoned` as dynamic relocatable. On Ubuntu it is not relocatable.
The impact is that on Debian, ASAN will use the stub `__asan_region_is_poisoned` defined by the main binary.
On Ubuntu, ASAN will use the `__asan_region_is_poisoned` from ASAN, because it is statically referenced.
This can be checked using the following command:
```
objdump --dynamic-reloc $(clang -print-file-name=libclang_rt.asan-x86_64.so) | grep __asan_region_is_poisoned
# Debian: 00000000001091c0 R_X86_64_JUMP_SLOT __asan_region_is_poisoned@@Base
# Ubuntu: not found
```
## Why does this behavior only show on Debian and not Ubuntu?
Like already described, the output of `objdump --dynamic-reloc $(clang -print-file-name=libclang_rt.asan-x86_64.so)` is different on Ubuntu and Debian.
The reason is that `dpkg-buildflags --get LDFLAGS` includes `-Wl,-Bsymbolic-functions`, which avoids that exported symbols are made relocatable during linking. That way on Ubuntu `libclang_rt.asan-x86_64.so` contains no relocatable functions.
## Proposed fix
Enable `-Bsymbolic-functions` for compiler-rt directly in LLVM, so the behavior is identical across distributions. This has already been done for clang-shlib [here](https://reviews.llvm.org/D102090).
Even though Ubuntu enabled this flag globally for performance reasons, it is actually the better behavior for shared ASAN, because it will avoid that binaries can "overwrite" functions which are internal to ASAN.
Another options would be to drastically limit which functions ASAN exports (this will also remove it from the relocation table). Though, this might not be the intended thing to do because certain internal functions are already called by tools.
## References:
* Similar issue with __asan_default_options: https://www.mail-archive.com/ubuntu-bugs@lists.ubuntu.com/msg6005262.html (Not fixed)
* http://maskray.me/blog/2021-05-16-elf-interposition-and-bsymbolic
* http://maskray.me/blog/2021-05-09-fno-semantic-interposition
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzMWFtv2zj2_zTMy4EMmU4c-yEPjl0P5o9MO5h2_t03gxKPLE4oUiApO-6nXxxSli9128XsFFjDjSuRPNffuVF4r7YG8Yk9PLOH1Z3oQm3dUyPeRNMIY-4KKw9PvwZQHlrrvSo0QrCglXmFxcfFe_BBBFUKrQ9gHciDEU16HMGnGkFiJTodQBlYamG2RCgdYXwJwsjjEVoIFgSU6IJQBiRuHSLgW4tONWiC0CP4YOCX5RIYn-1rVdbQefRQ2qZVGl3mAuNzCOds_UCfeKn4RpAEpEiFInQORyxfsXyR_n6uFa10X74os4W9CjUs1i-t7jz9g19BKl_aHTqUIOKDw1aY8gDWQG33xKPAWuzQ05s_i86EDnZ-BCsslDCMr9corROMr9-rtxH8Ck4oT9QMsIdn5X2H7GHF-KwOofVssmB8zfh6q0LdFaPSNoyvz0T66ilSoP-MH8dTMogypALjz_G7hKILsEcQDqEzvnMI-1qEaKB9jQ6jBVtnW3RQqTfwte20hAJBNa1G8gXKC6MtYIfuAF41SgsHUQLAN-WDTyZkfLbZCC_MpvfMxrZBWeNvabrf70eNUDoTrqzVDnudu2jKrOi2nt3nmoiP0rt-Q-O30zx_4FM-qkOjGZ-TtqG23bYGY6Mue-GjatZfqcD4hPEJ_IGts7IrSbjzZcJyZbW2e4LFCZQ-mrFFV1nXoCSXJz9Hc77_8OkEghF8RIQCtd1DZR001iEoQwcFsbsQ52PXkozRFSe-tXAGvSe1Ev5L2yr0UDnbgO2CVxIJR0JrW4qAEhxulTUUWd42CDbU6KDoqgodOeXA-PzSDNM8fcvBLMqUupMIbLL0wSmzHdVs8u72slT2e6taFWfLygRohDKMzwim7PE5vQcoa0HiLd6ATVYkJ71gfMH4vIm6MT57oA3g1Re01bBjTt_JQKfBxmNgfPZGJsvpz8PljiOnw3c4jfP_jFXZHpJJlxD5jfPLLZVDTLLceHu4fOswdM5APrxij6tzP202IgSnii7gZhMNONujeE1iwc4qSTL3MZdAsFF-01rlrUHJ-Oy4p8AtCUvabUL8uXJGv5fxOW2d3HgfD30l-_s_X15uiH8E2BEiMzbNvy3nNKeMTeGijA_ClDG3K4PyIq3FaEvgDtZqn2Lt56TTlOdHZOozhyxjEaIgJVHwDcsu0FOoVZ8DU7lMubR0wtcjWAw1KbiOksFF8SqtqdS2c19nB_a4hKWlCi0hftIL57qYVUFiwDKgZJM1LZ0OZacP3Hg-baQMEOt1n3JGJWT2-ADZhxyyygujQnT9SkjpaIHxKeNTGDG-PuaqaX4Skb3jbL5kFLsDs_9thTJfC4cy06ogiB4VfFltfv_j3cuHxYpNVozfU1aI1LPWKROySmnMjGiIlFZFXNu4MCIa2dtsupnej7wlNH1lqsFOKzZbXtuJypBD4a2JMRHBReih-s2m-XdYTXOqWtYFDz8KOHJCI17REzYdxlIS-6WhuMVGrG9tCLH-0BRWkySJB8rYZBh7cf7Y8VGYYmX7RqO0zmEZTr1FhHsqaT9Uaa-0psakrK3Hyxj5TDFHzWLyYAq_aKshwoYA3Fv32kdpak6jnGcHvxl9PwOn27L8h8KOCMJNNP1dTlmyz3U8XHGOfL8d7OcN1-f6EGeCMydRPk_DAgGIfEOJ9IQ-NlmfE_owtFzk2n8uCk6jwxmGz5GfMvcVyEenQFVNK8owxOdJfr5Mikb0dn2X50NX_FCoY-0rDvEMdU9QKCPcYTQYI0l3m8mP6Meoo2NxTsBS0MGk59mg57BCh6a8aqE_UTYqhUkBieUrSuh8KoLnXWyZYodK8a3OMz3a4i_ZNS1kWe-FLBoZ_qtkS7jcOmzh20Y4YnPA2gLy4TPO5-Myhz82_4pUN__352-_bz6-fPgE36F4n7P7_Fl4PNHuXTRZRPRUtqNMcrM9ug4VadGntB-HTGUdWKMP1Ffsr4YPIn1kdBEwL-oVQWiHQh5Aoi-dKqglXEY_2S60XQBbEVh-hhf6nk6qKqIonI3JJHZfYb5R9c6KnWxft1nRKS0rLbYesmyLAV5W65fFLx8jkzR7xFDPPmvGl9lzqlSqzKrOlGkCneaneUpQR9uzGEpZOpMmvUZIvKiJsqORKF6I0GgEn-joXhzOtPphSiqtCUIZyiUXtAcZb02qv_dTLI2158vvTDxLOt_WNnYOZ3cmIBUVYH2g_vPl5f9_i-OAjWAYQEZ9hkQTMwCI0llPHvRpBIkiQgz_WvgBWgWiAWkNJo6kf-ZrrQpqzKkHuNWXO9wp3PuR1rtmZN2W8fVqnPN8nl-Pqu92aI7TfW9pjLrLFCCECthqW8ScRSL0Y3qcIhKe4ijd3wqVoYs7k94h0KB8VJ9On5Woq-wYU2yETkJOzMg0l5exQnK7Q7d3KiDj_OTUI-TiLUBAZ4SmOf2rdmNh0mDT35jA_ngbEyxIJ_wxK2vVkCyR6IlJrAFDzeOzfh4hgbUnuDV2F5WIqT_UA7rjtUFq2-bkXLJzyhHKQ6O2dervilRYSAMjk-kp4VuQdjDR8UpvUPMkHml_xAup0de2OMPdvJ7pK4-_rh58AR8vrp9iN_eNS6fJAn7KlRMZ-D1ldPVGCXV-ko3YDdwa4V-dOIwaZHxdaEsg5zkfZ_lDNp5mqKssmqq1XpHAmTAyK47B_DeI5vOsMjbz2AgK4Uvq52a8k08TOZ_MxR0-jaczPp2Px_PZXf0kH-fjx7x85LO8Gk_lYzl9kGUxv3_I5-JxPB3fqSee80l-z2fj-_Fk_DiaF-W8yOeT-VxWOJ4Idp8jmXiI7Lvop6cpv5-M77QoUPt4Dc25wX1yIuOcPazu3BOduXTDQCWooPHp-RhylIhR9r38WVOZ7mQvq85OOGW7q0R21zn99J27AmLc_2Sts39hGc5vCaI6_w4AAP__BZp3sg">