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