advapi32: Test for OpenProcessToken with MAXIMUM_ALLOWED access.
[wine] / dlls / ntdll / heap.c
1 /*
2  * Win32 heap functions
3  *
4  * Copyright 1996 Alexandre Julliard
5  * Copyright 1998 Ulrich Weigand
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <string.h>
29 #ifdef HAVE_VALGRIND_MEMCHECK_H
30 #include <valgrind/memcheck.h>
31 #endif
32
33 #define NONAMELESSUNION
34 #define NONAMELESSSTRUCT
35 #include "ntstatus.h"
36 #define WIN32_NO_STATUS
37 #include "windef.h"
38 #include "winternl.h"
39 #include "winnt.h"
40 #include "winternl.h"
41 #include "wine/list.h"
42 #include "wine/debug.h"
43 #include "wine/server.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(heap);
46
47 /* Note: the heap data structures are based on what Pietrek describes in his
48  * book 'Windows 95 System Programming Secrets'. The layout is not exactly
49  * the same, but could be easily adapted if it turns out some programs
50  * require it.
51  */
52
53 /* FIXME: use SIZE_T for 'size' structure members, but we need to make sure
54  * that there is no unaligned accesses to structure fields.
55  */
56
57 typedef struct tagARENA_INUSE
58 {
59     DWORD  size;                    /* Block size; must be the first field */
60     DWORD  magic : 24;              /* Magic number */
61     DWORD  unused_bytes : 8;        /* Number of bytes in the block not used by user data (max value is HEAP_MIN_DATA_SIZE+HEAP_MIN_SHRINK_SIZE) */
62 } ARENA_INUSE;
63
64 typedef struct tagARENA_FREE
65 {
66     DWORD                 size;     /* Block size; must be the first field */
67     DWORD                 magic;    /* Magic number */
68     struct list           entry;    /* Entry in free list */
69 } ARENA_FREE;
70
71 #define ARENA_FLAG_FREE        0x00000001  /* flags OR'ed with arena size */
72 #define ARENA_FLAG_PREV_FREE   0x00000002
73 #define ARENA_SIZE_MASK        (~3)
74 #define ARENA_INUSE_MAGIC      0x455355        /* Value for arena 'magic' field */
75 #define ARENA_FREE_MAGIC       0x45455246      /* Value for arena 'magic' field */
76
77 #define ARENA_INUSE_FILLER     0x55
78 #define ARENA_FREE_FILLER      0xaa
79
80 #define ALIGNMENT              8   /* everything is aligned on 8 byte boundaries */
81 #define ROUND_SIZE(size)       (((size) + ALIGNMENT - 1) & ~(ALIGNMENT-1))
82
83 #define QUIET                  1           /* Suppress messages  */
84 #define NOISY                  0           /* Report all errors  */
85
86 /* minimum data size (without arenas) of an allocated block */
87 /* make sure that it's larger than a free list entry */
88 #define HEAP_MIN_DATA_SIZE    (2 * sizeof(struct list))
89 /* minimum size that must remain to shrink an allocated block */
90 #define HEAP_MIN_SHRINK_SIZE  (HEAP_MIN_DATA_SIZE+sizeof(ARENA_FREE))
91
92 /* Max size of the blocks on the free lists */
93 static const DWORD HEAP_freeListSizes[] =
94 {
95     0x10, 0x20, 0x30, 0x40, 0x60, 0x80, 0x100, 0x200, 0x400, 0x1000, ~0UL
96 };
97 #define HEAP_NB_FREE_LISTS  (sizeof(HEAP_freeListSizes)/sizeof(HEAP_freeListSizes[0]))
98
99 typedef struct
100 {
101     ARENA_FREE  arena;
102 } FREE_LIST_ENTRY;
103
104 struct tagHEAP;
105
106 typedef struct tagSUBHEAP
107 {
108     DWORD               size;       /* Size of the whole sub-heap */
109     DWORD               commitSize; /* Committed size of the sub-heap */
110     DWORD               headerSize; /* Size of the heap header */
111     struct tagSUBHEAP  *next;       /* Next sub-heap */
112     struct tagHEAP     *heap;       /* Main heap structure */
113     DWORD               magic;      /* Magic number */
114 } SUBHEAP;
115
116 #define SUBHEAP_MAGIC    ((DWORD)('S' | ('U'<<8) | ('B'<<16) | ('H'<<24)))
117
118 typedef struct tagHEAP
119 {
120     SUBHEAP          subheap;       /* First sub-heap */
121     struct list      entry;         /* Entry in process heap list */
122     RTL_CRITICAL_SECTION critSection; /* Critical section for serialization */
123     FREE_LIST_ENTRY  freeList[HEAP_NB_FREE_LISTS];  /* Free lists */
124     DWORD            flags;         /* Heap flags */
125     DWORD            magic;         /* Magic number */
126 } HEAP;
127
128 #define HEAP_MAGIC       ((DWORD)('H' | ('E'<<8) | ('A'<<16) | ('P'<<24)))
129
130 #define HEAP_DEF_SIZE        0x110000   /* Default heap size = 1Mb + 64Kb */
131 #define COMMIT_MASK          0xffff  /* bitmask for commit/decommit granularity */
132
133 static HEAP *processHeap;  /* main process heap */
134
135 static BOOL HEAP_IsRealArena( HEAP *heapPtr, DWORD flags, LPCVOID block, BOOL quiet );
136
137 /* mark a block of memory as free for debugging purposes */
138 static inline void mark_block_free( void *ptr, SIZE_T size )
139 {
140     if (TRACE_ON(heap)) memset( ptr, ARENA_FREE_FILLER, size );
141 #ifdef VALGRIND_MAKE_NOACCESS
142     VALGRIND_DISCARD( VALGRIND_MAKE_NOACCESS( ptr, size ));
143 #endif
144 }
145
146 /* mark a block of memory as initialized for debugging purposes */
147 static inline void mark_block_initialized( void *ptr, SIZE_T size )
148 {
149 #ifdef VALGRIND_MAKE_READABLE
150     VALGRIND_DISCARD( VALGRIND_MAKE_READABLE( ptr, size ));
151 #endif
152 }
153
154 /* mark a block of memory as uninitialized for debugging purposes */
155 static inline void mark_block_uninitialized( void *ptr, SIZE_T size )
156 {
157 #ifdef VALGRIND_MAKE_WRITABLE
158     VALGRIND_DISCARD( VALGRIND_MAKE_WRITABLE( ptr, size ));
159 #endif
160     if (TRACE_ON(heap))
161     {
162         memset( ptr, ARENA_INUSE_FILLER, size );
163 #ifdef VALGRIND_MAKE_WRITABLE
164         /* make it uninitialized to valgrind again */
165         VALGRIND_DISCARD( VALGRIND_MAKE_WRITABLE( ptr, size ));
166 #endif
167     }
168 }
169
170 /* clear contents of a block of memory */
171 static inline void clear_block( void *ptr, SIZE_T size )
172 {
173     mark_block_initialized( ptr, size );
174     memset( ptr, 0, size );
175 }
176
177 /* notify that a new block of memory has been allocated for debugging purposes */
178 static inline void notify_alloc( void *ptr, SIZE_T size, BOOL init )
179 {
180 #ifdef VALGRIND_MALLOCLIKE_BLOCK
181     VALGRIND_MALLOCLIKE_BLOCK( ptr, size, 0, init );
182 #endif
183 }
184
185 /* notify that a block of memory has been freed for debugging purposes */
186 static inline void notify_free( void *ptr )
187 {
188 #ifdef VALGRIND_FREELIKE_BLOCK
189     VALGRIND_FREELIKE_BLOCK( ptr, 0 );
190 #endif
191 }
192
193 /* locate a free list entry of the appropriate size */
194 /* size is the size of the whole block including the arena header */
195 static inline unsigned int get_freelist_index( SIZE_T size )
196 {
197     unsigned int i;
198
199     size -= sizeof(ARENA_FREE);
200     for (i = 0; i < HEAP_NB_FREE_LISTS - 1; i++) if (size <= HEAP_freeListSizes[i]) break;
201     return i;
202 }
203
204 static RTL_CRITICAL_SECTION_DEBUG process_heap_critsect_debug =
205 {
206     0, 0, NULL,  /* will be set later */
207     { &process_heap_critsect_debug.ProcessLocksList, &process_heap_critsect_debug.ProcessLocksList },
208       0, 0, { (DWORD_PTR)(__FILE__ ": main process heap section") }
209 };
210
211
212 /***********************************************************************
213  *           HEAP_Dump
214  */
215 static void HEAP_Dump( HEAP *heap )
216 {
217     int i;
218     SUBHEAP *subheap;
219     char *ptr;
220
221     DPRINTF( "Heap: %p\n", heap );
222     DPRINTF( "Next: %p  Sub-heaps: %p",
223              LIST_ENTRY( heap->entry.next, HEAP, entry ), &heap->subheap );
224     subheap = &heap->subheap;
225     while (subheap->next)
226     {
227         DPRINTF( " -> %p", subheap->next );
228         subheap = subheap->next;
229     }
230
231     DPRINTF( "\nFree lists:\n Block   Stat   Size    Id\n" );
232     for (i = 0; i < HEAP_NB_FREE_LISTS; i++)
233         DPRINTF( "%p free %08x prev=%p next=%p\n",
234                  &heap->freeList[i].arena, HEAP_freeListSizes[i],
235                  LIST_ENTRY( heap->freeList[i].arena.entry.prev, ARENA_FREE, entry ),
236                  LIST_ENTRY( heap->freeList[i].arena.entry.next, ARENA_FREE, entry ));
237
238     subheap = &heap->subheap;
239     while (subheap)
240     {
241         SIZE_T freeSize = 0, usedSize = 0, arenaSize = subheap->headerSize;
242         DPRINTF( "\n\nSub-heap %p: size=%08x committed=%08x\n",
243                 subheap, subheap->size, subheap->commitSize );
244
245         DPRINTF( "\n Block   Stat   Size    Id\n" );
246         ptr = (char*)subheap + subheap->headerSize;
247         while (ptr < (char *)subheap + subheap->size)
248         {
249             if (*(DWORD *)ptr & ARENA_FLAG_FREE)
250             {
251                 ARENA_FREE *pArena = (ARENA_FREE *)ptr;
252                 DPRINTF( "%p free %08x prev=%p next=%p\n",
253                          pArena, pArena->size & ARENA_SIZE_MASK,
254                          LIST_ENTRY( pArena->entry.prev, ARENA_FREE, entry ),
255                          LIST_ENTRY( pArena->entry.next, ARENA_FREE, entry ) );
256                 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
257                 arenaSize += sizeof(ARENA_FREE);
258                 freeSize += pArena->size & ARENA_SIZE_MASK;
259             }
260             else if (*(DWORD *)ptr & ARENA_FLAG_PREV_FREE)
261             {
262                 ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
263                 DPRINTF( "%p Used %08x back=%p\n",
264                         pArena, pArena->size & ARENA_SIZE_MASK, *((ARENA_FREE **)pArena - 1) );
265                 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
266                 arenaSize += sizeof(ARENA_INUSE);
267                 usedSize += pArena->size & ARENA_SIZE_MASK;
268             }
269             else
270             {
271                 ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
272                 DPRINTF( "%p used %08x\n", pArena, pArena->size & ARENA_SIZE_MASK );
273                 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
274                 arenaSize += sizeof(ARENA_INUSE);
275                 usedSize += pArena->size & ARENA_SIZE_MASK;
276             }
277         }
278         DPRINTF( "\nTotal: Size=%08x Committed=%08x Free=%08lx Used=%08lx Arenas=%08lx (%ld%%)\n\n",
279               subheap->size, subheap->commitSize, freeSize, usedSize,
280               arenaSize, (arenaSize * 100) / subheap->size );
281         subheap = subheap->next;
282     }
283 }
284
285
286 static void HEAP_DumpEntry( LPPROCESS_HEAP_ENTRY entry )
287 {
288     WORD rem_flags;
289     TRACE( "Dumping entry %p\n", entry );
290     TRACE( "lpData\t\t: %p\n", entry->lpData );
291     TRACE( "cbData\t\t: %08x\n", entry->cbData);
292     TRACE( "cbOverhead\t: %08x\n", entry->cbOverhead);
293     TRACE( "iRegionIndex\t: %08x\n", entry->iRegionIndex);
294     TRACE( "WFlags\t\t: ");
295     if (entry->wFlags & PROCESS_HEAP_REGION)
296         TRACE( "PROCESS_HEAP_REGION ");
297     if (entry->wFlags & PROCESS_HEAP_UNCOMMITTED_RANGE)
298         TRACE( "PROCESS_HEAP_UNCOMMITTED_RANGE ");
299     if (entry->wFlags & PROCESS_HEAP_ENTRY_BUSY)
300         TRACE( "PROCESS_HEAP_ENTRY_BUSY ");
301     if (entry->wFlags & PROCESS_HEAP_ENTRY_MOVEABLE)
302         TRACE( "PROCESS_HEAP_ENTRY_MOVEABLE ");
303     if (entry->wFlags & PROCESS_HEAP_ENTRY_DDESHARE)
304         TRACE( "PROCESS_HEAP_ENTRY_DDESHARE ");
305     rem_flags = entry->wFlags &
306         ~(PROCESS_HEAP_REGION | PROCESS_HEAP_UNCOMMITTED_RANGE |
307           PROCESS_HEAP_ENTRY_BUSY | PROCESS_HEAP_ENTRY_MOVEABLE|
308           PROCESS_HEAP_ENTRY_DDESHARE);
309     if (rem_flags)
310         TRACE( "Unknown %08x", rem_flags);
311     TRACE( "\n");
312     if ((entry->wFlags & PROCESS_HEAP_ENTRY_BUSY )
313         && (entry->wFlags & PROCESS_HEAP_ENTRY_MOVEABLE))
314     {
315         /* Treat as block */
316         TRACE( "BLOCK->hMem\t\t:%p\n", entry->u.Block.hMem);
317     }
318     if (entry->wFlags & PROCESS_HEAP_REGION)
319     {
320         TRACE( "Region.dwCommittedSize\t:%08x\n",entry->u.Region.dwCommittedSize);
321         TRACE( "Region.dwUnCommittedSize\t:%08x\n",entry->u.Region.dwUnCommittedSize);
322         TRACE( "Region.lpFirstBlock\t:%p\n",entry->u.Region.lpFirstBlock);
323         TRACE( "Region.lpLastBlock\t:%p\n",entry->u.Region.lpLastBlock);
324     }
325 }
326
327 /***********************************************************************
328  *           HEAP_GetPtr
329  * RETURNS
330  *      Pointer to the heap
331  *      NULL: Failure
332  */
333 static HEAP *HEAP_GetPtr(
334              HANDLE heap /* [in] Handle to the heap */
335 ) {
336     HEAP *heapPtr = (HEAP *)heap;
337     if (!heapPtr || (heapPtr->magic != HEAP_MAGIC))
338     {
339         ERR("Invalid heap %p!\n", heap );
340         return NULL;
341     }
342     if (TRACE_ON(heap) && !HEAP_IsRealArena( heapPtr, 0, NULL, NOISY ))
343     {
344         HEAP_Dump( heapPtr );
345         assert( FALSE );
346         return NULL;
347     }
348     return heapPtr;
349 }
350
351
352 /***********************************************************************
353  *           HEAP_InsertFreeBlock
354  *
355  * Insert a free block into the free list.
356  */
357 static inline void HEAP_InsertFreeBlock( HEAP *heap, ARENA_FREE *pArena, BOOL last )
358 {
359     FREE_LIST_ENTRY *pEntry = heap->freeList + get_freelist_index( pArena->size + sizeof(*pArena) );
360     if (last)
361     {
362         /* insert at end of free list, i.e. before the next free list entry */
363         pEntry++;
364         if (pEntry == &heap->freeList[HEAP_NB_FREE_LISTS]) pEntry = heap->freeList;
365         list_add_before( &pEntry->arena.entry, &pArena->entry );
366     }
367     else
368     {
369         /* insert at head of free list */
370         list_add_after( &pEntry->arena.entry, &pArena->entry );
371     }
372     pArena->size |= ARENA_FLAG_FREE;
373 }
374
375
376 /***********************************************************************
377  *           HEAP_FindSubHeap
378  * Find the sub-heap containing a given address.
379  *
380  * RETURNS
381  *      Pointer: Success
382  *      NULL: Failure
383  */
384 static SUBHEAP *HEAP_FindSubHeap(
385                 const HEAP *heap, /* [in] Heap pointer */
386                 LPCVOID ptr /* [in] Address */
387 ) {
388     const SUBHEAP *sub = &heap->subheap;
389     while (sub)
390     {
391         if (((const char *)ptr >= (const char *)sub) &&
392             ((const char *)ptr < (const char *)sub + sub->size)) return (SUBHEAP*)sub;
393         sub = sub->next;
394     }
395     return NULL;
396 }
397
398
399 /***********************************************************************
400  *           HEAP_Commit
401  *
402  * Make sure the heap storage is committed for a given size in the specified arena.
403  */
404 static inline BOOL HEAP_Commit( SUBHEAP *subheap, ARENA_INUSE *pArena, SIZE_T data_size )
405 {
406     void *ptr = (char *)(pArena + 1) + data_size + sizeof(ARENA_FREE);
407     SIZE_T size = (char *)ptr - (char *)subheap;
408     size = (size + COMMIT_MASK) & ~COMMIT_MASK;
409     if (size > subheap->size) size = subheap->size;
410     if (size <= subheap->commitSize) return TRUE;
411     size -= subheap->commitSize;
412     ptr = (char *)subheap + subheap->commitSize;
413     if (NtAllocateVirtualMemory( NtCurrentProcess(), &ptr, 0,
414                                  &size, MEM_COMMIT, PAGE_READWRITE ))
415     {
416         WARN("Could not commit %08lx bytes at %p for heap %p\n",
417                  size, ptr, subheap->heap );
418         return FALSE;
419     }
420     subheap->commitSize += size;
421     return TRUE;
422 }
423
424
425 /***********************************************************************
426  *           HEAP_Decommit
427  *
428  * If possible, decommit the heap storage from (including) 'ptr'.
429  */
430 static inline BOOL HEAP_Decommit( SUBHEAP *subheap, void *ptr )
431 {
432     void *addr;
433     SIZE_T decommit_size;
434     SIZE_T size = (char *)ptr - (char *)subheap;
435
436     /* round to next block and add one full block */
437     size = ((size + COMMIT_MASK) & ~COMMIT_MASK) + COMMIT_MASK + 1;
438     if (size >= subheap->commitSize) return TRUE;
439     decommit_size = subheap->commitSize - size;
440     addr = (char *)subheap + size;
441
442     if (NtFreeVirtualMemory( NtCurrentProcess(), &addr, &decommit_size, MEM_DECOMMIT ))
443     {
444         WARN("Could not decommit %08lx bytes at %p for heap %p\n",
445                 decommit_size, (char *)subheap + size, subheap->heap );
446         return FALSE;
447     }
448     subheap->commitSize -= decommit_size;
449     return TRUE;
450 }
451
452
453 /***********************************************************************
454  *           HEAP_CreateFreeBlock
455  *
456  * Create a free block at a specified address. 'size' is the size of the
457  * whole block, including the new arena.
458  */
459 static void HEAP_CreateFreeBlock( SUBHEAP *subheap, void *ptr, SIZE_T size )
460 {
461     ARENA_FREE *pFree;
462     char *pEnd;
463     BOOL last;
464
465     /* Create a free arena */
466     mark_block_uninitialized( ptr, sizeof( ARENA_FREE ) );
467     pFree = (ARENA_FREE *)ptr;
468     pFree->magic = ARENA_FREE_MAGIC;
469
470     /* If debugging, erase the freed block content */
471
472     pEnd = (char *)ptr + size;
473     if (pEnd > (char *)subheap + subheap->commitSize) pEnd = (char *)subheap + subheap->commitSize;
474     if (pEnd > (char *)(pFree + 1)) mark_block_free( pFree + 1, pEnd - (char *)(pFree + 1) );
475
476     /* Check if next block is free also */
477
478     if (((char *)ptr + size < (char *)subheap + subheap->size) &&
479         (*(DWORD *)((char *)ptr + size) & ARENA_FLAG_FREE))
480     {
481         /* Remove the next arena from the free list */
482         ARENA_FREE *pNext = (ARENA_FREE *)((char *)ptr + size);
483         list_remove( &pNext->entry );
484         size += (pNext->size & ARENA_SIZE_MASK) + sizeof(*pNext);
485         mark_block_free( pNext, sizeof(ARENA_FREE) );
486     }
487
488     /* Set the next block PREV_FREE flag and pointer */
489
490     last = ((char *)ptr + size >= (char *)subheap + subheap->size);
491     if (!last)
492     {
493         DWORD *pNext = (DWORD *)((char *)ptr + size);
494         *pNext |= ARENA_FLAG_PREV_FREE;
495         mark_block_initialized( pNext - 1, sizeof( ARENA_FREE * ) );
496         *((ARENA_FREE **)pNext - 1) = pFree;
497     }
498
499     /* Last, insert the new block into the free list */
500
501     pFree->size = size - sizeof(*pFree);
502     HEAP_InsertFreeBlock( subheap->heap, pFree, last );
503 }
504
505
506 /***********************************************************************
507  *           HEAP_MakeInUseBlockFree
508  *
509  * Turn an in-use block into a free block. Can also decommit the end of
510  * the heap, and possibly even free the sub-heap altogether.
511  */
512 static void HEAP_MakeInUseBlockFree( SUBHEAP *subheap, ARENA_INUSE *pArena )
513 {
514     ARENA_FREE *pFree;
515     SIZE_T size = (pArena->size & ARENA_SIZE_MASK) + sizeof(*pArena);
516
517     /* Check if we can merge with previous block */
518
519     if (pArena->size & ARENA_FLAG_PREV_FREE)
520     {
521         pFree = *((ARENA_FREE **)pArena - 1);
522         size += (pFree->size & ARENA_SIZE_MASK) + sizeof(ARENA_FREE);
523         /* Remove it from the free list */
524         list_remove( &pFree->entry );
525     }
526     else pFree = (ARENA_FREE *)pArena;
527
528     /* Create a free block */
529
530     HEAP_CreateFreeBlock( subheap, pFree, size );
531     size = (pFree->size & ARENA_SIZE_MASK) + sizeof(ARENA_FREE);
532     if ((char *)pFree + size < (char *)subheap + subheap->size)
533         return;  /* Not the last block, so nothing more to do */
534
535     /* Free the whole sub-heap if it's empty and not the original one */
536
537     if (((char *)pFree == (char *)subheap + subheap->headerSize) &&
538         (subheap != &subheap->heap->subheap))
539     {
540         SIZE_T size = 0;
541         SUBHEAP *pPrev = &subheap->heap->subheap;
542         /* Remove the free block from the list */
543         list_remove( &pFree->entry );
544         /* Remove the subheap from the list */
545         while (pPrev && (pPrev->next != subheap)) pPrev = pPrev->next;
546         if (pPrev) pPrev->next = subheap->next;
547         /* Free the memory */
548         subheap->magic = 0;
549         NtFreeVirtualMemory( NtCurrentProcess(), (void **)&subheap, &size, MEM_RELEASE );
550         return;
551     }
552
553     /* Decommit the end of the heap */
554
555     if (!(subheap->heap->flags & HEAP_SHARED)) HEAP_Decommit( subheap, pFree + 1 );
556 }
557
558
559 /***********************************************************************
560  *           HEAP_ShrinkBlock
561  *
562  * Shrink an in-use block.
563  */
564 static void HEAP_ShrinkBlock(SUBHEAP *subheap, ARENA_INUSE *pArena, SIZE_T size)
565 {
566     if ((pArena->size & ARENA_SIZE_MASK) >= size + HEAP_MIN_SHRINK_SIZE)
567     {
568         HEAP_CreateFreeBlock( subheap, (char *)(pArena + 1) + size,
569                               (pArena->size & ARENA_SIZE_MASK) - size );
570         /* assign size plus previous arena flags */
571         pArena->size = size | (pArena->size & ~ARENA_SIZE_MASK);
572     }
573     else
574     {
575         /* Turn off PREV_FREE flag in next block */
576         char *pNext = (char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK);
577         if (pNext < (char *)subheap + subheap->size)
578             *(DWORD *)pNext &= ~ARENA_FLAG_PREV_FREE;
579     }
580 }
581
582 /***********************************************************************
583  *           HEAP_InitSubHeap
584  */
585 static BOOL HEAP_InitSubHeap( HEAP *heap, LPVOID address, DWORD flags,
586                               SIZE_T commitSize, SIZE_T totalSize )
587 {
588     SUBHEAP *subheap;
589     FREE_LIST_ENTRY *pEntry;
590     int i;
591
592     /* Commit memory */
593
594     if (flags & HEAP_SHARED)
595         commitSize = totalSize;  /* always commit everything in a shared heap */
596     if (NtAllocateVirtualMemory( NtCurrentProcess(), &address, 0,
597                                  &commitSize, MEM_COMMIT, PAGE_READWRITE ))
598     {
599         WARN("Could not commit %08lx bytes for sub-heap %p\n", commitSize, address );
600         return FALSE;
601     }
602
603     /* Fill the sub-heap structure */
604
605     subheap = (SUBHEAP *)address;
606     subheap->heap       = heap;
607     subheap->size       = totalSize;
608     subheap->commitSize = commitSize;
609     subheap->magic      = SUBHEAP_MAGIC;
610
611     if ( subheap != (SUBHEAP *)heap )
612     {
613         /* If this is a secondary subheap, insert it into list */
614
615         subheap->headerSize = ROUND_SIZE( sizeof(SUBHEAP) );
616         subheap->next       = heap->subheap.next;
617         heap->subheap.next  = subheap;
618     }
619     else
620     {
621         /* If this is a primary subheap, initialize main heap */
622
623         subheap->headerSize = ROUND_SIZE( sizeof(HEAP) );
624         subheap->next       = NULL;
625         heap->flags         = flags;
626         heap->magic         = HEAP_MAGIC;
627
628         /* Build the free lists */
629
630         list_init( &heap->freeList[0].arena.entry );
631         for (i = 0, pEntry = heap->freeList; i < HEAP_NB_FREE_LISTS; i++, pEntry++)
632         {
633             pEntry->arena.size = 0 | ARENA_FLAG_FREE;
634             pEntry->arena.magic = ARENA_FREE_MAGIC;
635             if (i) list_add_after( &pEntry[-1].arena.entry, &pEntry->arena.entry );
636         }
637
638         /* Initialize critical section */
639
640         if (!processHeap)  /* do it by hand to avoid memory allocations */
641         {
642             heap->critSection.DebugInfo      = &process_heap_critsect_debug;
643             heap->critSection.LockCount      = -1;
644             heap->critSection.RecursionCount = 0;
645             heap->critSection.OwningThread   = 0;
646             heap->critSection.LockSemaphore  = 0;
647             heap->critSection.SpinCount      = 0;
648             process_heap_critsect_debug.CriticalSection = &heap->critSection;
649         }
650         else RtlInitializeCriticalSection( &heap->critSection );
651
652         if (flags & HEAP_SHARED)
653         {
654             /* let's assume that only one thread at a time will try to do this */
655             HANDLE sem = heap->critSection.LockSemaphore;
656             if (!sem) NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 );
657
658             NtDuplicateObject( NtCurrentProcess(), sem, NtCurrentProcess(), &sem, 0, 0,
659                                DUP_HANDLE_MAKE_GLOBAL | DUP_HANDLE_SAME_ACCESS | DUP_HANDLE_CLOSE_SOURCE );
660             heap->critSection.LockSemaphore = sem;
661         }
662     }
663
664     /* Create the first free block */
665
666     HEAP_CreateFreeBlock( subheap, (LPBYTE)subheap + subheap->headerSize,
667                           subheap->size - subheap->headerSize );
668
669     return TRUE;
670 }
671
672 /***********************************************************************
673  *           HEAP_CreateSubHeap
674  *
675  * Create a sub-heap of the given size.
676  * If heap == NULL, creates a main heap.
677  */
678 static SUBHEAP *HEAP_CreateSubHeap( HEAP *heap, void *base, DWORD flags,
679                                     SIZE_T commitSize, SIZE_T totalSize )
680 {
681     LPVOID address = base;
682
683     /* round-up sizes on a 64K boundary */
684     totalSize  = (totalSize + 0xffff) & 0xffff0000;
685     commitSize = (commitSize + 0xffff) & 0xffff0000;
686     if (!commitSize) commitSize = 0x10000;
687     if (totalSize < commitSize) totalSize = commitSize;
688
689     if (!address)
690     {
691         /* allocate the memory block */
692         if (NtAllocateVirtualMemory( NtCurrentProcess(), &address, 0, &totalSize,
693                                      MEM_RESERVE, PAGE_READWRITE ))
694         {
695             WARN("Could not allocate %08lx bytes\n", totalSize );
696             return NULL;
697         }
698     }
699
700     /* Initialize subheap */
701
702     if (!HEAP_InitSubHeap( heap ? heap : (HEAP *)address,
703                            address, flags, commitSize, totalSize ))
704     {
705         SIZE_T size = 0;
706         if (!base) NtFreeVirtualMemory( NtCurrentProcess(), &address, &size, MEM_RELEASE );
707         return NULL;
708     }
709
710     return (SUBHEAP *)address;
711 }
712
713
714 /***********************************************************************
715  *           HEAP_FindFreeBlock
716  *
717  * Find a free block at least as large as the requested size, and make sure
718  * the requested size is committed.
719  */
720 static ARENA_FREE *HEAP_FindFreeBlock( HEAP *heap, SIZE_T size,
721                                        SUBHEAP **ppSubHeap )
722 {
723     SUBHEAP *subheap;
724     struct list *ptr;
725     FREE_LIST_ENTRY *pEntry = heap->freeList + get_freelist_index( size + sizeof(ARENA_INUSE) );
726
727     /* Find a suitable free list, and in it find a block large enough */
728
729     ptr = &pEntry->arena.entry;
730     while ((ptr = list_next( &heap->freeList[0].arena.entry, ptr )))
731     {
732         ARENA_FREE *pArena = LIST_ENTRY( ptr, ARENA_FREE, entry );
733         SIZE_T arena_size = (pArena->size & ARENA_SIZE_MASK) +
734                             sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
735         if (arena_size >= size)
736         {
737             subheap = HEAP_FindSubHeap( heap, pArena );
738             if (!HEAP_Commit( subheap, (ARENA_INUSE *)pArena, size )) return NULL;
739             *ppSubHeap = subheap;
740             return pArena;
741         }
742     }
743
744     /* If no block was found, attempt to grow the heap */
745
746     if (!(heap->flags & HEAP_GROWABLE))
747     {
748         WARN("Not enough space in heap %p for %08lx bytes\n", heap, size );
749         return NULL;
750     }
751     /* make sure that we have a big enough size *committed* to fit another
752      * last free arena in !
753      * So just one heap struct, one first free arena which will eventually
754      * get used, and a second free arena that might get assigned all remaining
755      * free space in HEAP_ShrinkBlock() */
756     size += ROUND_SIZE(sizeof(SUBHEAP)) + sizeof(ARENA_INUSE) + sizeof(ARENA_FREE);
757     if (!(subheap = HEAP_CreateSubHeap( heap, NULL, heap->flags, size,
758                                         max( HEAP_DEF_SIZE, size ) )))
759         return NULL;
760
761     TRACE("created new sub-heap %p of %08lx bytes for heap %p\n",
762             subheap, size, heap );
763
764     *ppSubHeap = subheap;
765     return (ARENA_FREE *)(subheap + 1);
766 }
767
768
769 /***********************************************************************
770  *           HEAP_IsValidArenaPtr
771  *
772  * Check that the pointer is inside the range possible for arenas.
773  */
774 static BOOL HEAP_IsValidArenaPtr( const HEAP *heap, const void *ptr )
775 {
776     int i;
777     const SUBHEAP *subheap = HEAP_FindSubHeap( heap, ptr );
778     if (!subheap) return FALSE;
779     if ((const char *)ptr >= (const char *)subheap + subheap->headerSize) return TRUE;
780     if (subheap != &heap->subheap) return FALSE;
781     for (i = 0; i < HEAP_NB_FREE_LISTS; i++)
782         if (ptr == (const void *)&heap->freeList[i].arena) return TRUE;
783     return FALSE;
784 }
785
786
787 /***********************************************************************
788  *           HEAP_ValidateFreeArena
789  */
790 static BOOL HEAP_ValidateFreeArena( SUBHEAP *subheap, ARENA_FREE *pArena )
791 {
792     ARENA_FREE *prev, *next;
793     char *heapEnd = (char *)subheap + subheap->size;
794
795     /* Check for unaligned pointers */
796     if ( (ULONG_PTR)pArena % ALIGNMENT != 0 )
797     {
798         ERR("Heap %p: unaligned arena pointer %p\n", subheap->heap, pArena );
799         return FALSE;
800     }
801
802     /* Check magic number */
803     if (pArena->magic != ARENA_FREE_MAGIC)
804     {
805         ERR("Heap %p: invalid free arena magic for %p\n", subheap->heap, pArena );
806         return FALSE;
807     }
808     /* Check size flags */
809     if (!(pArena->size & ARENA_FLAG_FREE) ||
810         (pArena->size & ARENA_FLAG_PREV_FREE))
811     {
812         ERR("Heap %p: bad flags %08x for free arena %p\n",
813             subheap->heap, pArena->size & ~ARENA_SIZE_MASK, pArena );
814         return FALSE;
815     }
816     /* Check arena size */
817     if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) > heapEnd)
818     {
819         ERR("Heap %p: bad size %08x for free arena %p\n",
820             subheap->heap, pArena->size & ARENA_SIZE_MASK, pArena );
821         return FALSE;
822     }
823     /* Check that next pointer is valid */
824     next = LIST_ENTRY( pArena->entry.next, ARENA_FREE, entry );
825     if (!HEAP_IsValidArenaPtr( subheap->heap, next ))
826     {
827         ERR("Heap %p: bad next ptr %p for arena %p\n",
828             subheap->heap, next, pArena );
829         return FALSE;
830     }
831     /* Check that next arena is free */
832     if (!(next->size & ARENA_FLAG_FREE) || (next->magic != ARENA_FREE_MAGIC))
833     {
834         ERR("Heap %p: next arena %p invalid for %p\n",
835             subheap->heap, next, pArena );
836         return FALSE;
837     }
838     /* Check that prev pointer is valid */
839     prev = LIST_ENTRY( pArena->entry.prev, ARENA_FREE, entry );
840     if (!HEAP_IsValidArenaPtr( subheap->heap, prev ))
841     {
842         ERR("Heap %p: bad prev ptr %p for arena %p\n",
843             subheap->heap, prev, pArena );
844         return FALSE;
845     }
846     /* Check that prev arena is free */
847     if (!(prev->size & ARENA_FLAG_FREE) || (prev->magic != ARENA_FREE_MAGIC))
848     {
849         /* this often means that the prev arena got overwritten
850          * by a memory write before that prev arena */
851         ERR("Heap %p: prev arena %p invalid for %p\n",
852             subheap->heap, prev, pArena );
853         return FALSE;
854     }
855     /* Check that next block has PREV_FREE flag */
856     if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) < heapEnd)
857     {
858         if (!(*(DWORD *)((char *)(pArena + 1) +
859             (pArena->size & ARENA_SIZE_MASK)) & ARENA_FLAG_PREV_FREE))
860         {
861             ERR("Heap %p: free arena %p next block has no PREV_FREE flag\n",
862                 subheap->heap, pArena );
863             return FALSE;
864         }
865         /* Check next block back pointer */
866         if (*((ARENA_FREE **)((char *)(pArena + 1) +
867             (pArena->size & ARENA_SIZE_MASK)) - 1) != pArena)
868         {
869             ERR("Heap %p: arena %p has wrong back ptr %p\n",
870                 subheap->heap, pArena,
871                 *((ARENA_FREE **)((char *)(pArena+1) + (pArena->size & ARENA_SIZE_MASK)) - 1));
872             return FALSE;
873         }
874     }
875     return TRUE;
876 }
877
878
879 /***********************************************************************
880  *           HEAP_ValidateInUseArena
881  */
882 static BOOL HEAP_ValidateInUseArena( const SUBHEAP *subheap, const ARENA_INUSE *pArena, BOOL quiet )
883 {
884     const char *heapEnd = (const char *)subheap + subheap->size;
885
886     /* Check for unaligned pointers */
887     if ( (ULONG_PTR)pArena % ALIGNMENT != 0 )
888     {
889         if ( quiet == NOISY )
890         {
891             ERR( "Heap %p: unaligned arena pointer %p\n", subheap->heap, pArena );
892             if ( TRACE_ON(heap) )
893                 HEAP_Dump( subheap->heap );
894         }
895         else if ( WARN_ON(heap) )
896         {
897             WARN( "Heap %p: unaligned arena pointer %p\n", subheap->heap, pArena );
898             if ( TRACE_ON(heap) )
899                 HEAP_Dump( subheap->heap );
900         }
901         return FALSE;
902     }
903
904     /* Check magic number */
905     if (pArena->magic != ARENA_INUSE_MAGIC)
906     {
907         if (quiet == NOISY) {
908             ERR("Heap %p: invalid in-use arena magic for %p\n", subheap->heap, pArena );
909             if (TRACE_ON(heap))
910                HEAP_Dump( subheap->heap );
911         }  else if (WARN_ON(heap)) {
912             WARN("Heap %p: invalid in-use arena magic for %p\n", subheap->heap, pArena );
913             if (TRACE_ON(heap))
914                HEAP_Dump( subheap->heap );
915         }
916         return FALSE;
917     }
918     /* Check size flags */
919     if (pArena->size & ARENA_FLAG_FREE)
920     {
921         ERR("Heap %p: bad flags %08x for in-use arena %p\n",
922             subheap->heap, pArena->size & ~ARENA_SIZE_MASK, pArena );
923         return FALSE;
924     }
925     /* Check arena size */
926     if ((const char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) > heapEnd)
927     {
928         ERR("Heap %p: bad size %08x for in-use arena %p\n",
929             subheap->heap, pArena->size & ARENA_SIZE_MASK, pArena );
930         return FALSE;
931     }
932     /* Check next arena PREV_FREE flag */
933     if (((const char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) < heapEnd) &&
934         (*(const DWORD *)((const char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK)) & ARENA_FLAG_PREV_FREE))
935     {
936         ERR("Heap %p: in-use arena %p next block has PREV_FREE flag\n",
937             subheap->heap, pArena );
938         return FALSE;
939     }
940     /* Check prev free arena */
941     if (pArena->size & ARENA_FLAG_PREV_FREE)
942     {
943         const ARENA_FREE *pPrev = *((const ARENA_FREE * const*)pArena - 1);
944         /* Check prev pointer */
945         if (!HEAP_IsValidArenaPtr( subheap->heap, pPrev ))
946         {
947             ERR("Heap %p: bad back ptr %p for arena %p\n",
948                 subheap->heap, pPrev, pArena );
949             return FALSE;
950         }
951         /* Check that prev arena is free */
952         if (!(pPrev->size & ARENA_FLAG_FREE) ||
953             (pPrev->magic != ARENA_FREE_MAGIC))
954         {
955             ERR("Heap %p: prev arena %p invalid for in-use %p\n",
956                 subheap->heap, pPrev, pArena );
957             return FALSE;
958         }
959         /* Check that prev arena is really the previous block */
960         if ((const char *)(pPrev + 1) + (pPrev->size & ARENA_SIZE_MASK) != (const char *)pArena)
961         {
962             ERR("Heap %p: prev arena %p is not prev for in-use %p\n",
963                 subheap->heap, pPrev, pArena );
964             return FALSE;
965         }
966     }
967     return TRUE;
968 }
969
970
971 /***********************************************************************
972  *           HEAP_IsRealArena  [Internal]
973  * Validates a block is a valid arena.
974  *
975  * RETURNS
976  *      TRUE: Success
977  *      FALSE: Failure
978  */
979 static BOOL HEAP_IsRealArena( HEAP *heapPtr,   /* [in] ptr to the heap */
980               DWORD flags,   /* [in] Bit flags that control access during operation */
981               LPCVOID block, /* [in] Optional pointer to memory block to validate */
982               BOOL quiet )   /* [in] Flag - if true, HEAP_ValidateInUseArena
983                               *             does not complain    */
984 {
985     SUBHEAP *subheap;
986     BOOL ret = TRUE;
987
988     flags &= HEAP_NO_SERIALIZE;
989     flags |= heapPtr->flags;
990     /* calling HeapLock may result in infinite recursion, so do the critsect directly */
991     if (!(flags & HEAP_NO_SERIALIZE))
992         RtlEnterCriticalSection( &heapPtr->critSection );
993
994     if (block)
995     {
996         /* Only check this single memory block */
997
998         if (!(subheap = HEAP_FindSubHeap( heapPtr, block )) ||
999             ((const char *)block < (char *)subheap + subheap->headerSize
1000                                    + sizeof(ARENA_INUSE)))
1001         {
1002             if (quiet == NOISY)
1003                 ERR("Heap %p: block %p is not inside heap\n", heapPtr, block );
1004             else if (WARN_ON(heap))
1005                 WARN("Heap %p: block %p is not inside heap\n", heapPtr, block );
1006             ret = FALSE;
1007         } else
1008             ret = HEAP_ValidateInUseArena( subheap, (const ARENA_INUSE *)block - 1, quiet );
1009
1010         if (!(flags & HEAP_NO_SERIALIZE))
1011             RtlLeaveCriticalSection( &heapPtr->critSection );
1012         return ret;
1013     }
1014
1015     subheap = &heapPtr->subheap;
1016     while (subheap && ret)
1017     {
1018         char *ptr = (char *)subheap + subheap->headerSize;
1019         while (ptr < (char *)subheap + subheap->size)
1020         {
1021             if (*(DWORD *)ptr & ARENA_FLAG_FREE)
1022             {
1023                 if (!HEAP_ValidateFreeArena( subheap, (ARENA_FREE *)ptr )) {
1024                     ret = FALSE;
1025                     break;
1026                 }
1027                 ptr += sizeof(ARENA_FREE) + (*(DWORD *)ptr & ARENA_SIZE_MASK);
1028             }
1029             else
1030             {
1031                 if (!HEAP_ValidateInUseArena( subheap, (ARENA_INUSE *)ptr, NOISY )) {
1032                     ret = FALSE;
1033                     break;
1034                 }
1035                 ptr += sizeof(ARENA_INUSE) + (*(DWORD *)ptr & ARENA_SIZE_MASK);
1036             }
1037         }
1038         subheap = subheap->next;
1039     }
1040
1041     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1042     return ret;
1043 }
1044
1045
1046 /***********************************************************************
1047  *           RtlCreateHeap   (NTDLL.@)
1048  *
1049  * Create a new Heap.
1050  *
1051  * PARAMS
1052  *  flags      [I] HEAP_ flags from "winnt.h"
1053  *  addr       [I] Desired base address
1054  *  totalSize  [I] Total size of the heap, or 0 for a growable heap
1055  *  commitSize [I] Amount of heap space to commit
1056  *  unknown    [I] Not yet understood
1057  *  definition [I] Heap definition
1058  *
1059  * RETURNS
1060  *  Success: A HANDLE to the newly created heap.
1061  *  Failure: a NULL HANDLE.
1062  */
1063 HANDLE WINAPI RtlCreateHeap( ULONG flags, PVOID addr, SIZE_T totalSize, SIZE_T commitSize,
1064                              PVOID unknown, PRTL_HEAP_DEFINITION definition )
1065 {
1066     SUBHEAP *subheap;
1067
1068     /* Allocate the heap block */
1069
1070     if (!totalSize)
1071     {
1072         totalSize = HEAP_DEF_SIZE;
1073         flags |= HEAP_GROWABLE;
1074     }
1075
1076     if (!(subheap = HEAP_CreateSubHeap( NULL, addr, flags, commitSize, totalSize ))) return 0;
1077
1078     /* link it into the per-process heap list */
1079     if (processHeap)
1080     {
1081         HEAP *heapPtr = subheap->heap;
1082         RtlEnterCriticalSection( &processHeap->critSection );
1083         list_add_head( &processHeap->entry, &heapPtr->entry );
1084         RtlLeaveCriticalSection( &processHeap->critSection );
1085     }
1086     else
1087     {
1088         processHeap = subheap->heap;  /* assume the first heap we create is the process main heap */
1089         list_init( &processHeap->entry );
1090     }
1091
1092     return (HANDLE)subheap;
1093 }
1094
1095
1096 /***********************************************************************
1097  *           RtlDestroyHeap   (NTDLL.@)
1098  *
1099  * Destroy a Heap created with RtlCreateHeap().
1100  *
1101  * PARAMS
1102  *  heap [I] Heap to destroy.
1103  *
1104  * RETURNS
1105  *  Success: A NULL HANDLE, if heap is NULL or it was destroyed
1106  *  Failure: The Heap handle, if heap is the process heap.
1107  */
1108 HANDLE WINAPI RtlDestroyHeap( HANDLE heap )
1109 {
1110     HEAP *heapPtr = HEAP_GetPtr( heap );
1111     SUBHEAP *subheap;
1112
1113     TRACE("%p\n", heap );
1114     if (!heapPtr) return heap;
1115
1116     if (heap == processHeap) return heap; /* cannot delete the main process heap */
1117
1118     /* remove it from the per-process list */
1119     RtlEnterCriticalSection( &processHeap->critSection );
1120     list_remove( &heapPtr->entry );
1121     RtlLeaveCriticalSection( &processHeap->critSection );
1122
1123     RtlDeleteCriticalSection( &heapPtr->critSection );
1124     subheap = &heapPtr->subheap;
1125     while (subheap)
1126     {
1127         SUBHEAP *next = subheap->next;
1128         SIZE_T size = 0;
1129         void *addr = subheap;
1130         NtFreeVirtualMemory( NtCurrentProcess(), &addr, &size, MEM_RELEASE );
1131         subheap = next;
1132     }
1133     return 0;
1134 }
1135
1136
1137 /***********************************************************************
1138  *           RtlAllocateHeap   (NTDLL.@)
1139  *
1140  * Allocate a memory block from a Heap.
1141  *
1142  * PARAMS
1143  *  heap  [I] Heap to allocate block from
1144  *  flags [I] HEAP_ flags from "winnt.h"
1145  *  size  [I] Size of the memory block to allocate
1146  *
1147  * RETURNS
1148  *  Success: A pointer to the newly allocated block
1149  *  Failure: NULL.
1150  *
1151  * NOTES
1152  *  This call does not SetLastError().
1153  */
1154 PVOID WINAPI RtlAllocateHeap( HANDLE heap, ULONG flags, SIZE_T size )
1155 {
1156     ARENA_FREE *pArena;
1157     ARENA_INUSE *pInUse;
1158     SUBHEAP *subheap;
1159     HEAP *heapPtr = HEAP_GetPtr( heap );
1160     SIZE_T rounded_size;
1161
1162     /* Validate the parameters */
1163
1164     if (!heapPtr) return NULL;
1165     flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY;
1166     flags |= heapPtr->flags;
1167     rounded_size = ROUND_SIZE(size);
1168     if (rounded_size < HEAP_MIN_DATA_SIZE) rounded_size = HEAP_MIN_DATA_SIZE;
1169
1170     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1171     /* Locate a suitable free block */
1172
1173     if (!(pArena = HEAP_FindFreeBlock( heapPtr, rounded_size, &subheap )))
1174     {
1175         TRACE("(%p,%08x,%08lx): returning NULL\n",
1176                   heap, flags, size  );
1177         if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1178         if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
1179         return NULL;
1180     }
1181
1182     /* Remove the arena from the free list */
1183
1184     list_remove( &pArena->entry );
1185
1186     /* Build the in-use arena */
1187
1188     pInUse = (ARENA_INUSE *)pArena;
1189
1190     /* in-use arena is smaller than free arena,
1191      * so we have to add the difference to the size */
1192     pInUse->size  = (pInUse->size & ~ARENA_FLAG_FREE) + sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
1193     pInUse->magic = ARENA_INUSE_MAGIC;
1194
1195     /* Shrink the block */
1196
1197     HEAP_ShrinkBlock( subheap, pInUse, rounded_size );
1198     pInUse->unused_bytes = (pInUse->size & ARENA_SIZE_MASK) - size;
1199
1200     notify_alloc( pInUse + 1, size, flags & HEAP_ZERO_MEMORY );
1201
1202     if (flags & HEAP_ZERO_MEMORY)
1203         clear_block( pInUse + 1, pInUse->size & ARENA_SIZE_MASK );
1204     else
1205         mark_block_uninitialized( pInUse + 1, pInUse->size & ARENA_SIZE_MASK );
1206
1207     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1208
1209     TRACE("(%p,%08x,%08lx): returning %p\n", heap, flags, size, pInUse + 1 );
1210     return (LPVOID)(pInUse + 1);
1211 }
1212
1213
1214 /***********************************************************************
1215  *           RtlFreeHeap   (NTDLL.@)
1216  *
1217  * Free a memory block allocated with RtlAllocateHeap().
1218  *
1219  * PARAMS
1220  *  heap  [I] Heap that block was allocated from
1221  *  flags [I] HEAP_ flags from "winnt.h"
1222  *  ptr   [I] Block to free
1223  *
1224  * RETURNS
1225  *  Success: TRUE, if ptr is NULL or was freed successfully.
1226  *  Failure: FALSE.
1227  */
1228 BOOLEAN WINAPI RtlFreeHeap( HANDLE heap, ULONG flags, PVOID ptr )
1229 {
1230     ARENA_INUSE *pInUse;
1231     SUBHEAP *subheap;
1232     HEAP *heapPtr;
1233
1234     /* Validate the parameters */
1235
1236     if (!ptr) return TRUE;  /* freeing a NULL ptr isn't an error in Win2k */
1237
1238     heapPtr = HEAP_GetPtr( heap );
1239     if (!heapPtr)
1240     {
1241         RtlSetLastWin32ErrorAndNtStatusFromNtStatus( STATUS_INVALID_HANDLE );
1242         return FALSE;
1243     }
1244
1245     flags &= HEAP_NO_SERIALIZE;
1246     flags |= heapPtr->flags;
1247     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1248
1249     /* Some sanity checks */
1250
1251     pInUse  = (ARENA_INUSE *)ptr - 1;
1252     if (!(subheap = HEAP_FindSubHeap( heapPtr, pInUse ))) goto error;
1253     if ((char *)pInUse < (char *)subheap + subheap->headerSize) goto error;
1254     if (!HEAP_ValidateInUseArena( subheap, pInUse, QUIET )) goto error;
1255
1256     /* Turn the block into a free block */
1257
1258     notify_free( ptr );
1259
1260     HEAP_MakeInUseBlockFree( subheap, pInUse );
1261
1262     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1263
1264     TRACE("(%p,%08x,%p): returning TRUE\n", heap, flags, ptr );
1265     return TRUE;
1266
1267 error:
1268     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1269     RtlSetLastWin32ErrorAndNtStatusFromNtStatus( STATUS_INVALID_PARAMETER );
1270     TRACE("(%p,%08x,%p): returning FALSE\n", heap, flags, ptr );
1271     return FALSE;
1272 }
1273
1274
1275 /***********************************************************************
1276  *           RtlReAllocateHeap   (NTDLL.@)
1277  *
1278  * Change the size of a memory block allocated with RtlAllocateHeap().
1279  *
1280  * PARAMS
1281  *  heap  [I] Heap that block was allocated from
1282  *  flags [I] HEAP_ flags from "winnt.h"
1283  *  ptr   [I] Block to resize
1284  *  size  [I] Size of the memory block to allocate
1285  *
1286  * RETURNS
1287  *  Success: A pointer to the resized block (which may be different).
1288  *  Failure: NULL.
1289  */
1290 PVOID WINAPI RtlReAllocateHeap( HANDLE heap, ULONG flags, PVOID ptr, SIZE_T size )
1291 {
1292     ARENA_INUSE *pArena;
1293     HEAP *heapPtr;
1294     SUBHEAP *subheap;
1295     SIZE_T oldSize, rounded_size;
1296
1297     if (!ptr) return NULL;
1298     if (!(heapPtr = HEAP_GetPtr( heap )))
1299     {
1300         RtlSetLastWin32ErrorAndNtStatusFromNtStatus( STATUS_INVALID_HANDLE );
1301         return NULL;
1302     }
1303
1304     /* Validate the parameters */
1305
1306     flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY |
1307              HEAP_REALLOC_IN_PLACE_ONLY;
1308     flags |= heapPtr->flags;
1309     rounded_size = ROUND_SIZE(size);
1310     if (rounded_size < HEAP_MIN_DATA_SIZE) rounded_size = HEAP_MIN_DATA_SIZE;
1311
1312     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1313
1314     pArena = (ARENA_INUSE *)ptr - 1;
1315     if (!(subheap = HEAP_FindSubHeap( heapPtr, pArena ))) goto error;
1316     if ((char *)pArena < (char *)subheap + subheap->headerSize) goto error;
1317     if (!HEAP_ValidateInUseArena( subheap, pArena, QUIET )) goto error;
1318
1319     /* Check if we need to grow the block */
1320
1321     oldSize = (pArena->size & ARENA_SIZE_MASK);
1322     if (rounded_size > oldSize)
1323     {
1324         char *pNext = (char *)(pArena + 1) + oldSize;
1325         if ((pNext < (char *)subheap + subheap->size) &&
1326             (*(DWORD *)pNext & ARENA_FLAG_FREE) &&
1327             (oldSize + (*(DWORD *)pNext & ARENA_SIZE_MASK) + sizeof(ARENA_FREE) >= rounded_size))
1328         {
1329             /* The next block is free and large enough */
1330             ARENA_FREE *pFree = (ARENA_FREE *)pNext;
1331             list_remove( &pFree->entry );
1332             pArena->size += (pFree->size & ARENA_SIZE_MASK) + sizeof(*pFree);
1333             if (!HEAP_Commit( subheap, pArena, rounded_size ))
1334             {
1335                 if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1336                 if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
1337                 RtlSetLastWin32ErrorAndNtStatusFromNtStatus( STATUS_NO_MEMORY );
1338                 return NULL;
1339             }
1340             notify_free( pArena + 1 );
1341             HEAP_ShrinkBlock( subheap, pArena, rounded_size );
1342             notify_alloc( pArena + 1, size, FALSE );
1343             /* FIXME: this is wrong as we may lose old VBits settings */
1344             mark_block_initialized( pArena + 1, oldSize );
1345         }
1346         else  /* Do it the hard way */
1347         {
1348             ARENA_FREE *pNew;
1349             ARENA_INUSE *pInUse;
1350             SUBHEAP *newsubheap;
1351
1352             if ((flags & HEAP_REALLOC_IN_PLACE_ONLY) ||
1353                 !(pNew = HEAP_FindFreeBlock( heapPtr, rounded_size, &newsubheap )))
1354             {
1355                 if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1356                 if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
1357                 RtlSetLastWin32ErrorAndNtStatusFromNtStatus( STATUS_NO_MEMORY );
1358                 return NULL;
1359             }
1360
1361             /* Build the in-use arena */
1362
1363             list_remove( &pNew->entry );
1364             pInUse = (ARENA_INUSE *)pNew;
1365             pInUse->size = (pInUse->size & ~ARENA_FLAG_FREE)
1366                            + sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
1367             pInUse->magic = ARENA_INUSE_MAGIC;
1368             HEAP_ShrinkBlock( newsubheap, pInUse, rounded_size );
1369             mark_block_initialized( pInUse + 1, oldSize );
1370             notify_alloc( pInUse + 1, size, FALSE );
1371             memcpy( pInUse + 1, pArena + 1, oldSize );
1372
1373             /* Free the previous block */
1374
1375             notify_free( pArena + 1 );
1376             HEAP_MakeInUseBlockFree( subheap, pArena );
1377             subheap = newsubheap;
1378             pArena  = pInUse;
1379         }
1380     }
1381     else
1382     {
1383         /* Shrink the block */
1384         notify_free( pArena + 1 );
1385         HEAP_ShrinkBlock( subheap, pArena, rounded_size );
1386         notify_alloc( pArena + 1, size, FALSE );
1387         /* FIXME: this is wrong as we may lose old VBits settings */
1388         mark_block_initialized( pArena + 1, size );
1389     }
1390
1391     pArena->unused_bytes = (pArena->size & ARENA_SIZE_MASK) - size;
1392
1393     /* Clear the extra bytes if needed */
1394
1395     if (rounded_size > oldSize)
1396     {
1397         if (flags & HEAP_ZERO_MEMORY)
1398             clear_block( (char *)(pArena + 1) + oldSize,
1399                          (pArena->size & ARENA_SIZE_MASK) - oldSize );
1400         else
1401             mark_block_uninitialized( (char *)(pArena + 1) + oldSize,
1402                                       (pArena->size & ARENA_SIZE_MASK) - oldSize );
1403     }
1404
1405     /* Return the new arena */
1406
1407     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1408
1409     TRACE("(%p,%08x,%p,%08lx): returning %p\n", heap, flags, ptr, size, pArena + 1 );
1410     return (LPVOID)(pArena + 1);
1411
1412 error:
1413     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1414     RtlSetLastWin32ErrorAndNtStatusFromNtStatus( STATUS_INVALID_PARAMETER );
1415     TRACE("(%p,%08x,%p,%08lx): returning NULL\n", heap, flags, ptr, size );
1416     return NULL;
1417 }
1418
1419
1420 /***********************************************************************
1421  *           RtlCompactHeap   (NTDLL.@)
1422  *
1423  * Compact the free space in a Heap.
1424  *
1425  * PARAMS
1426  *  heap  [I] Heap that block was allocated from
1427  *  flags [I] HEAP_ flags from "winnt.h"
1428  *
1429  * RETURNS
1430  *  The number of bytes compacted.
1431  *
1432  * NOTES
1433  *  This function is a harmless stub.
1434  */
1435 ULONG WINAPI RtlCompactHeap( HANDLE heap, ULONG flags )
1436 {
1437     FIXME( "stub\n" );
1438     return 0;
1439 }
1440
1441
1442 /***********************************************************************
1443  *           RtlLockHeap   (NTDLL.@)
1444  *
1445  * Lock a Heap.
1446  *
1447  * PARAMS
1448  *  heap  [I] Heap to lock
1449  *
1450  * RETURNS
1451  *  Success: TRUE. The Heap is locked.
1452  *  Failure: FALSE, if heap is invalid.
1453  */
1454 BOOLEAN WINAPI RtlLockHeap( HANDLE heap )
1455 {
1456     HEAP *heapPtr = HEAP_GetPtr( heap );
1457     if (!heapPtr) return FALSE;
1458     RtlEnterCriticalSection( &heapPtr->critSection );
1459     return TRUE;
1460 }
1461
1462
1463 /***********************************************************************
1464  *           RtlUnlockHeap   (NTDLL.@)
1465  *
1466  * Unlock a Heap.
1467  *
1468  * PARAMS
1469  *  heap  [I] Heap to unlock
1470  *
1471  * RETURNS
1472  *  Success: TRUE. The Heap is unlocked.
1473  *  Failure: FALSE, if heap is invalid.
1474  */
1475 BOOLEAN WINAPI RtlUnlockHeap( HANDLE heap )
1476 {
1477     HEAP *heapPtr = HEAP_GetPtr( heap );
1478     if (!heapPtr) return FALSE;
1479     RtlLeaveCriticalSection( &heapPtr->critSection );
1480     return TRUE;
1481 }
1482
1483
1484 /***********************************************************************
1485  *           RtlSizeHeap   (NTDLL.@)
1486  *
1487  * Get the actual size of a memory block allocated from a Heap.
1488  *
1489  * PARAMS
1490  *  heap  [I] Heap that block was allocated from
1491  *  flags [I] HEAP_ flags from "winnt.h"
1492  *  ptr   [I] Block to get the size of
1493  *
1494  * RETURNS
1495  *  Success: The size of the block.
1496  *  Failure: -1, heap or ptr are invalid.
1497  *
1498  * NOTES
1499  *  The size may be bigger than what was passed to RtlAllocateHeap().
1500  */
1501 SIZE_T WINAPI RtlSizeHeap( HANDLE heap, ULONG flags, PVOID ptr )
1502 {
1503     SIZE_T ret;
1504     HEAP *heapPtr = HEAP_GetPtr( heap );
1505
1506     if (!heapPtr)
1507     {
1508         RtlSetLastWin32ErrorAndNtStatusFromNtStatus( STATUS_INVALID_HANDLE );
1509         return ~0UL;
1510     }
1511     flags &= HEAP_NO_SERIALIZE;
1512     flags |= heapPtr->flags;
1513     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1514     if (!HEAP_IsRealArena( heapPtr, HEAP_NO_SERIALIZE, ptr, QUIET ))
1515     {
1516         RtlSetLastWin32ErrorAndNtStatusFromNtStatus( STATUS_INVALID_PARAMETER );
1517         ret = ~0UL;
1518     }
1519     else
1520     {
1521         ARENA_INUSE *pArena = (ARENA_INUSE *)ptr - 1;
1522         ret = (pArena->size & ARENA_SIZE_MASK) - pArena->unused_bytes;
1523     }
1524     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1525
1526     TRACE("(%p,%08x,%p): returning %08lx\n", heap, flags, ptr, ret );
1527     return ret;
1528 }
1529
1530
1531 /***********************************************************************
1532  *           RtlValidateHeap   (NTDLL.@)
1533  *
1534  * Determine if a block is a valid allocation from a heap.
1535  *
1536  * PARAMS
1537  *  heap  [I] Heap that block was allocated from
1538  *  flags [I] HEAP_ flags from "winnt.h"
1539  *  ptr   [I] Block to check
1540  *
1541  * RETURNS
1542  *  Success: TRUE. The block was allocated from heap.
1543  *  Failure: FALSE, if heap is invalid or ptr was not allocated from it.
1544  */
1545 BOOLEAN WINAPI RtlValidateHeap( HANDLE heap, ULONG flags, LPCVOID ptr )
1546 {
1547     HEAP *heapPtr = HEAP_GetPtr( heap );
1548     if (!heapPtr) return FALSE;
1549     return HEAP_IsRealArena( heapPtr, flags, ptr, QUIET );
1550 }
1551
1552
1553 /***********************************************************************
1554  *           RtlWalkHeap    (NTDLL.@)
1555  *
1556  * FIXME
1557  *  The PROCESS_HEAP_ENTRY flag values seem different between this
1558  *  function and HeapWalk(). To be checked.
1559  */
1560 NTSTATUS WINAPI RtlWalkHeap( HANDLE heap, PVOID entry_ptr )
1561 {
1562     LPPROCESS_HEAP_ENTRY entry = entry_ptr; /* FIXME */
1563     HEAP *heapPtr = HEAP_GetPtr(heap);
1564     SUBHEAP *sub, *currentheap = NULL;
1565     NTSTATUS ret;
1566     char *ptr;
1567     int region_index = 0;
1568
1569     if (!heapPtr || !entry) return STATUS_INVALID_PARAMETER;
1570
1571     if (!(heapPtr->flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1572
1573     /* set ptr to the next arena to be examined */
1574
1575     if (!entry->lpData) /* first call (init) ? */
1576     {
1577         TRACE("begin walking of heap %p.\n", heap);
1578         currentheap = &heapPtr->subheap;
1579         ptr = (char*)currentheap + currentheap->headerSize;
1580     }
1581     else
1582     {
1583         ptr = entry->lpData;
1584         sub = &heapPtr->subheap;
1585         while (sub)
1586         {
1587             if (((char *)ptr >= (char *)sub) &&
1588                 ((char *)ptr < (char *)sub + sub->size))
1589             {
1590                 currentheap = sub;
1591                 break;
1592             }
1593             sub = sub->next;
1594             region_index++;
1595         }
1596         if (currentheap == NULL)
1597         {
1598             ERR("no matching subheap found, shouldn't happen !\n");
1599             ret = STATUS_NO_MORE_ENTRIES;
1600             goto HW_end;
1601         }
1602
1603         ptr += entry->cbData; /* point to next arena */
1604         if (ptr > (char *)currentheap + currentheap->size - 1)
1605         {   /* proceed with next subheap */
1606             if (!(currentheap = currentheap->next))
1607             {  /* successfully finished */
1608                 TRACE("end reached.\n");
1609                 ret = STATUS_NO_MORE_ENTRIES;
1610                 goto HW_end;
1611             }
1612             ptr = (char*)currentheap + currentheap->headerSize;
1613         }
1614     }
1615
1616     entry->wFlags = 0;
1617     if (*(DWORD *)ptr & ARENA_FLAG_FREE)
1618     {
1619         ARENA_FREE *pArena = (ARENA_FREE *)ptr;
1620
1621         /*TRACE("free, magic: %04x\n", pArena->magic);*/
1622
1623         entry->lpData = pArena + 1;
1624         entry->cbData = pArena->size & ARENA_SIZE_MASK;
1625         entry->cbOverhead = sizeof(ARENA_FREE);
1626         entry->wFlags = PROCESS_HEAP_UNCOMMITTED_RANGE;
1627     }
1628     else
1629     {
1630         ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
1631
1632         /*TRACE("busy, magic: %04x\n", pArena->magic);*/
1633
1634         entry->lpData = pArena + 1;
1635         entry->cbData = pArena->size & ARENA_SIZE_MASK;
1636         entry->cbOverhead = sizeof(ARENA_INUSE);
1637         entry->wFlags = PROCESS_HEAP_ENTRY_BUSY;
1638         /* FIXME: can't handle PROCESS_HEAP_ENTRY_MOVEABLE
1639         and PROCESS_HEAP_ENTRY_DDESHARE yet */
1640     }
1641
1642     entry->iRegionIndex = region_index;
1643
1644     /* first element of heap ? */
1645     if (ptr == (char *)(currentheap + currentheap->headerSize))
1646     {
1647         entry->wFlags |= PROCESS_HEAP_REGION;
1648         entry->u.Region.dwCommittedSize = currentheap->commitSize;
1649         entry->u.Region.dwUnCommittedSize =
1650                 currentheap->size - currentheap->commitSize;
1651         entry->u.Region.lpFirstBlock = /* first valid block */
1652                 currentheap + currentheap->headerSize;
1653         entry->u.Region.lpLastBlock  = /* first invalid block */
1654                 currentheap + currentheap->size;
1655     }
1656     ret = STATUS_SUCCESS;
1657     if (TRACE_ON(heap)) HEAP_DumpEntry(entry);
1658
1659 HW_end:
1660     if (!(heapPtr->flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1661     return ret;
1662 }
1663
1664
1665 /***********************************************************************
1666  *           RtlGetProcessHeaps    (NTDLL.@)
1667  *
1668  * Get the Heaps belonging to the current process.
1669  *
1670  * PARAMS
1671  *  count [I] size of heaps
1672  *  heaps [O] Destination array for heap HANDLE's
1673  *
1674  * RETURNS
1675  *  Success: The number of Heaps allocated by the process.
1676  *  Failure: 0.
1677  */
1678 ULONG WINAPI RtlGetProcessHeaps( ULONG count, HANDLE *heaps )
1679 {
1680     ULONG total = 1;  /* main heap */
1681     struct list *ptr;
1682
1683     RtlEnterCriticalSection( &processHeap->critSection );
1684     LIST_FOR_EACH( ptr, &processHeap->entry ) total++;
1685     if (total <= count)
1686     {
1687         *heaps++ = processHeap;
1688         LIST_FOR_EACH( ptr, &processHeap->entry )
1689             *heaps++ = LIST_ENTRY( ptr, HEAP, entry );
1690     }
1691     RtlLeaveCriticalSection( &processHeap->critSection );
1692     return total;
1693 }