NtAllocateVirtualMemory: Add check to make sure that BaseAddress + RegionSize is...
[reactos.git] / reactos / ntoskrnl / mm / anonmem.c
1 /*
2 * Copyright (C) 2002-2005 ReactOS Team (and the authors from the programmers section)
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 *
19 * PROJECT: ReactOS kernel
20 * FILE: ntoskrnl/mm/anonmem.c
21 * PURPOSE: Implementing anonymous memory.
22 *
23 * PROGRAMMERS: David Welch
24 * Casper Hornstrup
25 * KJK::Hyperion
26 * Ge van Geldorp
27 * Eric Kohl
28 * Royce Mitchell III
29 * Aleksey Bragin
30 * Jason Filby
31 * Art Yerkes
32 * Gunnar Andre' Dalsnes
33 * Filip Navara
34 * Thomas Weidenmueller
35 * Alex Ionescu
36 * Trevor McCort
37 * Steven Edwards
38 */
39
40 /* INCLUDE *****************************************************************/
41
42 #include <ntoskrnl.h>
43 #define NDEBUG
44 #include <debug.h>
45
46 /* FUNCTIONS *****************************************************************/
47
48 NTSTATUS
49 NTAPI
50 MmWritePageVirtualMemory(PMMSUPPORT AddressSpace,
51 PMEMORY_AREA MemoryArea,
52 PVOID Address,
53 PMM_PAGEOP PageOp)
54 {
55 SWAPENTRY SwapEntry;
56 PFN_TYPE Page;
57 NTSTATUS Status;
58 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
59
60 /*
61 * Check for paging out from a deleted virtual memory area.
62 */
63 if (MemoryArea->DeleteInProgress)
64 {
65 PageOp->Status = STATUS_UNSUCCESSFUL;
66 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
67 MmReleasePageOp(PageOp);
68 return(STATUS_UNSUCCESSFUL);
69 }
70
71 Page = MmGetPfnForProcess(Process, Address);
72
73 /*
74 * Get that the page actually is dirty.
75 */
76 if (!MmIsDirtyPage(Process, Address))
77 {
78 PageOp->Status = STATUS_SUCCESS;
79 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
80 MmReleasePageOp(PageOp);
81 return(STATUS_SUCCESS);
82 }
83
84 /*
85 * Speculatively set the mapping to clean.
86 */
87 MmSetCleanPage(Process, Address);
88
89 /*
90 * If necessary, allocate an entry in the paging file for this page
91 */
92 SwapEntry = MmGetSavedSwapEntryPage(Page);
93 if (SwapEntry == 0)
94 {
95 SwapEntry = MmAllocSwapPage();
96 if (SwapEntry == 0)
97 {
98 MmSetDirtyPage(Process, Address);
99 PageOp->Status = STATUS_PAGEFILE_QUOTA_EXCEEDED;
100 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
101 MmReleasePageOp(PageOp);
102 return(STATUS_PAGEFILE_QUOTA_EXCEEDED);
103 }
104 }
105
106 /*
107 * Write the page to the pagefile
108 */
109 Status = MmWriteToSwapPage(SwapEntry, Page);
110 if (!NT_SUCCESS(Status))
111 {
112 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
113 Status);
114 MmSetDirtyPage(Process, Address);
115 PageOp->Status = STATUS_UNSUCCESSFUL;
116 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
117 MmReleasePageOp(PageOp);
118 return(STATUS_UNSUCCESSFUL);
119 }
120
121 /*
122 * Otherwise we have succeeded.
123 */
124 MmSetSavedSwapEntryPage(Page, SwapEntry);
125 PageOp->Status = STATUS_SUCCESS;
126 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
127 MmReleasePageOp(PageOp);
128 return(STATUS_SUCCESS);
129 }
130
131 NTSTATUS
132 NTAPI
133 MmPageOutVirtualMemory(PMMSUPPORT AddressSpace,
134 PMEMORY_AREA MemoryArea,
135 PVOID Address,
136 PMM_PAGEOP PageOp)
137 {
138 PFN_TYPE Page;
139 BOOLEAN WasDirty;
140 SWAPENTRY SwapEntry;
141 NTSTATUS Status;
142 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
143
144 DPRINT("MmPageOutVirtualMemory(Address 0x%.8X) PID %d\n",
145 Address, Process->UniqueProcessId);
146
147 /*
148 * Check for paging out from a deleted virtual memory area.
149 */
150 if (MemoryArea->DeleteInProgress)
151 {
152 PageOp->Status = STATUS_UNSUCCESSFUL;
153 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
154 MmReleasePageOp(PageOp);
155 return(STATUS_UNSUCCESSFUL);
156 }
157
158 /*
159 * Disable the virtual mapping.
160 */
161 MmDisableVirtualMapping(Process, Address,
162 &WasDirty, &Page);
163
164 if (Page == 0)
165 {
166 KeBugCheck(MEMORY_MANAGEMENT);
167 }
168
169 /*
170 * Paging out non-dirty data is easy.
171 */
172 if (!WasDirty)
173 {
174 MmLockAddressSpace(AddressSpace);
175 MmDeleteVirtualMapping(Process, Address, FALSE, NULL, NULL);
176 MmDeleteAllRmaps(Page, NULL, NULL);
177 if ((SwapEntry = MmGetSavedSwapEntryPage(Page)) != 0)
178 {
179 MmCreatePageFileMapping(Process, Address, SwapEntry);
180 MmSetSavedSwapEntryPage(Page, 0);
181 }
182 MmUnlockAddressSpace(AddressSpace);
183 MmReleasePageMemoryConsumer(MC_USER, Page);
184 PageOp->Status = STATUS_SUCCESS;
185 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
186 MmReleasePageOp(PageOp);
187 return(STATUS_SUCCESS);
188 }
189
190 /*
191 * If necessary, allocate an entry in the paging file for this page
192 */
193 SwapEntry = MmGetSavedSwapEntryPage(Page);
194 if (SwapEntry == 0)
195 {
196 SwapEntry = MmAllocSwapPage();
197 if (SwapEntry == 0)
198 {
199 MmShowOutOfSpaceMessagePagingFile();
200 MmEnableVirtualMapping(Process, Address);
201 PageOp->Status = STATUS_UNSUCCESSFUL;
202 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
203 MmReleasePageOp(PageOp);
204 return(STATUS_PAGEFILE_QUOTA);
205 }
206 }
207
208 /*
209 * Write the page to the pagefile
210 */
211 Status = MmWriteToSwapPage(SwapEntry, Page);
212 if (!NT_SUCCESS(Status))
213 {
214 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
215 Status);
216 MmEnableVirtualMapping(Process, Address);
217 PageOp->Status = STATUS_UNSUCCESSFUL;
218 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
219 MmReleasePageOp(PageOp);
220 return(STATUS_UNSUCCESSFUL);
221 }
222
223 /*
224 * Otherwise we have succeeded, free the page
225 */
226 DPRINT("MM: Swapped out virtual memory page 0x%.8X!\n", Page << PAGE_SHIFT);
227 MmLockAddressSpace(AddressSpace);
228 MmDeleteVirtualMapping(Process, Address, FALSE, NULL, NULL);
229 MmCreatePageFileMapping(Process, Address, SwapEntry);
230 MmUnlockAddressSpace(AddressSpace);
231 MmDeleteAllRmaps(Page, NULL, NULL);
232 MmSetSavedSwapEntryPage(Page, 0);
233 MmReleasePageMemoryConsumer(MC_USER, Page);
234 PageOp->Status = STATUS_SUCCESS;
235 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
236 MmReleasePageOp(PageOp);
237 return(STATUS_SUCCESS);
238 }
239
240 NTSTATUS
241 NTAPI
242 MmNotPresentFaultVirtualMemory(PMMSUPPORT AddressSpace,
243 MEMORY_AREA* MemoryArea,
244 PVOID Address,
245 BOOLEAN Locked)
246 /*
247 * FUNCTION: Move data into memory to satisfy a page not present fault
248 * ARGUMENTS:
249 * AddressSpace = Address space within which the fault occurred
250 * MemoryArea = The memory area within which the fault occurred
251 * Address = The absolute address of fault
252 * RETURNS: Status
253 * NOTES: This function is called with the address space lock held.
254 */
255 {
256 PFN_TYPE Page;
257 NTSTATUS Status;
258 PMM_REGION Region;
259 PMM_PAGEOP PageOp;
260 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
261
262 /*
263 * There is a window between taking the page fault and locking the
264 * address space when another thread could load the page so we check
265 * that.
266 */
267 if (MmIsPagePresent(NULL, Address))
268 {
269 if (Locked)
270 {
271 MmLockPage(MmGetPfnForProcess(NULL, Address));
272 }
273 return(STATUS_SUCCESS);
274 }
275
276 /*
277 * Check for the virtual memory area being deleted.
278 */
279 if (MemoryArea->DeleteInProgress)
280 {
281 return(STATUS_UNSUCCESSFUL);
282 }
283
284 /*
285 * Get the segment corresponding to the virtual address
286 */
287 Region = MmFindRegion(MemoryArea->StartingAddress,
288 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
289 Address, NULL);
290
291 if (Region->Type == MEM_RESERVE || Region->Protect == PAGE_NOACCESS)
292 {
293 return(STATUS_ACCESS_VIOLATION);
294 }
295
296 /*
297 * FIXME
298 */
299 if (Region->Protect & PAGE_GUARD)
300 {
301 return(STATUS_GUARD_PAGE_VIOLATION);
302 }
303
304 /*
305 * Get or create a page operation
306 */
307 PageOp = MmGetPageOp(MemoryArea, Process->UniqueProcessId,
308 (PVOID)PAGE_ROUND_DOWN(Address), NULL, 0,
309 MM_PAGEOP_PAGEIN, FALSE);
310 if (PageOp == NULL)
311 {
312 DPRINT1("MmGetPageOp failed");
313 KeBugCheck(MEMORY_MANAGEMENT);
314 }
315
316 /*
317 * Check if someone else is already handling this fault, if so wait
318 * for them
319 */
320 if (PageOp->Thread != PsGetCurrentThread())
321 {
322 MmUnlockAddressSpace(AddressSpace);
323 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
324 0,
325 KernelMode,
326 FALSE,
327 NULL);
328 /*
329 * Check for various strange conditions
330 */
331 if (Status != STATUS_SUCCESS)
332 {
333 DPRINT1("Failed to wait for page op\n");
334 KeBugCheck(MEMORY_MANAGEMENT);
335 }
336 if (PageOp->Status == STATUS_PENDING)
337 {
338 DPRINT1("Woke for page op before completion\n");
339 KeBugCheck(MEMORY_MANAGEMENT);
340 }
341 /*
342 * If this wasn't a pagein then we need to restart the handling
343 */
344 if (PageOp->OpType != MM_PAGEOP_PAGEIN)
345 {
346 MmLockAddressSpace(AddressSpace);
347 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
348 MmReleasePageOp(PageOp);
349 return(STATUS_MM_RESTART_OPERATION);
350 }
351 /*
352 * If the thread handling this fault has failed then we don't retry
353 */
354 if (!NT_SUCCESS(PageOp->Status))
355 {
356 MmLockAddressSpace(AddressSpace);
357 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
358 Status = PageOp->Status;
359 MmReleasePageOp(PageOp);
360 return(Status);
361 }
362 MmLockAddressSpace(AddressSpace);
363 if (Locked)
364 {
365 MmLockPage(MmGetPfnForProcess(NULL, Address));
366 }
367 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
368 MmReleasePageOp(PageOp);
369 return(STATUS_SUCCESS);
370 }
371
372 /*
373 * Try to allocate a page
374 */
375 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
376 if (Status == STATUS_NO_MEMORY)
377 {
378 MmUnlockAddressSpace(AddressSpace);
379 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
380 MmLockAddressSpace(AddressSpace);
381 }
382 if (!NT_SUCCESS(Status))
383 {
384 DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status);
385 KeBugCheck(MEMORY_MANAGEMENT);
386 }
387
388 /*
389 * Handle swapped out pages.
390 */
391 if (MmIsPageSwapEntry(NULL, Address))
392 {
393 SWAPENTRY SwapEntry;
394
395 MmDeletePageFileMapping(Process, Address, &SwapEntry);
396 Status = MmReadFromSwapPage(SwapEntry, Page);
397 if (!NT_SUCCESS(Status))
398 {
399 KeBugCheck(MEMORY_MANAGEMENT);
400 }
401 MmSetSavedSwapEntryPage(Page, SwapEntry);
402 }
403
404 /*
405 * Set the page. If we fail because we are out of memory then
406 * try again
407 */
408 Status = MmCreateVirtualMapping(Process,
409 (PVOID)PAGE_ROUND_DOWN(Address),
410 Region->Protect,
411 &Page,
412 1);
413 while (Status == STATUS_NO_MEMORY)
414 {
415 MmUnlockAddressSpace(AddressSpace);
416 Status = MmCreateVirtualMapping(Process,
417 Address,
418 Region->Protect,
419 &Page,
420 1);
421 MmLockAddressSpace(AddressSpace);
422 }
423 if (!NT_SUCCESS(Status))
424 {
425 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
426 KeBugCheck(MEMORY_MANAGEMENT);
427 return(Status);
428 }
429
430 /*
431 * Add the page to the process's working set
432 */
433 MmInsertRmap(Page, Process, (PVOID)PAGE_ROUND_DOWN(Address));
434
435 /*
436 * Finish the operation
437 */
438 if (Locked)
439 {
440 MmLockPage(Page);
441 }
442 PageOp->Status = STATUS_SUCCESS;
443 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
444 MmReleasePageOp(PageOp);
445 return(STATUS_SUCCESS);
446 }
447
448 static VOID
449 MmModifyAttributes(PMMSUPPORT AddressSpace,
450 PVOID BaseAddress,
451 ULONG RegionSize,
452 ULONG OldType,
453 ULONG OldProtect,
454 ULONG NewType,
455 ULONG NewProtect)
456 /*
457 * FUNCTION: Modify the attributes of a memory region
458 */
459 {
460 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
461
462 /*
463 * If we are switching a previously committed region to reserved then
464 * free any allocated pages within the region
465 */
466 if (NewType == MEM_RESERVE && OldType == MEM_COMMIT)
467 {
468 ULONG i;
469
470 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
471 {
472 PFN_TYPE Page;
473
474 if (MmIsPageSwapEntry(Process,
475 (char*)BaseAddress + (i * PAGE_SIZE)))
476 {
477 SWAPENTRY SwapEntry;
478
479 MmDeletePageFileMapping(Process,
480 (char*)BaseAddress + (i * PAGE_SIZE),
481 &SwapEntry);
482 MmFreeSwapPage(SwapEntry);
483 }
484 else
485 {
486 MmDeleteVirtualMapping(Process,
487 (char*)BaseAddress + (i*PAGE_SIZE),
488 FALSE, NULL, &Page);
489 if (Page != 0)
490 {
491 SWAPENTRY SavedSwapEntry;
492 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
493 if (SavedSwapEntry != 0)
494 {
495 MmFreeSwapPage(SavedSwapEntry);
496 MmSetSavedSwapEntryPage(Page, 0);
497 }
498 MmDeleteRmap(Page, Process,
499 (char*)BaseAddress + (i * PAGE_SIZE));
500 MmReleasePageMemoryConsumer(MC_USER, Page);
501 }
502 }
503 }
504 }
505
506 /*
507 * If we are changing the protection attributes of a committed region then
508 * alter the attributes for any allocated pages within the region
509 */
510 if (NewType == MEM_COMMIT && OldType == MEM_COMMIT &&
511 OldProtect != NewProtect)
512 {
513 ULONG i;
514
515 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
516 {
517 if (MmIsPagePresent(Process,
518 (char*)BaseAddress + (i*PAGE_SIZE)))
519 {
520 MmSetPageProtect(Process,
521 (char*)BaseAddress + (i*PAGE_SIZE),
522 NewProtect);
523 }
524 }
525 }
526 }
527
528 /*
529 * @implemented
530 */
531 NTSTATUS NTAPI
532 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
533 IN OUT PVOID* UBaseAddress,
534 IN ULONG_PTR ZeroBits,
535 IN OUT PSIZE_T URegionSize,
536 IN ULONG AllocationType,
537 IN ULONG Protect)
538 /*
539 * FUNCTION: Allocates a block of virtual memory in the process address space
540 * ARGUMENTS:
541 * ProcessHandle = The handle of the process which owns the virtual memory
542 * BaseAddress = A pointer to the virtual memory allocated. If you
543 * supply a non zero value the system will try to
544 * allocate the memory at the address supplied. It round
545 * it down to a multiple of the page size.
546 * ZeroBits = (OPTIONAL) You can specify the number of high order bits
547 * that must be zero, ensuring that the memory will be
548 * allocated at a address below a certain value.
549 * RegionSize = The number of bytes to allocate
550 * AllocationType = Indicates the type of virtual memory you like to
551 * allocated, can be a combination of MEM_COMMIT,
552 * MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN.
553 * Protect = Indicates the protection type of the pages allocated.
554 * RETURNS: Status
555 */
556 {
557 PEPROCESS Process;
558 MEMORY_AREA* MemoryArea;
559 ULONG_PTR MemoryAreaLength;
560 ULONG Type;
561 NTSTATUS Status;
562 PMMSUPPORT AddressSpace;
563 PVOID BaseAddress;
564 ULONG RegionSize;
565 PVOID PBaseAddress;
566 ULONG PRegionSize;
567 ULONG MemProtection;
568 PHYSICAL_ADDRESS BoundaryAddressMultiple;
569 KPROCESSOR_MODE PreviousMode;
570
571 PAGED_CODE();
572
573 DPRINT("NtAllocateVirtualMemory(*UBaseAddress %x, "
574 "ZeroBits %d, *URegionSize %x, AllocationType %x, Protect %x)\n",
575 *UBaseAddress,ZeroBits,*URegionSize,AllocationType,
576 Protect);
577
578 /* Check for valid protection flags */
579 MemProtection = Protect & ~(PAGE_GUARD|PAGE_NOCACHE);
580 if (MemProtection != PAGE_NOACCESS &&
581 MemProtection != PAGE_READONLY &&
582 MemProtection != PAGE_READWRITE &&
583 MemProtection != PAGE_WRITECOPY &&
584 MemProtection != PAGE_EXECUTE &&
585 MemProtection != PAGE_EXECUTE_READ &&
586 MemProtection != PAGE_EXECUTE_READWRITE &&
587 MemProtection != PAGE_EXECUTE_WRITECOPY)
588 {
589 DPRINT1("Invalid page protection\n");
590 return STATUS_INVALID_PAGE_PROTECTION;
591 }
592
593 /* Check for valid Zero bits */
594 if (ZeroBits > 21)
595 {
596 DPRINT1("Too many zero bits\n");
597 return STATUS_INVALID_PARAMETER_3;
598 }
599
600 /* Check for valid Allocation Types */
601 if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
602 MEM_TOP_DOWN | MEM_WRITE_WATCH)))
603 {
604 DPRINT1("Invalid Allocation Type\n");
605 return STATUS_INVALID_PARAMETER_5;
606 }
607
608 /* Check for at least one of these Allocation Types to be set */
609 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
610 {
611 DPRINT1("No memory allocation base type\n");
612 return STATUS_INVALID_PARAMETER_5;
613 }
614
615 /* MEM_RESET is an exclusive flag, make sure that is valid too */
616 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
617 {
618 DPRINT1("Invalid use of MEM_RESET\n");
619 return STATUS_INVALID_PARAMETER_5;
620 }
621
622 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
623 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
624 {
625 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
626 return STATUS_INVALID_PARAMETER_5;
627 }
628
629 /* MEM_PHYSICAL can only be used with MEM_RESERVE, and can only be R/W */
630 if (AllocationType & MEM_PHYSICAL)
631 {
632 /* First check for MEM_RESERVE exclusivity */
633 if (AllocationType != (MEM_RESERVE | MEM_PHYSICAL))
634 {
635 DPRINT1("MEM_PHYSICAL used with other flags then MEM_RESERVE or"
636 "MEM_RESERVE was not present at all\n");
637 return STATUS_INVALID_PARAMETER_5;
638 }
639
640 /* Then make sure PAGE_READWRITE is used */
641 if (Protect != PAGE_READWRITE)
642 {
643 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
644 return STATUS_INVALID_PAGE_PROTECTION;
645 }
646 }
647
648 PreviousMode = KeGetPreviousMode();
649
650 _SEH2_TRY
651 {
652 if (PreviousMode != KernelMode)
653 {
654 ProbeForWritePointer(UBaseAddress);
655 ProbeForWriteUlong(URegionSize);
656 }
657 PBaseAddress = *UBaseAddress;
658 PRegionSize = *URegionSize;
659 }
660 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
661 {
662 /* Return the exception code */
663 _SEH2_YIELD(return _SEH2_GetExceptionCode());
664 }
665 _SEH2_END;
666
667 BoundaryAddressMultiple.QuadPart = 0;
668
669 BaseAddress = (PVOID)PAGE_ROUND_DOWN(PBaseAddress);
670 RegionSize = PAGE_ROUND_UP((ULONG_PTR)PBaseAddress + PRegionSize) -
671 PAGE_ROUND_DOWN(PBaseAddress);
672
673 /*
674 * We've captured and calculated the data, now do more checks
675 * Yes, MmCreateMemoryArea does similar checks, but they don't return
676 * the right status codes that a caller of this routine would expect.
677 */
678 if ((ULONG_PTR)BaseAddress >= USER_SHARED_DATA)
679 {
680 DPRINT1("Virtual allocation base above User Space\n");
681 return STATUS_INVALID_PARAMETER_2;
682 }
683 if (!RegionSize)
684 {
685 DPRINT1("Region size is invalid (zero)\n");
686 return STATUS_INVALID_PARAMETER_4;
687 }
688 if ((USER_SHARED_DATA - (ULONG_PTR)BaseAddress) < RegionSize)
689 {
690 DPRINT1("Region size would overflow into kernel-memory\n");
691 return STATUS_INVALID_PARAMETER_4;
692 }
693
694 /*
695 * Copy on Write is reserved for system use. This case is a certain failure
696 * but there may be other cases...needs more testing
697 */
698 if ((!BaseAddress || (AllocationType & MEM_RESERVE)) &&
699 (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)))
700 {
701 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
702 return STATUS_INVALID_PAGE_PROTECTION;
703 }
704
705
706 Status = ObReferenceObjectByHandle(ProcessHandle,
707 PROCESS_VM_OPERATION,
708 PsProcessType,
709 PreviousMode,
710 (PVOID*)(&Process),
711 NULL);
712 if (!NT_SUCCESS(Status))
713 {
714 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
715 return(Status);
716 }
717
718 Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
719 DPRINT("Type %x\n", Type);
720
721 AddressSpace = &Process->Vm;
722 MmLockAddressSpace(AddressSpace);
723
724 if (PBaseAddress != 0)
725 {
726 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
727
728 if (MemoryArea != NULL)
729 {
730 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
731 (ULONG_PTR)MemoryArea->StartingAddress;
732
733 if (((ULONG)BaseAddress + RegionSize) > (ULONG)MemoryArea->EndingAddress)
734 {
735 DPRINT("BaseAddress + RegionSize %x is larger than MemoryArea's EndingAddress %x\n",
736 (ULONG)BaseAddress + RegionSize, MemoryArea->EndingAddress);
737
738 MmUnlockAddressSpace(AddressSpace);
739 ObDereferenceObject(Process);
740
741 return STATUS_MEMORY_NOT_ALLOCATED;
742 }
743
744 if (AllocationType == MEM_RESET)
745 {
746 if (MmIsPagePresent(Process, BaseAddress))
747 {
748 /* FIXME: mark pages as not modified */
749 }
750 else
751 {
752 /* FIXME: if pages are in paging file discard them and bring in pages of zeros */
753 }
754
755 MmUnlockAddressSpace(AddressSpace);
756 ObDereferenceObject(Process);
757
758 /* MEM_RESET does not modify any attributes of region */
759 return STATUS_SUCCESS;
760 }
761
762 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
763 MemoryAreaLength >= RegionSize)
764 {
765 Status =
766 MmAlterRegion(AddressSpace,
767 MemoryArea->StartingAddress,
768 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
769 BaseAddress, RegionSize,
770 Type, Protect, MmModifyAttributes);
771 MmUnlockAddressSpace(AddressSpace);
772 ObDereferenceObject(Process);
773 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
774
775 /* Give the caller rounded BaseAddress and area length */
776 if (NT_SUCCESS(Status))
777 {
778 *UBaseAddress = BaseAddress;
779 *URegionSize = RegionSize;
780 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
781 }
782
783 return(Status);
784 }
785 else if (MemoryAreaLength >= RegionSize)
786 {
787 /* Region list initialized? */
788 if (MemoryArea->Data.SectionData.RegionListHead.Flink)
789 {
790 Status =
791 MmAlterRegion(AddressSpace,
792 MemoryArea->StartingAddress,
793 &MemoryArea->Data.SectionData.RegionListHead,
794 BaseAddress, RegionSize,
795 Type, Protect, MmModifyAttributes);
796 }
797 else
798 {
799 Status = STATUS_ACCESS_VIOLATION;
800 }
801
802 MmUnlockAddressSpace(AddressSpace);
803 ObDereferenceObject(Process);
804 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
805
806 /* Give the caller rounded BaseAddress and area length */
807 if (NT_SUCCESS(Status))
808 {
809 *UBaseAddress = BaseAddress;
810 *URegionSize = RegionSize;
811 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
812 }
813
814 return(Status);
815 }
816 else
817 {
818 MmUnlockAddressSpace(AddressSpace);
819 ObDereferenceObject(Process);
820 return(STATUS_UNSUCCESSFUL);
821 }
822 }
823 }
824
825 Status = MmCreateMemoryArea(AddressSpace,
826 MEMORY_AREA_VIRTUAL_MEMORY,
827 &BaseAddress,
828 RegionSize,
829 Protect,
830 &MemoryArea,
831 PBaseAddress != 0,
832 AllocationType & MEM_TOP_DOWN,
833 BoundaryAddressMultiple);
834 if (!NT_SUCCESS(Status))
835 {
836 MmUnlockAddressSpace(AddressSpace);
837 ObDereferenceObject(Process);
838 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
839 return(Status);
840 }
841
842 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
843 (ULONG_PTR)MemoryArea->StartingAddress;
844
845 MmInitializeRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
846 MemoryAreaLength, Type, Protect);
847
848 if ((AllocationType & MEM_COMMIT) &&
849 (Protect & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)))
850 {
851 const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
852 MmReserveSwapPages(nPages);
853 }
854
855 *UBaseAddress = BaseAddress;
856 *URegionSize = MemoryAreaLength;
857 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
858
859 MmUnlockAddressSpace(AddressSpace);
860 ObDereferenceObject(Process);
861 return(STATUS_SUCCESS);
862 }
863
864 static VOID
865 MmFreeVirtualMemoryPage(PVOID Context,
866 MEMORY_AREA* MemoryArea,
867 PVOID Address,
868 PFN_TYPE Page,
869 SWAPENTRY SwapEntry,
870 BOOLEAN Dirty)
871 {
872 PEPROCESS Process = (PEPROCESS)Context;
873
874 if (Page != 0)
875 {
876 SWAPENTRY SavedSwapEntry;
877 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
878 if (SavedSwapEntry != 0)
879 {
880 MmFreeSwapPage(SavedSwapEntry);
881 MmSetSavedSwapEntryPage(Page, 0);
882 }
883 MmDeleteRmap(Page, Process, Address);
884 MmReleasePageMemoryConsumer(MC_USER, Page);
885 }
886 else if (SwapEntry != 0)
887 {
888 MmFreeSwapPage(SwapEntry);
889 }
890 }
891
892 VOID
893 NTAPI
894 MmFreeVirtualMemory(PEPROCESS Process,
895 PMEMORY_AREA MemoryArea)
896 {
897 PLIST_ENTRY current_entry;
898 PMM_REGION current;
899 ULONG i;
900
901 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process,
902 MemoryArea);
903
904 /* Mark this memory area as about to be deleted. */
905 MemoryArea->DeleteInProgress = TRUE;
906
907 /*
908 * Wait for any ongoing paging operations. Notice that since we have
909 * flagged this memory area as deleted no more page ops will be added.
910 */
911 if (MemoryArea->PageOpCount > 0)
912 {
913 ULONG_PTR MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
914 (ULONG_PTR)MemoryArea->StartingAddress;
915 const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
916
917 for (i = 0; i < nPages && MemoryArea->PageOpCount != 0; ++i)
918 {
919 PMM_PAGEOP PageOp;
920 PageOp = MmCheckForPageOp(MemoryArea, Process->UniqueProcessId,
921 (PVOID)((ULONG_PTR)MemoryArea->StartingAddress + (i * PAGE_SIZE)),
922 NULL, 0);
923 if (PageOp != NULL)
924 {
925 NTSTATUS Status;
926 MmUnlockAddressSpace(&Process->Vm);
927 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
928 0,
929 KernelMode,
930 FALSE,
931 NULL);
932 if (Status != STATUS_SUCCESS)
933 {
934 DPRINT1("Failed to wait for page op\n");
935 KeBugCheck(MEMORY_MANAGEMENT);
936 }
937 MmLockAddressSpace(&Process->Vm);
938 MmReleasePageOp(PageOp);
939 }
940 }
941 }
942
943 /* Free all the individual segments. */
944 current_entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
945 while (current_entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
946 {
947 current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
948 current_entry = current_entry->Flink;
949 ExFreePool(current);
950 }
951
952 /* Actually free the memory area. */
953 MmFreeMemoryArea(&Process->Vm,
954 MemoryArea,
955 MmFreeVirtualMemoryPage,
956 (PVOID)Process);
957 }
958
959 /*
960 * @implemented
961 */
962 NTSTATUS NTAPI
963 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
964 IN PVOID* PBaseAddress,
965 IN PSIZE_T PRegionSize,
966 IN ULONG FreeType)
967 /*
968 * FUNCTION: Frees a range of virtual memory
969 * ARGUMENTS:
970 * ProcessHandle = Points to the process that allocated the virtual
971 * memory
972 * BaseAddress = Points to the memory address, rounded down to a
973 * multiple of the pagesize
974 * RegionSize = Limits the range to free, rounded up to a multiple of
975 * the paging size
976 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
977 * RETURNS: Status
978 */
979 {
980 MEMORY_AREA* MemoryArea;
981 NTSTATUS Status;
982 PEPROCESS Process;
983 PMMSUPPORT AddressSpace;
984 PVOID BaseAddress;
985 ULONG RegionSize;
986
987 PAGED_CODE();
988
989 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *PBaseAddress %x, "
990 "*PRegionSize %x, FreeType %x)\n",ProcessHandle,*PBaseAddress,
991 *PRegionSize,FreeType);
992
993 if (!(FreeType & (MEM_RELEASE | MEM_DECOMMIT)))
994 {
995 DPRINT1("Invalid FreeType\n");
996 return STATUS_INVALID_PARAMETER_4;
997 }
998
999 if (ExGetPreviousMode() != KernelMode)
1000 {
1001 _SEH2_TRY
1002 {
1003 /* Probe user pointers */
1004 ProbeForWriteSize_t(PRegionSize);
1005 ProbeForWritePointer(PBaseAddress);
1006 }
1007 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1008 {
1009 /* Return the exception code */
1010 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1011 }
1012 _SEH2_END;
1013 }
1014
1015 BaseAddress = (PVOID)PAGE_ROUND_DOWN((*PBaseAddress));
1016 RegionSize = PAGE_ROUND_UP((ULONG_PTR)(*PBaseAddress) + (*PRegionSize)) -
1017 PAGE_ROUND_DOWN((*PBaseAddress));
1018
1019 Status = ObReferenceObjectByHandle(ProcessHandle,
1020 PROCESS_VM_OPERATION,
1021 PsProcessType,
1022 UserMode,
1023 (PVOID*)(&Process),
1024 NULL);
1025 if (!NT_SUCCESS(Status))
1026 {
1027 return(Status);
1028 }
1029
1030 AddressSpace = &Process->Vm;
1031
1032 MmLockAddressSpace(AddressSpace);
1033 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
1034 if (MemoryArea == NULL)
1035 {
1036 Status = STATUS_UNSUCCESSFUL;
1037 goto unlock_deref_and_return;
1038 }
1039
1040 switch (FreeType)
1041 {
1042 case MEM_RELEASE:
1043 /* We can only free a memory area in one step. */
1044 if (MemoryArea->StartingAddress != BaseAddress ||
1045 MemoryArea->Type != MEMORY_AREA_VIRTUAL_MEMORY)
1046 {
1047 Status = STATUS_UNSUCCESSFUL;
1048 goto unlock_deref_and_return;
1049 }
1050
1051 MmFreeVirtualMemory(Process, MemoryArea);
1052 Status = STATUS_SUCCESS;
1053 goto unlock_deref_and_return;
1054
1055 case MEM_DECOMMIT:
1056 Status =
1057 MmAlterRegion(AddressSpace,
1058 MemoryArea->StartingAddress,
1059 (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW) ?
1060 &MemoryArea->Data.SectionData.RegionListHead :
1061 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1062 BaseAddress,
1063 RegionSize,
1064 MEM_RESERVE,
1065 PAGE_NOACCESS,
1066 MmModifyAttributes);
1067 goto unlock_deref_and_return;
1068 }
1069
1070 Status = STATUS_NOT_IMPLEMENTED;
1071
1072 unlock_deref_and_return:
1073
1074 MmUnlockAddressSpace(AddressSpace);
1075 ObDereferenceObject(Process);
1076
1077 return(Status);
1078 }
1079
1080 NTSTATUS
1081 NTAPI
1082 MmProtectAnonMem(PMMSUPPORT AddressSpace,
1083 PMEMORY_AREA MemoryArea,
1084 PVOID BaseAddress,
1085 ULONG Length,
1086 ULONG Protect,
1087 PULONG OldProtect)
1088 {
1089 PMM_REGION Region;
1090 NTSTATUS Status = STATUS_SUCCESS;
1091 ULONG LengthCount = 0;
1092
1093 /* Search all Regions in MemoryArea up to Length */
1094 /* Every Region up to Length must be committed for success */
1095 for (;;)
1096 {
1097 Region = MmFindRegion(MemoryArea->StartingAddress,
1098 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1099 (PVOID)((ULONG_PTR)BaseAddress + (ULONG_PTR)LengthCount), NULL);
1100
1101 /* If a Region was found and it is committed */
1102 if ((Region) && (Region->Type == MEM_COMMIT))
1103 {
1104 LengthCount += Region->Length;
1105 if (Length <= LengthCount) break;
1106 continue;
1107 }
1108 /* If Region was found and it is not commited */
1109 else if (Region)
1110 {
1111 Status = STATUS_NOT_COMMITTED;
1112 break;
1113 }
1114 /* If no Region was found at all */
1115 else if (LengthCount == 0)
1116 {
1117 Status = STATUS_INVALID_ADDRESS;
1118 break;
1119 }
1120 }
1121
1122 if (NT_SUCCESS(Status))
1123 {
1124 *OldProtect = Region->Protect;
1125 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
1126 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1127 BaseAddress, Length, Region->Type, Protect,
1128 MmModifyAttributes);
1129 }
1130
1131 return (Status);
1132 }
1133
1134 NTSTATUS NTAPI
1135 MmQueryAnonMem(PMEMORY_AREA MemoryArea,
1136 PVOID Address,
1137 PMEMORY_BASIC_INFORMATION Info,
1138 PULONG ResultLength)
1139 {
1140 PMM_REGION Region;
1141 PVOID RegionBase = NULL;
1142
1143 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
1144
1145 Region = MmFindRegion(MemoryArea->StartingAddress,
1146 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1147 Address, &RegionBase);
1148 Info->BaseAddress = RegionBase;
1149 Info->AllocationBase = MemoryArea->StartingAddress;
1150 Info->AllocationProtect = MemoryArea->Protect;
1151 Info->RegionSize = Region->Length;
1152 Info->State = Region->Type;
1153 Info->Protect = Region->Protect;
1154 Info->Type = MEM_PRIVATE;
1155
1156 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
1157 return(STATUS_SUCCESS);
1158 }
1159
1160 /* EOF */