<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/57510>57510</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
clang-15 regression w/ `-fsantize=local-bounds` and fortify source
</td>
</tr>
<tr>
<th>Labels</th>
<td>
compiler-rt:ubsan,
regression
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
nickdesaulniers
</td>
</tr>
</table>
<pre>
In the Linux kernel, our fortified string rountines, we basically have:
```c
#include <stddef.h>
#define __compiletime_strlen(p) \
({ \
const char *__p = (const char *)(p); \
size_t __ret = (size_t)-1; \
size_t __p_size = __builtin_object_size(p, 1); \
if (__p_size != (size_t)-1) { \
size_t __p_len = __p_size - 1; \
if (__builtin_constant_p(__p[__p_len]) && \
__p[__p_len] == '\0') \
__ret = __builtin_strlen(__p); \
} \
__ret; \
})
#define __RENAME(x) __asm__(#x)
extern size_t __real_strnlen(const char *, size_t) __RENAME(strnlen);
# define __pass_object_size(type) __attribute__((__pass_object_size__(type)))
#define POS __pass_object_size(1)
extern void fortify_panic(const char*);
__attribute__((always_inline, gnu_inline, overloadable))
static inline
size_t strnlen(const char * const POS p, size_t maxlen)
{
size_t p_size = __builtin_object_size(p, 1);
size_t p_len = __compiletime_strlen(p);
size_t ret;
/* We can take compile-time actions when maxlen is const. */
if (__builtin_constant_p(maxlen) && p_len != (size_t)-1) {
/* If p is const, we can use its compile-time-known len. */
if (maxlen >= p_size)
return p_len;
}
/* Do not check characters beyond the end of p. */
ret = __real_strnlen(p, maxlen < p_size ? maxlen : p_size);
if (p_size <= ret && maxlen != ret)
fortify_panic(__func__);
return ret;
}
#define __is_constexpr(x) (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
#define strlen(p) \
__builtin_choose_expr(__is_constexpr(__builtin_strlen(p)), \
__builtin_strlen(p), __fortify_strlen(p))
extern size_t __underlying_strlen(const char *p) __RENAME(strlen);
__attribute__((always_inline, gnu_inline, overloadable, diagnose_as_builtin(__builtin_strlen, 1)))
static inline
size_t __fortify_strlen(const char * const POS p)
{
size_t ret;
size_t p_size = __builtin_object_size(p, 1);
/* Give up if we don't know how large p is. */
if (p_size == (size_t)-1)
return __underlying_strlen(p);
ret = strnlen(p, p_size);
if (p_size <= ret)
fortify_panic(__func__);
return ret;
}
extern int x;
extern size_t len;
void foo (void) {
const char* suffix = NULL;
switch (x) {
case 42:
suffix = "hello";
break;
case 100:
suffix = "goodbye";
break;
case 1000:
suffix = "asdfasdfasdf";
break;
}
if (suffix)
len = strlen(suffix);
}
```
in clang-15, after commit d8e0a6d5e9dd ("[LowerConstantIntrinsics] Support phi operand in __builtin_object_size folder"), we observe a runtime trap due to the `ud2` emitted from the above:
```sh
$ clang -fno-pic -S -O2 -fsanitize=local-bounds -o - x.c
```
```asm
.text
.file "x.c"
.globl foo # -- Begin function foo
.p2align 4, 0x90
.type foo,@function
foo: # @foo
.cfi_startproc
# %bb.0: # %entry
pushq %rbx
.cfi_def_cfa_offset 16
.cfi_offset %rbx, -16
movl x(%rip), %eax
cmpl $42, %eax
je .LBB0_1
# %bb.2: # %entry
cmpl $1000, %eax # imm = 0x3E8
je .LBB0_5
# %bb.3: # %entry
cmpl $100, %eax
jne .LBB0_12
# %bb.4: # %sw.bb1
movl $.L.str.1, %edi
xorl %eax, %eax
jmp .LBB0_6
.LBB0_1:
movl $.L.str, %edi
xorl %eax, %eax
jmp .LBB0_6
.LBB0_5: # %sw.bb2
movl $.L.str.2, %edi
movb $1, %al
.LBB0_6: # %if.end.i
testb %al, %al
je .LBB0_13
# %bb.7: # %if.end14.i.i
movl $13, %esi
callq strnlen
movq %rax, %rbx
cmpq $14, %rax
jb .LBB0_9
# %bb.8: # %if.then23.i.i
movl $.L__func__._ZL7strnlenPKcU17pass_object_size1m, %edi
callq fortify_panic
.LBB0_9: # %_ZL7strnlenPKcU17pass_object_size1m.exit.i
cmpq $13, %rbx
jb .LBB0_11
# %bb.10: # %if.then3.i
movl $.L__func__._ZL16__fortify_strlenPKcU17pass_object_size1, %edi
callq fortify_panic
.LBB0_11: # %_ZL16__fortify_strlenPKcU17pass_object_size1.exit
movq %rbx, len(%rip)
.LBB0_12: # %if.end
popq %rbx
.cfi_def_cfa_offset 8
retq
.LBB0_13: # %trap
.cfi_def_cfa_offset 16
ud2
.Lfunc_end0:
...
```
tagging this as regression until I have a better handle on what precisely is going wrong here. Apologies on the late report; we're a bit behind in the toolchain upgrade for Android, which is where we hit this.
I think `__builtin_object_size` when passed a runtime-dependent value has surprising behavior: it returns the max of the two rather than `-1`. The NUL-termination check is getting tripped with `-fsanitize=local-bounds`. Maybe that code needs to be rewritten...
cc @serge-sans-paille @nikic
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJy9WVlv67oR_jXOC2FBi9cHPyTOSRE0d0FPLwr0RaAk2uINLeqQUmz313eGolbLsXN6UcOxLVGc-WafYSKZnDevGSlSRt54Vp7IO1MZExN_S2SpyE6qgu84S4guFM_2RMkyK3jGND5xZCSimsdUiDNJ6QebBI8T93ni1p8Lt3rH9toPeBaLMmFkEmx1kSRs56ST4Ftvkx_AbWBBwjCWh5wLVvADCwGAYNnEX-UTf01uvSbzbU1uNVk-3Xz-PmL1jVhmuiBxShWZ-I9hmIM8z_Bz1V8AoBbvJBiFcEFY8_-wsADJFStqmtU9IDL1rpC5h2Ae4k9DMwyjkguwYiijP1lcmJUK6JZ4Q7QXJPkOUbUUfW8EKJjoc7VfkB1BDPa2gC2vKRlXwVViNdZaYGMfmhVhXokwmT9ZRpP5swHtL-B9iyy-hrsRaaWIJexz8euqn35KuCJeO0CLvQkA5HzLSJPl8zXaN14XpAyWm573KSlAg4ivRPk_vv36-Ms3EOyEGgtDqg9hiHHrB6dmGzsVkJm6AUIFqiSrdDIMvC1pvLHLodmA6hvgIQ2gnGo9iI3inDODZQ34CsiFUVkwi3J1ucOs1Huq90Ds33_7Ps7JG2jKCv4heWLT8Rm2ZTzuSW2zTV-oEahUHOlZhzwTAALVtM_KzpX8YEpImtBIsC5uCJqCx8Q-WN2qLHHNBDZHoph5aw5yoCerf-sYT6P56ovZ6gqNNn9cLSTXNlcu31Vm49T-C4r3L0ZiCoWTvsOPivoUyRMaFxxEJ8cUuFfiEq4rdTiVc76MJ9TRJNUorE5NVqxPsu54arG4X3ckb_DYIo6ClJoRXuieLNP3TB4zAvzGgfcFsLJiOQdkubXT-vNEB3ouwburJDq0BaaN6xZ4liST6HAsfjduB5pnSpOInWWWmJaGwbcEgcfhtyl2kEyMazXibFtvfGnvPnYkHOKu9NHs2iITw6yyYE2jMiF62jUtDeM9DHdlFmMsXzK1muw5bl9_3ZzLdeVm7JSrOvNaZ5I7-MEzkzptTWtuGzWuVkYVVUIxeanpdeAtZLavLizRR-KKKpcYtVXE7ZbVMD8OgH6t6Rt_jRS0JtJSKTULrRIutDJSe_Ma8PYK8Usmg71bWKrtekH2Mu83Ba_MEsjNZ2jC2239lJtfFrvRWve_loUtSTjdZ6g5qmsxR9VlE_RdhWREK59VlBsFpBsGg6Wfqi2jGehv_IORMsd4hyyaSMC8LAgmTZLCn6Bqz0y2_Szvt3BGE_q4d9lgH_eK0bpWJ7tBlvtyEvt_5apuDGC-ODVP9gOjWzeqT9spSWKz00VV7HVNRJe7HT8Z3fz6x9tbD6s-8iJOSZPLelQoVMyZ34y8jZe19Ca-nzIhJHxfqCCCmvPeu2sIeq57g-JeyiQ6s6_RvEWU6mRX_91HubFX6ysVyQsHqbuwxj_b5y6NX58YVJc8I7Gg2X7qzdFZ6Q4sjx3KgRckWTGXLpI5WydJVYt8mMfe5JGprW2gXjM8tNA81jiffS_zHHyU5CknMmeKQnPAs_EcAP4jILCQZpWzIb5lpJmCgKdE4RkItHqFojlJSvghTbsBuMvEh0_CAGHBwA2VPJglGsnu8Ugtpk7rijerJCXTXSanOaTI6Xcy_c2Haw0BVWCQBs9CxlRMIwlRr8lUwjx8cuJRzTWXMEzVwbF2oLAV7dUO-jz4BiGRCojarOyFjAT8wCj67IVT03RKntgeFInhjq0vxl5LKvep4PsMfs5Qj-5p7Xbw4IRk2MDaZObWJKon8HZwtXMc4sDtXcbxjkNKpKrIlWzPn-DReRQ57h2E7dMMnOjckM1Lnf4wSpur6NTnBj1LGO9oKHc7DdnWW_SX7W27E3Qx7TxxkB-o8JNx5Lnida-AAGjLJz7kwnCfQea5XP4Ttem8PT25oTcU2f9ZkVueJpE0XK9S4IeDCXj3FHxbjWGbD7EFfwm2MYVkHY34Q7az-9nqoxNF3tBcwNd5cyCxOV7NPOHNQyepqocMpBFwh7wBZz2htl17jnrB6q9kNP-iAvzrCvBHcMFDkbWOXaWiy35xP3u-c2CYc1raBdNFRRyJ9qkPIiEY2n35Vb7ezOEOHxEeaFuxdbuM5-GYI-pWq7OtTh2NmbpJBJz5hyU7q5e7VowakdZDiVZfkggKUuYHV0Ry3urOzQn__ba0Qvz-9_gPbzk8rvIOI1avxe83hB2zr-8HewcCh5140ZGko8VgRMkdLXoXOdK7oy701RjcoUNvMZxtrsjyM8r0vLshfwWK0eqY51alq2rk2krVBXRHoekFV1tbZX5naW2rCowQP3rc7yglljt2b_cWcGzrLBdjV4B92VI7jjPaixV0v8f_nRUp14RqGHv2immNrRI2koK8mn-eQWMZsQI73BRaUwENZ0aOKYWGVbGYaybOeG63l0jqqCR8pkwxhzzmUsg9Zxo3YK8paMGACTa7eGx_hFlhqQx5aJsjlvKq8cVHCykFDEJwVeZ7RRNsfBV5zBJlBifoe1MOAxA3x5lAA9rgFIigJLWs5vMVb2Xv2AGPz9TQE5sDUXQ06IubHnqasByUCVWdfFABrXQKCtKlyhXXKCjApR9cKrQqL-zgqA32A7QgcleJcZREUfil4JJmCAOG54XrkH_CKox1U1DrgWfU9KfViSHqEtRtDANenAMqmPZSs_daz21Ikl_oOWLICIZICSrLGIN2HGaACNV-VNj6Z60zmM84xgYVBog9mwJxPc0pF2BjuJnxd2j3H9jGWyx8dzH33eAh2QTJOljTh4IXgm3qIajrOseJ_9KAHcdKcMSx4U60LFXMHkolNmlR5BrdF48xXvYgdRk5MFbBhRAf9dcUGmc0IFxyrUv8l-_LfDn33Id0Eyc-TahHvVWMI9h8FS1n1GPzZbBkq-Vq_SBoxITewEQGg4U9U1ZTdMjHMgLAZrLCbOe3IpkJ7vmBb3zX99216_nubDELHG-VBMB3EdCdF8TBGnTGDqA-B1E6Uu0f1MYAjsq9hkXBdaHbRfA4GEAYM2CAPi2LVKoN5NH3hGlaiowzpR-MjBsj4H8Bb9J-IQ">