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)65 auto 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)77 auto 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