1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_interceptor_VMSharingPolicies_h 8 #define mozilla_interceptor_VMSharingPolicies_h 9 10 #include "mozilla/Assertions.h" 11 #include "mozilla/Attributes.h" 12 #include "mozilla/Maybe.h" 13 #include "mozilla/Types.h" 14 15 namespace mozilla { 16 namespace interceptor { 17 18 /** 19 * This class is an abstraction of a reservation of virtual address space that 20 * has been obtained from a VMSharingPolicy via the policy's |Reserve| method. 21 * 22 * TrampolinePool allows us to obtain a trampoline without needing to concern 23 * ourselves with the underlying implementation of the VM sharing policy. 24 * 25 * For example, VMSharingPolicyShared delegates to VMSharingPolicyUnique, but 26 * also requires taking a lock before doing so. By invoking |GetNextTrampoline| 27 * on a TrampolinePool, the caller does not need to concern themselves with 28 * that detail. 29 * 30 * We accompolish this with a recursive implementation that provides an inner 31 * TrampolinePool that is provided by the delegated VMSharingPolicy. 32 */ 33 template <typename VMPolicyT, typename InnerT> 34 class MOZ_STACK_CLASS TrampolinePool final { 35 public: 36 TrampolinePool(TrampolinePool&& aOther) = default; 37 TrampolinePool(VMPolicyT & aVMPolicy,InnerT && aInner)38 TrampolinePool(VMPolicyT& aVMPolicy, InnerT&& aInner) 39 : mVMPolicy(aVMPolicy), mInner(std::move(aInner)) {} 40 41 TrampolinePool& operator=(TrampolinePool&& aOther) = delete; 42 TrampolinePool(const TrampolinePool&) = delete; 43 TrampolinePool& operator=(const TrampolinePool&) = delete; 44 45 using MMPolicyT = typename VMPolicyT::MMPolicyT; 46 GetNextTrampoline()47 Maybe<Trampoline<MMPolicyT>> GetNextTrampoline() { 48 return mVMPolicy.GetNextTrampoline(mInner); 49 } 50 51 #if defined(_M_X64) IsInLowest2GB()52 bool IsInLowest2GB() const { 53 return mVMPolicy.IsTrampolineSpaceInLowest2GB(mInner); 54 } 55 #endif // defined(_M_X64) 56 57 private: 58 VMPolicyT& mVMPolicy; 59 InnerT mInner; 60 }; 61 62 /** 63 * This specialization is the base case for TrampolinePool, and is used by 64 * VMSharingPolicyUnique (since that policy does not delegate anything). 65 */ 66 template <typename VMPolicyT> 67 class MOZ_STACK_CLASS TrampolinePool<VMPolicyT, decltype(nullptr)> final { 68 public: TrampolinePool(VMPolicyT & aVMPolicy)69 explicit TrampolinePool(VMPolicyT& aVMPolicy) : mVMPolicy(aVMPolicy) {} 70 71 TrampolinePool(TrampolinePool&& aOther) = default; 72 73 TrampolinePool& operator=(TrampolinePool&& aOther) = delete; 74 TrampolinePool(const TrampolinePool&) = delete; 75 TrampolinePool& operator=(const TrampolinePool&) = delete; 76 77 using MMPolicyT = typename VMPolicyT::MMPolicyT; 78 GetNextTrampoline()79 Maybe<Trampoline<MMPolicyT>> GetNextTrampoline() { 80 return mVMPolicy.GetNextTrampoline(); 81 } 82 83 #if defined(_M_X64) IsInLowest2GB()84 bool IsInLowest2GB() const { 85 return mVMPolicy.IsTrampolineSpaceInLowest2GB(); 86 } 87 #endif // defined(_M_X64) 88 89 private: 90 VMPolicyT& mVMPolicy; 91 }; 92 93 template <typename MMPolicy> 94 class VMSharingPolicyUnique : public MMPolicy { 95 using ThisType = VMSharingPolicyUnique<MMPolicy>; 96 97 public: 98 using PoolType = TrampolinePool<ThisType, decltype(nullptr)>; 99 100 template <typename... Args> VMSharingPolicyUnique(Args &&...aArgs)101 explicit VMSharingPolicyUnique(Args&&... aArgs) 102 : MMPolicy(std::forward<Args>(aArgs)...), mNextChunkIndex(0) {} 103 Reserve(const uintptr_t aPivotAddr,const uint32_t aMaxDistanceFromPivot)104 Maybe<PoolType> Reserve(const uintptr_t aPivotAddr, 105 const uint32_t aMaxDistanceFromPivot) { 106 // Win32 allocates VM addresses at a 64KiB granularity, so we might as well 107 // utilize that entire 64KiB reservation. 108 uint32_t len = MMPolicy::GetAllocGranularity(); 109 110 Maybe<Span<const uint8_t>> maybeBounds = MMPolicy::SpanFromPivotAndDistance( 111 len, aPivotAddr, aMaxDistanceFromPivot); 112 113 return Reserve(len, maybeBounds); 114 } 115 Reserve(const uint32_t aSize,const Maybe<Span<const uint8_t>> & aBounds)116 Maybe<PoolType> Reserve(const uint32_t aSize, 117 const Maybe<Span<const uint8_t>>& aBounds) { 118 uint32_t bytesReserved = MMPolicy::Reserve(aSize, aBounds); 119 if (!bytesReserved) { 120 return Nothing(); 121 } 122 123 return Some(PoolType(*this)); 124 } 125 Items()126 TrampolineCollection<MMPolicy> Items() const { 127 return TrampolineCollection<MMPolicy>(*this, this->GetLocalView(), 128 this->GetRemoteView(), kChunkSize, 129 mNextChunkIndex); 130 } 131 Clear()132 void Clear() { mNextChunkIndex = 0; } 133 134 ~VMSharingPolicyUnique() = default; 135 136 VMSharingPolicyUnique(const VMSharingPolicyUnique&) = delete; 137 VMSharingPolicyUnique& operator=(const VMSharingPolicyUnique&) = delete; 138 VMSharingPolicyUnique(VMSharingPolicyUnique && aOther)139 VMSharingPolicyUnique(VMSharingPolicyUnique&& aOther) 140 : MMPolicy(std::move(aOther)), mNextChunkIndex(aOther.mNextChunkIndex) { 141 aOther.mNextChunkIndex = 0; 142 } 143 144 VMSharingPolicyUnique& operator=(VMSharingPolicyUnique&& aOther) { 145 static_cast<MMPolicy&>(*this) = std::move(aOther); 146 mNextChunkIndex = aOther.mNextChunkIndex; 147 aOther.mNextChunkIndex = 0; 148 return *this; 149 } 150 151 protected: 152 // In VMSharingPolicyUnique we do not implement the overload that accepts 153 // an inner trampoline pool, as this policy is expected to be the 154 // implementation of the base case. GetNextTrampoline()155 Maybe<Trampoline<MMPolicy>> GetNextTrampoline() { 156 uint32_t offset = mNextChunkIndex * kChunkSize; 157 if (!this->MaybeCommitNextPage(offset, kChunkSize)) { 158 return Nothing(); 159 } 160 161 Trampoline<MMPolicy> result(this, this->GetLocalView() + offset, 162 this->GetRemoteView() + offset, kChunkSize); 163 if (!!result) { 164 ++mNextChunkIndex; 165 } 166 167 return Some(std::move(result)); 168 } 169 170 private: 171 uint32_t mNextChunkIndex; 172 static const uint32_t kChunkSize = 128; 173 174 template <typename VMPolicyT, typename FriendT> 175 friend class TrampolinePool; 176 }; 177 178 } // namespace interceptor 179 } // namespace mozilla 180 181 // We don't include RangeMap.h until this point because it depends on the 182 // TrampolinePool definitions from above. 183 #include "mozilla/interceptor/RangeMap.h" 184 185 namespace mozilla { 186 namespace interceptor { 187 188 // We only support this policy for in-proc MMPolicy. 189 class MOZ_TRIVIAL_CTOR_DTOR VMSharingPolicyShared : public MMPolicyInProcess { 190 typedef VMSharingPolicyUnique<MMPolicyInProcess> UniquePolicyT; 191 typedef VMSharingPolicyShared ThisType; 192 193 public: 194 using PoolType = TrampolinePool<ThisType, UniquePolicyT::PoolType>; 195 using MMPolicyT = MMPolicyInProcess; 196 VMSharingPolicyShared()197 constexpr VMSharingPolicyShared() {} 198 ShouldUnhookUponDestruction()199 bool ShouldUnhookUponDestruction() const { return false; } 200 Reserve(const uintptr_t aPivotAddr,const uint32_t aMaxDistanceFromPivot)201 Maybe<PoolType> Reserve(const uintptr_t aPivotAddr, 202 const uint32_t aMaxDistanceFromPivot) { 203 // Win32 allocates VM addresses at a 64KiB granularity, so we might as well 204 // utilize that entire 64KiB reservation. 205 uint32_t len = this->GetAllocGranularity(); 206 207 Maybe<Span<const uint8_t>> maybeBounds = 208 MMPolicyInProcess::SpanFromPivotAndDistance(len, aPivotAddr, 209 aMaxDistanceFromPivot); 210 211 AutoCriticalSection lock(GetCS()); 212 VMSharingPolicyUnique<MMPolicyT>* uniquePol = sVMMap.GetPolicy(maybeBounds); 213 MOZ_ASSERT(uniquePol); 214 if (!uniquePol) { 215 return Nothing(); 216 } 217 218 Maybe<UniquePolicyT::PoolType> maybeUnique = 219 uniquePol->Reserve(len, maybeBounds); 220 if (!maybeUnique) { 221 return Nothing(); 222 } 223 224 return Some(PoolType(*this, std::move(maybeUnique.ref()))); 225 } 226 Items()227 TrampolineCollection<MMPolicyInProcess> Items() const { 228 // Since ShouldUnhookUponDestruction returns false, this can be empty 229 return TrampolineCollection<MMPolicyInProcess>(*this); 230 } 231 Clear()232 void Clear() { 233 // This must be a no-op for shared VM policy; we can't have one interceptor 234 // wiping out trampolines for all interceptors in the process. 235 } 236 237 VMSharingPolicyShared(const VMSharingPolicyShared&) = delete; 238 VMSharingPolicyShared(VMSharingPolicyShared&&) = delete; 239 VMSharingPolicyShared& operator=(const VMSharingPolicyShared&) = delete; 240 VMSharingPolicyShared& operator=(VMSharingPolicyShared&&) = delete; 241 242 private: GetCS()243 static CRITICAL_SECTION* GetCS() { 244 static const bool isAlloc = []() -> bool { 245 DWORD flags = 0; 246 #if defined(RELEASE_OR_BETA) 247 flags |= CRITICAL_SECTION_NO_DEBUG_INFO; 248 #endif // defined(RELEASE_OR_BETA) 249 ::InitializeCriticalSectionEx(&sCS, 4000, flags); 250 return true; 251 }(); 252 Unused << isAlloc; 253 254 return &sCS; 255 } 256 257 // In VMSharingPolicyShared, we only implement the overload that accepts 258 // a VMSharingPolicyUnique trampoline pool as |aInner|, since we require the 259 // former policy to wrap the latter. GetNextTrampoline(UniquePolicyT::PoolType & aInner)260 Maybe<Trampoline<MMPolicyInProcess>> GetNextTrampoline( 261 UniquePolicyT::PoolType& aInner) { 262 AutoCriticalSection lock(GetCS()); 263 return aInner.GetNextTrampoline(); 264 } 265 266 #if defined(_M_X64) IsTrampolineSpaceInLowest2GB(const UniquePolicyT::PoolType & aInner)267 bool IsTrampolineSpaceInLowest2GB( 268 const UniquePolicyT::PoolType& aInner) const { 269 AutoCriticalSection lock(GetCS()); 270 return aInner.IsInLowest2GB(); 271 } 272 #endif // defined(_M_X64) 273 274 private: 275 template <typename VMPolicyT, typename InnerT> 276 friend class TrampolinePool; 277 278 inline static RangeMap<MMPolicyInProcess> sVMMap; 279 inline static CRITICAL_SECTION sCS; 280 }; 281 282 } // namespace interceptor 283 } // namespace mozilla 284 285 #endif // mozilla_interceptor_VMSharingPolicies_h 286