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