- Implement a special "debug pool" allocator which catches pool overruns. It evolved...
[reactos.git] / reactos / ntoskrnl / mm / pool.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/mm/pool.c
5 * PURPOSE: Implements the kernel memory pool
6 *
7 * PROGRAMMERS: David Welch (welch@mcmail.com)
8 */
9
10 /* INCLUDES ****************************************************************/
11
12 #include <ntoskrnl.h>
13
14 #define NDEBUG
15 #include <debug.h>
16
17 /* Uncomment to enable pool overruns debugging */
18 //#define DEBUG_NPOOL
19 //#define DEBUG_PPOOL
20
21 extern PVOID MiNonPagedPoolStart;
22 extern ULONG MiNonPagedPoolLength;
23 extern ULONG MmTotalPagedPoolQuota;
24 extern ULONG MmTotalNonPagedPoolQuota;
25 extern MM_STATS MmStats;
26
27 /* FUNCTIONS ***************************************************************/
28
29 ULONG NTAPI
30 EiGetPagedPoolTag(IN PVOID Block);
31
32 ULONG NTAPI
33 EiGetNonPagedPoolTag(IN PVOID Block);
34
35 static PVOID NTAPI
36 EiAllocatePool(POOL_TYPE PoolType,
37 ULONG NumberOfBytes,
38 ULONG Tag,
39 PVOID Caller)
40 {
41 PVOID Block;
42 PCHAR TagChars = (PCHAR)&Tag;
43
44 if (Tag == 0)
45 KeBugCheckEx(BAD_POOL_CALLER, 0x9b, PoolType, NumberOfBytes, (ULONG_PTR)Caller);
46 if (Tag == TAG('B','I','G',0))
47 KeBugCheckEx(BAD_POOL_CALLER, 0x9c, PoolType, NumberOfBytes, (ULONG_PTR)Caller);
48
49 #define IS_LETTER_OR_DIGIT(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z') || ((c) >= '0' && (c) <= '9'))
50 if (!IS_LETTER_OR_DIGIT(TagChars[0]) &&
51 !IS_LETTER_OR_DIGIT(TagChars[1]) &&
52 !IS_LETTER_OR_DIGIT(TagChars[2]) &&
53 !IS_LETTER_OR_DIGIT(TagChars[3]))
54 KeBugCheckEx(BAD_POOL_CALLER, 0x9d, Tag, PoolType, (ULONG_PTR)Caller);
55
56 /* FIXME: Handle SESSION_POOL_MASK, VERIFIER_POOL_MASK, QUOTA_POOL_MASK */
57 if (PoolType & PAGED_POOL_MASK)
58 {
59 if (KeGetCurrentIrql() > APC_LEVEL)
60 KeBugCheckEx(BAD_POOL_CALLER, 0x08, KeGetCurrentIrql(), PoolType, Tag);
61 Block = ExAllocatePagedPoolWithTag(PoolType, NumberOfBytes, Tag);
62 }
63 else
64 {
65 if (KeGetCurrentIrql() > DISPATCH_LEVEL)
66 KeBugCheckEx(BAD_POOL_CALLER, 0x08, KeGetCurrentIrql(), PoolType, Tag);
67 #ifdef DEBUG_NPOOL
68 if (ExpIsPoolTagDebuggable(Tag))
69 Block = ExpAllocateDebugPool(PoolType, NumberOfBytes, Tag, Caller, TRUE);
70 else
71 #endif
72 Block = ExAllocateNonPagedPoolWithTag(PoolType, NumberOfBytes, Tag, Caller);
73 }
74
75 if ((PoolType & MUST_SUCCEED_POOL_MASK) && !Block)
76 KeBugCheckEx(BAD_POOL_CALLER, 0x9a, PoolType, NumberOfBytes, Tag);
77 return Block;
78 }
79
80 /*
81 * @implemented
82 */
83 PVOID NTAPI
84 ExAllocatePool (POOL_TYPE PoolType, ULONG NumberOfBytes)
85 /*
86 * FUNCTION: Allocates pool memory of a specified type and returns a pointer
87 * to the allocated block. This routine is used for general purpose allocation
88 * of memory
89 * ARGUMENTS:
90 * PoolType
91 * Specifies the type of memory to allocate which can be one
92 * of the following:
93 *
94 * NonPagedPool
95 * NonPagedPoolMustSucceed
96 * NonPagedPoolCacheAligned
97 * NonPagedPoolCacheAlignedMustS
98 * PagedPool
99 * PagedPoolCacheAligned
100 *
101 * NumberOfBytes
102 * Specifies the number of bytes to allocate
103 * RETURNS: The allocated block on success
104 * NULL on failure
105 */
106 {
107 PVOID Block;
108
109 #if defined(__GNUC__)
110
111 Block = EiAllocatePool(PoolType,
112 NumberOfBytes,
113 TAG_NONE,
114 (PVOID)__builtin_return_address(0));
115 #elif defined(_MSC_VER)
116
117 Block = EiAllocatePool(PoolType,
118 NumberOfBytes,
119 TAG_NONE,
120 &ExAllocatePool);
121 #else
122 #error Unknown compiler
123 #endif
124
125 return(Block);
126 }
127
128
129 /*
130 * @implemented
131 */
132 PVOID NTAPI
133 ExAllocatePoolWithTag (POOL_TYPE PoolType, ULONG NumberOfBytes, ULONG Tag)
134 {
135 PVOID Block;
136
137 #if defined(__GNUC__)
138
139 Block = EiAllocatePool(PoolType,
140 NumberOfBytes,
141 Tag,
142 (PVOID)__builtin_return_address(0));
143 #elif defined(_MSC_VER)
144
145 Block = EiAllocatePool(PoolType,
146 NumberOfBytes,
147 Tag,
148 &ExAllocatePoolWithTag);
149 #else
150 #error Unknown compiler
151 #endif
152
153 return(Block);
154 }
155
156
157 /*
158 * @implemented
159 */
160 #undef ExAllocatePoolWithQuota
161 PVOID NTAPI
162 ExAllocatePoolWithQuota (POOL_TYPE PoolType, ULONG NumberOfBytes)
163 {
164 return(ExAllocatePoolWithQuotaTag(PoolType, NumberOfBytes, TAG_NONE));
165 }
166
167 /*
168 * @implemented
169 */
170 PVOID
171 NTAPI
172 ExAllocatePoolWithTagPriority(
173 IN POOL_TYPE PoolType,
174 IN SIZE_T NumberOfBytes,
175 IN ULONG Tag,
176 IN EX_POOL_PRIORITY Priority
177 )
178 {
179 /* Check if this is one of the "Special" Flags, used by the Verifier */
180 if (Priority & 8) {
181 /* Check if this is a xxSpecialUnderrun */
182 if (Priority & 1) {
183 return MiAllocateSpecialPool(PoolType, NumberOfBytes, Tag, 1);
184 } else { /* xxSpecialOverrun */
185 return MiAllocateSpecialPool(PoolType, NumberOfBytes, Tag, 0);
186 }
187 }
188
189 /* FIXME: Do Ressource Checking Based on Priority and fail if resources too low*/
190
191 /* Do the allocation */
192 return ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag);
193 }
194
195 /*
196 * @implemented
197 */
198 #undef ExAllocatePoolWithQuotaTag
199 PVOID
200 NTAPI
201 ExAllocatePoolWithQuotaTag (IN POOL_TYPE PoolType,
202 IN ULONG NumberOfBytes,
203 IN ULONG Tag)
204 {
205 PEPROCESS Process;
206 PVOID Block;
207
208 /* Allocate the Pool First */
209 Block = EiAllocatePool(PoolType,
210 NumberOfBytes,
211 Tag,
212 &ExAllocatePoolWithQuotaTag);
213
214 /* "Quota is not charged to the thread for allocations >= PAGE_SIZE" - OSR Docs */
215 if (!(NumberOfBytes >= PAGE_SIZE))
216 {
217 /* Get the Current Process */
218 Process = PsGetCurrentProcess();
219
220 /* PsChargePoolQuota returns an exception, so this needs SEH */
221 _SEH2_TRY
222 {
223 /* FIXME: Is there a way to get the actual Pool size allocated from the pool header? */
224 PsChargePoolQuota(Process,
225 PoolType & PAGED_POOL_MASK,
226 NumberOfBytes);
227 }
228 _SEH2_EXCEPT((ExFreePool(Block), EXCEPTION_CONTINUE_SEARCH))
229 {
230 /* Quota Exceeded and the caller had no SEH! */
231 KeBugCheck(STATUS_QUOTA_EXCEEDED);
232 }
233 _SEH2_END;
234 }
235
236 /* Return the allocated block */
237 return Block;
238 }
239
240 /*
241 * @implemented
242 */
243 #undef ExFreePool
244 VOID NTAPI
245 ExFreePool(IN PVOID Block)
246 {
247 ExFreePoolWithTag(Block, 0);
248 }
249
250 /*
251 * @implemented
252 */
253 VOID
254 NTAPI
255 ExFreePoolWithTag(
256 IN PVOID Block,
257 IN ULONG Tag)
258 {
259 /* Check for paged pool */
260 if (Block >= MmPagedPoolBase &&
261 (char*)Block < ((char*)MmPagedPoolBase + MmPagedPoolSize))
262 {
263 /* Validate tag */
264 if (Tag != 0 && Tag != EiGetPagedPoolTag(Block))
265 KeBugCheckEx(BAD_POOL_CALLER,
266 0x0a,
267 (ULONG_PTR)Block,
268 EiGetPagedPoolTag(Block),
269 Tag);
270
271 /* Validate IRQL */
272 if (KeGetCurrentIrql() > APC_LEVEL)
273 KeBugCheckEx(BAD_POOL_CALLER,
274 0x09,
275 KeGetCurrentIrql(),
276 PagedPool,
277 (ULONG_PTR)Block);
278
279 /* Free from paged pool */
280 ExFreePagedPool(Block);
281 }
282
283 /* Check for non-paged pool */
284 else if (Block >= MiNonPagedPoolStart &&
285 (char*)Block < ((char*)MiNonPagedPoolStart + MiNonPagedPoolLength))
286 {
287 /* Validate tag */
288 if (Tag != 0 && Tag != EiGetNonPagedPoolTag(Block))
289 KeBugCheckEx(BAD_POOL_CALLER,
290 0x0a,
291 (ULONG_PTR)Block,
292 EiGetNonPagedPoolTag(Block),
293 Tag);
294
295 /* Validate IRQL */
296 if (KeGetCurrentIrql() > DISPATCH_LEVEL)
297 KeBugCheckEx(BAD_POOL_CALLER,
298 0x09,
299 KeGetCurrentIrql(),
300 NonPagedPool,
301 (ULONG_PTR)Block);
302
303 /* Free from non-paged pool */
304 #ifdef DEBUG_NPOOL
305 if (ExpIsPoolTagDebuggable(Tag))
306 ExpFreeDebugPool(Block);
307 else
308 #endif
309 ExFreeNonPagedPool(Block);
310 }
311 else
312 {
313 /* Warn only for NULL pointers */
314 if (Block == NULL)
315 {
316 DPRINT1("Warning: Trying to free a NULL pointer!\n");
317 return;
318 }
319
320 /* Block was not inside any pool! */
321 KeBugCheckEx(BAD_POOL_CALLER, 0x42, (ULONG_PTR)Block, 0, 0);
322 }
323 }
324
325 /*
326 * @unimplemented
327 */
328 SIZE_T
329 NTAPI
330 ExQueryPoolBlockSize (
331 IN PVOID PoolBlock,
332 OUT PBOOLEAN QuotaCharged
333 )
334 {
335 UNIMPLEMENTED;
336 return FALSE;
337 }
338
339 /*
340 * @unimplemented
341 */
342 PVOID
343 NTAPI
344 MmAllocateMappingAddress (
345 IN SIZE_T NumberOfBytes,
346 IN ULONG PoolTag
347 )
348 {
349 UNIMPLEMENTED;
350 return 0;
351 }
352
353
354 /*
355 * @unimplemented
356 */
357 VOID
358 NTAPI
359 MmFreeMappingAddress (
360 IN PVOID BaseAddress,
361 IN ULONG PoolTag
362 )
363 {
364 UNIMPLEMENTED;
365 }
366
367 BOOLEAN
368 NTAPI
369 MiRaisePoolQuota(
370 IN POOL_TYPE PoolType,
371 IN ULONG CurrentMaxQuota,
372 OUT PULONG NewMaxQuota
373 )
374 {
375 /* Different quota raises depending on the type (64K vs 512K) */
376 if (PoolType == PagedPool) {
377
378 /* Make sure that 4MB is still left */
379 if ((MM_PAGED_POOL_SIZE >> 12) < ((MmPagedPoolSize + 4194304) >> 12)) {
380 return FALSE;
381 }
382
383 /* Increase Paged Pool Quota by 512K */
384 MmTotalPagedPoolQuota += 524288;
385 *NewMaxQuota = CurrentMaxQuota + 524288;
386 return TRUE;
387
388 } else { /* Nonpaged Pool */
389
390 /* Check if we still have 200 pages free*/
391 if (MmStats.NrFreePages < 200) return FALSE;
392
393 /* Check that 4MB is still left */
394 if ((MM_NONPAGED_POOL_SIZE >> 12) < ((MiNonPagedPoolLength + 4194304) >> 12)) {
395 return FALSE;
396 }
397
398 /* Increase Non Paged Pool Quota by 64K */
399 MmTotalNonPagedPoolQuota += 65536;
400 *NewMaxQuota = CurrentMaxQuota + 65536;
401 return TRUE;
402 }
403 }
404
405 /* EOF */