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 : #include "syzygy/agent/asan/memory_interceptors.h"
15 :
16 : #include <stdint.h>
17 :
18 : #include "base/logging.h"
19 : #include "base/macros.h"
20 : #include "syzygy/agent/asan/rtl_utils.h"
21 : #include "syzygy/agent/asan/shadow.h"
22 :
23 : using agent::asan::Shadow;
24 :
25 : namespace agent {
26 : namespace asan {
27 : namespace {
28 :
29 E : RedirectEntryCallback redirect_entry_callback;
30 :
31 : // The global shadow memory that is used by the memory interceptors.
32 : // This is only used by interceptors that make use of the Shadow API.
33 : // Interceptors with direct reference (the basic read/write probes) to the
34 : // shadow memory must be patched directly.
35 : Shadow* memory_interceptor_shadow_ = nullptr;
36 :
37 : } // namespace
38 :
39 E : Shadow* SetMemoryInterceptorShadow(Shadow* shadow) {
40 E : Shadow* old_shadow = memory_interceptor_shadow_;
41 E : memory_interceptor_shadow_ = shadow;
42 E : return old_shadow;
43 E : }
44 :
45 : const MemoryAccessorVariants kMemoryAccessorVariants[] = {
46 : #define ENUM_MEM_INTERCEPT_FUNCTION_VARIANTS(access_size, access_mode_str, \
47 : access_mode_value) \
48 : { "asan_check_" #access_size "_byte_" #access_mode_str, \
49 : asan_redirect_##access_size##_byte_##access_mode_str, asan_no_check, \
50 : asan_check_##access_size##_byte_##access_mode_str##_2gb, \
51 : asan_check_##access_size##_byte_##access_mode_str##_4gb \
52 : }, \
53 : { "asan_check_" #access_size "_byte_" #access_mode_str "_no_flags", \
54 : asan_redirect_##access_size##_byte_##access_mode_str##_no_flags, \
55 : asan_no_check, \
56 : asan_check_##access_size##_byte_##access_mode_str##_no_flags_2gb, \
57 : asan_check_##access_size##_byte_##access_mode_str##_no_flags_4gb},
58 :
59 : ASAN_MEM_INTERCEPT_FUNCTIONS(ENUM_MEM_INTERCEPT_FUNCTION_VARIANTS)
60 :
61 : #undef ENUM_MEM_INTERCEPT_FUNCTION_VARIANTS
62 :
63 : #define ENUM_STRING_INTERCEPT_FUNCTION_VARIANTS( \
64 : func, prefix, counter, dst_mode, src_mode, access_size, compare) \
65 : { "asan_check" #prefix #access_size "_byte_" #func "_access", \
66 : asan_redirect ## prefix ## access_size ## _byte_ ## func ## _access, \
67 : asan_string_no_check, \
68 : asan_check ## prefix ## access_size ## _byte_ ## func ## _access, \
69 : asan_check ## prefix ## access_size ## _byte_ ## func ## _access, \
70 : },
71 :
72 : ASAN_STRING_INTERCEPT_FUNCTIONS(ENUM_STRING_INTERCEPT_FUNCTION_VARIANTS)
73 :
74 : #undef ENUM_STRING_INTERCEPT_FUNCTION_VARIANTS
75 : };
76 :
77 : const size_t kNumMemoryAccessorVariants = arraysize(kMemoryAccessorVariants);
78 :
79 E : void SetRedirectEntryCallback(const RedirectEntryCallback& callback) {
80 E : redirect_entry_callback = callback;
81 E : }
82 :
83 : // Check if the memory location is accessible and report an error on bad memory
84 : // accesses.
85 : // @param location The memory address of the access.
86 : // @param access_mode The mode of the access.
87 : // @param access_size The size of the access.
88 : // @param context The registers context of the access.
89 : void CheckMemoryAccess(void* location,
90 : AccessMode access_mode,
91 : size_t access_size,
92 E : const AsanContext& context) {
93 : if (memory_interceptor_shadow_ &&
94 E : !memory_interceptor_shadow_->IsAccessible(location)) {
95 E : ReportBadMemoryAccess(location, access_mode, access_size, context);
96 : }
97 E : }
98 :
99 : // The slow path relies on the fact that the shadow memory non accessible byte
100 : // mask has its upper bit set to 1.
101 : static_assert((kHeapNonAccessibleMarkerMask & (1 << 7)) != 0,
102 : "Asan shadow mask upper bit is 0.");
103 :
104 : extern "C" {
105 :
106 : // Check if the memory accesses done by a string instructions are valid.
107 : // @param dst The destination memory address of the access.
108 : // @param dst_access_mode The destination mode of the access.
109 : // @param src The source memory address of the access.
110 : // @param src_access_mode The source mode of the access.
111 : // @param length The number of memory accesses.
112 : // @param access_size The size of each the access in byte.
113 : // @param increment The increment to move dst/src after each access.
114 : // @param compare Flag to activate shortcut of the execution on difference.
115 : // @param context The registers context of the access.
116 : void asan_check_strings_memory_accesses(uint8* dst,
117 : AccessMode dst_access_mode,
118 : uint8* src,
119 : AccessMode src_access_mode,
120 : uint32 length,
121 : size_t access_size,
122 : int32 increment,
123 : bool compare,
124 E : const AsanContext& context) {
125 E : int32 offset = 0;
126 :
127 E : for (uint32 i = 0; i < length; ++i) {
128 : // Check next memory location at src[offset].
129 E : if (src_access_mode != agent::asan::ASAN_UNKNOWN_ACCESS)
130 E : CheckMemoryAccess(&src[offset], src_access_mode, access_size, context);
131 :
132 : // Check next memory location at dst[offset].
133 E : if (dst_access_mode != agent::asan::ASAN_UNKNOWN_ACCESS)
134 E : CheckMemoryAccess(&dst[offset], dst_access_mode, access_size, context);
135 :
136 : // For CMPS instructions, we shortcut the execution of prefix REPZ when
137 : // memory contents differ.
138 E : if (compare) {
139 E : uint32 src_content = 0;
140 E : uint32 dst_content = 0;
141 E : switch (access_size) {
142 : case 4:
143 E : src_content = *reinterpret_cast<uint32*>(&src[offset]);
144 E : dst_content = *reinterpret_cast<uint32*>(&dst[offset]);
145 E : break;
146 : case 2:
147 E : src_content = *reinterpret_cast<uint16*>(&src[offset]);
148 E : dst_content = *reinterpret_cast<uint16*>(&dst[offset]);
149 E : break;
150 : case 1:
151 E : src_content = *reinterpret_cast<uint8*>(&src[offset]);
152 E : dst_content = *reinterpret_cast<uint8*>(&dst[offset]);
153 E : break;
154 : default:
155 i : NOTREACHED() << "Unexpected access_size.";
156 : break;
157 : }
158 :
159 E : if (src_content != dst_content)
160 E : return;
161 : }
162 :
163 : // Increments offset of dst/src to the next memory location.
164 E : offset += increment;
165 E : }
166 E : }
167 :
168 : MemoryAccessorFunction asan_redirect_stub_entry(
169 : const void* caller_address,
170 E : MemoryAccessorFunction called_redirect) {
171 E : MemoryAccessorMode mode = MEMORY_ACCESSOR_MODE_NOOP;
172 :
173 : // TODO(siggi): Does it make sense to CHECK on this?
174 E : if (!redirect_entry_callback.is_null())
175 E : mode = redirect_entry_callback.Run(caller_address);
176 :
177 E : for (size_t i = 0; i < arraysize(kMemoryAccessorVariants); ++i) {
178 E : if (kMemoryAccessorVariants[i].redirect_accessor != called_redirect)
179 E : continue;
180 E : CHECK_LE(0u, mode);
181 E : CHECK_GT(MEMORY_ACCESSOR_MODE_MAX, mode);
182 E : return kMemoryAccessorVariants[i].accessors[mode];
183 i : }
184 :
185 i : NOTREACHED();
186 i : return NULL;
187 E : }
188 :
189 : // A simple wrapper to agent::asan::ReportBadMemoryAccess that has C linkage
190 : // so it can be referred to in memory_interceptors.asm.
191 : void asan_report_bad_memory_access(void* location,
192 : AccessMode access_mode,
193 : size_t access_size,
194 E : const AsanContext& asan_context) {
195 : return agent::asan::ReportBadMemoryAccess(location, access_mode, access_size,
196 E : asan_context);
197 E : }
198 :
199 : } // extern "C"
200 :
201 : } // namespace asan
202 : } // namespace agent
|