1 : // Copyright 2015 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 : //
15 : // Declares a helper class for use in performing hot-patching operations.
16 : // The class takes care of modifying page protections as the patcher works.
17 :
18 : #include "syzygy/agent/asan/scoped_page_protections.h"
19 :
20 : #include "base/logging.h"
21 : #include "syzygy/agent/asan/constants.h"
22 : #include "syzygy/common/align.h"
23 : #include "syzygy/common/com_utils.h"
24 :
25 : namespace agent {
26 : namespace asan {
27 :
28 E : ScopedPageProtections::~ScopedPageProtections() {
29 E : RestorePageProtections();
30 E : }
31 :
32 : bool ScopedPageProtections::EnsureContainingPagesWritable(void* addr,
33 E : size_t size) {
34 : // Ensure the entire space of pages covered by the provided range is
35 : // writable.
36 E : uint8* cursor = reinterpret_cast<uint8*>(addr);
37 E : uint8* page_begin = common::AlignDown(cursor, GetPageSize());
38 E : uint8* page_end = common::AlignUp(cursor + size, GetPageSize());
39 E : while (page_begin < page_end) {
40 E : if (!EnsurePageWritable(page_begin))
41 i : return false;
42 E : page_begin += GetPageSize();
43 E : }
44 :
45 E : return true;
46 E : }
47 :
48 E : bool ScopedPageProtections::RestorePageProtections() {
49 : // Grab the list of pages to unprotect.
50 E : UnprotectedPages to_unprotect;
51 E : unprotected_pages_.swap(to_unprotect);
52 :
53 : // Best-effort restore the old page protections, and remember pages for
54 : // which the effort failed.
55 E : bool did_succeed = true;
56 E : for (const auto& unprotected_page : to_unprotect) {
57 E : DWORD old_prot = 0;
58 : if (!::VirtualProtect(unprotected_page.first, GetPageSize(),
59 E : unprotected_page.second, &old_prot)) {
60 i : DWORD error = ::GetLastError();
61 i : LOG(ERROR) << "VirtualProtect failed: " << common::LogWe(error);
62 :
63 : // Pages that failed to be unprotected are reinserted into the set of
64 : // pages being tracked.
65 i : bool inserted = unprotected_pages_.insert(unprotected_page).second;
66 i : DCHECK(inserted);
67 :
68 i : did_succeed = false;
69 : }
70 E : }
71 :
72 E : return did_succeed;
73 E : }
74 :
75 E : bool ScopedPageProtections::EnsurePageWritable(void* page) {
76 E : DCHECK(common::IsAligned(page, GetPageSize()));
77 :
78 : // Check whether we've already unprotected this page.
79 E : if (unprotected_pages_.find(page) != unprotected_pages_.end())
80 E : return true;
81 :
82 : // We didn't unprotect this yet, make the page writable.
83 E : MEMORY_BASIC_INFORMATION memory_info{};
84 E : if (!::VirtualQuery(page, &memory_info, sizeof(memory_info))) {
85 i : DWORD error = ::GetLastError();
86 i : LOG(ERROR) << "VirtualQuery failed: " << common::LogWe(error);
87 i : return false;
88 : }
89 :
90 : // Preserve executable status while patching.
91 : DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
92 : PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
93 E : memory_info.Protect;
94 E : DWORD new_prot = PAGE_READWRITE;
95 E : if (is_executable)
96 E : new_prot = PAGE_EXECUTE_READWRITE;
97 :
98 E : DWORD old_prot = 0;
99 E : if (!::VirtualProtect(page, GetPageSize(), new_prot, &old_prot)) {
100 i : DWORD error = ::GetLastError();
101 i : LOG(ERROR) << "VirtualProtect failed: " << common::LogWe(error);
102 i : return false;
103 : }
104 :
105 : // Make a note that we modified this page, as well as its original settings.
106 : bool inserted =
107 E : unprotected_pages_.insert(std::make_pair(page, old_prot)).second;
108 E : DCHECK(inserted);
109 :
110 E : return true;
111 E : }
112 :
113 : } // namespace asan
114 : } // namespace agent
|