1 //===-- mem_map_base.h ------------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef SCUDO_MEM_MAP_BASE_H_ 10 #define SCUDO_MEM_MAP_BASE_H_ 11 12 #include "common.h" 13 14 namespace scudo { 15 16 // In Scudo, every memory operation will be fulfilled through a 17 // platform-specific `MemMap` instance. The essential APIs are listed in the 18 // `MemMapBase` below. This is implemented in CRTP, so for each implementation, 19 // it has to implement all of the 'Impl' named functions. 20 template <class Derived> class MemMapBase { 21 public: 22 constexpr MemMapBase() = default; 23 24 // This is used to map a new set of contiguous pages. Note that the `Addr` is 25 // only a suggestion to the system. 26 bool map(uptr Addr, uptr Size, const char *Name, uptr Flags = 0) { 27 DCHECK(!isAllocated()); 28 return invokeImpl(&Derived::mapImpl, Addr, Size, Name, Flags); 29 } 30 31 // This is used to unmap partial/full pages from the beginning or the end. 32 // I.e., the result pages are expected to be still contiguous. unmap(uptr Addr,uptr Size)33 void unmap(uptr Addr, uptr Size) { 34 DCHECK(isAllocated()); 35 DCHECK((Addr == getBase()) || (Addr + Size == getBase() + getCapacity())); 36 invokeImpl(&Derived::unmapImpl, Addr, Size); 37 } 38 39 // This is used to remap a mapped range (either from map() or dispatched from 40 // ReservedMemory). For example, we have reserved several pages and then we 41 // want to remap them with different accessibility. 42 bool remap(uptr Addr, uptr Size, const char *Name, uptr Flags = 0) { 43 DCHECK(isAllocated()); 44 DCHECK((Addr >= getBase()) && (Addr + Size <= getBase() + getCapacity())); 45 return invokeImpl(&Derived::remapImpl, Addr, Size, Name, Flags); 46 } 47 48 // This is used to update the pages' access permission. For example, mark 49 // pages as no read/write permission. setMemoryPermission(uptr Addr,uptr Size,uptr Flags)50 void setMemoryPermission(uptr Addr, uptr Size, uptr Flags) { 51 DCHECK(isAllocated()); 52 DCHECK((Addr >= getBase()) && (Addr + Size <= getBase() + getCapacity())); 53 return invokeImpl(&Derived::setMemoryPermissionImpl, Addr, Size, Flags); 54 } 55 56 // Suggest releasing a set of contiguous physical pages back to the OS. Note 57 // that only physical pages are supposed to be released. Any release of 58 // virtual pages may lead to undefined behavior. releasePagesToOS(uptr From,uptr Size)59 void releasePagesToOS(uptr From, uptr Size) { 60 DCHECK(isAllocated()); 61 DCHECK((From >= getBase()) && (From + Size <= getBase() + getCapacity())); 62 invokeImpl(&Derived::releasePagesToOSImpl, From, Size); 63 } 64 // This is similar to the above one except that any subsequent access to the 65 // released pages will return with zero-filled pages. releaseAndZeroPagesToOS(uptr From,uptr Size)66 void releaseAndZeroPagesToOS(uptr From, uptr Size) { 67 DCHECK(isAllocated()); 68 DCHECK((From >= getBase()) && (From + Size <= getBase() + getCapacity())); 69 invokeImpl(&Derived::releaseAndZeroPagesToOSImpl, From, Size); 70 } 71 getBase()72 uptr getBase() { return invokeImpl(&Derived::getBaseImpl); } getCapacity()73 uptr getCapacity() { return invokeImpl(&Derived::getCapacityImpl); } 74 isAllocated()75 bool isAllocated() { return getBase() != 0U; } 76 77 protected: 78 template <typename R, typename... Args> invokeImpl(R (Derived::* MemFn)(Args...),Args...args)79 R invokeImpl(R (Derived::*MemFn)(Args...), Args... args) { 80 return (static_cast<Derived *>(this)->*MemFn)(args...); 81 } 82 }; 83 84 // `ReservedMemory` is a special memory handle which can be viewed as a page 85 // allocator. `ReservedMemory` will reserve a contiguous pages and the later 86 // page request can be fulfilled at the designated address. This is used when 87 // we want to ensure the virtual address of the MemMap will be in a known range. 88 // This is implemented in CRTP, so for each 89 // implementation, it has to implement all of the 'Impl' named functions. 90 template <class Derived, typename MemMapTy> class ReservedMemory { 91 public: 92 using MemMapT = MemMapTy; 93 constexpr ReservedMemory() = default; 94 95 // Reserve a chunk of memory at a suggested address. 96 bool create(uptr Addr, uptr Size, const char *Name, uptr Flags = 0) { 97 DCHECK(!isCreated()); 98 return invokeImpl(&Derived::createImpl, Addr, Size, Name, Flags); 99 } 100 101 // Release the entire reserved memory. release()102 void release() { 103 DCHECK(isCreated()); 104 invokeImpl(&Derived::releaseImpl); 105 } 106 107 // Dispatch a sub-range of reserved memory. Note that any fragmentation of 108 // the reserved pages is managed by each implementation. dispatch(uptr Addr,uptr Size)109 MemMapT dispatch(uptr Addr, uptr Size) { 110 DCHECK(isCreated()); 111 DCHECK((Addr >= getBase()) && (Addr + Size <= getBase() + getCapacity())); 112 return invokeImpl(&Derived::dispatchImpl, Addr, Size); 113 } 114 getBase()115 uptr getBase() { return invokeImpl(&Derived::getBaseImpl); } getCapacity()116 uptr getCapacity() { return invokeImpl(&Derived::getCapacityImpl); } 117 isCreated()118 bool isCreated() { return getBase() != 0U; } 119 120 protected: 121 template <typename R, typename... Args> invokeImpl(R (Derived::* MemFn)(Args...),Args...args)122 R invokeImpl(R (Derived::*MemFn)(Args...), Args... args) { 123 return (static_cast<Derived *>(this)->*MemFn)(args...); 124 } 125 }; 126 127 } // namespace scudo 128 129 #endif // SCUDO_MEM_MAP_BASE_H_ 130