Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(76)

Side by Side Diff: sandbox/win/src/sandbox_nt_util.cc

Issue 1851213002: Remove sandbox on Windows. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix nacl compile issues Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sandbox/win/src/sandbox_nt_util.h ('k') | sandbox/win/src/sandbox_nt_util_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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) {}
OLDNEW
« no previous file with comments | « sandbox/win/src/sandbox_nt_util.h ('k') | sandbox/win/src/sandbox_nt_util_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698