xref: /reactos/win32ss/user/ntuser/usrheap.c (revision 98e8827a)
1 /*
2  *  ReactOS W32 Subsystem
3  *  Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include <win32k.h>
21 
22 #define NDEBUG
23 #include <debug.h>
24 
25 HANDLE GlobalUserHeap = NULL;
26 PVOID GlobalUserHeapSection = NULL;
27 
28 
29 _Function_class_(RTL_HEAP_COMMIT_ROUTINE)
30 _IRQL_requires_same_
31 static
32 NTSTATUS
33 NTAPI
34 IntUserHeapCommitRoutine(
35     _In_ PVOID Base,
36     _Inout_ PVOID *CommitAddress,
37     _Inout_ PSIZE_T CommitSize)
38 {
39     PPROCESSINFO W32Process;
40     PW32HEAP_USER_MAPPING Mapping;
41     PVOID UserBase = NULL;
42     NTSTATUS Status;
43     SIZE_T Delta;
44     PVOID UserCommitAddress;
45 
46     W32Process = PsGetCurrentProcessWin32Process();
47 
48     if (W32Process != NULL)
49     {
50         /* Search for the mapping */
51         Mapping = &W32Process->HeapMappings;
52         while (Mapping != NULL)
53         {
54             if (Mapping->KernelMapping == Base)
55             {
56                 UserBase = Mapping->UserMapping;
57                 break;
58             }
59 
60             Mapping = Mapping->Next;
61         }
62 
63         ASSERT(UserBase != NULL);
64     }
65     else
66     {
67         SIZE_T ViewSize = 0;
68         LARGE_INTEGER Offset;
69 
70         /* HACK: This needs to be handled during startup only... */
71         ASSERT(Base == (PVOID)GlobalUserHeap);
72 
73         /* Temporarily map it into user space */
74         Offset.QuadPart = 0;
75         Status = MmMapViewOfSection(GlobalUserHeapSection,
76                                     PsGetCurrentProcess(),
77                                     &UserBase,
78                                     0,
79                                     0,
80                                     &Offset,
81                                     &ViewSize,
82                                     ViewUnmap,
83                                     SEC_NO_CHANGE,
84                                     PAGE_EXECUTE_READ); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
85 
86         if (!NT_SUCCESS(Status))
87             return Status;
88     }
89 
90     /* Apply the commit address offset to the user base address */
91     Delta = (SIZE_T)((ULONG_PTR)(*CommitAddress) - (ULONG_PTR)Base);
92     UserCommitAddress = (PVOID)((ULONG_PTR)UserBase + Delta);
93 
94     /* Perform the actual commit */
95     Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
96                                      &UserCommitAddress,
97                                      0,
98                                      CommitSize,
99                                      MEM_COMMIT,
100                                      PAGE_EXECUTE_READ);
101 
102     if (NT_SUCCESS(Status))
103     {
104         /* Determine the address to return */
105         Delta = (SIZE_T)((ULONG_PTR)UserCommitAddress - (ULONG_PTR)UserBase);
106         *CommitAddress = (PVOID)((ULONG_PTR)Base + Delta);
107     }
108 
109     if (W32Process == NULL)
110     {
111         MmUnmapViewOfSection(PsGetCurrentProcess(),
112                              UserBase);
113     }
114 
115     return Status;
116 }
117 
118 static PWIN32HEAP
119 IntUserHeapCreate(IN PVOID SectionObject,
120                   IN PVOID *SystemMappedBase,
121                   IN ULONG HeapSize)
122 {
123     PVOID MappedView = NULL;
124     LARGE_INTEGER Offset;
125     SIZE_T ViewSize = PAGE_SIZE;
126     RTL_HEAP_PARAMETERS Parameters = {0};
127     PVOID pHeap;
128     NTSTATUS Status;
129 
130     Offset.QuadPart = 0;
131 
132     /* Commit the first page before creating the heap! */
133     Status = MmMapViewOfSection(SectionObject,
134                                 PsGetCurrentProcess(),
135                                 &MappedView,
136                                 0,
137                                 0,
138                                 &Offset,
139                                 &ViewSize,
140                                 ViewUnmap,
141                                 SEC_NO_CHANGE,
142                                 PAGE_EXECUTE_READ); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
143     if (!NT_SUCCESS(Status))
144         return NULL;
145 
146     Status = ZwAllocateVirtualMemory(NtCurrentProcess(),
147                                      &MappedView,
148                                      0,
149                                      &ViewSize,
150                                      MEM_COMMIT,
151                                      PAGE_EXECUTE_READ); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
152 
153     MmUnmapViewOfSection(PsGetCurrentProcess(),
154                          MappedView);
155 
156     if (!NT_SUCCESS(Status))
157         return NULL;
158 
159     /* Create the heap, don't serialize in kmode! The caller is responsible
160        to synchronize the heap! */
161     Parameters.Length = sizeof(Parameters);
162     Parameters.InitialCommit = ViewSize;
163     Parameters.InitialReserve = (SIZE_T)HeapSize;
164     Parameters.CommitRoutine = IntUserHeapCommitRoutine;
165 
166     pHeap = RtlCreateHeap(
167 #if DBG /* Enable checks on debug builds */
168                           HEAP_FREE_CHECKING_ENABLED | HEAP_TAIL_CHECKING_ENABLED |
169 #endif
170                           HEAP_ZERO_MEMORY | HEAP_NO_SERIALIZE,
171                           *SystemMappedBase,
172                           (SIZE_T)HeapSize,
173                           ViewSize,
174                           NULL,
175                           &Parameters);
176 
177     return pHeap;
178 }
179 
180 PWIN32HEAP
181 UserCreateHeap(OUT PVOID *SectionObject,
182                IN OUT PVOID *SystemBase,
183                IN SIZE_T HeapSize)
184 {
185     LARGE_INTEGER SizeHeap;
186     PWIN32HEAP pHeap = NULL;
187     NTSTATUS Status;
188 
189     SizeHeap.QuadPart = HeapSize;
190 
191     /* Create the section and map it into session space */
192     Status = MmCreateSection((PVOID*)SectionObject,
193                              SECTION_ALL_ACCESS,
194                              NULL,
195                              &SizeHeap,
196                              PAGE_EXECUTE_READWRITE, /* Would prefer PAGE_READWRITE, but thanks to RTL heaps... */
197                              SEC_RESERVE | 1,
198                              NULL,
199                              NULL);
200 
201     if (!NT_SUCCESS(Status))
202     {
203         SetLastNtError(Status);
204         return FALSE;
205     }
206 
207     Status = MmMapViewInSessionSpace(*SectionObject,
208                                      SystemBase,
209                                      &HeapSize);
210     if (!NT_SUCCESS(Status))
211     {
212         ObDereferenceObject(*SectionObject);
213         *SectionObject = NULL;
214 
215         SetLastNtError(Status);
216         return FALSE;
217     }
218 
219     /* Create the heap */
220     pHeap = IntUserHeapCreate(*SectionObject,
221                               SystemBase,
222                               HeapSize);
223 
224     if (pHeap == NULL)
225     {
226         ObDereferenceObject(*SectionObject);
227         *SectionObject = NULL;
228 
229         SetLastNtError(STATUS_UNSUCCESSFUL);
230     }
231 
232     return pHeap;
233 }
234 
235 NTSTATUS
236 UnmapGlobalUserHeap(IN PEPROCESS Process)
237 {
238     NTSTATUS Status = STATUS_SUCCESS;
239     PPROCESSINFO W32Process;
240     PW32HEAP_USER_MAPPING HeapMapping;
241 
242     TRACE_CH(UserProcess, "IntUnmapDesktopView called for process 0x%p\n", Process);
243 
244     W32Process = PsGetProcessWin32Process(Process);
245     if (W32Process == NULL)
246     {
247         ERR_CH(UserProcess, "UnmapGlobalUserHeap - We don't have a Win32 process!\n");
248         ASSERT(FALSE);
249     }
250 
251     /* The first mapping entry must be the global user heap */
252     HeapMapping = &W32Process->HeapMappings;
253     ASSERT(HeapMapping->KernelMapping == (PVOID)GlobalUserHeap);
254 
255     /* Unmap if we're the last thread using the global user heap */
256     if (--HeapMapping->Count == 0)
257     {
258         TRACE_CH(UserProcess, "UnmapGlobalUserHeap - Unmapping\n");
259         Status = MmUnmapViewOfSection(Process, HeapMapping->UserMapping);
260     }
261 
262     return Status;
263 }
264 
265 NTSTATUS
266 MapGlobalUserHeap(IN  PEPROCESS Process,
267                   OUT PVOID* KernelMapping,
268                   OUT PVOID* UserMapping)
269 {
270     NTSTATUS Status;
271     PPROCESSINFO W32Process;
272     PW32HEAP_USER_MAPPING HeapMapping;
273     PVOID UserBase = NULL;
274 
275     SIZE_T ViewSize = 0;
276     LARGE_INTEGER Offset;
277 
278     TRACE_CH(UserProcess, "MapGlobalUserHeap called for process 0x%p\n", Process);
279 
280     W32Process = PsGetProcessWin32Process(Process);
281     if (W32Process == NULL)
282     {
283         ERR_CH(UserProcess, "MapGlobalUserHeap - We don't have a Win32 process!\n");
284         ASSERT(FALSE);
285     }
286 
287     TRACE_CH(UserProcess, "MapGlobalUserHeap - We got a Win32 process, find for existing global user heap mapping...\n");
288 
289     /* The first mapping entry must be the global user heap */
290     HeapMapping = &W32Process->HeapMappings;
291 
292     /* Find out if another thread already mapped the global user heap */
293     if (HeapMapping->KernelMapping == (PVOID)GlobalUserHeap)
294     {
295         HeapMapping->Count++;
296 
297         TRACE_CH(UserProcess, "MapGlobalUserHeap - A mapping was found, return it.\n");
298 
299         *KernelMapping = HeapMapping->KernelMapping;
300         *UserMapping   = HeapMapping->UserMapping;
301 
302         return STATUS_SUCCESS;
303     }
304 
305     TRACE_CH(UserProcess, "MapGlobalUserHeap - No mapping was found, let's map...\n");
306 
307     /* We're the first, map the global heap into the process */
308     Offset.QuadPart = 0;
309     Status = MmMapViewOfSection(GlobalUserHeapSection,
310                                 Process,
311                                 &UserBase,
312                                 0,
313                                 0,
314                                 &Offset,
315                                 &ViewSize,
316                                 ViewUnmap,
317                                 SEC_NO_CHANGE,
318                                 PAGE_EXECUTE_READ); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
319     if (!NT_SUCCESS(Status))
320     {
321         ERR_CH(UserProcess, "MapGlobalUserHeap - Failed to map the global heap! 0x%x\n", Status);
322         return Status;
323     }
324 
325     TRACE_CH(UserProcess, "MapGlobalUserHeap -- Mapped kernel global heap 0x%p to user space at 0x%p\n",
326            GlobalUserHeap, UserBase);
327 
328     /* Add the mapping */
329     HeapMapping->Next = NULL;
330     HeapMapping->KernelMapping = (PVOID)GlobalUserHeap;
331     HeapMapping->UserMapping = UserBase;
332     HeapMapping->Limit = ViewSize;
333     HeapMapping->Count = 1;
334 
335     *KernelMapping = HeapMapping->KernelMapping;
336     *UserMapping   = HeapMapping->UserMapping;
337 
338     return STATUS_SUCCESS;
339 }
340 
341 /* EOF */
342