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_TargetFunction_h
8 #define mozilla_interceptor_TargetFunction_h
9 
10 #include "mozilla/Assertions.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/BinarySearch.h"
13 #include "mozilla/CheckedInt.h"
14 #include "mozilla/Maybe.h"
15 #include "mozilla/Tuple.h"
16 #include "mozilla/Types.h"
17 #include "mozilla/Unused.h"
18 #include "mozilla/Vector.h"
19 
20 #include <memory>
21 #include <type_traits>
22 
23 namespace mozilla {
24 namespace interceptor {
25 
26 #if defined(_M_IX86)
27 
28 template <typename T>
29 bool CommitAndWriteShortInternal(const T& aMMPolicy, void* aDest,
30                                  uint16_t aValue);
31 
32 template <>
33 inline bool CommitAndWriteShortInternal<MMPolicyInProcess>(
34     const MMPolicyInProcess& aMMPolicy, void* aDest, uint16_t aValue) {
35   return aMMPolicy.WriteAtomic(aDest, aValue);
36 }
37 
38 template <>
39 inline bool CommitAndWriteShortInternal<MMPolicyOutOfProcess>(
40     const MMPolicyOutOfProcess& aMMPolicy, void* aDest, uint16_t aValue) {
41   return aMMPolicy.Write(aDest, &aValue, sizeof(uint16_t));
42 }
43 
44 #endif  // defined(_M_IX86)
45 
46 // Forward declaration
47 template <typename MMPolicy>
48 class ReadOnlyTargetFunction;
49 
50 template <typename MMPolicy>
51 class MOZ_STACK_CLASS WritableTargetFunction final {
52   class AutoProtect final {
53     using ProtectParams = Tuple<uintptr_t, uint32_t>;
54 
55    public:
AutoProtect(const MMPolicy & aMMPolicy)56     explicit AutoProtect(const MMPolicy& aMMPolicy) : mMMPolicy(aMMPolicy) {}
57 
AutoProtect(const MMPolicy & aMMPolicy,uintptr_t aAddr,size_t aNumBytes,uint32_t aNewProt)58     AutoProtect(const MMPolicy& aMMPolicy, uintptr_t aAddr, size_t aNumBytes,
59                 uint32_t aNewProt)
60         : mMMPolicy(aMMPolicy) {
61       const uint32_t pageSize = mMMPolicy.GetPageSize();
62       const uintptr_t limit = aAddr + aNumBytes - 1;
63       const uintptr_t limitPageNum = limit / pageSize;
64       const uintptr_t basePageNum = aAddr / pageSize;
65       const uintptr_t numPagesToChange = limitPageNum - basePageNum + 1;
66 
67       // We'll use the base address of the page instead of aAddr
68       uintptr_t curAddr = basePageNum * pageSize;
69 
70       // Now change the protection on each page
71       for (uintptr_t curPage = 0; curPage < numPagesToChange;
72            ++curPage, curAddr += pageSize) {
73         uint32_t prevProt;
74         if (!aMMPolicy.Protect(reinterpret_cast<void*>(curAddr), pageSize,
75                                aNewProt, &prevProt)) {
76           Clear();
77           return;
78         }
79 
80         // Save the previous protection for curAddr so that we can revert this
81         // in the destructor.
82         if (!mProtects.append(MakeTuple(curAddr, prevProt))) {
83           Clear();
84           return;
85         }
86       }
87     }
88 
AutoProtect(AutoProtect && aOther)89     AutoProtect(AutoProtect&& aOther)
90         : mMMPolicy(aOther.mMMPolicy), mProtects(std::move(aOther.mProtects)) {
91       aOther.mProtects.clear();
92     }
93 
~AutoProtect()94     ~AutoProtect() { Clear(); }
95 
96     explicit operator bool() const { return !mProtects.empty(); }
97 
98     AutoProtect(const AutoProtect&) = delete;
99     AutoProtect& operator=(const AutoProtect&) = delete;
100     AutoProtect& operator=(AutoProtect&&) = delete;
101 
102    private:
Clear()103     void Clear() {
104       const uint32_t pageSize = mMMPolicy.GetPageSize();
105       for (auto&& entry : mProtects) {
106         uint32_t prevProt;
107         DebugOnly<bool> ok =
108             mMMPolicy.Protect(reinterpret_cast<void*>(Get<0>(entry)), pageSize,
109                               Get<1>(entry), &prevProt);
110         MOZ_ASSERT(ok);
111       }
112 
113       mProtects.clear();
114     }
115 
116    private:
117     const MMPolicy& mMMPolicy;
118     // We include two entries of inline storage as that is most common in the
119     // worst case.
120     Vector<ProtectParams, 2> mProtects;
121   };
122 
123  public:
124   /**
125    * Used to initialize an invalid WritableTargetFunction, thus signalling an
126    * error.
127    */
WritableTargetFunction(const MMPolicy & aMMPolicy)128   explicit WritableTargetFunction(const MMPolicy& aMMPolicy)
129       : mMMPolicy(aMMPolicy),
130         mFunc(0),
131         mNumBytes(0),
132         mOffset(0),
133         mStartWriteOffset(0),
134         mAccumulatedStatus(false),
135         mProtect(aMMPolicy) {}
136 
WritableTargetFunction(const MMPolicy & aMMPolicy,uintptr_t aFunc,size_t aNumBytes)137   WritableTargetFunction(const MMPolicy& aMMPolicy, uintptr_t aFunc,
138                          size_t aNumBytes)
139       : mMMPolicy(aMMPolicy),
140         mFunc(aFunc),
141         mNumBytes(aNumBytes),
142         mOffset(0),
143         mStartWriteOffset(0),
144         mAccumulatedStatus(true),
145         mProtect(aMMPolicy, aFunc, aNumBytes, PAGE_EXECUTE_READWRITE) {}
146 
WritableTargetFunction(WritableTargetFunction && aOther)147   WritableTargetFunction(WritableTargetFunction&& aOther)
148       : mMMPolicy(aOther.mMMPolicy),
149         mFunc(aOther.mFunc),
150         mNumBytes(aOther.mNumBytes),
151         mOffset(aOther.mOffset),
152         mStartWriteOffset(aOther.mStartWriteOffset),
153         mLocalBytes(std::move(aOther.mLocalBytes)),
154         mAccumulatedStatus(aOther.mAccumulatedStatus),
155         mProtect(std::move(aOther.mProtect)) {
156     aOther.mAccumulatedStatus = false;
157   }
158 
~WritableTargetFunction()159   ~WritableTargetFunction() {
160     MOZ_ASSERT(mLocalBytes.empty(), "Did you forget to call Commit?");
161   }
162 
163   WritableTargetFunction(const WritableTargetFunction&) = delete;
164   WritableTargetFunction& operator=(const WritableTargetFunction&) = delete;
165   WritableTargetFunction& operator=(WritableTargetFunction&&) = delete;
166 
167   /**
168    * @return true if data was successfully committed.
169    */
Commit()170   bool Commit() {
171     if (!(*this)) {
172       return false;
173     }
174 
175     if (mLocalBytes.empty()) {
176       // Nothing to commit, treat like success
177       return true;
178     }
179 
180     bool ok =
181         mMMPolicy.Write(reinterpret_cast<void*>(mFunc + mStartWriteOffset),
182                         mLocalBytes.begin(), mLocalBytes.length());
183     if (!ok) {
184       return false;
185     }
186 
187     mMMPolicy.FlushInstructionCache();
188 
189     mStartWriteOffset += mLocalBytes.length();
190 
191     mLocalBytes.clear();
192     return true;
193   }
194 
195   explicit operator bool() const { return mProtect && mAccumulatedStatus; }
196 
WriteByte(const uint8_t & aValue)197   void WriteByte(const uint8_t& aValue) {
198     if (!mLocalBytes.append(aValue)) {
199       mAccumulatedStatus = false;
200       return;
201     }
202 
203     mOffset += sizeof(uint8_t);
204   }
205 
ReadByte()206   Maybe<uint8_t> ReadByte() {
207     // Reading is only permitted prior to any writing
208     MOZ_ASSERT(mOffset == mStartWriteOffset);
209     if (mOffset > mStartWriteOffset) {
210       mAccumulatedStatus = false;
211       return Nothing();
212     }
213 
214     uint8_t value;
215     if (!mMMPolicy.Read(&value, reinterpret_cast<const void*>(mFunc + mOffset),
216                         sizeof(uint8_t))) {
217       mAccumulatedStatus = false;
218       return Nothing();
219     }
220 
221     mOffset += sizeof(uint8_t);
222     mStartWriteOffset += sizeof(uint8_t);
223     return Some(value);
224   }
225 
ReadEncodedPtr()226   Maybe<uintptr_t> ReadEncodedPtr() {
227     // Reading is only permitted prior to any writing
228     MOZ_ASSERT(mOffset == mStartWriteOffset);
229     if (mOffset > mStartWriteOffset) {
230       mAccumulatedStatus = false;
231       return Nothing();
232     }
233 
234     uintptr_t value;
235     if (!mMMPolicy.Read(&value, reinterpret_cast<const void*>(mFunc + mOffset),
236                         sizeof(uintptr_t))) {
237       mAccumulatedStatus = false;
238       return Nothing();
239     }
240 
241     mOffset += sizeof(uintptr_t);
242     mStartWriteOffset += sizeof(uintptr_t);
243     return Some(ReadOnlyTargetFunction<MMPolicy>::DecodePtr(value));
244   }
245 
ReadLong()246   Maybe<uint32_t> ReadLong() {
247     // Reading is only permitted prior to any writing
248     MOZ_ASSERT(mOffset == mStartWriteOffset);
249     if (mOffset > mStartWriteOffset) {
250       mAccumulatedStatus = false;
251       return Nothing();
252     }
253 
254     uint32_t value;
255     if (!mMMPolicy.Read(&value, reinterpret_cast<const void*>(mFunc + mOffset),
256                         sizeof(uint32_t))) {
257       mAccumulatedStatus = false;
258       return Nothing();
259     }
260 
261     mOffset += sizeof(uint32_t);
262     mStartWriteOffset += sizeof(uint32_t);
263     return Some(value);
264   }
265 
WriteShort(const uint16_t & aValue)266   void WriteShort(const uint16_t& aValue) {
267     if (!mLocalBytes.append(reinterpret_cast<const uint8_t*>(&aValue),
268                             sizeof(uint16_t))) {
269       mAccumulatedStatus = false;
270       return;
271     }
272 
273     mOffset += sizeof(uint16_t);
274   }
275 
276 #if defined(_M_IX86)
277  public:
278   /**
279    * Commits any dirty writes, and then writes a short, atomically if possible.
280    * This call may succeed in both inproc and outproc cases, but atomicity
281    * is only guaranteed in the inproc case.
282    */
CommitAndWriteShort(const uint16_t aValue)283   bool CommitAndWriteShort(const uint16_t aValue) {
284     // First, commit everything that has been written until now
285     if (!Commit()) {
286       return false;
287     }
288 
289     // Now immediately write the short, atomically if inproc
290     bool ok = CommitAndWriteShortInternal(
291         mMMPolicy, reinterpret_cast<void*>(mFunc + mStartWriteOffset), aValue);
292     if (!ok) {
293       return false;
294     }
295 
296     mMMPolicy.FlushInstructionCache();
297     mStartWriteOffset += sizeof(uint16_t);
298     return true;
299   }
300 #endif  // defined(_M_IX86)
301 
WriteDisp32(const uintptr_t aAbsTarget)302   void WriteDisp32(const uintptr_t aAbsTarget) {
303     intptr_t diff = static_cast<intptr_t>(aAbsTarget) -
304                     static_cast<intptr_t>(mFunc + mOffset + sizeof(int32_t));
305 
306     CheckedInt<int32_t> checkedDisp(diff);
307     MOZ_ASSERT(checkedDisp.isValid());
308     if (!checkedDisp.isValid()) {
309       mAccumulatedStatus = false;
310       return;
311     }
312 
313     int32_t disp = checkedDisp.value();
314     if (!mLocalBytes.append(reinterpret_cast<uint8_t*>(&disp),
315                             sizeof(int32_t))) {
316       mAccumulatedStatus = false;
317       return;
318     }
319 
320     mOffset += sizeof(int32_t);
321   }
322 
323 #if defined(_M_X64) || defined(_M_ARM64)
WriteLong(const uint32_t aValue)324   void WriteLong(const uint32_t aValue) {
325     if (!mLocalBytes.append(reinterpret_cast<const uint8_t*>(&aValue),
326                             sizeof(uint32_t))) {
327       mAccumulatedStatus = false;
328       return;
329     }
330 
331     mOffset += sizeof(uint32_t);
332   }
333 #endif  // defined(_M_X64)
334 
WritePointer(const uintptr_t aAbsTarget)335   void WritePointer(const uintptr_t aAbsTarget) {
336     if (!mLocalBytes.append(reinterpret_cast<const uint8_t*>(&aAbsTarget),
337                             sizeof(uintptr_t))) {
338       mAccumulatedStatus = false;
339       return;
340     }
341 
342     mOffset += sizeof(uintptr_t);
343   }
344 
345   /**
346    * @param aValues N-sized array of type T that specifies the set of values
347    *                that are permissible in the first M bytes of the target
348    *                function at aOffset.
349    * @return true if M values of type T in the function are members of the
350    *         set specified by aValues.
351    */
352   template <typename T, size_t M, size_t N>
353   bool VerifyValuesAreOneOf(const T (&aValues)[N], const uint8_t aOffset = 0) {
354     T buf[M];
355     if (!mMMPolicy.Read(
356             buf, reinterpret_cast<const void*>(mFunc + mOffset + aOffset),
357             M * sizeof(T))) {
358       return false;
359     }
360 
361     for (auto&& fnValue : buf) {
362       bool match = false;
363       for (auto&& testValue : aValues) {
364         match |= (fnValue == testValue);
365       }
366 
367       if (!match) {
368         return false;
369       }
370     }
371 
372     return true;
373   }
374 
GetCurrentAddress()375   uintptr_t GetCurrentAddress() const { return mFunc + mOffset; }
376 
377  private:
378   const MMPolicy& mMMPolicy;
379   const uintptr_t mFunc;
380   const size_t mNumBytes;
381   uint32_t mOffset;
382   uint32_t mStartWriteOffset;
383 
384   // In an ideal world, we'd only read 5 bytes on 32-bit and 13 bytes on 64-bit,
385   // to match the minimum bytes that we need to write in in order to patch the
386   // target function. Since the actual opcodes will often require us to pull in
387   // extra bytes above that minimum, we set the inline storage to be larger than
388   // those minima in an effort to give the Vector extra wiggle room before it
389   // needs to touch the heap.
390 #if defined(_M_IX86)
391   static const size_t kInlineStorage = 16;
392 #elif defined(_M_X64) || defined(_M_ARM64)
393   static const size_t kInlineStorage = 32;
394 #endif
395   Vector<uint8_t, kInlineStorage> mLocalBytes;
396   bool mAccumulatedStatus;
397   AutoProtect mProtect;
398 };
399 
400 template <typename MMPolicy>
401 class ReadOnlyTargetBytes {
402  public:
ReadOnlyTargetBytes(const MMPolicy & aMMPolicy,const void * aBase)403   ReadOnlyTargetBytes(const MMPolicy& aMMPolicy, const void* aBase)
404       : mMMPolicy(aMMPolicy), mBase(reinterpret_cast<const uint8_t*>(aBase)) {}
405 
ReadOnlyTargetBytes(ReadOnlyTargetBytes && aOther)406   ReadOnlyTargetBytes(ReadOnlyTargetBytes&& aOther)
407       : mMMPolicy(aOther.mMMPolicy), mBase(aOther.mBase) {}
408 
409   ReadOnlyTargetBytes(const ReadOnlyTargetBytes& aOther,
410                       const uint32_t aOffsetFromOther = 0)
411       : mMMPolicy(aOther.mMMPolicy), mBase(aOther.mBase + aOffsetFromOther) {}
412 
EnsureLimit(uint32_t aDesiredLimit)413   void EnsureLimit(uint32_t aDesiredLimit) {
414     // In the out-proc case we use this function to read the target function's
415     // bytes in the other process into a local buffer. We don't need that for
416     // the in-process case because we already have direct access to our target
417     // function's bytes.
418   }
419 
TryEnsureLimit(uint32_t aDesiredLimit)420   uint32_t TryEnsureLimit(uint32_t aDesiredLimit) {
421     // Same as EnsureLimit above.  We don't need to ensure for the in-process.
422     return aDesiredLimit;
423   }
424 
IsValidAtOffset(const int8_t aOffset)425   bool IsValidAtOffset(const int8_t aOffset) const {
426     if (!aOffset) {
427       return true;
428     }
429 
430     uintptr_t base = reinterpret_cast<uintptr_t>(mBase);
431     uintptr_t adjusted = base + aOffset;
432     uint32_t pageSize = mMMPolicy.GetPageSize();
433 
434     // If |adjusted| is within the same page as |mBase|, we're still valid
435     if ((base / pageSize) == (adjusted / pageSize)) {
436       return true;
437     }
438 
439     // Otherwise, let's query |adjusted|
440     return mMMPolicy.IsPageAccessible(adjusted);
441   }
442 
443   /**
444    * This returns a pointer to a *potentially local copy* of the target
445    * function's bytes. The returned pointer should not be used for any
446    * pointer arithmetic relating to the target function.
447    */
GetLocalBytes()448   const uint8_t* GetLocalBytes() const { return mBase; }
449 
450   /**
451    * This returns a pointer to the target function's bytes. The returned pointer
452    * may possibly belong to another process, so while it should be used for
453    * pointer arithmetic, it *must not* be dereferenced.
454    */
GetBase()455   uintptr_t GetBase() const { return reinterpret_cast<uintptr_t>(mBase); }
456 
GetMMPolicy()457   const MMPolicy& GetMMPolicy() const { return mMMPolicy; }
458 
459   ReadOnlyTargetBytes& operator=(const ReadOnlyTargetBytes&) = delete;
460   ReadOnlyTargetBytes& operator=(ReadOnlyTargetBytes&&) = delete;
461 
462  private:
463   const MMPolicy& mMMPolicy;
464   uint8_t const* const mBase;
465 };
466 
467 template <>
468 class ReadOnlyTargetBytes<MMPolicyOutOfProcess> {
469  public:
ReadOnlyTargetBytes(const MMPolicyOutOfProcess & aMMPolicy,const void * aBase)470   ReadOnlyTargetBytes(const MMPolicyOutOfProcess& aMMPolicy, const void* aBase)
471       : mMMPolicy(aMMPolicy), mBase(reinterpret_cast<const uint8_t*>(aBase)) {}
472 
ReadOnlyTargetBytes(ReadOnlyTargetBytes && aOther)473   ReadOnlyTargetBytes(ReadOnlyTargetBytes&& aOther)
474       : mMMPolicy(aOther.mMMPolicy),
475         mLocalBytes(std::move(aOther.mLocalBytes)),
476         mBase(aOther.mBase) {}
477 
ReadOnlyTargetBytes(const ReadOnlyTargetBytes & aOther)478   ReadOnlyTargetBytes(const ReadOnlyTargetBytes& aOther)
479       : mMMPolicy(aOther.mMMPolicy), mBase(aOther.mBase) {
480     Unused << mLocalBytes.appendAll(aOther.mLocalBytes);
481   }
482 
ReadOnlyTargetBytes(const ReadOnlyTargetBytes & aOther,const uint32_t aOffsetFromOther)483   ReadOnlyTargetBytes(const ReadOnlyTargetBytes& aOther,
484                       const uint32_t aOffsetFromOther)
485       : mMMPolicy(aOther.mMMPolicy), mBase(aOther.mBase + aOffsetFromOther) {
486     if (aOffsetFromOther >= aOther.mLocalBytes.length()) {
487       return;
488     }
489 
490     Unused << mLocalBytes.append(aOther.mLocalBytes.begin() + aOffsetFromOther,
491                                  aOther.mLocalBytes.end());
492   }
493 
EnsureLimit(uint32_t aDesiredLimit)494   void EnsureLimit(uint32_t aDesiredLimit) {
495     size_t prevSize = mLocalBytes.length();
496     if (aDesiredLimit < prevSize) {
497       return;
498     }
499 
500     size_t newSize = aDesiredLimit + 1;
501     if (newSize < kInlineStorage) {
502       // Always try to read as much memory as we can at once
503       newSize = kInlineStorage;
504     }
505 
506     bool resizeOk = mLocalBytes.resize(newSize);
507     MOZ_RELEASE_ASSERT(resizeOk);
508 
509     bool ok = mMMPolicy.Read(&mLocalBytes[prevSize], mBase + prevSize,
510                              newSize - prevSize);
511     if (ok) {
512       return;
513     }
514 
515     // We couldn't pull more bytes than needed (which may happen if those extra
516     // bytes are not accessible). In this case, we try just to get the bare
517     // minimum.
518     newSize = aDesiredLimit + 1;
519     resizeOk = mLocalBytes.resize(newSize);
520     MOZ_RELEASE_ASSERT(resizeOk);
521 
522     ok = mMMPolicy.Read(&mLocalBytes[prevSize], mBase + prevSize,
523                         newSize - prevSize);
524     MOZ_RELEASE_ASSERT(ok);
525   }
526 
527   // This function tries to ensure as many bytes as possible up to
528   // |aDesiredLimit| bytes, returning how many bytes were actually ensured.
529   // As EnsureLimit does, we allocate an extra byte in local to make sure
530   // mLocalBytes always has at least one byte even though the target memory
531   // was inaccessible at all.
TryEnsureLimit(uint32_t aDesiredLimit)532   uint32_t TryEnsureLimit(uint32_t aDesiredLimit) {
533     size_t prevSize = mLocalBytes.length();
534     if (aDesiredLimit < prevSize) {
535       return aDesiredLimit;
536     }
537 
538     size_t newSize = aDesiredLimit;
539     if (newSize < kInlineStorage) {
540       // Always try to read as much memory as we can at once
541       newSize = kInlineStorage;
542     }
543 
544     bool resizeOk = mLocalBytes.resize(newSize);
545     MOZ_RELEASE_ASSERT(resizeOk);
546 
547     size_t bytesRead = mMMPolicy.TryRead(&mLocalBytes[prevSize],
548                                          mBase + prevSize, newSize - prevSize);
549 
550     newSize = prevSize + bytesRead;
551 
552     resizeOk = mLocalBytes.resize(newSize + 1);
553     MOZ_RELEASE_ASSERT(resizeOk);
554 
555     mLocalBytes[newSize] = 0;
556     return newSize;
557   }
558 
IsValidAtOffset(const int8_t aOffset)559   bool IsValidAtOffset(const int8_t aOffset) const {
560     if (!aOffset) {
561       return true;
562     }
563 
564     uintptr_t base = reinterpret_cast<uintptr_t>(mBase);
565     uintptr_t adjusted = base + aOffset;
566     uint32_t pageSize = mMMPolicy.GetPageSize();
567 
568     // If |adjusted| is within the same page as |mBase|, we're still valid
569     if ((base / pageSize) == (adjusted / pageSize)) {
570       return true;
571     }
572 
573     // Otherwise, let's query |adjusted|
574     return mMMPolicy.IsPageAccessible(adjusted);
575   }
576 
577   /**
578    * This returns a pointer to a *potentially local copy* of the target
579    * function's bytes. The returned pointer should not be used for any
580    * pointer arithmetic relating to the target function.
581    */
GetLocalBytes()582   const uint8_t* GetLocalBytes() const {
583     if (mLocalBytes.empty()) {
584       return nullptr;
585     }
586 
587     return mLocalBytes.begin();
588   }
589 
590   /**
591    * This returns a pointer to the target function's bytes. The returned pointer
592    * may possibly belong to another process, so while it should be used for
593    * pointer arithmetic, it *must not* be dereferenced.
594    */
GetBase()595   uintptr_t GetBase() const { return reinterpret_cast<uintptr_t>(mBase); }
596 
GetMMPolicy()597   const MMPolicyOutOfProcess& GetMMPolicy() const { return mMMPolicy; }
598 
599   ReadOnlyTargetBytes& operator=(const ReadOnlyTargetBytes&) = delete;
600   ReadOnlyTargetBytes& operator=(ReadOnlyTargetBytes&&) = delete;
601 
602  private:
603   // In an ideal world, we'd only read 5 bytes on 32-bit and 13 bytes on 64-bit,
604   // to match the minimum bytes that we need to write in in order to patch the
605   // target function. Since the actual opcodes will often require us to pull in
606   // extra bytes above that minimum, we set the inline storage to be larger than
607   // those minima in an effort to give the Vector extra wiggle room before it
608   // needs to touch the heap.
609 #if defined(_M_IX86)
610   static const size_t kInlineStorage = 16;
611 #elif defined(_M_X64) || defined(_M_ARM64)
612   static const size_t kInlineStorage = 32;
613 #endif
614 
615   const MMPolicyOutOfProcess& mMMPolicy;
616   Vector<uint8_t, kInlineStorage> mLocalBytes;
617   uint8_t const* const mBase;
618 };
619 
620 template <typename MMPolicy>
621 class TargetBytesPtr {
622  public:
623   typedef TargetBytesPtr<MMPolicy> Type;
624 
Make(const MMPolicy & aMMPolicy,const void * aFunc)625   static Type Make(const MMPolicy& aMMPolicy, const void* aFunc) {
626     return TargetBytesPtr(aMMPolicy, aFunc);
627   }
628 
CopyFromOffset(const TargetBytesPtr & aOther,const uint32_t aOffsetFromOther)629   static Type CopyFromOffset(const TargetBytesPtr& aOther,
630                              const uint32_t aOffsetFromOther) {
631     return TargetBytesPtr(aOther, aOffsetFromOther);
632   }
633 
634   ReadOnlyTargetBytes<MMPolicy>* operator->() { return &mTargetBytes; }
635 
TargetBytesPtr(TargetBytesPtr && aOther)636   TargetBytesPtr(TargetBytesPtr&& aOther)
637       : mTargetBytes(std::move(aOther.mTargetBytes)) {}
638 
TargetBytesPtr(const TargetBytesPtr & aOther)639   TargetBytesPtr(const TargetBytesPtr& aOther)
640       : mTargetBytes(aOther.mTargetBytes) {}
641 
642   TargetBytesPtr& operator=(const TargetBytesPtr&) = delete;
643   TargetBytesPtr& operator=(TargetBytesPtr&&) = delete;
644 
645  private:
TargetBytesPtr(const MMPolicy & aMMPolicy,const void * aFunc)646   TargetBytesPtr(const MMPolicy& aMMPolicy, const void* aFunc)
647       : mTargetBytes(aMMPolicy, aFunc) {}
648 
TargetBytesPtr(const TargetBytesPtr & aOther,const uint32_t aOffsetFromOther)649   TargetBytesPtr(const TargetBytesPtr& aOther, const uint32_t aOffsetFromOther)
650       : mTargetBytes(aOther.mTargetBytes, aOffsetFromOther) {}
651 
652   ReadOnlyTargetBytes<MMPolicy> mTargetBytes;
653 };
654 
655 template <>
656 class TargetBytesPtr<MMPolicyOutOfProcess> {
657  public:
658   typedef std::shared_ptr<ReadOnlyTargetBytes<MMPolicyOutOfProcess>> Type;
659 
Make(const MMPolicyOutOfProcess & aMMPolicy,const void * aFunc)660   static Type Make(const MMPolicyOutOfProcess& aMMPolicy, const void* aFunc) {
661     return std::make_shared<ReadOnlyTargetBytes<MMPolicyOutOfProcess>>(
662         aMMPolicy, aFunc);
663   }
664 
CopyFromOffset(const Type & aOther,const uint32_t aOffsetFromOther)665   static Type CopyFromOffset(const Type& aOther,
666                              const uint32_t aOffsetFromOther) {
667     return std::make_shared<ReadOnlyTargetBytes<MMPolicyOutOfProcess>>(
668         *aOther, aOffsetFromOther);
669   }
670 };
671 
672 template <typename MMPolicy>
673 class MOZ_STACK_CLASS ReadOnlyTargetFunction final {
674  public:
ReadOnlyTargetFunction(const MMPolicy & aMMPolicy,const void * aFunc)675   ReadOnlyTargetFunction(const MMPolicy& aMMPolicy, const void* aFunc)
676       : mTargetBytes(TargetBytesPtr<MMPolicy>::Make(aMMPolicy, aFunc)),
677         mOffset(0) {}
678 
ReadOnlyTargetFunction(const MMPolicy & aMMPolicy,FARPROC aFunc)679   ReadOnlyTargetFunction(const MMPolicy& aMMPolicy, FARPROC aFunc)
680       : mTargetBytes(TargetBytesPtr<MMPolicy>::Make(
681             aMMPolicy, reinterpret_cast<const void*>(aFunc))),
682         mOffset(0) {}
683 
ReadOnlyTargetFunction(const MMPolicy & aMMPolicy,uintptr_t aFunc)684   ReadOnlyTargetFunction(const MMPolicy& aMMPolicy, uintptr_t aFunc)
685       : mTargetBytes(TargetBytesPtr<MMPolicy>::Make(
686             aMMPolicy, reinterpret_cast<const void*>(aFunc))),
687         mOffset(0) {}
688 
ReadOnlyTargetFunction(ReadOnlyTargetFunction && aOther)689   ReadOnlyTargetFunction(ReadOnlyTargetFunction&& aOther)
690       : mTargetBytes(std::move(aOther.mTargetBytes)), mOffset(aOther.mOffset) {}
691 
692   ReadOnlyTargetFunction& operator=(const ReadOnlyTargetFunction&) = delete;
693   ReadOnlyTargetFunction& operator=(ReadOnlyTargetFunction&&) = delete;
694 
695   ~ReadOnlyTargetFunction() = default;
696 
697   ReadOnlyTargetFunction operator+(const uint32_t aOffset) const {
698     return ReadOnlyTargetFunction(*this, mOffset + aOffset);
699   }
700 
GetBaseAddress()701   uintptr_t GetBaseAddress() const { return mTargetBytes->GetBase(); }
702 
GetAddress()703   uintptr_t GetAddress() const { return mTargetBytes->GetBase() + mOffset; }
704 
AsEncodedPtr()705   uintptr_t AsEncodedPtr() const {
706     return EncodePtr(
707         reinterpret_cast<void*>(mTargetBytes->GetBase() + mOffset));
708   }
709 
EncodePtr(void * aPtr)710   static uintptr_t EncodePtr(void* aPtr) {
711     return reinterpret_cast<uintptr_t>(::EncodePointer(aPtr));
712   }
713 
DecodePtr(uintptr_t aEncodedPtr)714   static uintptr_t DecodePtr(uintptr_t aEncodedPtr) {
715     return reinterpret_cast<uintptr_t>(
716         ::DecodePointer(reinterpret_cast<PVOID>(aEncodedPtr)));
717   }
718 
IsValidAtOffset(const int8_t aOffset)719   bool IsValidAtOffset(const int8_t aOffset) const {
720     return mTargetBytes->IsValidAtOffset(aOffset);
721   }
722 
723 #if defined(_M_ARM64)
724 
ReadNextInstruction()725   uint32_t ReadNextInstruction() {
726     mTargetBytes->EnsureLimit(mOffset + sizeof(uint32_t));
727     uint32_t instruction = *reinterpret_cast<const uint32_t*>(
728         mTargetBytes->GetLocalBytes() + mOffset);
729     mOffset += sizeof(uint32_t);
730     return instruction;
731   }
732 
BackUpOneInstruction()733   bool BackUpOneInstruction() {
734     if (mOffset < sizeof(uint32_t)) {
735       return false;
736     }
737 
738     mOffset -= sizeof(uint32_t);
739     return true;
740   }
741 
742 #else
743 
744   uint8_t const& operator*() const {
745     mTargetBytes->EnsureLimit(mOffset);
746     return *(mTargetBytes->GetLocalBytes() + mOffset);
747   }
748 
749   uint8_t const& operator[](uint32_t aIndex) const {
750     mTargetBytes->EnsureLimit(mOffset + aIndex);
751     return *(mTargetBytes->GetLocalBytes() + mOffset + aIndex);
752   }
753 
754   ReadOnlyTargetFunction& operator++() {
755     ++mOffset;
756     return *this;
757   }
758 
759   ReadOnlyTargetFunction& operator+=(uint32_t aDelta) {
760     mOffset += aDelta;
761     return *this;
762   }
763 
ReadDisp32AsAbsolute()764   uintptr_t ReadDisp32AsAbsolute() {
765     mTargetBytes->EnsureLimit(mOffset + sizeof(int32_t));
766     int32_t disp = *reinterpret_cast<const int32_t*>(
767         mTargetBytes->GetLocalBytes() + mOffset);
768     uintptr_t result =
769         mTargetBytes->GetBase() + mOffset + sizeof(int32_t) + disp;
770     mOffset += sizeof(int32_t);
771     return result;
772   }
773 
IsRelativeShortJump(uintptr_t * aOutTarget)774   bool IsRelativeShortJump(uintptr_t* aOutTarget) {
775     if ((*this)[0] == 0xeb) {
776       int8_t offset = static_cast<int8_t>((*this)[1]);
777       *aOutTarget = GetAddress() + 2 + offset;
778       return true;
779     }
780     return false;
781   }
782 
783 #  if defined(_M_X64)
784   // Currently this function is used only in x64.
IsRelativeNearJump(uintptr_t * aOutTarget)785   bool IsRelativeNearJump(uintptr_t* aOutTarget) {
786     if ((*this)[0] == 0xe9) {
787       *aOutTarget = (*this + 1).ReadDisp32AsAbsolute();
788       return true;
789     }
790     return false;
791   }
792 #  endif  // defined(_M_X64)
793 
IsIndirectNearJump(uintptr_t * aOutTarget)794   bool IsIndirectNearJump(uintptr_t* aOutTarget) {
795     if ((*this)[0] == 0xff && (*this)[1] == 0x25) {
796 #  if defined(_M_X64)
797       *aOutTarget = (*this + 2).ChasePointerFromDisp();
798 #  else
799       *aOutTarget = (*this + 2).template ChasePointer<uintptr_t*>();
800 #  endif  // defined(_M_X64)
801       return true;
802     }
803 #  if defined(_M_X64)
804     else if ((*this)[0] == 0x48 && (*this)[1] == 0xff && (*this)[2] == 0x25) {
805       // According to Intel SDM, JMP does not have REX.W except JMP m16:64,
806       // but CPU can execute JMP r/m32 with REX.W.  We handle it just in case.
807       *aOutTarget = (*this + 3).ChasePointerFromDisp();
808       return true;
809     }
810 #  endif  // defined(_M_X64)
811     return false;
812   }
813 
814 #endif  // defined(_M_ARM64)
815 
Rewind()816   void Rewind() { mOffset = 0; }
817 
GetOffset()818   uint32_t GetOffset() const { return mOffset; }
819 
OffsetToAbsolute(const uint8_t aOffset)820   uintptr_t OffsetToAbsolute(const uint8_t aOffset) const {
821     return mTargetBytes->GetBase() + mOffset + aOffset;
822   }
823 
GetCurrentAbsolute()824   uintptr_t GetCurrentAbsolute() const { return OffsetToAbsolute(0); }
825 
826   /**
827    * This method promotes the code referenced by this object to be writable.
828    *
829    * @param aLen    The length of the function's code to make writable. If set
830    *                to zero, this object's current offset is used as the length.
831    * @param aOffset The result's base address will be offset from this
832    *                object's base address by |aOffset| bytes. This value may be
833    *                negative.
834    */
835   WritableTargetFunction<MMPolicy> Promote(const uint32_t aLen = 0,
836                                            const int8_t aOffset = 0) const {
837     const uint32_t effectiveLength = aLen ? aLen : mOffset;
838     MOZ_RELEASE_ASSERT(effectiveLength,
839                        "Cannot Promote a zero-length function");
840 
841     if (!mTargetBytes->IsValidAtOffset(aOffset)) {
842       return WritableTargetFunction<MMPolicy>(mTargetBytes->GetMMPolicy());
843     }
844 
845     WritableTargetFunction<MMPolicy> result(mTargetBytes->GetMMPolicy(),
846                                             mTargetBytes->GetBase() + aOffset,
847                                             effectiveLength);
848 
849     return result;
850   }
851 
852  private:
853   template <typename T>
854   struct ChasePointerHelper {
855     template <typename MMPolicy_>
ResultChasePointerHelper856     static T Result(const MMPolicy_&, T aValue) {
857       return aValue;
858     }
859   };
860 
861   template <typename T>
862   struct ChasePointerHelper<T*> {
863     template <typename MMPolicy_>
864     static auto Result(const MMPolicy_& aPolicy, T* aValue) {
865       ReadOnlyTargetFunction<MMPolicy_> ptr(aPolicy, aValue);
866       return ptr.template ChasePointer<T>();
867     }
868   };
869 
870  public:
871   // Keep chasing pointers until T is not a pointer type anymore
872   template <typename T>
873   auto ChasePointer() {
874     mTargetBytes->EnsureLimit(mOffset + sizeof(T));
875     const std::remove_cv_t<T> result =
876         *reinterpret_cast<const std::remove_cv_t<T>*>(
877             mTargetBytes->GetLocalBytes() + mOffset);
878     return ChasePointerHelper<std::remove_cv_t<T>>::Result(
879         mTargetBytes->GetMMPolicy(), result);
880   }
881 
882   uintptr_t ChasePointerFromDisp() {
883     uintptr_t ptrFromDisp = ReadDisp32AsAbsolute();
884     ReadOnlyTargetFunction<MMPolicy> ptr(
885         mTargetBytes->GetMMPolicy(),
886         reinterpret_cast<const void*>(ptrFromDisp));
887     return ptr.template ChasePointer<uintptr_t>();
888   }
889 
890  private:
891   ReadOnlyTargetFunction(const ReadOnlyTargetFunction& aOther)
892       : mTargetBytes(aOther.mTargetBytes), mOffset(aOther.mOffset) {}
893 
894   ReadOnlyTargetFunction(const ReadOnlyTargetFunction& aOther,
895                          const uint32_t aOffsetFromOther)
896       : mTargetBytes(TargetBytesPtr<MMPolicy>::CopyFromOffset(
897             aOther.mTargetBytes, aOffsetFromOther)),
898         mOffset(0) {}
899 
900  private:
901   mutable typename TargetBytesPtr<MMPolicy>::Type mTargetBytes;
902   uint32_t mOffset;
903 };
904 
905 template <typename MMPolicy, typename T>
906 class MOZ_STACK_CLASS TargetObject {
907   mutable typename TargetBytesPtr<MMPolicy>::Type mTargetBytes;
908 
909   TargetObject(const MMPolicy& aMMPolicy, const void* aBaseAddress)
910       : mTargetBytes(TargetBytesPtr<MMPolicy>::Make(aMMPolicy, aBaseAddress)) {
911     mTargetBytes->EnsureLimit(sizeof(T));
912   }
913 
914  public:
915   explicit TargetObject(const MMPolicy& aMMPolicy)
916       : mTargetBytes(TargetBytesPtr<MMPolicy>::Make(aMMPolicy, nullptr)) {}
917 
918   TargetObject(const MMPolicy& aMMPolicy, uintptr_t aBaseAddress)
919       : TargetObject(aMMPolicy, reinterpret_cast<const void*>(aBaseAddress)) {}
920 
921   TargetObject(const TargetObject&) = delete;
922   TargetObject(TargetObject&&) = delete;
923   TargetObject& operator=(const TargetObject&) = delete;
924   TargetObject& operator=(TargetObject&&) = delete;
925 
926   explicit operator bool() const {
927     return mTargetBytes->GetBase() && mTargetBytes->GetLocalBytes();
928   }
929 
930   const T* operator->() const {
931     return reinterpret_cast<const T*>(mTargetBytes->GetLocalBytes());
932   }
933 
934   const T* GetLocalBase() const {
935     return reinterpret_cast<const T*>(mTargetBytes->GetLocalBytes());
936   }
937 };
938 
939 template <typename MMPolicy, typename T>
940 class MOZ_STACK_CLASS TargetObjectArray {
941   mutable typename TargetBytesPtr<MMPolicy>::Type mTargetBytes;
942   size_t mNumOfItems;
943 
944   TargetObjectArray(const MMPolicy& aMMPolicy, const void* aBaseAddress,
945                     size_t aNumOfItems)
946       : mTargetBytes(TargetBytesPtr<MMPolicy>::Make(aMMPolicy, aBaseAddress)),
947         mNumOfItems(aNumOfItems) {
948     uint32_t itemsRead =
949         mTargetBytes->TryEnsureLimit(sizeof(T) * mNumOfItems) / sizeof(T);
950     // itemsRead may be bigger than the requested amount because of buffering,
951     // but mNumOfItems should not include extra bytes of buffering.
952     if (itemsRead < mNumOfItems) {
953       mNumOfItems = itemsRead;
954     }
955   }
956 
957   const T* GetLocalBase() const {
958     return reinterpret_cast<const T*>(mTargetBytes->GetLocalBytes());
959   }
960 
961  public:
962   explicit TargetObjectArray(const MMPolicy& aMMPolicy)
963       : mTargetBytes(TargetBytesPtr<MMPolicy>::Make(aMMPolicy, nullptr)),
964         mNumOfItems(0) {}
965 
966   TargetObjectArray(const MMPolicy& aMMPolicy, uintptr_t aBaseAddress,
967                     size_t aNumOfItems)
968       : TargetObjectArray(aMMPolicy,
969                           reinterpret_cast<const void*>(aBaseAddress),
970                           aNumOfItems) {}
971 
972   TargetObjectArray(const TargetObjectArray&) = delete;
973   TargetObjectArray(TargetObjectArray&&) = delete;
974   TargetObjectArray& operator=(const TargetObjectArray&) = delete;
975   TargetObjectArray& operator=(TargetObjectArray&&) = delete;
976 
977   explicit operator bool() const {
978     return mTargetBytes->GetBase() && mNumOfItems;
979   }
980 
981   const T* operator[](size_t aIndex) const {
982     if (aIndex >= mNumOfItems) {
983       return nullptr;
984     }
985 
986     return &GetLocalBase()[aIndex];
987   }
988 
989   template <typename Comparator>
990   bool BinarySearchIf(const Comparator& aCompare,
991                       size_t* aMatchOrInsertionPoint) const {
992     return mozilla::BinarySearchIf(GetLocalBase(), 0, mNumOfItems, aCompare,
993                                    aMatchOrInsertionPoint);
994   }
995 };
996 
997 }  // namespace interceptor
998 }  // namespace mozilla
999 
1000 #endif  // mozilla_interceptor_TargetFunction_h
1001