|
1 // Copyright (c) 2012 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. |
|
4 |
|
5 #include "sandbox/win/src/sandbox_nt_util.h" |
|
6 |
|
7 #include "base/win/pe_image.h" |
|
8 #include "sandbox/win/src/sandbox_factory.h" |
|
9 #include "sandbox/win/src/target_services.h" |
|
10 |
|
11 namespace sandbox { |
|
12 |
|
13 // This is the list of all imported symbols from ntdll.dll. |
|
14 SANDBOX_INTERCEPT NtExports g_nt = { NULL }; |
|
15 |
|
16 } // namespace sandbox |
|
17 |
|
18 namespace { |
|
19 |
|
20 #if defined(_WIN64) |
|
21 void* AllocateNearTo(void* source, size_t size) { |
|
22 using sandbox::g_nt; |
|
23 |
|
24 // Start with 1 GB above the source. |
|
25 const size_t kOneGB = 0x40000000; |
|
26 void* base = reinterpret_cast<char*>(source) + kOneGB; |
|
27 SIZE_T actual_size = size; |
|
28 ULONG_PTR zero_bits = 0; // Not the correct type if used. |
|
29 ULONG type = MEM_RESERVE; |
|
30 |
|
31 NTSTATUS ret; |
|
32 int attempts = 0; |
|
33 for (; attempts < 41; attempts++) { |
|
34 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits, |
|
35 &actual_size, type, PAGE_READWRITE); |
|
36 if (NT_SUCCESS(ret)) { |
|
37 if (base < source || |
|
38 base >= reinterpret_cast<char*>(source) + 4 * kOneGB) { |
|
39 // We won't be able to patch this dll. |
|
40 VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size, |
|
41 MEM_RELEASE)); |
|
42 return NULL; |
|
43 } |
|
44 break; |
|
45 } |
|
46 |
|
47 if (attempts == 30) { |
|
48 // Try the first GB. |
|
49 base = reinterpret_cast<char*>(source); |
|
50 } else if (attempts == 40) { |
|
51 // Try the highest available address. |
|
52 base = NULL; |
|
53 type |= MEM_TOP_DOWN; |
|
54 } |
|
55 |
|
56 // Try 100 MB higher. |
|
57 base = reinterpret_cast<char*>(base) + 100 * 0x100000; |
|
58 }; |
|
59 |
|
60 if (attempts == 41) |
|
61 return NULL; |
|
62 |
|
63 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits, |
|
64 &actual_size, MEM_COMMIT, PAGE_READWRITE); |
|
65 |
|
66 if (!NT_SUCCESS(ret)) { |
|
67 VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size, |
|
68 MEM_RELEASE)); |
|
69 base = NULL; |
|
70 } |
|
71 |
|
72 return base; |
|
73 } |
|
74 #else // defined(_WIN64). |
|
75 void* AllocateNearTo(void* source, size_t size) { |
|
76 using sandbox::g_nt; |
|
77 UNREFERENCED_PARAMETER(source); |
|
78 |
|
79 // In 32-bit processes allocations below 512k are predictable, so mark |
|
80 // anything in that range as reserved and retry until we get a good address. |
|
81 const void* const kMinAddress = reinterpret_cast<void*>(512 * 1024); |
|
82 NTSTATUS ret; |
|
83 SIZE_T actual_size; |
|
84 void* base; |
|
85 do { |
|
86 base = NULL; |
|
87 actual_size = 64 * 1024; |
|
88 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size, |
|
89 MEM_RESERVE, PAGE_NOACCESS); |
|
90 if (!NT_SUCCESS(ret)) |
|
91 return NULL; |
|
92 } while (base < kMinAddress); |
|
93 |
|
94 actual_size = size; |
|
95 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size, |
|
96 MEM_COMMIT, PAGE_READWRITE); |
|
97 if (!NT_SUCCESS(ret)) |
|
98 return NULL; |
|
99 return base; |
|
100 } |
|
101 #endif // defined(_WIN64). |
|
102 |
|
103 } // namespace. |
|
104 |
|
105 namespace sandbox { |
|
106 |
|
107 // Handle for our private heap. |
|
108 void* g_heap = NULL; |
|
109 |
|
110 SANDBOX_INTERCEPT HANDLE g_shared_section; |
|
111 SANDBOX_INTERCEPT size_t g_shared_IPC_size = 0; |
|
112 SANDBOX_INTERCEPT size_t g_shared_policy_size = 0; |
|
113 |
|
114 void* volatile g_shared_policy_memory = NULL; |
|
115 void* volatile g_shared_IPC_memory = NULL; |
|
116 |
|
117 // Both the IPC and the policy share a single region of memory in which the IPC |
|
118 // memory is first and the policy memory is last. |
|
119 bool MapGlobalMemory() { |
|
120 if (NULL == g_shared_IPC_memory) { |
|
121 void* memory = NULL; |
|
122 SIZE_T size = 0; |
|
123 // Map the entire shared section from the start. |
|
124 NTSTATUS ret = g_nt.MapViewOfSection(g_shared_section, NtCurrentProcess, |
|
125 &memory, 0, 0, NULL, &size, ViewUnmap, |
|
126 0, PAGE_READWRITE); |
|
127 |
|
128 if (!NT_SUCCESS(ret) || NULL == memory) { |
|
129 NOTREACHED_NT(); |
|
130 return false; |
|
131 } |
|
132 |
|
133 if (NULL != _InterlockedCompareExchangePointer(&g_shared_IPC_memory, |
|
134 memory, NULL)) { |
|
135 // Somebody beat us to the memory setup. |
|
136 ret = g_nt.UnmapViewOfSection(NtCurrentProcess, memory); |
|
137 VERIFY_SUCCESS(ret); |
|
138 } |
|
139 DCHECK_NT(g_shared_IPC_size > 0); |
|
140 g_shared_policy_memory = reinterpret_cast<char*>(g_shared_IPC_memory) |
|
141 + g_shared_IPC_size; |
|
142 } |
|
143 DCHECK_NT(g_shared_policy_memory); |
|
144 DCHECK_NT(g_shared_policy_size > 0); |
|
145 return true; |
|
146 } |
|
147 |
|
148 void* GetGlobalIPCMemory() { |
|
149 if (!MapGlobalMemory()) |
|
150 return NULL; |
|
151 return g_shared_IPC_memory; |
|
152 } |
|
153 |
|
154 void* GetGlobalPolicyMemory() { |
|
155 if (!MapGlobalMemory()) |
|
156 return NULL; |
|
157 return g_shared_policy_memory; |
|
158 } |
|
159 |
|
160 bool InitHeap() { |
|
161 if (!g_heap) { |
|
162 // Create a new heap using default values for everything. |
|
163 void* heap = g_nt.RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL); |
|
164 if (!heap) |
|
165 return false; |
|
166 |
|
167 if (NULL != _InterlockedCompareExchangePointer(&g_heap, heap, NULL)) { |
|
168 // Somebody beat us to the memory setup. |
|
169 g_nt.RtlDestroyHeap(heap); |
|
170 } |
|
171 } |
|
172 return (g_heap != NULL); |
|
173 } |
|
174 |
|
175 // Physically reads or writes from memory to verify that (at this time), it is |
|
176 // valid. Returns a dummy value. |
|
177 int TouchMemory(void* buffer, size_t size_bytes, RequiredAccess intent) { |
|
178 const int kPageSize = 4096; |
|
179 int dummy = 0; |
|
180 char* start = reinterpret_cast<char*>(buffer); |
|
181 char* end = start + size_bytes - 1; |
|
182 |
|
183 if (WRITE == intent) { |
|
184 for (; start < end; start += kPageSize) { |
|
185 *start = 0; |
|
186 } |
|
187 *end = 0; |
|
188 } else { |
|
189 for (; start < end; start += kPageSize) { |
|
190 dummy += *start; |
|
191 } |
|
192 dummy += *end; |
|
193 } |
|
194 |
|
195 return dummy; |
|
196 } |
|
197 |
|
198 bool ValidParameter(void* buffer, size_t size, RequiredAccess intent) { |
|
199 DCHECK_NT(size); |
|
200 __try { |
|
201 TouchMemory(buffer, size, intent); |
|
202 } __except(EXCEPTION_EXECUTE_HANDLER) { |
|
203 return false; |
|
204 } |
|
205 return true; |
|
206 } |
|
207 |
|
208 NTSTATUS CopyData(void* destination, const void* source, size_t bytes) { |
|
209 NTSTATUS ret = STATUS_SUCCESS; |
|
210 __try { |
|
211 if (SandboxFactory::GetTargetServices()->GetState()->InitCalled()) { |
|
212 memcpy(destination, source, bytes); |
|
213 } else { |
|
214 const char* from = reinterpret_cast<const char*>(source); |
|
215 char* to = reinterpret_cast<char*>(destination); |
|
216 for (size_t i = 0; i < bytes; i++) { |
|
217 to[i] = from[i]; |
|
218 } |
|
219 } |
|
220 } __except(EXCEPTION_EXECUTE_HANDLER) { |
|
221 ret = GetExceptionCode(); |
|
222 } |
|
223 return ret; |
|
224 } |
|
225 |
|
226 // Hacky code... replace with AllocAndCopyObjectAttributes. |
|
227 NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object, |
|
228 wchar_t** out_name, uint32* attributes, |
|
229 HANDLE* root) { |
|
230 if (!InitHeap()) |
|
231 return STATUS_NO_MEMORY; |
|
232 |
|
233 DCHECK_NT(out_name); |
|
234 *out_name = NULL; |
|
235 NTSTATUS ret = STATUS_UNSUCCESSFUL; |
|
236 __try { |
|
237 do { |
|
238 if (in_object->RootDirectory != static_cast<HANDLE>(0) && !root) |
|
239 break; |
|
240 if (NULL == in_object->ObjectName) |
|
241 break; |
|
242 if (NULL == in_object->ObjectName->Buffer) |
|
243 break; |
|
244 |
|
245 size_t size = in_object->ObjectName->Length + sizeof(wchar_t); |
|
246 *out_name = new(NT_ALLOC) wchar_t[size/sizeof(wchar_t)]; |
|
247 if (NULL == *out_name) |
|
248 break; |
|
249 |
|
250 ret = CopyData(*out_name, in_object->ObjectName->Buffer, |
|
251 size - sizeof(wchar_t)); |
|
252 if (!NT_SUCCESS(ret)) |
|
253 break; |
|
254 |
|
255 (*out_name)[size / sizeof(wchar_t) - 1] = L'\0'; |
|
256 |
|
257 if (attributes) |
|
258 *attributes = in_object->Attributes; |
|
259 |
|
260 if (root) |
|
261 *root = in_object->RootDirectory; |
|
262 ret = STATUS_SUCCESS; |
|
263 } while (false); |
|
264 } __except(EXCEPTION_EXECUTE_HANDLER) { |
|
265 ret = GetExceptionCode(); |
|
266 } |
|
267 |
|
268 if (!NT_SUCCESS(ret) && *out_name) { |
|
269 operator delete(*out_name, NT_ALLOC); |
|
270 *out_name = NULL; |
|
271 } |
|
272 |
|
273 return ret; |
|
274 } |
|
275 |
|
276 NTSTATUS GetProcessId(HANDLE process, ULONG *process_id) { |
|
277 PROCESS_BASIC_INFORMATION proc_info; |
|
278 ULONG bytes_returned; |
|
279 |
|
280 NTSTATUS ret = g_nt.QueryInformationProcess(process, ProcessBasicInformation, |
|
281 &proc_info, sizeof(proc_info), |
|
282 &bytes_returned); |
|
283 if (!NT_SUCCESS(ret) || sizeof(proc_info) != bytes_returned) |
|
284 return ret; |
|
285 |
|
286 *process_id = proc_info.UniqueProcessId; |
|
287 return STATUS_SUCCESS; |
|
288 } |
|
289 |
|
290 bool IsSameProcess(HANDLE process) { |
|
291 if (NtCurrentProcess == process) |
|
292 return true; |
|
293 |
|
294 static ULONG s_process_id = 0; |
|
295 |
|
296 if (!s_process_id) { |
|
297 NTSTATUS ret = GetProcessId(NtCurrentProcess, &s_process_id); |
|
298 if (!NT_SUCCESS(ret)) |
|
299 return false; |
|
300 } |
|
301 |
|
302 ULONG process_id; |
|
303 NTSTATUS ret = GetProcessId(process, &process_id); |
|
304 if (!NT_SUCCESS(ret)) |
|
305 return false; |
|
306 |
|
307 return (process_id == s_process_id); |
|
308 } |
|
309 |
|
310 bool IsValidImageSection(HANDLE section, PVOID *base, PLARGE_INTEGER offset, |
|
311 PSIZE_T view_size) { |
|
312 if (!section || !base || !view_size || offset) |
|
313 return false; |
|
314 |
|
315 HANDLE query_section; |
|
316 |
|
317 NTSTATUS ret = g_nt.DuplicateObject(NtCurrentProcess, section, |
|
318 NtCurrentProcess, &query_section, |
|
319 SECTION_QUERY, 0, 0); |
|
320 if (!NT_SUCCESS(ret)) |
|
321 return false; |
|
322 |
|
323 SECTION_BASIC_INFORMATION basic_info; |
|
324 SIZE_T bytes_returned; |
|
325 ret = g_nt.QuerySection(query_section, SectionBasicInformation, &basic_info, |
|
326 sizeof(basic_info), &bytes_returned); |
|
327 |
|
328 VERIFY_SUCCESS(g_nt.Close(query_section)); |
|
329 |
|
330 if (!NT_SUCCESS(ret) || sizeof(basic_info) != bytes_returned) |
|
331 return false; |
|
332 |
|
333 if (!(basic_info.Attributes & SEC_IMAGE)) |
|
334 return false; |
|
335 |
|
336 return true; |
|
337 } |
|
338 |
|
339 UNICODE_STRING* AnsiToUnicode(const char* string) { |
|
340 ANSI_STRING ansi_string; |
|
341 ansi_string.Length = static_cast<USHORT>(g_nt.strlen(string)); |
|
342 ansi_string.MaximumLength = ansi_string.Length + 1; |
|
343 ansi_string.Buffer = const_cast<char*>(string); |
|
344 |
|
345 if (ansi_string.Length > ansi_string.MaximumLength) |
|
346 return NULL; |
|
347 |
|
348 size_t name_bytes = ansi_string.MaximumLength * sizeof(wchar_t) + |
|
349 sizeof(UNICODE_STRING); |
|
350 |
|
351 UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>( |
|
352 new(NT_ALLOC) char[name_bytes]); |
|
353 if (!out_string) |
|
354 return NULL; |
|
355 |
|
356 out_string->MaximumLength = ansi_string.MaximumLength * sizeof(wchar_t); |
|
357 out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]); |
|
358 |
|
359 BOOLEAN alloc_destination = FALSE; |
|
360 NTSTATUS ret = g_nt.RtlAnsiStringToUnicodeString(out_string, &ansi_string, |
|
361 alloc_destination); |
|
362 DCHECK_NT(STATUS_BUFFER_OVERFLOW != ret); |
|
363 if (!NT_SUCCESS(ret)) { |
|
364 operator delete(out_string, NT_ALLOC); |
|
365 return NULL; |
|
366 } |
|
367 |
|
368 return out_string; |
|
369 } |
|
370 |
|
371 UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32* flags) { |
|
372 UNICODE_STRING* out_name = NULL; |
|
373 __try { |
|
374 do { |
|
375 *flags = 0; |
|
376 base::win::PEImage pe(module); |
|
377 |
|
378 if (!pe.VerifyMagic()) |
|
379 break; |
|
380 *flags |= MODULE_IS_PE_IMAGE; |
|
381 |
|
382 PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory(); |
|
383 if (exports) { |
|
384 char* name = reinterpret_cast<char*>(pe.RVAToAddr(exports->Name)); |
|
385 out_name = AnsiToUnicode(name); |
|
386 } |
|
387 |
|
388 PIMAGE_NT_HEADERS headers = pe.GetNTHeaders(); |
|
389 if (headers) { |
|
390 if (headers->OptionalHeader.AddressOfEntryPoint) |
|
391 *flags |= MODULE_HAS_ENTRY_POINT; |
|
392 if (headers->OptionalHeader.SizeOfCode) |
|
393 *flags |= MODULE_HAS_CODE; |
|
394 } |
|
395 } while (false); |
|
396 } __except(EXCEPTION_EXECUTE_HANDLER) { |
|
397 } |
|
398 |
|
399 return out_name; |
|
400 } |
|
401 |
|
402 UNICODE_STRING* GetBackingFilePath(PVOID address) { |
|
403 // We'll start with something close to max_path charactes for the name. |
|
404 ULONG buffer_bytes = MAX_PATH * 2; |
|
405 |
|
406 for (;;) { |
|
407 MEMORY_SECTION_NAME* section_name = reinterpret_cast<MEMORY_SECTION_NAME*>( |
|
408 new(NT_ALLOC) char[buffer_bytes]); |
|
409 |
|
410 if (!section_name) |
|
411 return NULL; |
|
412 |
|
413 ULONG returned_bytes; |
|
414 NTSTATUS ret = g_nt.QueryVirtualMemory(NtCurrentProcess, address, |
|
415 MemorySectionName, section_name, |
|
416 buffer_bytes, &returned_bytes); |
|
417 |
|
418 if (STATUS_BUFFER_OVERFLOW == ret) { |
|
419 // Retry the call with the given buffer size. |
|
420 operator delete(section_name, NT_ALLOC); |
|
421 section_name = NULL; |
|
422 buffer_bytes = returned_bytes; |
|
423 continue; |
|
424 } |
|
425 if (!NT_SUCCESS(ret)) { |
|
426 operator delete(section_name, NT_ALLOC); |
|
427 return NULL; |
|
428 } |
|
429 |
|
430 return reinterpret_cast<UNICODE_STRING*>(section_name); |
|
431 } |
|
432 } |
|
433 |
|
434 UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path) { |
|
435 if ((!module_path) || (!module_path->Buffer)) |
|
436 return NULL; |
|
437 |
|
438 wchar_t* sep = NULL; |
|
439 int start_pos = module_path->Length / sizeof(wchar_t) - 1; |
|
440 int ix = start_pos; |
|
441 |
|
442 for (; ix >= 0; --ix) { |
|
443 if (module_path->Buffer[ix] == L'\\') { |
|
444 sep = &module_path->Buffer[ix]; |
|
445 break; |
|
446 } |
|
447 } |
|
448 |
|
449 // Ends with path separator. Not a valid module name. |
|
450 if ((ix == start_pos) && sep) |
|
451 return NULL; |
|
452 |
|
453 // No path separator found. Use the entire name. |
|
454 if (!sep) { |
|
455 sep = &module_path->Buffer[-1]; |
|
456 } |
|
457 |
|
458 // Add one to the size so we can null terminate the string. |
|
459 size_t size_bytes = (start_pos - ix + 1) * sizeof(wchar_t); |
|
460 |
|
461 // Based on the code above, size_bytes should always be small enough |
|
462 // to make the static_cast below safe. |
|
463 DCHECK_NT(kuint16max > size_bytes); |
|
464 char* str_buffer = new(NT_ALLOC) char[size_bytes + sizeof(UNICODE_STRING)]; |
|
465 if (!str_buffer) |
|
466 return NULL; |
|
467 |
|
468 UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>(str_buffer); |
|
469 out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]); |
|
470 out_string->Length = static_cast<USHORT>(size_bytes - sizeof(wchar_t)); |
|
471 out_string->MaximumLength = static_cast<USHORT>(size_bytes); |
|
472 |
|
473 NTSTATUS ret = CopyData(out_string->Buffer, &sep[1], out_string->Length); |
|
474 if (!NT_SUCCESS(ret)) { |
|
475 operator delete(out_string, NT_ALLOC); |
|
476 return NULL; |
|
477 } |
|
478 |
|
479 out_string->Buffer[out_string->Length / sizeof(wchar_t)] = L'\0'; |
|
480 return out_string; |
|
481 } |
|
482 |
|
483 NTSTATUS AutoProtectMemory::ChangeProtection(void* address, size_t bytes, |
|
484 ULONG protect) { |
|
485 DCHECK_NT(!changed_); |
|
486 SIZE_T new_bytes = bytes; |
|
487 NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address, |
|
488 &new_bytes, protect, &old_protect_); |
|
489 if (NT_SUCCESS(ret)) { |
|
490 changed_ = true; |
|
491 address_ = address; |
|
492 bytes_ = new_bytes; |
|
493 } |
|
494 |
|
495 return ret; |
|
496 } |
|
497 |
|
498 NTSTATUS AutoProtectMemory::RevertProtection() { |
|
499 if (!changed_) |
|
500 return STATUS_SUCCESS; |
|
501 |
|
502 DCHECK_NT(address_); |
|
503 DCHECK_NT(bytes_); |
|
504 |
|
505 SIZE_T new_bytes = bytes_; |
|
506 NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address_, |
|
507 &new_bytes, old_protect_, |
|
508 &old_protect_); |
|
509 DCHECK_NT(NT_SUCCESS(ret)); |
|
510 |
|
511 changed_ = false; |
|
512 address_ = NULL; |
|
513 bytes_ = 0; |
|
514 old_protect_ = 0; |
|
515 |
|
516 return ret; |
|
517 } |
|
518 |
|
519 bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, DWORD length, |
|
520 uint32 file_info_class) { |
|
521 if (FileRenameInformation != file_info_class) |
|
522 return false; |
|
523 |
|
524 if (length < sizeof(FILE_RENAME_INFORMATION)) |
|
525 return false; |
|
526 |
|
527 // Make sure file name length doesn't exceed the message length |
|
528 if (length - offsetof(FILE_RENAME_INFORMATION, FileName) < |
|
529 file_info->FileNameLength) |
|
530 return false; |
|
531 |
|
532 // We don't support a root directory. |
|
533 if (file_info->RootDirectory) |
|
534 return false; |
|
535 |
|
536 // Check if it starts with \\??\\. We don't support relative paths. |
|
537 if (file_info->FileNameLength < 4 || file_info->FileNameLength > kuint16max) |
|
538 return false; |
|
539 |
|
540 if (file_info->FileName[0] != L'\\' || |
|
541 file_info->FileName[1] != L'?' || |
|
542 file_info->FileName[2] != L'?' || |
|
543 file_info->FileName[3] != L'\\') |
|
544 return false; |
|
545 |
|
546 return true; |
|
547 } |
|
548 |
|
549 } // namespace sandbox |
|
550 |
|
551 void* operator new(size_t size, sandbox::AllocationType type, |
|
552 void* near_to) { |
|
553 using namespace sandbox; |
|
554 |
|
555 if (NT_ALLOC == type) { |
|
556 if (!InitHeap()) |
|
557 return NULL; |
|
558 |
|
559 // Use default flags for the allocation. |
|
560 return g_nt.RtlAllocateHeap(sandbox::g_heap, 0, size); |
|
561 } else if (NT_PAGE == type) { |
|
562 return AllocateNearTo(near_to, size); |
|
563 } |
|
564 NOTREACHED_NT(); |
|
565 return NULL; |
|
566 } |
|
567 |
|
568 void operator delete(void* memory, sandbox::AllocationType type) { |
|
569 using namespace sandbox; |
|
570 |
|
571 if (NT_ALLOC == type) { |
|
572 // Use default flags. |
|
573 VERIFY(g_nt.RtlFreeHeap(sandbox::g_heap, 0, memory)); |
|
574 } else if (NT_PAGE == type) { |
|
575 void* base = memory; |
|
576 SIZE_T size = 0; |
|
577 VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size, |
|
578 MEM_RELEASE)); |
|
579 } else { |
|
580 NOTREACHED_NT(); |
|
581 } |
|
582 } |
|
583 |
|
584 void operator delete(void* memory, sandbox::AllocationType type, |
|
585 void* near_to) { |
|
586 UNREFERENCED_PARAMETER(near_to); |
|
587 operator delete(memory, type); |
|
588 } |
|
589 |
|
590 void* __cdecl operator new(size_t size, void* buffer, |
|
591 sandbox::AllocationType type) { |
|
592 UNREFERENCED_PARAMETER(size); |
|
593 UNREFERENCED_PARAMETER(type); |
|
594 return buffer; |
|
595 } |
|
596 |
|
597 void __cdecl operator delete(void* memory, void* buffer, |
|
598 sandbox::AllocationType type) { |
|
599 UNREFERENCED_PARAMETER(memory); |
|
600 UNREFERENCED_PARAMETER(buffer); |
|
601 UNREFERENCED_PARAMETER(type); |
|
602 } |