// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Helper routines to call function pointers stored in protected memory with // Control Flow Integrity indirect call checking disabled. Some indirect calls, // e.g. dynamically resolved symbols in another DSO, can not be accounted for by // CFI-icall. These routines allow those symbols to be called without CFI-icall // checking safely by ensuring that they are placed in protected memory. #ifndef BASE_MEMORY_PROTECTED_MEMORY_CFI_H_ #define BASE_MEMORY_PROTECTED_MEMORY_CFI_H_ #include #include "base/cfi_buildflags.h" #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/protected_memory.h" #include "build/build_config.h" #if BUILDFLAG(CFI_ICALL_CHECK) && !PROTECTED_MEMORY_ENABLED #error "CFI-icall enabled for platform without protected memory support" #endif // BUILDFLAG(CFI_ICALL_CHECK) && !PROTECTED_MEMORY_ENABLED namespace base { namespace internal { // This class is used to exempt calls to function pointers stored in // ProtectedMemory from cfi-icall checking. It's not secure to use directly, it // should only be used by the UnsanitizedCfiCall() functions below. Given an // UnsanitizedCfiCall object, you can use operator() to call the encapsulated // function pointer without cfi-icall checking. template class UnsanitizedCfiCall { public: explicit UnsanitizedCfiCall(FunctionType function) : function_(function) {} UnsanitizedCfiCall(UnsanitizedCfiCall&&) = default; template NO_SANITIZE("cfi-icall") auto operator()(Args&&... args) { return function_(std::forward(args)...); } private: FunctionType function_; DISALLOW_IMPLICIT_CONSTRUCTORS(UnsanitizedCfiCall); }; } // namespace internal // These functions can be used to call function pointers in ProtectedMemory // without cfi-icall checking. They are intended to be used to create an // UnsanitizedCfiCall object and immediately call it. UnsanitizedCfiCall objects // should not initialized directly or stored because they hold a function // pointer that will be called without CFI-icall checking in mutable memory. The // functions can be used as shown below: // ProtectedMemory p; // UnsanitizedCfiCall(p)(5); /* In place of (*p)(5); */ template auto UnsanitizedCfiCall(const ProtectedMemory& PM) { #if PROTECTED_MEMORY_ENABLED DCHECK(&PM >= ProtectedMemoryStart && &PM < ProtectedMemoryEnd); #endif // PROTECTED_MEMORY_ENABLED return internal::UnsanitizedCfiCall(*PM); } // struct S { void (*fp)(int); } s; // ProtectedMemory p; // UnsanitizedCfiCall(p, &S::fp)(5); /* In place of p->fp(5); */ template auto UnsanitizedCfiCall(const ProtectedMemory& PM, Member member) { #if PROTECTED_MEMORY_ENABLED DCHECK(&PM >= ProtectedMemoryStart && &PM < ProtectedMemoryEnd); #endif // PROTECTED_MEMORY_ENABLED return internal::UnsanitizedCfiCall(*PM.*member); } } // namespace base #endif // BASE_MEMORY_PROTECTED_MEMORY_CFI_H_