1 : // Copyright 2014 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 : #include "syzygy/agent/asan/page_protection_helpers.h"
16 :
17 : namespace agent {
18 : namespace asan {
19 :
20 : // TODO(chrisha): Move the page protections bits out of the shadow to an entire
21 : // class that lives here. Or move all of this to shadow.
22 :
23 E : ::common::RecursiveLock block_protect_lock;
24 :
25 : bool GetBlockInfo(const Shadow* shadow,
26 : const BlockBody* body,
27 E : CompactBlockInfo* block_info) {
28 E : DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
29 E : DCHECK_NE(static_cast<BlockBody*>(nullptr), body);
30 E : DCHECK_NE(static_cast<CompactBlockInfo*>(nullptr), block_info);
31 :
32 : // Try reading directly from memory first.
33 E : const uint8* addr_in_redzone = reinterpret_cast<const uint8*>(body) - 1;
34 E : if (!shadow->PageIsProtected(addr_in_redzone)) {
35 : // If this succeeds then we're done. It can fail if the page protections
36 : // are actually active, or if the header is corrupt. In this case we'll
37 : // fall through and look at the shadow memory.
38 E : BlockHeader* header = BlockGetHeaderFromBody(body);
39 E : if (header != nullptr && BlockInfoFromMemory(header, block_info))
40 E : return true;
41 : }
42 :
43 E : if (!shadow->BlockInfoFromShadow(body, block_info))
44 i : return false;
45 :
46 E : return true;
47 E : }
48 :
49 : bool GetBlockInfo(const Shadow* shadow,
50 : const BlockBody* body,
51 E : BlockInfo* block_info) {
52 E : DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
53 E : DCHECK_NE(static_cast<BlockBody*>(nullptr), body);
54 E : DCHECK_NE(static_cast<BlockInfo*>(nullptr), block_info);
55 E : CompactBlockInfo compact = {};
56 E : if (!GetBlockInfo(shadow, body, &compact))
57 i : return false;
58 E : ConvertBlockInfo(compact, block_info);
59 E : return true;
60 E : }
61 :
62 E : void BlockProtectNone(const BlockInfo& block_info, Shadow* shadow) {
63 E : DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
64 E : if (block_info.block_pages_size == 0)
65 E : return;
66 :
67 E : ::common::AutoRecursiveLock lock(block_protect_lock);
68 E : DCHECK_NE(static_cast<uint8*>(nullptr), block_info.block_pages);
69 E : DWORD old_protection = 0;
70 : DWORD ret = ::VirtualProtect(block_info.block_pages,
71 : block_info.block_pages_size,
72 E : PAGE_READWRITE, &old_protection);
73 E : CHECK_NE(0u, ret);
74 : shadow->MarkPagesUnprotected(block_info.block_pages,
75 E : block_info.block_pages_size);
76 E : }
77 :
78 E : void BlockProtectRedzones(const BlockInfo& block_info, Shadow* shadow) {
79 E : DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
80 E : if (block_info.block_pages_size == 0)
81 E : return;
82 :
83 E : ::common::AutoRecursiveLock lock(block_protect_lock);
84 E : BlockProtectNone(block_info, shadow);
85 :
86 : // Protect the left redzone pages if any.
87 E : DWORD old_protection = 0;
88 E : DWORD ret = 0;
89 E : if (block_info.left_redzone_pages_size > 0) {
90 E : DCHECK_NE(static_cast<uint8*>(nullptr), block_info.left_redzone_pages);
91 : ret = ::VirtualProtect(block_info.left_redzone_pages,
92 : block_info.left_redzone_pages_size,
93 E : PAGE_NOACCESS, &old_protection);
94 E : DCHECK_NE(0u, ret);
95 : shadow->MarkPagesProtected(block_info.left_redzone_pages,
96 E : block_info.left_redzone_pages_size);
97 : }
98 :
99 : // Protect the right redzone pages if any.
100 E : if (block_info.right_redzone_pages_size > 0) {
101 : DCHECK_NE(static_cast<uint8*>(nullptr),
102 E : block_info.right_redzone_pages);
103 : ret = ::VirtualProtect(block_info.right_redzone_pages,
104 : block_info.right_redzone_pages_size,
105 E : PAGE_NOACCESS, &old_protection);
106 E : DCHECK_NE(0u, ret);
107 : shadow->MarkPagesProtected(block_info.right_redzone_pages,
108 E : block_info.right_redzone_pages_size);
109 : }
110 E : }
111 :
112 E : void BlockProtectAll(const BlockInfo& block_info, Shadow* shadow) {
113 E : DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
114 E : if (block_info.block_pages_size == 0)
115 E : return;
116 :
117 E : ::common::AutoRecursiveLock lock(block_protect_lock);
118 E : DCHECK_NE(static_cast<uint8*>(nullptr), block_info.block_pages);
119 E : DWORD old_protection = 0;
120 : DWORD ret = ::VirtualProtect(block_info.block_pages,
121 : block_info.block_pages_size,
122 E : PAGE_NOACCESS, &old_protection);
123 E : DCHECK_NE(0u, ret);
124 : shadow->MarkPagesProtected(block_info.block_pages,
125 E : block_info.block_pages_size);
126 E : }
127 :
128 E : void BlockProtectAuto(const BlockInfo& block_info, Shadow* shadow) {
129 E : DCHECK_NE(static_cast<Shadow*>(nullptr), shadow);
130 E : if (block_info.block_pages_size == 0)
131 E : return;
132 :
133 E : ::common::AutoRecursiveLock lock(block_protect_lock);
134 :
135 : // Remove the page protection from the header if necessary.
136 E : if (!shadow->IsAccessible(block_info.block_pages)) {
137 E : DWORD old_protection = 0;
138 : DWORD ret = ::VirtualProtect(block_info.block_pages,
139 : GetPageSize(),
140 E : PAGE_READWRITE, &old_protection);
141 E : DCHECK_NE(0u, ret);
142 : }
143 :
144 : // Now set page protections based on the block state.
145 E : switch (block_info.header->state) {
146 : // An allocated block has an accessible body but protected redzones.
147 : case ALLOCATED_BLOCK: {
148 E : BlockProtectRedzones(block_info, shadow);
149 E : break;
150 : }
151 :
152 : // No part of a quarantined or freed block is accessible.
153 : case QUARANTINED_BLOCK:
154 : case QUARANTINED_FLOODED_BLOCK:
155 : case FREED_BLOCK: {
156 E : BlockProtectAll(block_info, shadow);
157 E : break;
158 : }
159 :
160 i : default: NOTREACHED();
161 : }
162 E : }
163 :
164 : } // namespace asan
165 : } // namespace agent
|