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