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