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

    <tr>
        <th>Summary</th>
        <td>
            Compile time fortify warning in Linux kernel after commit d77067d08a3f56d
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            missed-optimization
      </td>
    </tr>

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

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

<pre>
    After https://github.com/llvm/llvm-project/commit/d77067d08a3f56dc2d0e6c95bd2852c943df743a, there is a new warning in the Linux kernel from its compile time fortify protections (from [`include/linux/fortify-string.h`](https://elixir.bootlin.com/linux/v6.7/source/include/linux/fortify-string.h)) in [`fs/smb/client/cifsencrypt.c`](https://elixir.bootlin.com/linux/v6.7/source/fs/smb/client/cifsencrypt.c#L532), which indicates that a condition that can be checked at compile time was triggered. However, it seems to be a false positive.

I've come up with a standalone reproducer from the kernel sources directly, which can be run in user space:

<details>
<summary><code>repro.c</code></summary>

```c
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>

typedef unsigned long   __kernel_ulong_t;
typedef __kernel_ulong_t __kernel_size_t;
typedef __kernel_size_t         size_t;

#define SIZE_MAX        (~(size_t)0)

#define __alloc_size(x, ...)                    __attribute__((__alloc_size__(x, ## __VA_ARGS__))) __attribute__((__malloc__))
#define __cold                                  __attribute__((__cold__))
#define __compiletime_error(msg)                __attribute__((__error__(msg)))
#define __compiletime_warning(msg)              __attribute__((__warning__(msg)))
#define __diagnose_as(builtin...)               __attribute__((__diagnose_as_builtin__(builtin)))
#define __pass_dynamic_object_size(type)        __attribute__((__pass_dynamic_object_size__(type)))
#define __gnu_inline                            __attribute__((__gnu_inline__))
#define __noreturn                              __attribute__((__noreturn__))
#define __overloadable                          __attribute__((__overloadable__))

#define __FORTIFY_INLINE extern __always_inline __gnu_inline __overloadable
#define __RENAME(x) __asm__(#x)

#define __struct_size(p)        __builtin_dynamic_object_size(p, 0)
#define __member_size(p)        __builtin_dynamic_object_size(p, 1)
#define POS                     __pass_dynamic_object_size(1)
#define POS0                    __pass_dynamic_object_size(0)

void fortify_panic(const char *name) __noreturn __cold;
void __write_overflow(void) __compiletime_error("detected write beyond size of object (1st parameter)");
void __write_overflow_field(size_t avail, size_t wanted) __compiletime_warning("detected write beyond size of field (1st parameter); maybe use struct_group()?");

#define __compiletime_lessthan(bounds, length)  (       \
        __builtin_constant_p((bounds) < (length)) && \
        (bounds) < (length)                          \
)

#define __compiletime_strlen(p)                                 \
({                                                              \
        char *__p = (char *)(p);                                \
        size_t __ret = SIZE_MAX;                                \
        const size_t __p_size = __member_size(p);               \
        if (__p_size != SIZE_MAX &&                             \
            __builtin_constant_p(*__p)) {                       \
                size_t __p_len = __p_size - 1;                  \
                if (__builtin_constant_p(__p[__p_len]) &&       \
                    __p[__p_len] == '\0')                     \
                        __ret = __builtin_strlen(__p);          \
        }                                                       \
        __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))

#define __underlying_memset     __builtin_memset
#define __underlying_strlen     __builtin_strlen

extern __kernel_size_t __real_strnlen(const char *, __kernel_size_t) __RENAME(strnlen);
/**
 * strnlen - Return bounded count of characters in a NUL-terminated string
 *
 * @p: pointer to NUL-terminated string to count.
 * @maxlen: maximum number of characters to count.
 *
 * Returns number of characters in @p (NOT including the final NUL), or
 * @maxlen, if no NUL has been found up to there.
 *
 */
__FORTIFY_INLINE __kernel_size_t strnlen(const char * const POS p, __kernel_size_t maxlen)
{
        const size_t p_size = __member_size(p);
        const 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_MAX) {
                /* 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;
}

__FORTIFY_INLINE __diagnose_as(__builtin_strlen, 1)
__kernel_size_t __fortify_strlen(const char * const POS p)
{
        const size_t p_size = __member_size(p);
        __kernel_size_t ret;

        /* Give up if we don't know how large p is. */
        if (p_size == SIZE_MAX)
                return __underlying_strlen(p);
        ret = strnlen(p, p_size);
        if (p_size <= ret)
                fortify_panic(__func__);
        return ret;
}

__FORTIFY_INLINE void fortify_memset_chk(__kernel_size_t size,
                                         const size_t p_size,
                                         const size_t p_size_field)
{
        if (__builtin_constant_p(size)) {
                /*
                 * Length argument is a constant expression, so we
                 * can perform compile-time bounds checking where
                 * buffer sizes are also known at compile time.
                 */

                /* Error when size is larger than enclosing struct. */
                if (__compiletime_lessthan(p_size_field, p_size) &&
                    __compiletime_lessthan(p_size, size))
                        __write_overflow();

                /* Warn when write size is larger than dest field. */
                if (__compiletime_lessthan(p_size_field, size))
                        __write_overflow_field(p_size_field, size);
        }
        /*
         * At this point, length argument may not be a constant expression,
         * so run-time bounds checking can be done where buffer sizes are
         * known. (This is not an "else" because the above checks may only
         * be compile-time warnings, and we want to still warn for run-time
         * overflows.)
         */

        /*
         * Always stop accesses beyond the struct that contains the
         * field, when the buffer's remaining size is known.
         * (The SIZE_MAX test is to optimize away checks where the buffer
         * lengths are unknown.)
         */
        if (p_size != SIZE_MAX && p_size < size)
                fortify_panic("memset");
}

#define __fortify_memset_chk(p, c, size, p_size, p_size_field) ({       \
        size_t __fortify_size = (size_t)(size);                         \
        fortify_memset_chk(__fortify_size, p_size, p_size_field),       \
        __underlying_memset(p, c, __fortify_size);                      \
})

#define memset(p, c, s) __fortify_memset_chk(p, c, s,                   \
                __struct_size(p), __member_size(p))

#define show(exp) printf(#exp ": %zu\n", exp)

extern int crypto_shash_update(const unsigned char *buffer, unsigned int len);

static __always_inline __alloc_size(1) void *kmalloc(size_t size)
{
        return malloc(size);
}

int main(int argc, char **argv)
{
        unsigned short *user;
        char *user_name = NULL;
        int len;

        if (argc > 1)
                user_name = argv[1];

        len = user_name ? strlen(user_name) : 0;
        user = kmalloc(2 + (len * 2));
        if (!user)
                return -1;

        show(__struct_size(user));
        show(__member_size(user));

        if (len)
                printf("Length of user_name is not zero\n");
        else
                memset(user, '\0', 2);

        return crypto_shash_update((unsigned char *)user, len * 2);
}
```

</details>

<details>
<summary><code>lib.c</code></summary>

```c
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>

void fortify_panic(const char *name)
{
        fprintf(stderr, "detected buffer overflow in %s\n", name);
        exit(1);
}

size_t strnlen(const char *s, size_t count)
{
        const char *sc;

        for (sc = s; count-- && *sc != '\0'; ++sc)
                /* nothing */;
        return sc - s;
}

int crypto_shash_update(const unsigned char *buffer, unsigned int len)
{
        return len % 2;
}

void __write_overflow_field(size_t avail, size_t wanted)
{
}
```

</details>

```
$ clang -O2 repro.c lib.c
repro.c:127:4: warning: call to '__write_overflow_field' declared with 'warning' attribute: detected write beyond size of field (1st parameter); maybe use struct_group()? [-Wattribute-warning]
  127 | __write_overflow_field(p_size_field, size);
      | ^
1 warning generated.

$ ./a.out test
__struct_size(user): 10
__member_size(user): 10
Length of user_name is not zero

$ ./a.out
__struct_size(user): 2
__member_size(user): 2
```

In the `else` block, `__builtin_dynamic_object_size()` can only ever return `2` because we know `len` is `zero` (i.e., `p_size_field == 2` due to `__alloc_size(1)` on `kmalloc()`) and the size to `memset()` is `2` (i.e., `size == 2`), so the condition `p_size_field < size` is always false, but it appears that after that change it is no longer eliminated (or sunk in some other manner?).

Some other potentially relevant information is in [the downstream issue](https://github.com/ClangBuiltLinux/linux/issues/1966).

cc @nikic @nickdesaulniers @kees 
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzMWltz47hy_jXwS5dVFGTdHvxgy-OTqZozc2pnTzbJCwsiWxJiElABoGXvQ357qgGQ4k2yd7NJZcvrMUl0o9H4-goIa-VeId6z-SObP92Iyh20uVfCHYTK6H-82er8_f5h59DAwbmjZbMHxp8Zf95Ld6i2k0yXjD8XxWv9z-3R6P_EzDH-nOmylPRHvlwmi2WerMRsN1_kGc8TXGTr-TbnqznP1nezfLe8mwnGN-AOaBCkBQEKT3ASRkm1B6noC3yTqnqDFzQKC9gZXYJ0FjJdHmWB4GSJsNPGyd07HI12mDmplQXGV34wLXSRSJUVVY4kMLFj_DnS3FpnpNpPDmyRsPkT46vumrGQb9JMtlq7Qqp67ZHH62KyZPzZ6spkxPsTs_A142taWpBrZ4m-3JLqConK61DuLKrMvB_dJPsrxPp4Ej77Np9xL9wGTgeZHUCqXGbCoQV3EA4EZFrlknQbXmRCwRYhO2D2gjnQm_aWnIQFZ-R-jwbzCfyLPuErGmIvHVjE0oLTxEDAThQW4aitdPIVJyx5YslD-P2V8eUrEmeE6ggn6Q4gwDqhclFohWDwaHReZWgCNAgwESph-RZyaTBzxft5aVF0UynaiMqiAXsUGZJ2W5Oz2SZHJ2Rh2exL88pWZSnMO72abTKdI5t98VJMMjbbeBPw78JDa3Sb8yIJP1l85rMIHaAZXJ7jbnI4E_U_S33tayG37c_-t3s_Yo47qJS3_xwKrfYsWadp0FZa0YvUsdljl6A_4PzCyt_xGkH8nqxZsu6NbQTPcScVws-v__El_fvDv9FgvvovxleRgK8TAuUoUZqKotCZn4fx1Rvt72Qy8ePjT5oK54zcVg7TlPEV46s2lX_n6RifMT6DNP3Xh_Thl7_9pC_raKujTMrApR42EC3TRd6IcUUYGneNi7coMqgUjdGG8VVp980SR1n6gf4xDv0E8-hyP8M-Dv3EBLkUe6UtpsIyvtpWsnBStXdolH-LKo00_mP8-8p8R2Ftmr8rUcos1VuKSDU4CJxh2tE5L1H6ETXthVn3qkqlKqTCDzf7PPTylitt0FVGfcisHniZlX5FU2iRi23xsWztwT2WA8bPP3759evzv6dfv3_7-v0L4JtDo7w9nsS7jUvsqGYgTZ_lL1--P_z9SzBHb3K2jLLN3q5IYp2pzrt8rLe4xs04GI5k8cmoykost2j-DL_pCL9__PjZUvoVeF4gTj5H3XeRr1rmdUqUHoWSGeOrTCvrIDsIA4w_KFFiUHQNo9pn1S7aM0nTk5EO_d7tCn1ifEXvA-WYc2Kc50gpGObgSWGL71rlQKKC3kGQnJKzqXVwFEaU6CgvWDPu84-r86c7iUXehAcQr0IWpP34fBLK4Yh4Z_f2oYB-hlH5Zo9QivctUsIAEXh7o6ujZ7tms-feEq673AKtpaSbXJuuVG5pHQWqvTtEB8lX9Hu-qbm1gOh3UyiXhskbDmtKAUj6MyN6xxf002V2naoduhqiy3bYXph1pkB1Np8LvFZs-dj_OhyWrGvIpukR2OyJxKxf-eWFaWaPl-gjNNLUoPMMmmTjMk0wloby6C3NE4-6iIZRi4XcQQgtkZZP23O3tmRsfgCAi5tNmqg3tq3C9t62ln1MC1RR9ijMLUw7i-9S1pKPTk9zzx8jV1-YXIJXvYrueBIk7OKSzTcJ_cPHdVA7vnrbzgI1AIuKGFM_Wz59iCzPu4-C4cDl0xXgSxv0g29HU0evnpWRyvWO8ZWkwmtda6B57YG8WgGbPUOwZu_7GnyTXVKy7h_CBPwBkqLGwOwBAvNIsuonKz2hr9rniJIaHBw0pWVxpYOlj-zPsZZkM9zbi8M3kKZ18BpwurALlcrRFO-UlpZYWnSdCepXV6jiRCNitWZs8pxOgeM9iyhovArCdkNtWFG3JvIBqkl5GspW6KAi_4F-_KPf8TgMbuGXELG9-8YcMl0pR7GLphSZQ2OprhXw_Z_fbh2aUipB8S70IM4cW7zZXXIkIB21VA4NleajxPTBTzfpEpfijVYwe4BSvMmyKkFV5CZ7Uo1Qt_iEZdlxUqm8kAT17z9-hVDxeokOCDupREESRwRpMyoe34DcgfJrg4OwsEVUsCM1QnUk6XwnalQ62hH_MEh_-3i4BAQIj__48ROOI6CAWsga5cvH8Xj0mWg0TncOBJfjdYs60tVOsmV65NcIoPAb-maKEy9Yt39ufftHxC7c6YAqrgykDSqYdPT5UcRp1BLjTL2ObkBtomEregQRv-7g2Ezte0BBZsriWo1EL_bti9InRVnYQMZGzLgY3-F5ipvRck3RhcakOoS9lk59XBrT5JMGpV1op7VxH9NTQjmqnIziOJDtHCJ7nsjjrBF4c4bO8_ntQ2sNLUHDYhuCDfH384RNqMnDLhBEuirolx9puqtU7Je0Zolq6kCsq6ERc-v2FYaBpF2MDZ31ILZcs9K_0Bb7klwzq7_JV9_vlDtCa64V40sHBE446BMUwuzRo_qCKZ3F6tlIe4uaum8YB4fS1xDrYeuPQOf_DCGd-jcE_zQ7vPg5eq7aC9_POWP2OrLRf2RsXauOQOiqv6sVesmhdTJsAss3X7SBMPuqROXCOUrNEig7Q2ul9oZhNZxwwIHc4RHNTpuy68VDgRicEsXaE4XHAfm22u3Q-NVbEAZBFFZDcKW9Y4FJn7iF3YHn_mKMNiGCeChJG5BvgOpmQJUV2pJYoRi_7LIvFt7dnWrBualp-tXMVUZ1J6KbrDaVzKCXMtIsaK__N2FUWH7oVIwpIUfrQs_iL1j-p2Vv-jCXOIxHvD6CPXoeHLiDtCH1PPdAznguxbsPjf6saBzXXY5Wg6nUOILj0U-uFQY0D9Db5eVRTKpd_UpCSutFERT5OBYWGeewxUxQNkExWmz1azwUs15yrYr3LsttL1WKzSnf_xEqJ49_ohU6DdbJovADyJ81q-ryqzfFTtobd8G4xjbAd23BOn0EkWVoLXbSjmBd8dBPKyeksvShy6WBgAcs0QXFMr60YLAU0p_p1hgOeu2y8Eo-HwaBI2xLXzXoo5MlkYqTeK_VG_avNVWHW4BRcEiVivNdVlAvao22a87h_oz0KxGNcR5Lz15fsJcDtkrS0ajlI212Nq-zn9r0Yw2c-2pjLbAm96mTlvZJWyv2XO6MXQirbcZXBRz2Asaq9_aa-8zX462rK12aEaY2lOAfqTtKO9abGj178OKOpIEXWzEHHwfwjUbB0UjlQkdohm9U6vLQ3Jn_XrH5RnkYbSCMHmlMSOXAH-jr1B6EPaTVMRcOmxS3Ofutc93aQjfnT8Sj34vwv60TTmYjhzydQ1hKvKFuX72EQ9Jzu75rM8tBktcZf8VipI8KUsW2lzB7v13njsuDMPvX0ZmahdqDNr5jVlk0nZo5cqH3qRJlMJTv__z2rZPjRj0NwndwIyQSlYjtYyEPmy5XL-b8ccrmT0NOdbXeJnk-d--a13UTMGnL5y81EPV5Ezgw_hi7_N5D8gjNQerO-NRrZbReuJ0ORY047ttEzaTXVqgHdw1ldHBHrE53xIvUshge02C9aykshuvf0ejGgDqy-BDeYti4iiDNptOn3sDY8U6jmHHTI159u2N8XfPv7MUQ7vU9kc6M_mLJ4GZK_e2TV1YKuf1_fWHl84eYQyPfNbiwLkcTN_J88BdTvjpx8r1FPrctH1uzbkPlTbr6qPaCX_qgAWhbJ5W-DXqlv9DQZEO8USpIa8tCRc5mj4Hd7W2dp3jCOolpQXhG9v_I-KPN-kl-KDmUdgdK02JuNKzDbQa3fsqLjvkvjECX4kQwmjnwS3L8zw6P-_P-eWvsEfA7yAqh9nD7g0O8MwbBEP2A5hrZw5Q26-GO3Hp9dj2jOr0oKBtmfHlpcUvIMSuEwTxclmN82Rx-L6G590Hc_vfOwYHNH29_aya7rSWYR0UCTPkS2HJzeY8-KizB_0cs2DzqfNpcHN2jQiMc5pNu2nUHE8afxURXzhcXdf9oPGrNHmCa1EPGY1VryEfh55IgH8rAPxSBX4Hn11CLsUXiQ90igW2hsxfvERfJRzdbaIJF4otmqmMBX9FANEK2SLjnF6vfE4YGJVskZL6LhJbPFolf_SLxZ5QTnMSZ2_tb9yk9u7xCD3ESbpBa0gDtpz6nNeE1JUGiLlgJwoFHE88jcRCJD-Vp90t54Bf7ZcTxfPV1KHrEZmAekuNwo5Xot5UD6UAcjyhMfZPW36wO5fRBqD3SCA8VfykTDWAh62M3xlfagK3UC0Upq0sE7Q5ooBRKUeL6zPi6A_Of5zFH7VA5KYriHQwW-CqUA6l22pTCr0baeBOZFpnrk7LOoChBWlvh2LXjzg3wDfmyRwLQt3jxuL6A7Okt48_T9WLRFzDLgN0lSr7I-Ef2kqMVVaEkGkuvXhAt3OT3s3w9W4sbvJ8uk7v1erm6S24O93nOsyTLVvluLfJdvlisVnfzVTLl_G62zTJxI-95wu-S6XQ6Xc2S2WoyT5aY7JY83yaz6V0u2F2CpZDFpChey4k2-xsv8P1yuZrObgqxxcL6C_Kcl9JazG9j_8FrjVKE-dONufdX37fV3rK7pJDW2TM_J12B95uxO-qt2-2dm-0BFuH-PPRuz99Uprj_w3fxm13w6_rvAAAA___je3fn">