blob: a90023bc81f4ab158c773d1d30e1bc149b8dbd0e [file] [log] [blame]
// 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 <utility>
#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 <typename FunctionType>
class UnsanitizedCfiCall {
public:
explicit UnsanitizedCfiCall(FunctionType function) : function_(function) {}
UnsanitizedCfiCall(UnsanitizedCfiCall&&) = default;
template <typename... Args>
NO_SANITIZE("cfi-icall")
auto operator()(Args&&... args) {
return function_(std::forward<Args>(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<void (*)(int)> p;
// UnsanitizedCfiCall(p)(5); /* In place of (*p)(5); */
template <typename T>
auto UnsanitizedCfiCall(const ProtectedMemory<T>& PM) {
#if PROTECTED_MEMORY_ENABLED
DCHECK(&PM >= ProtectedMemoryStart && &PM < ProtectedMemoryEnd);
#endif // PROTECTED_MEMORY_ENABLED
return internal::UnsanitizedCfiCall<T>(*PM);
}
// struct S { void (*fp)(int); } s;
// ProtectedMemory<S> p;
// UnsanitizedCfiCall(p, &S::fp)(5); /* In place of p->fp(5); */
template <typename T, typename Member>
auto UnsanitizedCfiCall(const ProtectedMemory<T>& PM, Member member) {
#if PROTECTED_MEMORY_ENABLED
DCHECK(&PM >= ProtectedMemoryStart && &PM < ProtectedMemoryEnd);
#endif // PROTECTED_MEMORY_ENABLED
return internal::UnsanitizedCfiCall<decltype(*PM.*member)>(*PM.*member);
}
} // namespace base
#endif // BASE_MEMORY_PROTECTED_MEMORY_CFI_H_