Index: lib/ubsan/ubsan_handlers.h =================================================================== --- lib/ubsan/ubsan_handlers.h +++ lib/ubsan/ubsan_handlers.h @@ -140,6 +140,14 @@ /// \brief Handle passing null pointer to function with nonnull attribute. RECOVERABLE(nonnull_arg, NonNullArgData *Data) +struct CFIBadIcallData { + SourceLocation Loc; + const TypeDescriptor &Type; +}; + +/// \brief Handle control flow integrity failure for indirect function calls. +RECOVERABLE(cfi_bad_icall, CFIBadIcallData *Data, ValueHandle Function) + } #endif // UBSAN_HANDLERS_H Index: lib/ubsan/ubsan_handlers.cc =================================================================== --- lib/ubsan/ubsan_handlers.cc +++ lib/ubsan/ubsan_handlers.cc @@ -422,4 +422,33 @@ Die(); } +static void HandleCFIBadIcall(CFIBadIcallData *Data, ValueHandle Function, + ReportOptions Opts) { + SourceLocation Loc = Data->Loc.acquire(); + ScopedReport R(Opts, Loc); + + Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during " + "indirect function call") + << Data->Type; + + SymbolizedStackHolder FLoc(getSymbolizedLocation(Function)); + const char *FName = FLoc.get()->info.function; + if (!FName) + FName = "(unknown)"; + Diag(FLoc, DL_Note, "%0 defined here") << FName; +} + +void __ubsan::__ubsan_handle_cfi_bad_icall(CFIBadIcallData *Data, + ValueHandle Function) { + GET_REPORT_OPTIONS(false); + HandleCFIBadIcall(Data, Function, Opts); +} + +void __ubsan::__ubsan_handle_cfi_bad_icall_abort(CFIBadIcallData *Data, + ValueHandle Function) { + GET_REPORT_OPTIONS(true); + HandleCFIBadIcall(Data, Function, Opts); + Die(); +} + #endif // CAN_SANITIZE_UB Index: test/cfi/bad-signature.c =================================================================== --- /dev/null +++ test/cfi/bad-signature.c @@ -0,0 +1,26 @@ +// RUN: %clangxx -o %t1 %s +// RUN: %t1 2>&1 | FileCheck --check-prefix=NCFI %s + +// RUN: %clangxx_cfi -o %t2 %s +// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s + +// RUN: %clangxx_cfi_diag -o %t3 %s +// RUN: %t3 2>&1 | FileCheck --check-prefix=CFI-DIAG %s + +#include + +void f() { +} + +int main() { + // CFI: 1 + // NCFI: 1 + fprintf(stderr, "1\n"); + + // CFI-DIAG: runtime error: control flow integrity check for type 'void (int)' failed during indirect function call + ((void (*)(int))f)(42); // UB here + + // CFI-NOT: 2 + // NCFI: 2 + fprintf(stderr, "2\n"); +} Index: test/cfi/external-call.c =================================================================== --- /dev/null +++ test/cfi/external-call.c @@ -0,0 +1,23 @@ +// RUN: %clangxx_cfi -o %t1 %s +// RUN: %t1 c 1 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %t1 s 2 2>&1 | FileCheck --check-prefix=CFI %s + +#include +#include +#include + +int main(int argc, char **argv) { + // CFI: 1 + fprintf(stderr, "1\n"); + + double (*fn)(double); + if (argv[1][0] == 's') + fn = sin; + else + fn = cos; + + fn(atof(argv[2])); + + // CFI: 2 + fprintf(stderr, "2\n"); +} Index: test/cfi/lit.cfg =================================================================== --- test/cfi/lit.cfg +++ test/cfi/lit.cfg @@ -2,7 +2,7 @@ import os config.name = 'cfi' -config.suffixes = ['.cpp', '.test'] +config.suffixes = ['.c', '.cpp', '.test'] config.test_source_root = os.path.dirname(__file__) clangxx = ' '.join([config.clang] + config.cxx_mode_flags)