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 template <typename MMPolicy, bool Dummy> 189 class VMSharingPolicyShared; 190 191 // We only support this policy for in-proc MMPolicy. 192 // Dummy is not actually needed for the implementation, but we need this 193 // to be a partial specialization so that its statics are treated as inlines 194 // (At least until we have C++17 enabled by default) 195 template <bool Dummy> 196 class MOZ_TRIVIAL_CTOR_DTOR VMSharingPolicyShared<MMPolicyInProcess, Dummy> 197 : public MMPolicyInProcess { 198 typedef VMSharingPolicyUnique<MMPolicyInProcess> UniquePolicyT; 199 typedef VMSharingPolicyShared<MMPolicyInProcess, Dummy> ThisType; 200 201 public: 202 using PoolType = TrampolinePool<ThisType, UniquePolicyT::PoolType>; 203 using MMPolicyT = MMPolicyInProcess; 204 VMSharingPolicyShared()205 constexpr VMSharingPolicyShared() {} 206 ShouldUnhookUponDestruction()207 bool ShouldUnhookUponDestruction() const { return false; } 208 Reserve(const uintptr_t aPivotAddr,const uint32_t aMaxDistanceFromPivot)209 Maybe<PoolType> Reserve(const uintptr_t aPivotAddr, 210 const uint32_t aMaxDistanceFromPivot) { 211 // Win32 allocates VM addresses at a 64KiB granularity, so we might as well 212 // utilize that entire 64KiB reservation. 213 uint32_t len = this->GetAllocGranularity(); 214 215 Maybe<Span<const uint8_t>> maybeBounds = 216 MMPolicyInProcess::SpanFromPivotAndDistance(len, aPivotAddr, 217 aMaxDistanceFromPivot); 218 219 AutoCriticalSection lock(GetCS()); 220 VMSharingPolicyUnique<MMPolicyT>* uniquePol = sVMMap.GetPolicy(maybeBounds); 221 MOZ_ASSERT(uniquePol); 222 if (!uniquePol) { 223 return Nothing(); 224 } 225 226 Maybe<UniquePolicyT::PoolType> maybeUnique = 227 uniquePol->Reserve(len, maybeBounds); 228 if (!maybeUnique) { 229 return Nothing(); 230 } 231 232 return Some(PoolType(*this, std::move(maybeUnique.ref()))); 233 } 234 Items()235 TrampolineCollection<MMPolicyInProcess> Items() const { 236 // Since ShouldUnhookUponDestruction returns false, this can be empty 237 return TrampolineCollection<MMPolicyInProcess>(*this); 238 } 239 Clear()240 void Clear() { 241 // This must be a no-op for shared VM policy; we can't have one interceptor 242 // wiping out trampolines for all interceptors in the process. 243 } 244 245 VMSharingPolicyShared(const VMSharingPolicyShared&) = delete; 246 VMSharingPolicyShared(VMSharingPolicyShared&&) = delete; 247 VMSharingPolicyShared& operator=(const VMSharingPolicyShared&) = delete; 248 VMSharingPolicyShared& operator=(VMSharingPolicyShared&&) = delete; 249 250 private: GetCS()251 static CRITICAL_SECTION* GetCS() { 252 static const bool isAlloc = []() -> bool { 253 DWORD flags = 0; 254 #if defined(RELEASE_OR_BETA) 255 flags |= CRITICAL_SECTION_NO_DEBUG_INFO; 256 #endif // defined(RELEASE_OR_BETA) 257 ::InitializeCriticalSectionEx(&sCS, 4000, flags); 258 return true; 259 }(); 260 Unused << isAlloc; 261 262 return &sCS; 263 } 264 265 // In VMSharingPolicyShared, we only implement the overload that accepts 266 // a VMSharingPolicyUnique trampoline pool as |aInner|, since we require the 267 // former policy to wrap the latter. GetNextTrampoline(UniquePolicyT::PoolType & aInner)268 Maybe<Trampoline<MMPolicyInProcess>> GetNextTrampoline( 269 UniquePolicyT::PoolType& aInner) { 270 AutoCriticalSection lock(GetCS()); 271 return aInner.GetNextTrampoline(); 272 } 273 274 #if defined(_M_X64) IsTrampolineSpaceInLowest2GB(const UniquePolicyT::PoolType & aInner)275 bool IsTrampolineSpaceInLowest2GB( 276 const UniquePolicyT::PoolType& aInner) const { 277 AutoCriticalSection lock(GetCS()); 278 return aInner.IsInLowest2GB(); 279 } 280 #endif // defined(_M_X64) 281 282 private: 283 template <typename VMPolicyT, typename InnerT> 284 friend class TrampolinePool; 285 286 static RangeMap<MMPolicyInProcess> sVMMap; 287 static CRITICAL_SECTION sCS; 288 }; 289 290 template <bool Dummy> 291 RangeMap<MMPolicyInProcess> 292 VMSharingPolicyShared<MMPolicyInProcess, Dummy>::sVMMap; 293 294 template <bool Dummy> 295 CRITICAL_SECTION VMSharingPolicyShared<MMPolicyInProcess, Dummy>::sCS; 296 297 } // namespace interceptor 298 } // namespace mozilla 299 300 #endif // mozilla_interceptor_VMSharingPolicies_h 301