1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // This file contains unit tests for InterceptionManager.
6 // The tests require private information so the whole interception.cc file is
7 // included from this file.
14 #include "base/bits.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "sandbox/win/src/interception.h"
17 #include "sandbox/win/src/interceptors.h"
18 #include "sandbox/win/src/interception_internal.h"
19 #include "sandbox/win/src/target_process.h"
20 #include "testing/gtest/include/gtest/gtest.h"
25 size_t GetGranularAlignedRandomOffset(size_t size
);
28 // Walks the settings buffer, verifying that the values make sense and counting
31 // buffer (in): the buffer to walk.
32 // size (in): buffer size
33 // num_dlls (out): count of the dlls on the buffer.
34 // num_function (out): count of intercepted functions.
35 // num_names (out): count of named interceptor functions.
36 void WalkBuffer(void* buffer
, size_t size
, int* num_dlls
, int* num_functions
,
38 ASSERT_TRUE(NULL
!= buffer
);
39 ASSERT_TRUE(NULL
!= num_functions
);
40 ASSERT_TRUE(NULL
!= num_names
);
41 *num_dlls
= *num_functions
= *num_names
= 0;
42 SharedMemory
*memory
= reinterpret_cast<SharedMemory
*>(buffer
);
44 ASSERT_GT(size
, sizeof(SharedMemory
));
45 DllPatchInfo
*dll
= &memory
->dll_list
[0];
47 for (int i
= 0; i
< memory
->num_intercepted_dlls
; i
++) {
48 ASSERT_NE(0u, wcslen(dll
->dll_name
));
49 ASSERT_EQ(0u, dll
->record_bytes
% sizeof(size_t));
50 ASSERT_EQ(0u, dll
->offset_to_functions
% sizeof(size_t));
51 ASSERT_NE(0, dll
->num_functions
);
53 FunctionInfo
*function
= reinterpret_cast<FunctionInfo
*>(
54 reinterpret_cast<char*>(dll
) + dll
->offset_to_functions
);
56 for (int j
= 0; j
< dll
->num_functions
; j
++) {
57 ASSERT_EQ(0u, function
->record_bytes
% sizeof(size_t));
59 char* name
= function
->function
;
60 size_t length
= strlen(name
);
61 ASSERT_NE(0u, length
);
65 ASSERT_GT(reinterpret_cast<char*>(buffer
) + size
, name
+ strlen(name
));
67 // look for a named interceptor
70 EXPECT_TRUE(NULL
== function
->interceptor_address
);
72 EXPECT_TRUE(NULL
!= function
->interceptor_address
);
76 function
= reinterpret_cast<FunctionInfo
*>(
77 reinterpret_cast<char*>(function
) + function
->record_bytes
);
81 dll
= reinterpret_cast<DllPatchInfo
*>(reinterpret_cast<char*>(dll
) +
86 TEST(InterceptionManagerTest
, GetGranularAlignedRandomOffset
) {
87 std::set
<size_t> sizes
;
89 // 544 is current value of interceptions_.size() * sizeof(ThunkData) +
90 // sizeof(DllInterceptionData).
91 const size_t kThunkBytes
= 544;
93 // ciel(log2(544)) = 10.
94 // Alignment must be 2^10 = 1024.
95 const size_t kAlignmentBits
= base::bits::Log2Ceiling(kThunkBytes
);
96 const size_t kAlignment
= 1 << kAlignmentBits
;
98 const size_t kAllocGranularity
= 65536;
100 // Generate enough sample data to ensure there is at least one value in each
102 for (size_t i
= 0; i
< 1000000; i
++)
103 sizes
.insert(internal::GetGranularAlignedRandomOffset(kThunkBytes
));
106 size_t min_val
= kAllocGranularity
;
107 size_t min_nonzero_val
= kAllocGranularity
;
110 for (size_t val
: sizes
) {
111 ASSERT_LT(val
, kAllocGranularity
);
113 ASSERT_EQ(val
- prev_val
, kAlignment
);
115 min_nonzero_val
= std::min(val
, min_nonzero_val
);
116 min_val
= std::min(val
, min_val
);
118 max_val
= std::max(val
, max_val
);
120 ASSERT_EQ(max_val
, kAllocGranularity
- kAlignment
);
121 ASSERT_EQ(min_val
, 0);
122 ASSERT_EQ(min_nonzero_val
, kAlignment
);
125 TEST(InterceptionManagerTest
, BufferLayout1
) {
126 wchar_t exe_name
[MAX_PATH
];
127 ASSERT_NE(0u, GetModuleFileName(NULL
, exe_name
, MAX_PATH
- 1));
129 TargetProcess
*target
= MakeTestTargetProcess(::GetCurrentProcess(),
130 ::GetModuleHandle(exe_name
));
132 InterceptionManager
interceptions(target
, true);
134 // Any pointer will do for a function pointer.
135 void* function
= &interceptions
;
137 // We don't care about the interceptor id.
138 interceptions
.AddToPatchedFunctions(L
"ntdll.dll", "NtCreateFile",
139 INTERCEPTION_SERVICE_CALL
, function
,
141 interceptions
.AddToPatchedFunctions(L
"kernel32.dll", "CreateFileEx",
142 INTERCEPTION_EAT
, function
, OPEN_KEY_ID
);
143 interceptions
.AddToPatchedFunctions(L
"kernel32.dll", "SomeFileEx",
144 INTERCEPTION_SMART_SIDESTEP
, function
,
146 interceptions
.AddToPatchedFunctions(L
"user32.dll", "FindWindow",
147 INTERCEPTION_EAT
, function
, OPEN_KEY_ID
);
148 interceptions
.AddToPatchedFunctions(L
"kernel32.dll", "CreateMutex",
149 INTERCEPTION_EAT
, function
, OPEN_KEY_ID
);
150 interceptions
.AddToPatchedFunctions(L
"user32.dll", "PostMsg",
151 INTERCEPTION_EAT
, function
, OPEN_KEY_ID
);
152 interceptions
.AddToPatchedFunctions(L
"user32.dll", "PostMsg",
153 INTERCEPTION_EAT
, "replacement",
155 interceptions
.AddToPatchedFunctions(L
"comctl.dll", "SaveAsDlg",
156 INTERCEPTION_EAT
, function
, OPEN_KEY_ID
);
157 interceptions
.AddToPatchedFunctions(L
"ntdll.dll", "NtClose",
158 INTERCEPTION_SERVICE_CALL
, function
,
160 interceptions
.AddToPatchedFunctions(L
"ntdll.dll", "NtOpenFile",
161 INTERCEPTION_SIDESTEP
, function
,
163 interceptions
.AddToPatchedFunctions(L
"some.dll", "Superfn",
164 INTERCEPTION_EAT
, function
, OPEN_KEY_ID
);
165 interceptions
.AddToPatchedFunctions(L
"comctl.dll", "SaveAsDlg",
166 INTERCEPTION_EAT
, "a", OPEN_KEY_ID
);
167 interceptions
.AddToPatchedFunctions(L
"comctl.dll", "SaveAsDlg",
168 INTERCEPTION_SIDESTEP
, "ab", OPEN_KEY_ID
);
169 interceptions
.AddToPatchedFunctions(L
"comctl.dll", "SaveAsDlg",
170 INTERCEPTION_EAT
, "abc", OPEN_KEY_ID
);
171 interceptions
.AddToPatchedFunctions(L
"a.dll", "p",
172 INTERCEPTION_EAT
, function
, OPEN_KEY_ID
);
173 interceptions
.AddToPatchedFunctions(L
"b.dll",
174 "TheIncredibleCallToSaveTheWorld",
175 INTERCEPTION_EAT
, function
, OPEN_KEY_ID
);
176 interceptions
.AddToPatchedFunctions(L
"a.dll", "BIsLame",
177 INTERCEPTION_EAT
, function
, OPEN_KEY_ID
);
178 interceptions
.AddToPatchedFunctions(L
"a.dll", "ARules",
179 INTERCEPTION_EAT
, function
, OPEN_KEY_ID
);
181 // Verify that all interceptions were added
182 ASSERT_EQ(18, interceptions
.interceptions_
.size());
184 size_t buffer_size
= interceptions
.GetBufferSize();
185 scoped_ptr
<BYTE
[]> local_buffer(new BYTE
[buffer_size
]);
187 ASSERT_TRUE(interceptions
.SetupConfigBuffer(local_buffer
.get(),
190 // At this point, the interceptions should have been separated into two
191 // groups: one group with the local ("cold") interceptions, consisting of
192 // everything from ntdll and stuff set as INTRECEPTION_SERVICE_CALL, and
193 // another group with the interceptions belonging to dlls that will be "hot"
194 // patched on the client. The second group lives on local_buffer, and the
195 // first group remains on the list of interceptions (inside the object
196 // "interceptions"). There are 3 local interceptions (of ntdll); the
197 // other 15 have to be sent to the child to be performed "hot".
198 EXPECT_EQ(3, interceptions
.interceptions_
.size());
200 int num_dlls
, num_functions
, num_names
;
201 WalkBuffer(local_buffer
.get(), buffer_size
, &num_dlls
, &num_functions
,
204 // The 15 interceptions on the buffer (to the child) should be grouped on 6
205 // dlls. Only four interceptions are using an explicit name for the
206 // interceptor function.
207 EXPECT_EQ(6, num_dlls
);
208 EXPECT_EQ(15, num_functions
);
209 EXPECT_EQ(4, num_names
);
212 TEST(InterceptionManagerTest
, BufferLayout2
) {
213 wchar_t exe_name
[MAX_PATH
];
214 ASSERT_NE(0u, GetModuleFileName(NULL
, exe_name
, MAX_PATH
- 1));
216 TargetProcess
*target
= MakeTestTargetProcess(::GetCurrentProcess(),
217 ::GetModuleHandle(exe_name
));
219 InterceptionManager
interceptions(target
, true);
221 // Any pointer will do for a function pointer.
222 void* function
= &interceptions
;
223 interceptions
.AddToUnloadModules(L
"some01.dll");
224 // We don't care about the interceptor id.
225 interceptions
.AddToPatchedFunctions(L
"ntdll.dll", "NtCreateFile",
226 INTERCEPTION_SERVICE_CALL
, function
,
228 interceptions
.AddToPatchedFunctions(L
"kernel32.dll", "CreateFileEx",
229 INTERCEPTION_EAT
, function
, OPEN_FILE_ID
);
230 interceptions
.AddToUnloadModules(L
"some02.dll");
231 interceptions
.AddToPatchedFunctions(L
"kernel32.dll", "SomeFileEx",
232 INTERCEPTION_SMART_SIDESTEP
, function
,
234 // Verify that all interceptions were added
235 ASSERT_EQ(5, interceptions
.interceptions_
.size());
237 size_t buffer_size
= interceptions
.GetBufferSize();
238 scoped_ptr
<BYTE
[]> local_buffer(new BYTE
[buffer_size
]);
240 ASSERT_TRUE(interceptions
.SetupConfigBuffer(local_buffer
.get(),
243 // At this point, the interceptions should have been separated into two
244 // groups: one group with the local ("cold") interceptions, and another
245 // group with the interceptions belonging to dlls that will be "hot"
246 // patched on the client. The second group lives on local_buffer, and the
247 // first group remains on the list of interceptions, in this case just one.
248 EXPECT_EQ(1, interceptions
.interceptions_
.size());
250 int num_dlls
, num_functions
, num_names
;
251 WalkBuffer(local_buffer
.get(), buffer_size
, &num_dlls
, &num_functions
,
254 EXPECT_EQ(3, num_dlls
);
255 EXPECT_EQ(4, num_functions
);
256 EXPECT_EQ(0, num_names
);
259 } // namespace sandbox