Index: cfe/trunk/docs/ControlFlowIntegrity.rst =================================================================== --- cfe/trunk/docs/ControlFlowIntegrity.rst +++ cfe/trunk/docs/ControlFlowIntegrity.rst @@ -27,8 +27,8 @@ with CFI enabled, and are statically linked into the program. This may preclude the use of shared libraries in some cases. -Clang currently implements forward-edge CFI for virtual calls. More schemes -are under development. +Clang currently implements forward-edge CFI for member function calls and +bad cast checking. More schemes are under development. .. _gold plugin: http://llvm.org/docs/GoldPlugin.html @@ -38,11 +38,11 @@ This scheme checks that virtual calls take place using a vptr of the correct dynamic type; that is, the dynamic type of the called object must be a derived class of the static type of the object used to make the call. -This CFI scheme can be enabled on its own using ``-fsanitize=cfi-vptr``. +This CFI scheme can be enabled on its own using ``-fsanitize=cfi-vcall``. For this scheme to work, all translation units containing the definition of a virtual member function (whether inline or not) must be compiled -with ``-fsanitize=cfi-vptr`` enabled and be statically linked into the +with ``-fsanitize=cfi-vcall`` enabled and be statically linked into the program. Classes in the C++ standard library (under namespace ``std``) are exempted from checking, and therefore programs may be linked against a pre-built standard library, but this may change in the future. @@ -95,6 +95,23 @@ may be linked against a pre-built standard library, but this may change in the future. +Non-Virtual Member Function Call Checking +----------------------------------------- + +This scheme checks that non-virtual calls take place using an object of +the correct dynamic type; that is, the dynamic type of the called object +must be a derived class of the static type of the object used to make the +call. The checks are currently only introduced where the object is of a +polymorphic class type. This CFI scheme can be enabled on its own using +``-fsanitize=cfi-nvcall``. + +For this scheme to work, all translation units containing the definition +of a virtual member function (whether inline or not) must be compiled +with ``-fsanitize=cfi-nvcall`` enabled and be statically linked into the +program. Classes in the C++ standard library (under namespace ``std``) are +exempted from checking, and therefore programs may be linked against a +pre-built standard library, but this may change in the future. + .. _cfi-strictness: Strictness Index: cfe/trunk/docs/UsersManual.rst =================================================================== --- cfe/trunk/docs/UsersManual.rst +++ cfe/trunk/docs/UsersManual.rst @@ -974,7 +974,9 @@ dynamic type. Implies ``-flto``. - ``-fsanitize=cfi-unrelated-cast``: Cast from ``void*`` or another unrelated type to the wrong dynamic type. Implies ``-flto``. - - ``-fsanitize=cfi-vptr``: Use of an object whose vptr is of the + - ``-fsanitize=cfi-nvcall``: Non-virtual call via an object whose vptr is of + the wrong dynamic type. Implies ``-flto``. + - ``-fsanitize=cfi-vcall``: Virtual call via an object whose vptr is of the wrong dynamic type. Implies ``-flto``. - ``-fsanitize=enum``: Load of a value of an enumerated type which is not in the range of representable values for that enumerated Index: cfe/trunk/include/clang/Basic/Sanitizers.def =================================================================== --- cfe/trunk/include/clang/Basic/Sanitizers.def +++ cfe/trunk/include/clang/Basic/Sanitizers.def @@ -82,8 +82,10 @@ SANITIZER("cfi-cast-strict", CFICastStrict) SANITIZER("cfi-derived-cast", CFIDerivedCast) SANITIZER("cfi-unrelated-cast", CFIUnrelatedCast) -SANITIZER("cfi-vptr", CFIVptr) -SANITIZER_GROUP("cfi", CFI, CFIDerivedCast | CFIUnrelatedCast | CFIVptr) +SANITIZER("cfi-nvcall", CFINVCall) +SANITIZER("cfi-vcall", CFIVCall) +SANITIZER_GROUP("cfi", CFI, + CFIDerivedCast | CFIUnrelatedCast | CFINVCall | CFIVCall) // -fsanitize=undefined-trap includes sanitizers from -fsanitize=undefined // that can be used without runtime support, generally by providing extra Index: cfe/trunk/lib/CodeGen/CGClass.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGClass.cpp +++ cfe/trunk/lib/CodeGen/CGClass.cpp @@ -2088,14 +2088,6 @@ return VTable; } -void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXMethodDecl *MD, - llvm::Value *VTable) { - if (!SanOpts.has(SanitizerKind::CFIVptr)) - return; - - EmitVTablePtrCheck(MD->getParent(), VTable); -} - // If a class has a single non-virtual base and does not introduce or override // virtual member functions or fields, it will have the same layout as its base. // This function returns the least derived such class. @@ -2131,6 +2123,15 @@ RD->bases_begin()->getType()->getAsCXXRecordDecl()); } +void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXMethodDecl *MD, + llvm::Value *VTable) { + const CXXRecordDecl *ClassDecl = MD->getParent(); + if (!SanOpts.has(SanitizerKind::CFICastStrict)) + ClassDecl = LeastDerivedClassWithSameLayout(ClassDecl); + + EmitVTablePtrCheck(ClassDecl, VTable); +} + void CodeGenFunction::EmitVTablePtrCheckForCast(QualType T, llvm::Value *Derived, bool MayBeNull) { Index: cfe/trunk/lib/CodeGen/CGExprCXX.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGExprCXX.cpp +++ cfe/trunk/lib/CodeGen/CGExprCXX.cpp @@ -256,6 +256,12 @@ } else if (UseVirtualCall) { Callee = CGM.getCXXABI().getVirtualFunctionPointer(*this, MD, This, Ty); } else { + if (SanOpts.has(SanitizerKind::CFINVCall) && + MD->getParent()->isDynamicClass()) { + llvm::Value *VTable = GetVTablePtr(This, Int8PtrTy); + EmitVTablePtrCheckForCall(MD, VTable); + } + if (getLangOpts().AppleKext && MD->isVirtual() && HasQualifier) Callee = BuildAppleKextVirtualCall(MD, Qualifier, Ty); else if (!DevirtualizedMethod) Index: cfe/trunk/lib/CodeGen/CGVTables.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGVTables.cpp +++ cfe/trunk/lib/CodeGen/CGVTables.cpp @@ -842,7 +842,10 @@ void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable, const VTableLayout &VTLayout) { - if (!LangOpts.Sanitize.has(SanitizerKind::CFIVptr)) + if (!LangOpts.Sanitize.has(SanitizerKind::CFIVCall) && + !LangOpts.Sanitize.has(SanitizerKind::CFINVCall) && + !LangOpts.Sanitize.has(SanitizerKind::CFIDerivedCast) && + !LangOpts.Sanitize.has(SanitizerKind::CFIUnrelatedCast)) return; llvm::Metadata *VTableMD = llvm::ConstantAsMetadata::get(VTable); Index: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp +++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp @@ -1443,7 +1443,8 @@ Ty = Ty->getPointerTo()->getPointerTo(); llvm::Value *VTable = CGF.GetVTablePtr(This, Ty); - CGF.EmitVTablePtrCheckForCall(cast(GD.getDecl()), VTable); + if (CGF.SanOpts.has(SanitizerKind::CFIVCall)) + CGF.EmitVTablePtrCheckForCall(cast(GD.getDecl()), VTable); uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD); llvm::Value *VFuncPtr = Index: cfe/trunk/lib/Driver/SanitizerArgs.cpp =================================================================== --- cfe/trunk/lib/Driver/SanitizerArgs.cpp +++ cfe/trunk/lib/Driver/SanitizerArgs.cpp @@ -48,7 +48,7 @@ RecoverableByDefault = Undefined | Integer, Unrecoverable = Address | Unreachable | Return, LegacyFsanitizeRecoverMask = Undefined | Integer, - NeedsLTO = CFIDerivedCast | CFIUnrelatedCast | CFIVptr, + NeedsLTO = CFI, }; } Index: cfe/trunk/test/CodeGenCXX/cfi-nvcall.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/cfi-nvcall.cpp +++ cfe/trunk/test/CodeGenCXX/cfi-nvcall.cpp @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-nvcall -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-nvcall,cfi-cast-strict -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-STRICT %s + +struct A { + virtual void f(); +}; + +struct B : A { + int i; + void g(); +}; + +struct C : A { + void g(); +}; + +// CHECK-LABEL: @bg +// CHECK-STRICT-LABEL: @bg +extern "C" void bg(B *b) { + // CHECK: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") + // CHECK-STRICT: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") + b->g(); +} + +// CHECK-LABEL: @cg +// CHECK-STRICT-LABEL: @cg +extern "C" void cg(C *c) { + // http://clang.llvm.org/docs/ControlFlowIntegrity.html#strictness + // In this case C's layout is the same as its base class, so we allow + // c to be of type A in non-strict mode. + + // CHECK: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1A") + // CHECK-STRICT: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1C") + c->g(); +} Index: cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp +++ cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck %s + +struct A { + A(); + virtual void f(); +}; + +struct B : virtual A { + B(); +}; + +struct C : virtual A { + C(); +}; + +namespace { + +struct D : B, C { + D(); + virtual void f(); +}; + +} + +A::A() {} +B::B() {} +C::C() {} +D::D() {} + +void A::f() { +} + +void D::f() { +} + +// CHECK: define void @_Z2afP1A +void af(A *a) { + // CHECK: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1A") + // CHECK-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ]*]] + + // CHECK: [[TRAPBB]] + // CHECK-NEXT: call void @llvm.trap() + // CHECK-NEXT: unreachable + + // CHECK: [[CONTBB]] + // CHECK: call void % + a->f(); +} + +// CHECK: define internal void @_Z2dfPN12_GLOBAL__N_11DE +void df(D *d) { + // CHECK: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE") + d->f(); +} + +D d; + +void foo() { + df(&d); +} + +// CHECK-DAG: !{!"1A", [3 x i8*]* @_ZTV1A, i64 16} +// CHECK-DAG: !{!"1A", [5 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} +// CHECK-DAG: !{!"1B", [5 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} +// CHECK-DAG: !{!"1A", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64} +// CHECK-DAG: !{!"1C", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32} +// CHECK-DAG: !{!"1A", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// CHECK-DAG: !{!"1B", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// CHECK-DAG: !{!"1C", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 72} +// CHECK-DAG: !{!"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// CHECK-DAG: !{!"1A", [5 x i8*]* @_ZTV1B, i64 32} +// CHECK-DAG: !{!"1B", [5 x i8*]* @_ZTV1B, i64 32} +// CHECK-DAG: !{!"1A", [5 x i8*]* @_ZTV1C, i64 32} +// CHECK-DAG: !{!"1C", [5 x i8*]* @_ZTV1C, i64 32} Index: cfe/trunk/test/CodeGenCXX/cfi-vptr.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/cfi-vptr.cpp +++ cfe/trunk/test/CodeGenCXX/cfi-vptr.cpp @@ -1,74 +0,0 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vptr -emit-llvm -o - %s | FileCheck %s - -struct A { - A(); - virtual void f(); -}; - -struct B : virtual A { - B(); -}; - -struct C : virtual A { - C(); -}; - -namespace { - -struct D : B, C { - D(); - virtual void f(); -}; - -} - -A::A() {} -B::B() {} -C::C() {} -D::D() {} - -void A::f() { -} - -void D::f() { -} - -// CHECK: define void @_Z2afP1A -void af(A *a) { - // CHECK: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1A") - // CHECK-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ]*]] - - // CHECK: [[TRAPBB]] - // CHECK-NEXT: call void @llvm.trap() - // CHECK-NEXT: unreachable - - // CHECK: [[CONTBB]] - // CHECK: call void % - a->f(); -} - -// CHECK: define internal void @_Z2dfPN12_GLOBAL__N_11DE -void df(D *d) { - // CHECK: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vptr.cpp]N12_GLOBAL__N_11DE") - d->f(); -} - -D d; - -void foo() { - df(&d); -} - -// CHECK-DAG: !{!"1A", [3 x i8*]* @_ZTV1A, i64 16} -// CHECK-DAG: !{!"1A", [5 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} -// CHECK-DAG: !{!"1B", [5 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} -// CHECK-DAG: !{!"1A", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64} -// CHECK-DAG: !{!"1C", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32} -// CHECK-DAG: !{!"1A", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} -// CHECK-DAG: !{!"1B", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} -// CHECK-DAG: !{!"1C", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 72} -// CHECK-DAG: !{!"[{{.*}}cfi-vptr.cpp]N12_GLOBAL__N_11DE", [10 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} -// CHECK-DAG: !{!"1A", [5 x i8*]* @_ZTV1B, i64 32} -// CHECK-DAG: !{!"1B", [5 x i8*]* @_ZTV1B, i64 32} -// CHECK-DAG: !{!"1A", [5 x i8*]* @_ZTV1C, i64 32} -// CHECK-DAG: !{!"1C", [5 x i8*]* @_ZTV1C, i64 32} Index: cfe/trunk/test/Driver/fsanitize.c =================================================================== --- cfe/trunk/test/Driver/fsanitize.c +++ cfe/trunk/test/Driver/fsanitize.c @@ -206,11 +206,13 @@ // RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI // RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-derived-cast -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-DCAST // RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-unrelated-cast -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-UCAST -// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-vptr -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-VPTR -// CHECK-CFI: -emit-llvm-bc{{.*}}-fsanitize=cfi-derived-cast,cfi-unrelated-cast,cfi-vptr +// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-nvcall -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-NVCALL +// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-vcall -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-VCALL +// CHECK-CFI: -emit-llvm-bc{{.*}}-fsanitize=cfi-derived-cast,cfi-unrelated-cast,cfi-nvcall,cfi-vcall // CHECK-CFI-DCAST: -emit-llvm-bc{{.*}}-fsanitize=cfi-derived-cast // CHECK-CFI-UCAST: -emit-llvm-bc{{.*}}-fsanitize=cfi-unrelated-cast -// CHECK-CFI-VPTR: -emit-llvm-bc{{.*}}-fsanitize=cfi-vptr +// CHECK-CFI-NVCALL: -emit-llvm-bc{{.*}}-fsanitize=cfi-nvcall +// CHECK-CFI-VCALL: -emit-llvm-bc{{.*}}-fsanitize=cfi-vcall // RUN: %clang_cl -fsanitize=address -c -MDd -### -- %s 2>&1 | FileCheck %s -check-prefix=CHECK-ASAN-DEBUGRTL // RUN: %clang_cl -fsanitize=address -c -MTd -### -- %s 2>&1 | FileCheck %s -check-prefix=CHECK-ASAN-DEBUGRTL