-fsanitize-coverage=control-flow does not sign entries into basic blocks on arm64e. This test compares a local pointer to a function [signed] with the basic block pointer. Because the entry into the basic block is unsigned the addresses being compared are signed and unsigned, causing the path never to be taken. This is a "bandaid" to get this test passing. We strip the signed bits from the pointer to the local functions so that the comparisons pass. Filed radar: rdar://103042879 to note the behavior. context: https://github.com/llvm/llvm-project/blob/main/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp#L1068 // blockaddress can not be used on function's entry block. if (&BB == &F.getEntryBlock()) CFs.push_back((Constant *)IRB.CreatePointerCast(&F, IntptrPtrTy)); else CFs.push_back((Constant *)IRB.CreatePointerCast(BlockAddress::get(&BB), IntptrPtrTy)); BlockAddress::get is responsible for signing the pointer. Because of: https://reviews.llvm.org/D133157 rdar://103042879 Differential Revision: https://reviews.llvm.org/D139661
81 lines
1.9 KiB
C++
81 lines
1.9 KiB
C++
// Tests -fsanitize-coverage=control-flow.
|
|
|
|
// REQUIRES: has_sancovcc,stable-runtime
|
|
// UNSUPPORTED: i386-darwin, x86_64-darwin
|
|
|
|
// RUN: %clangxx -O0 -std=c++11 -fsanitize-coverage=control-flow %s -o %t
|
|
// RUN: %run %t 2>&1 | FileCheck %s
|
|
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#if __has_feature(ptrauth_calls)
|
|
#include <ptrauth.h>
|
|
#else
|
|
#define ptrauth_strip(__value, __key) (__value)
|
|
#endif
|
|
|
|
uintptr_t *CFS_BEG, *CFS_END;
|
|
|
|
extern "C" void __sanitizer_cov_cfs_init(const uintptr_t *cfs_beg,
|
|
const uintptr_t *cfs_end) {
|
|
CFS_BEG = (uintptr_t *)cfs_beg;
|
|
CFS_END = (uintptr_t *)cfs_end;
|
|
}
|
|
|
|
__attribute__((noinline)) void foo(int x) { /* empty body */
|
|
}
|
|
|
|
void check_cfs_section(uintptr_t main_ptr, uintptr_t foo_ptr) {
|
|
printf("Control Flow section boundaries: [%p %p)\n", CFS_BEG, CFS_END);
|
|
uintptr_t *pt = CFS_BEG;
|
|
uintptr_t currBB;
|
|
|
|
while (pt < CFS_END) {
|
|
currBB = *pt;
|
|
pt++;
|
|
|
|
if (currBB == main_ptr)
|
|
printf("Saw the main().\n");
|
|
else if (currBB == foo_ptr)
|
|
printf("Saw the foo().\n");
|
|
|
|
// Iterate over successors.
|
|
while (*pt) {
|
|
pt++;
|
|
}
|
|
pt++;
|
|
// Iterate over callees.
|
|
while (*pt) {
|
|
if (*pt == foo_ptr && currBB != main_ptr)
|
|
printf("Direct call matched.\n");
|
|
if (*pt == -1 && currBB != main_ptr)
|
|
printf("Indirect call matched.\n");
|
|
pt++;
|
|
}
|
|
pt++;
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
auto main_ptr = ptrauth_strip(&main, ptrauth_key_function_pointer);
|
|
auto foo_ptr = ptrauth_strip(&foo, ptrauth_key_function_pointer);
|
|
int x = 10;
|
|
|
|
if (x > 0)
|
|
foo(x);
|
|
else
|
|
(*foo_ptr)(x);
|
|
|
|
check_cfs_section((uintptr_t)(*main_ptr), (uintptr_t)(*foo_ptr));
|
|
|
|
printf("Finished!\n");
|
|
return 0;
|
|
}
|
|
|
|
// CHECK: Control Flow section boundaries
|
|
// CHECK-DAG: Saw the foo().
|
|
// CHECK-DAG: Saw the main().
|
|
// CHECK-DAG: Direct call matched.
|
|
// CHECK-DAG: Indirect call matched.
|
|
// CHECK: Finished!
|