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

    <tr>
        <th>Summary</th>
        <td>
            [CFI] Inconsistent Behaviour in Compiler-RT leads to possible cDSO-CFI Check Bypass when Diagnostics are enabled
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
      </td>
    </tr>

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

    <tr>
      <th>Reporter</th>
      <td>
          marben-olvbar
      </td>
    </tr>
</table>

<pre>
    When compiling cDSO CFI protected binaries on Linux (x64), the compiler-rt exposes inconsistent behaviour regarding program termination if `-fno-sanitize-trap=cfi` is passed:

1. In the "usual" case, CFI check failures occur in `__cfi_check` of the corresponding module that the target address is located in. When compiling using `-fno-sanitize-trap=cfi`, `__cfi_check` calls `__cfi_check_fail`, which then calls `__ubsan_handle_cfi_check_fail_abort`, causing program termination.
2. CFI Check failures can also happen if the target address is located at an "invalid" address, i.e. an address having a value of zero in the Shadow Mapping. In that case, if diagnostic messages are enabled, the code in [/compiler-rt/lib/cfi/cfi.cpp#L349](../blob/main/compiler-rt/lib/cfi/cfi.cpp#L349) is reached, calling `__ubsan_handle_cfi_check_fail`. This function does _not_ abort the program, hence the execution continues and the target address is used.

     Usually, the program will crash anyways, because an address having a zero value in the Shadow Mapping is normally unmapped, and will therefore segfault. If an application manually maps memory using `mmap()`, this assumption might not hold. In such cases, using the target address after the failed check will result in unprevented CFI policy violations. 

Since the behaviour in the "usual" case is to terminate the program after the corresponding error message was printed, I personally would expect this behaviour in such other cases as well. If this is intended behaviour, maybe it could be indicated in the CFI documentation page.

If it is not intended behaviour, this might lead to CFI check bypasses in builds meeting above criteria. However, in that case the security implications of this behaviour will probably be rather small, since `-fno-sanitize-trap=cfi` is as far as I know not really intended for production builds, and applications that use `mmap()` to load executable code are rare.

-----

#### Steps to Reproduce

Compile the following Code (see Clang invocation below):
```c
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>

int a(int);
int a(int x) {
    puts("Function A called.");
    return x;
}

int main(int argc, char **argv) {
    int (*func_ptr)(int);

    if(argc < 2) {
        printf("Usage: %s <number>", argv[0]);
        return 0;
    }

    int fd = open("code.bin", O_RDONLY);
    if(fd < 0) {
        perror("open");
        return -1;
    }
    
    void *b = mmap(0, 48, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
    if(b == (void*)-1) {
        perror("failed to mmap");
        return -1;
    }
    
    void* ptrs[] = {(void*)&a, b};
    // the potential out-of-bound read is intended.
    func_ptr = (int(*)(int)) ptrs[*argv[1] - '0'];
    return func_ptr(0);
}
```
The `code.bin` file was created using pwntools by running
```python
from pwn import *
open("code.bin", "wb").write(asm(shellcraft.amd64.sh(), arch='amd64'))
```
altough any other testcode works too. One should adapt the length argument in the mmap() call though.

For compilation, the following arguments were used:
```
clang -Weverything -Werror -Wno-declaration-after-statement -pedantic -std=c99 -O3 -fvisibility=hidden -fuse-ld=lld -flto -fno-sanitize-trap=cfi -fsanitize=cfi -fsanitize-cfi-cross-dso cfi_icall_mmap.c
```
Running the resulting executable with the argument `1` produces the following:
```
$[shell]: ./a.out 1
cfi_icall_mmap.c:34:12: runtime error: control flow integrity check for type 'int (int)' failed during indirect function call
LLVMSymbolizer: error reading file: The file was not recognized as a valid object file
(code.bin+0x0): note: (unknown) defined here cfi_icall_mmap.c:34:12: note: check failed in a.out, destination function located in code.bin
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior cfi_icall_mmap.c:34:12 in 
$
```
Note that the resulting prompt is a different shell (/bin/sh) than before - indicating that the code in `code.bin` was executed.

#### Expected Behaviour:
Program should be terminated after printing the runtime error message. 
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJytWFtv27gS_jXOCyHBluMkfvBDrjgF2k2RtNuzTwYlURZPaVKgqDjurz_fDCVfEqeLBTYIbIuXmW_uM8pduV38qJUVhVs32mi7EsXd86O4ffgkGu-CKoIqRa6t9Fq1wlnxWdvuVYyyq9eL81E2H2W3ItSqv6984oNQr41rcVrbwtlWt0HZIHJVyxftOi-8WklfEitwWHm5FkH5NVgEDfq6EqOLcVJZl7TS6qB_qSR42Yymd0WlsSV0KxrZtqocTa9H47vRuP-cpOKTZTCjLOvaThp8i0K2ikCSREWtip-iktp0nqQpCsDRlhgul6C-5APEw1W9VB4HG2cZ7tqVnVHYkIF3g_QrFYQsSxxqCZdxhSSFaZuKN2rtWvr8vWiE8z2WQhrTvllfkhD9jU2ti5oA2cOjXQ4ey1ra0qg395Yydz70twsZkZ2wRRr1mqWsvNtj5RXSCmlaJ2rZNIrt9nudQGm4ApNo-yKNLsk4_THCoVOV0oHhJnkLYEmBw50ig_xS3pG1iM1zLUu3EV_AG6d6w4PDYG2gKbVcWdcGXYg1CMoVQEuvhLIyN3CeneOWin1gdjPKHg7cGE9G57QG2_BnWjTNKJt-np7PR7M7hECaYiM3jk6tpbb_hEA2J-V4JWGXMhrCmN5Ffms97KfiW43LVWcLjpnSQbaldWEp2LQsWG9QIg3XKBQvqldVdHwHoRkQyqQUW35gug5Blh6GmKC_7xRaZjsocHCcjTZGFF62NShuN3LLZs0VOZg6aVk2aDTvSbMSBOv8mriJzq7J0VhVhJjZ4Y5XlYNVW7WqZGcCXKFiZk1jdBFTylpaRowfTQtnWDu_3cfjGquwJeWyGBGBdIsE062beF2v6gAgQdTOlOxrbYeII19jGSOpEyqUFaKJN8hwiIGYgBg6DgAuCd7ZxqsX5Egc4LzrgHwrkCwN429TcWiDZz0Yc59S9em8RwoMbhfS6shee3DHWU557_wQMmIjkW29JnAkKtAp3zrL6ty4zpSU7VElotaOELGSHJkoqgo6FRtlDJuIj9M_KNuSasxwldis5TYHesQzs6DfADfkVgZNmipd0a2huGjmBniPvBVsQIKdKHzAiGFEAxslS9LWvlDkW64zBFLknTYlOY8K7Ly5e4HivIYOtUzFf9wGJmSS-iAVMdIWIYeDW6HXO59sY4U5Uhm7BayTIz9tSWYvWXktBQBRbtnyf18doedKevr6JH5axBPJj0xDNtupAVFDzMouppAo3xBcB9HTRmkoht8GC2nLOFn2WYXSakynlGY9Po6skdDf4QIy4e5fPAfVsLM-qYhKHR69jVk1hpIzxm3ICLfEC2haBXcwkhKGfXF90OcKpwjnrklAdPN_sWMPfZqOaExv21Bql9aj6f3JXUpmDxDefnikKmwwh7v8CXULCYj4Ziw375bFK5WC0eXNPsM2XWhZydnDkOGvuTxQNs6yQ0J03KvQeQs6w-Lo8u4thlidmB9SVMH1poaPjDKIcY2ll3co6CyjuKY6s2yC53bvrSgHFyrsEnVSh8jeEWTRKJVUUbjvlGBgHnCZtXTHduscMQQNkpC3gmHNbsZcbI9lPpB7fLTxRvZBkKoEgzvh0KlE5uSnaU5KYVaPy6e7xz8-__WWD8vEl2_B6KREnC8j0Uj-nYEOwCaT02j5YffrxemSTJMz6D7muDadX9Hn16fHb8un--s7EOif7v97f0tbX66_Lr8-ffrz-ts9PVactccnxWLqxAC_iSP7whwI_07MvpghWCO0f0dgsBdwspYasdkdS04oDrGNsgvJTQWROSKbPeA_ljdH44aWRrguJK5Kctcho3nK7gf1Jt1fHtxb9LpgB78aOA7-DqUM6PqAmd1MCGiCO5fQ8CW56Ym43EfP1ZEd9p46JKb4-K3mTLtzUOTZinIfVeICYlAN7Fv2jQ3OoeHPt8J31mLtDcVmG2pn42Ll3ZquUBmiHpEE5I0PgwJfmzyaN91QqaP4bteUcWvUcfR6VUjlurw4T9u6rwoctkVNnpVd8h5phvV3UlppgutW3DL2vUJQbeAqsnH-J5UEl4pHizJacy8gS9nEBtcouwo1ZQluAobGYF-iOGdijRgclaIH1L7YqHOtGDrZfWUZaFLDglrWHQ2bxwIUXHiSH1T-oW0dH7iHSn6gTJcKJzzzSbjlSlr0K4oRJ-hnpaUJBYsl1fD5XCSPU5FUL7rVOSbHsMVyrcsSI1ZSAUhi6KCBJpIKuhMf9QLYGFbfLSR4TArv2jYpMcHRfKFJV0vSXVqclPMpOhgrKvau3CzuK_9GB55C9wbB3Qm5b1_Q22Mlf6TQUXaO0GIP44i6FjRlyRThLCa9zt8Cnl5Pz_Exyeg4QiHotYqNLC3QqOOdERUYcwJYcTvWvw2AocK2oUbisi95Q8RfDm17if6Nu4tSe-p1d4MXYYiQPn_-88vzdp2jd_-lmGv0Aco7dJdCmFYpunfhHPuywq0sLpXUsPG4i-Tv8v8xI7rVa-VqH50349eYSq6JRF9DrzpLzZ4lxy9VpS0o0nj03rxH2hoI7N-NxB6bFU6RUSIeh5czO8H3LzrEDlacTr5_-XL99BdR_G57GDexxfXPvfuxerphN-k7YP8boDyg77zjlNf84cLBm5m9g8L3MMZxVyxKXVVQCIzM3hW7mwfW6AMlsDkRoM6RJ8pkmDmi1_eUd68LjjM0WTPGwpuJ-ajJvedRCWq72c0gQxB87aeyPsuh-99NbWU_qnHvtAvBQycfprVUnKnF5OICcTefXozPysW0nE_n8izoYNQCYYXhhmrWp8M3czeHQ9vt8P7i6RtPRNyTN8gUmrv7u-fHZP8y6IYHJLGhV093u9ctR69ZzjpvFnUITUuicpleIVF0eYoETG9IzMvwlcBW5PV41Ji-abZ-mF2dT87P6sWsuJyPZXZxleXVZDadV-oyn83KWVFdTIpZNT8zEg1_u4jNw5leZOMsm4wnl5PZeJ6N02o8nqsLInJ1XmTois7HCj2xSYlx6vzqzC8YQ96tWmwa6Kbdb0JKvbJKDfRlh6riF2vpc2UTZ15y6c8Y9IIR_x9xOcs8">