1 // Copyright 2017 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Helper routines to call function pointers stored in protected memory with 6 // Control Flow Integrity indirect call checking disabled. Some indirect calls, 7 // e.g. dynamically resolved symbols in another DSO, can not be accounted for by 8 // CFI-icall. These routines allow those symbols to be called without CFI-icall 9 // checking safely by ensuring that they are placed in protected memory. 10 11 #ifndef BASE_MEMORY_PROTECTED_MEMORY_CFI_H_ 12 #define BASE_MEMORY_PROTECTED_MEMORY_CFI_H_ 13 14 #include <utility> 15 16 #include "base/cfi_buildflags.h" 17 #include "base/compiler_specific.h" 18 #include "base/macros.h" 19 #include "base/memory/protected_memory.h" 20 #include "build/build_config.h" 21 22 #if BUILDFLAG(CFI_ICALL_CHECK) && !PROTECTED_MEMORY_ENABLED 23 #error "CFI-icall enabled for platform without protected memory support" 24 #endif // BUILDFLAG(CFI_ICALL_CHECK) && !PROTECTED_MEMORY_ENABLED 25 26 namespace base { 27 namespace internal { 28 29 // This class is used to exempt calls to function pointers stored in 30 // ProtectedMemory from cfi-icall checking. It's not secure to use directly, it 31 // should only be used by the UnsanitizedCfiCall() functions below. Given an 32 // UnsanitizedCfiCall object, you can use operator() to call the encapsulated 33 // function pointer without cfi-icall checking. 34 template <typename FunctionType> 35 class UnsanitizedCfiCall { 36 public: UnsanitizedCfiCall(FunctionType function)37 explicit UnsanitizedCfiCall(FunctionType function) : function_(function) {} 38 UnsanitizedCfiCall(UnsanitizedCfiCall&&) = default; 39 40 template <typename... Args> 41 NO_SANITIZE("cfi-icall") operator()42 auto operator()(Args&&... args) { 43 return function_(std::forward<Args>(args)...); 44 } 45 46 private: 47 FunctionType function_; 48 49 DISALLOW_IMPLICIT_CONSTRUCTORS(UnsanitizedCfiCall); 50 }; 51 52 } // namespace internal 53 54 // These functions can be used to call function pointers in ProtectedMemory 55 // without cfi-icall checking. They are intended to be used to create an 56 // UnsanitizedCfiCall object and immediately call it. UnsanitizedCfiCall objects 57 // should not initialized directly or stored because they hold a function 58 // pointer that will be called without CFI-icall checking in mutable memory. The 59 // functions can be used as shown below: 60 61 // ProtectedMemory<void (*)(int)> p; 62 // UnsanitizedCfiCall(p)(5); /* In place of (*p)(5); */ 63 64 template <typename T> UnsanitizedCfiCall(const ProtectedMemory<T> & PM)65auto UnsanitizedCfiCall(const ProtectedMemory<T>& PM) { 66 #if PROTECTED_MEMORY_ENABLED 67 DCHECK(&PM >= ProtectedMemoryStart && &PM < ProtectedMemoryEnd); 68 #endif // PROTECTED_MEMORY_ENABLED 69 return internal::UnsanitizedCfiCall<T>(*PM); 70 } 71 72 // struct S { void (*fp)(int); } s; 73 // ProtectedMemory<S> p; 74 // UnsanitizedCfiCall(p, &S::fp)(5); /* In place of p->fp(5); */ 75 76 template <typename T, typename Member> UnsanitizedCfiCall(const ProtectedMemory<T> & PM,Member member)77auto UnsanitizedCfiCall(const ProtectedMemory<T>& PM, Member member) { 78 #if PROTECTED_MEMORY_ENABLED 79 DCHECK(&PM >= ProtectedMemoryStart && &PM < ProtectedMemoryEnd); 80 #endif // PROTECTED_MEMORY_ENABLED 81 return internal::UnsanitizedCfiCall<decltype(*PM.*member)>(*PM.*member); 82 } 83 84 } // namespace base 85 86 #endif // BASE_MEMORY_PROTECTED_MEMORY_CFI_H_ 87