ntdll: Disable debug flags when running on Valgrind.
[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 #include "wine/port.h"
24
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <string.h>
30 #ifdef HAVE_VALGRIND_MEMCHECK_H
31 #include <valgrind/memcheck.h>
32 #else
33 #define RUNNING_ON_VALGRIND 0
34 #endif
35
36 #define NONAMELESSUNION
37 #define NONAMELESSSTRUCT
38 #include "ntstatus.h"
39 #define WIN32_NO_STATUS
40 #include "windef.h"
41 #include "winnt.h"
42 #include "winternl.h"
43 #include "wine/list.h"
44 #include "wine/debug.h"
45 #include "wine/server.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(heap);
48
49 /* Note: the heap data structures are loosely based on what Pietrek describes in his
50  * book 'Windows 95 System Programming Secrets', with some adaptations for
51  * better compatibility with NT.
52  */
53
54 typedef struct tagARENA_INUSE
55 {
56     DWORD  size;                    /* Block size; must be the first field */
57     DWORD  magic : 24;              /* Magic number */
58     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) */
59 } ARENA_INUSE;
60
61 typedef struct tagARENA_FREE
62 {
63     DWORD                 size;     /* Block size; must be the first field */
64     DWORD                 magic;    /* Magic number */
65     struct list           entry;    /* Entry in free list */
66 } ARENA_FREE;
67
68 typedef struct
69 {
70     struct list           entry;      /* entry in heap large blocks list */
71     SIZE_T                data_size;  /* size of user data */
72     SIZE_T                block_size; /* total size of virtual memory block */
73     DWORD                 pad[2];     /* padding to ensure 16-byte alignment of data */
74     DWORD                 size;       /* fields for compatibility with normal arenas */
75     DWORD                 magic;      /* these must remain at the end of the structure */
76 } ARENA_LARGE;
77
78 #define ARENA_FLAG_FREE        0x00000001  /* flags OR'ed with arena size */
79 #define ARENA_FLAG_PREV_FREE   0x00000002
80 #define ARENA_SIZE_MASK        (~3)
81 #define ARENA_LARGE_SIZE       0xfedcba90  /* magic value for 'size' field in large blocks */
82
83 /* Value for arena 'magic' field */
84 #define ARENA_INUSE_MAGIC      0x455355
85 #define ARENA_FREE_MAGIC       0x45455246
86 #define ARENA_LARGE_MAGIC      0x6752614c
87
88 #define ARENA_INUSE_FILLER     0x55
89 #define ARENA_TAIL_FILLER      0xab
90 #define ARENA_FREE_FILLER      0xfeeefeee
91
92 /* everything is aligned on 8 byte boundaries (16 for Win64) */
93 #define ALIGNMENT              (2*sizeof(void*))
94 #define LARGE_ALIGNMENT        16  /* large blocks have stricter alignment */
95 #define ARENA_OFFSET           (ALIGNMENT - sizeof(ARENA_INUSE))
96
97 C_ASSERT( sizeof(ARENA_LARGE) % LARGE_ALIGNMENT == 0 );
98
99 #define ROUND_SIZE(size)       ((((size) + ALIGNMENT - 1) & ~(ALIGNMENT-1)) + ARENA_OFFSET)
100
101 #define QUIET                  1           /* Suppress messages  */
102 #define NOISY                  0           /* Report all errors  */
103
104 /* minimum data size (without arenas) of an allocated block */
105 /* make sure that it's larger than a free list entry */
106 #define HEAP_MIN_DATA_SIZE    ROUND_SIZE(2 * sizeof(struct list))
107 /* minimum size that must remain to shrink an allocated block */
108 #define HEAP_MIN_SHRINK_SIZE  (HEAP_MIN_DATA_SIZE+sizeof(ARENA_FREE))
109 /* minimum size to start allocating large blocks */
110 #define HEAP_MIN_LARGE_BLOCK_SIZE  0x7f000
111 /* extra size to add at the end of block for tail checking */
112 #define HEAP_TAIL_EXTRA_SIZE(flags) ((flags & HEAP_TAIL_CHECKING_ENABLED) || RUNNING_ON_VALGRIND ? 8 : 0)
113
114 /* Max size of the blocks on the free lists */
115 static const SIZE_T HEAP_freeListSizes[] =
116 {
117     0x10, 0x20, 0x30, 0x40, 0x60, 0x80, 0x100, 0x200, 0x400, 0x1000, ~0UL
118 };
119 #define HEAP_NB_FREE_LISTS  (sizeof(HEAP_freeListSizes)/sizeof(HEAP_freeListSizes[0]))
120
121 typedef union
122 {
123     ARENA_FREE  arena;
124     void       *alignment[4];
125 } FREE_LIST_ENTRY;
126
127 struct tagHEAP;
128
129 typedef struct tagSUBHEAP
130 {
131     void               *base;       /* Base address of the sub-heap memory block */
132     SIZE_T              size;       /* Size of the whole sub-heap */
133     SIZE_T              min_commit; /* Minimum committed size */
134     SIZE_T              commitSize; /* Committed size of the sub-heap */
135     struct list         entry;      /* Entry in sub-heap list */
136     struct tagHEAP     *heap;       /* Main heap structure */
137     DWORD               headerSize; /* Size of the heap header */
138     DWORD               magic;      /* Magic number */
139 } SUBHEAP;
140
141 #define SUBHEAP_MAGIC    ((DWORD)('S' | ('U'<<8) | ('B'<<16) | ('H'<<24)))
142
143 typedef struct tagHEAP
144 {
145     DWORD            unknown[3];
146     DWORD            flags;         /* Heap flags */
147     DWORD            force_flags;   /* Forced heap flags for debugging */
148     SUBHEAP          subheap;       /* First sub-heap */
149     struct list      entry;         /* Entry in process heap list */
150     struct list      subheap_list;  /* Sub-heap list */
151     struct list      large_list;    /* Large blocks list */
152     SIZE_T           grow_size;     /* Size of next subheap for growing heap */
153     DWORD            magic;         /* Magic number */
154     RTL_CRITICAL_SECTION critSection; /* Critical section for serialization */
155     FREE_LIST_ENTRY *freeList;      /* Free lists */
156 } HEAP;
157
158 #define HEAP_MAGIC       ((DWORD)('H' | ('E'<<8) | ('A'<<16) | ('P'<<24)))
159
160 #define HEAP_DEF_SIZE        0x110000   /* Default heap size = 1Mb + 64Kb */
161 #define COMMIT_MASK          0xffff  /* bitmask for commit/decommit granularity */
162
163 /* some undocumented flags (names are made up) */
164 #define HEAP_PAGE_ALLOCS      0x01000000
165 #define HEAP_VALIDATE         0x10000000
166 #define HEAP_VALIDATE_ALL     0x20000000
167 #define HEAP_VALIDATE_PARAMS  0x40000000
168
169 static HEAP *processHeap;  /* main process heap */
170
171 static BOOL HEAP_IsRealArena( HEAP *heapPtr, DWORD flags, LPCVOID block, BOOL quiet );
172
173 /* mark a block of memory as free for debugging purposes */
174 static inline void mark_block_free( void *ptr, SIZE_T size, DWORD flags )
175 {
176     if (flags & HEAP_FREE_CHECKING_ENABLED)
177     {
178         SIZE_T i;
179         for (i = 0; i < size / sizeof(DWORD); i++) ((DWORD *)ptr)[i] = ARENA_FREE_FILLER;
180     }
181 #if defined(VALGRIND_MAKE_MEM_NOACCESS)
182     VALGRIND_DISCARD( VALGRIND_MAKE_MEM_NOACCESS( ptr, size ));
183 #elif defined( VALGRIND_MAKE_NOACCESS)
184     VALGRIND_DISCARD( VALGRIND_MAKE_NOACCESS( ptr, size ));
185 #endif
186 }
187
188 /* mark a block of memory as initialized for debugging purposes */
189 static inline void mark_block_initialized( void *ptr, SIZE_T size )
190 {
191 #if defined(VALGRIND_MAKE_MEM_DEFINED)
192     VALGRIND_DISCARD( VALGRIND_MAKE_MEM_DEFINED( ptr, size ));
193 #elif defined(VALGRIND_MAKE_READABLE)
194     VALGRIND_DISCARD( VALGRIND_MAKE_READABLE( ptr, size ));
195 #endif
196 }
197
198 /* mark a block of memory as uninitialized for debugging purposes */
199 static inline void mark_block_uninitialized( void *ptr, SIZE_T size )
200 {
201 #if defined(VALGRIND_MAKE_MEM_UNDEFINED)
202     VALGRIND_DISCARD( VALGRIND_MAKE_MEM_UNDEFINED( ptr, size ));
203 #elif defined(VALGRIND_MAKE_WRITABLE)
204     VALGRIND_DISCARD( VALGRIND_MAKE_WRITABLE( ptr, size ));
205 #endif
206 }
207
208 /* mark a block of memory as a tail block */
209 static inline void mark_block_tail( void *ptr, SIZE_T size, DWORD flags )
210 {
211     if (flags & HEAP_TAIL_CHECKING_ENABLED)
212     {
213         mark_block_uninitialized( ptr, size );
214         memset( ptr, ARENA_TAIL_FILLER, size );
215     }
216 #if defined(VALGRIND_MAKE_MEM_NOACCESS)
217     VALGRIND_DISCARD( VALGRIND_MAKE_MEM_NOACCESS( ptr, size ));
218 #elif defined( VALGRIND_MAKE_NOACCESS)
219     VALGRIND_DISCARD( VALGRIND_MAKE_NOACCESS( ptr, size ));
220 #endif
221 }
222
223 /* initialize contents of a newly created block of memory */
224 static inline void initialize_block( void *ptr, SIZE_T size, SIZE_T unused, DWORD flags )
225 {
226     if (flags & HEAP_ZERO_MEMORY)
227     {
228         mark_block_initialized( ptr, size );
229         memset( ptr, 0, size );
230     }
231     else
232     {
233         mark_block_uninitialized( ptr, size );
234         if (flags & HEAP_FREE_CHECKING_ENABLED)
235         {
236             memset( ptr, ARENA_INUSE_FILLER, size );
237             mark_block_uninitialized( ptr, size );
238         }
239     }
240
241     mark_block_tail( (char *)ptr + size, unused, flags );
242 }
243
244 /* notify that a new block of memory has been allocated for debugging purposes */
245 static inline void notify_alloc( void *ptr, SIZE_T size, BOOL init )
246 {
247 #ifdef VALGRIND_MALLOCLIKE_BLOCK
248     VALGRIND_MALLOCLIKE_BLOCK( ptr, size, 0, init );
249 #endif
250 }
251
252 /* notify that a block of memory has been freed for debugging purposes */
253 static inline void notify_free( void const *ptr )
254 {
255 #ifdef VALGRIND_FREELIKE_BLOCK
256     VALGRIND_FREELIKE_BLOCK( ptr, 0 );
257 #endif
258 }
259
260 static void subheap_notify_free_all(SUBHEAP const *subheap)
261 {
262 #ifdef VALGRIND_FREELIKE_BLOCK
263     char const *ptr = (char const *)subheap->base + subheap->headerSize;
264
265     if (!RUNNING_ON_VALGRIND) return;
266
267     while (ptr < (char const *)subheap->base + subheap->size)
268     {
269         if (*(const DWORD *)ptr & ARENA_FLAG_FREE)
270         {
271             ARENA_FREE const *pArena = (ARENA_FREE const *)ptr;
272             if (pArena->magic!=ARENA_FREE_MAGIC) ERR("bad free_magic @%p\n", pArena);
273             ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
274         }
275         else
276         {
277             ARENA_INUSE const *pArena = (ARENA_INUSE const *)ptr;
278             if (pArena->magic!=ARENA_INUSE_MAGIC) ERR("bad inuse_magic @%p\n", pArena);
279             notify_free(pArena + 1);
280             ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
281         }
282     }
283 #endif
284 }
285
286 /* locate a free list entry of the appropriate size */
287 /* size is the size of the whole block including the arena header */
288 static inline unsigned int get_freelist_index( SIZE_T size )
289 {
290     unsigned int i;
291
292     size -= sizeof(ARENA_FREE);
293     for (i = 0; i < HEAP_NB_FREE_LISTS - 1; i++) if (size <= HEAP_freeListSizes[i]) break;
294     return i;
295 }
296
297 /* get the memory protection type to use for a given heap */
298 static inline ULONG get_protection_type( DWORD flags )
299 {
300     return (flags & HEAP_CREATE_ENABLE_EXECUTE) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
301 }
302
303 static RTL_CRITICAL_SECTION_DEBUG process_heap_critsect_debug =
304 {
305     0, 0, NULL,  /* will be set later */
306     { &process_heap_critsect_debug.ProcessLocksList, &process_heap_critsect_debug.ProcessLocksList },
307       0, 0, { (DWORD_PTR)(__FILE__ ": main process heap section") }
308 };
309
310
311 /***********************************************************************
312  *           HEAP_Dump
313  */
314 static void HEAP_Dump( HEAP *heap )
315 {
316     unsigned int i;
317     SUBHEAP *subheap;
318     char *ptr;
319
320     DPRINTF( "Heap: %p\n", heap );
321     DPRINTF( "Next: %p  Sub-heaps:", LIST_ENTRY( heap->entry.next, HEAP, entry ) );
322     LIST_FOR_EACH_ENTRY( subheap, &heap->subheap_list, SUBHEAP, entry ) DPRINTF( " %p", subheap );
323
324     DPRINTF( "\nFree lists:\n Block   Stat   Size    Id\n" );
325     for (i = 0; i < HEAP_NB_FREE_LISTS; i++)
326         DPRINTF( "%p free %08lx prev=%p next=%p\n",
327                  &heap->freeList[i].arena, HEAP_freeListSizes[i],
328                  LIST_ENTRY( heap->freeList[i].arena.entry.prev, ARENA_FREE, entry ),
329                  LIST_ENTRY( heap->freeList[i].arena.entry.next, ARENA_FREE, entry ));
330
331     LIST_FOR_EACH_ENTRY( subheap, &heap->subheap_list, SUBHEAP, entry )
332     {
333         SIZE_T freeSize = 0, usedSize = 0, arenaSize = subheap->headerSize;
334         DPRINTF( "\n\nSub-heap %p: base=%p size=%08lx committed=%08lx\n",
335                  subheap, subheap->base, subheap->size, subheap->commitSize );
336
337         DPRINTF( "\n Block    Arena   Stat   Size    Id\n" );
338         ptr = (char *)subheap->base + subheap->headerSize;
339         while (ptr < (char *)subheap->base + subheap->size)
340         {
341             if (*(DWORD *)ptr & ARENA_FLAG_FREE)
342             {
343                 ARENA_FREE *pArena = (ARENA_FREE *)ptr;
344                 DPRINTF( "%p %08x free %08x prev=%p next=%p\n",
345                          pArena, pArena->magic,
346                          pArena->size & ARENA_SIZE_MASK,
347                          LIST_ENTRY( pArena->entry.prev, ARENA_FREE, entry ),
348                          LIST_ENTRY( pArena->entry.next, ARENA_FREE, entry ) );
349                 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
350                 arenaSize += sizeof(ARENA_FREE);
351                 freeSize += pArena->size & ARENA_SIZE_MASK;
352             }
353             else if (*(DWORD *)ptr & ARENA_FLAG_PREV_FREE)
354             {
355                 ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
356                 DPRINTF( "%p %08x Used %08x back=%p\n",
357                         pArena, pArena->magic, pArena->size & ARENA_SIZE_MASK, *((ARENA_FREE **)pArena - 1) );
358                 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
359                 arenaSize += sizeof(ARENA_INUSE);
360                 usedSize += pArena->size & ARENA_SIZE_MASK;
361             }
362             else
363             {
364                 ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
365                 DPRINTF( "%p %08x used %08x\n", pArena, pArena->magic, pArena->size & ARENA_SIZE_MASK );
366                 ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
367                 arenaSize += sizeof(ARENA_INUSE);
368                 usedSize += pArena->size & ARENA_SIZE_MASK;
369             }
370         }
371         DPRINTF( "\nTotal: Size=%08lx Committed=%08lx Free=%08lx Used=%08lx Arenas=%08lx (%ld%%)\n\n",
372               subheap->size, subheap->commitSize, freeSize, usedSize,
373               arenaSize, (arenaSize * 100) / subheap->size );
374     }
375 }
376
377
378 static void HEAP_DumpEntry( LPPROCESS_HEAP_ENTRY entry )
379 {
380     WORD rem_flags;
381     TRACE( "Dumping entry %p\n", entry );
382     TRACE( "lpData\t\t: %p\n", entry->lpData );
383     TRACE( "cbData\t\t: %08x\n", entry->cbData);
384     TRACE( "cbOverhead\t: %08x\n", entry->cbOverhead);
385     TRACE( "iRegionIndex\t: %08x\n", entry->iRegionIndex);
386     TRACE( "WFlags\t\t: ");
387     if (entry->wFlags & PROCESS_HEAP_REGION)
388         TRACE( "PROCESS_HEAP_REGION ");
389     if (entry->wFlags & PROCESS_HEAP_UNCOMMITTED_RANGE)
390         TRACE( "PROCESS_HEAP_UNCOMMITTED_RANGE ");
391     if (entry->wFlags & PROCESS_HEAP_ENTRY_BUSY)
392         TRACE( "PROCESS_HEAP_ENTRY_BUSY ");
393     if (entry->wFlags & PROCESS_HEAP_ENTRY_MOVEABLE)
394         TRACE( "PROCESS_HEAP_ENTRY_MOVEABLE ");
395     if (entry->wFlags & PROCESS_HEAP_ENTRY_DDESHARE)
396         TRACE( "PROCESS_HEAP_ENTRY_DDESHARE ");
397     rem_flags = entry->wFlags &
398         ~(PROCESS_HEAP_REGION | PROCESS_HEAP_UNCOMMITTED_RANGE |
399           PROCESS_HEAP_ENTRY_BUSY | PROCESS_HEAP_ENTRY_MOVEABLE|
400           PROCESS_HEAP_ENTRY_DDESHARE);
401     if (rem_flags)
402         TRACE( "Unknown %08x", rem_flags);
403     TRACE( "\n");
404     if ((entry->wFlags & PROCESS_HEAP_ENTRY_BUSY )
405         && (entry->wFlags & PROCESS_HEAP_ENTRY_MOVEABLE))
406     {
407         /* Treat as block */
408         TRACE( "BLOCK->hMem\t\t:%p\n", entry->u.Block.hMem);
409     }
410     if (entry->wFlags & PROCESS_HEAP_REGION)
411     {
412         TRACE( "Region.dwCommittedSize\t:%08x\n",entry->u.Region.dwCommittedSize);
413         TRACE( "Region.dwUnCommittedSize\t:%08x\n",entry->u.Region.dwUnCommittedSize);
414         TRACE( "Region.lpFirstBlock\t:%p\n",entry->u.Region.lpFirstBlock);
415         TRACE( "Region.lpLastBlock\t:%p\n",entry->u.Region.lpLastBlock);
416     }
417 }
418
419 /***********************************************************************
420  *           HEAP_GetPtr
421  * RETURNS
422  *      Pointer to the heap
423  *      NULL: Failure
424  */
425 static HEAP *HEAP_GetPtr(
426              HANDLE heap /* [in] Handle to the heap */
427 ) {
428     HEAP *heapPtr = heap;
429     if (!heapPtr || (heapPtr->magic != HEAP_MAGIC))
430     {
431         ERR("Invalid heap %p!\n", heap );
432         return NULL;
433     }
434     if ((heapPtr->flags & HEAP_VALIDATE_ALL) && !HEAP_IsRealArena( heapPtr, 0, NULL, NOISY ))
435     {
436         if (TRACE_ON(heap))
437         {
438             HEAP_Dump( heapPtr );
439             assert( FALSE );
440         }
441         return NULL;
442     }
443     return heapPtr;
444 }
445
446
447 /***********************************************************************
448  *           HEAP_InsertFreeBlock
449  *
450  * Insert a free block into the free list.
451  */
452 static inline void HEAP_InsertFreeBlock( HEAP *heap, ARENA_FREE *pArena, BOOL last )
453 {
454     FREE_LIST_ENTRY *pEntry = heap->freeList + get_freelist_index( pArena->size + sizeof(*pArena) );
455     if (last)
456     {
457         /* insert at end of free list, i.e. before the next free list entry */
458         pEntry++;
459         if (pEntry == &heap->freeList[HEAP_NB_FREE_LISTS]) pEntry = heap->freeList;
460         list_add_before( &pEntry->arena.entry, &pArena->entry );
461     }
462     else
463     {
464         /* insert at head of free list */
465         list_add_after( &pEntry->arena.entry, &pArena->entry );
466     }
467     pArena->size |= ARENA_FLAG_FREE;
468 }
469
470
471 /***********************************************************************
472  *           HEAP_FindSubHeap
473  * Find the sub-heap containing a given address.
474  *
475  * RETURNS
476  *      Pointer: Success
477  *      NULL: Failure
478  */
479 static SUBHEAP *HEAP_FindSubHeap(
480                 const HEAP *heap, /* [in] Heap pointer */
481                 LPCVOID ptr ) /* [in] Address */
482 {
483     SUBHEAP *sub;
484     LIST_FOR_EACH_ENTRY( sub, &heap->subheap_list, SUBHEAP, entry )
485         if ((ptr >= sub->base) &&
486             ((const char *)ptr < (const char *)sub->base + sub->size - sizeof(ARENA_INUSE)))
487             return sub;
488     return NULL;
489 }
490
491
492 /***********************************************************************
493  *           HEAP_Commit
494  *
495  * Make sure the heap storage is committed for a given size in the specified arena.
496  */
497 static inline BOOL HEAP_Commit( SUBHEAP *subheap, ARENA_INUSE *pArena, SIZE_T data_size )
498 {
499     void *ptr = (char *)(pArena + 1) + data_size + sizeof(ARENA_FREE);
500     SIZE_T size = (char *)ptr - (char *)subheap->base;
501     size = (size + COMMIT_MASK) & ~COMMIT_MASK;
502     if (size > subheap->size) size = subheap->size;
503     if (size <= subheap->commitSize) return TRUE;
504     size -= subheap->commitSize;
505     ptr = (char *)subheap->base + subheap->commitSize;
506     if (NtAllocateVirtualMemory( NtCurrentProcess(), &ptr, 0,
507                                  &size, MEM_COMMIT, get_protection_type( subheap->heap->flags ) ))
508     {
509         WARN("Could not commit %08lx bytes at %p for heap %p\n",
510                  size, ptr, subheap->heap );
511         return FALSE;
512     }
513     subheap->commitSize += size;
514     return TRUE;
515 }
516
517
518 /***********************************************************************
519  *           HEAP_Decommit
520  *
521  * If possible, decommit the heap storage from (including) 'ptr'.
522  */
523 static inline BOOL HEAP_Decommit( SUBHEAP *subheap, void *ptr )
524 {
525     void *addr;
526     SIZE_T decommit_size;
527     SIZE_T size = (char *)ptr - (char *)subheap->base;
528
529     /* round to next block and add one full block */
530     size = ((size + COMMIT_MASK) & ~COMMIT_MASK) + COMMIT_MASK + 1;
531     size = max( size, subheap->min_commit );
532     if (size >= subheap->commitSize) return TRUE;
533     decommit_size = subheap->commitSize - size;
534     addr = (char *)subheap->base + size;
535
536     if (NtFreeVirtualMemory( NtCurrentProcess(), &addr, &decommit_size, MEM_DECOMMIT ))
537     {
538         WARN("Could not decommit %08lx bytes at %p for heap %p\n",
539              decommit_size, (char *)subheap->base + size, subheap->heap );
540         return FALSE;
541     }
542     subheap->commitSize -= decommit_size;
543     return TRUE;
544 }
545
546
547 /***********************************************************************
548  *           HEAP_CreateFreeBlock
549  *
550  * Create a free block at a specified address. 'size' is the size of the
551  * whole block, including the new arena.
552  */
553 static void HEAP_CreateFreeBlock( SUBHEAP *subheap, void *ptr, SIZE_T size )
554 {
555     ARENA_FREE *pFree;
556     char *pEnd;
557     BOOL last;
558     DWORD flags = subheap->heap->flags;
559
560     /* Create a free arena */
561     mark_block_uninitialized( ptr, sizeof(ARENA_FREE) );
562     pFree = ptr;
563     pFree->magic = ARENA_FREE_MAGIC;
564
565     /* If debugging, erase the freed block content */
566
567     pEnd = (char *)ptr + size;
568     if (pEnd > (char *)subheap->base + subheap->commitSize)
569         pEnd = (char *)subheap->base + subheap->commitSize;
570     if (pEnd > (char *)(pFree + 1)) mark_block_free( pFree + 1, pEnd - (char *)(pFree + 1), flags );
571
572     /* Check if next block is free also */
573
574     if (((char *)ptr + size < (char *)subheap->base + subheap->size) &&
575         (*(DWORD *)((char *)ptr + size) & ARENA_FLAG_FREE))
576     {
577         /* Remove the next arena from the free list */
578         ARENA_FREE *pNext = (ARENA_FREE *)((char *)ptr + size);
579         list_remove( &pNext->entry );
580         size += (pNext->size & ARENA_SIZE_MASK) + sizeof(*pNext);
581         mark_block_free( pNext, sizeof(ARENA_FREE), flags );
582     }
583
584     /* Set the next block PREV_FREE flag and pointer */
585
586     last = ((char *)ptr + size >= (char *)subheap->base + subheap->size);
587     if (!last)
588     {
589         DWORD *pNext = (DWORD *)((char *)ptr + size);
590         *pNext |= ARENA_FLAG_PREV_FREE;
591         mark_block_initialized( pNext - 1, sizeof( ARENA_FREE * ) );
592         *((ARENA_FREE **)pNext - 1) = pFree;
593     }
594
595     /* Last, insert the new block into the free list */
596
597     pFree->size = size - sizeof(*pFree);
598     HEAP_InsertFreeBlock( subheap->heap, pFree, last );
599 }
600
601
602 /***********************************************************************
603  *           HEAP_MakeInUseBlockFree
604  *
605  * Turn an in-use block into a free block. Can also decommit the end of
606  * the heap, and possibly even free the sub-heap altogether.
607  */
608 static void HEAP_MakeInUseBlockFree( SUBHEAP *subheap, ARENA_INUSE *pArena )
609 {
610     ARENA_FREE *pFree;
611     SIZE_T size = (pArena->size & ARENA_SIZE_MASK) + sizeof(*pArena);
612
613     /* Check if we can merge with previous block */
614
615     if (pArena->size & ARENA_FLAG_PREV_FREE)
616     {
617         pFree = *((ARENA_FREE **)pArena - 1);
618         size += (pFree->size & ARENA_SIZE_MASK) + sizeof(ARENA_FREE);
619         /* Remove it from the free list */
620         list_remove( &pFree->entry );
621     }
622     else pFree = (ARENA_FREE *)pArena;
623
624     /* Create a free block */
625
626     HEAP_CreateFreeBlock( subheap, pFree, size );
627     size = (pFree->size & ARENA_SIZE_MASK) + sizeof(ARENA_FREE);
628     if ((char *)pFree + size < (char *)subheap->base + subheap->size)
629         return;  /* Not the last block, so nothing more to do */
630
631     /* Free the whole sub-heap if it's empty and not the original one */
632
633     if (((char *)pFree == (char *)subheap->base + subheap->headerSize) &&
634         (subheap != &subheap->heap->subheap))
635     {
636         SIZE_T size = 0;
637         void *addr = subheap->base;
638         /* Remove the free block from the list */
639         list_remove( &pFree->entry );
640         /* Remove the subheap from the list */
641         list_remove( &subheap->entry );
642         /* Free the memory */
643         subheap->magic = 0;
644         NtFreeVirtualMemory( NtCurrentProcess(), &addr, &size, MEM_RELEASE );
645         return;
646     }
647
648     /* Decommit the end of the heap */
649
650     if (!(subheap->heap->flags & HEAP_SHARED)) HEAP_Decommit( subheap, pFree + 1 );
651 }
652
653
654 /***********************************************************************
655  *           HEAP_ShrinkBlock
656  *
657  * Shrink an in-use block.
658  */
659 static void HEAP_ShrinkBlock(SUBHEAP *subheap, ARENA_INUSE *pArena, SIZE_T size)
660 {
661     if ((pArena->size & ARENA_SIZE_MASK) >= size + HEAP_MIN_SHRINK_SIZE)
662     {
663         HEAP_CreateFreeBlock( subheap, (char *)(pArena + 1) + size,
664                               (pArena->size & ARENA_SIZE_MASK) - size );
665         /* assign size plus previous arena flags */
666         pArena->size = size | (pArena->size & ~ARENA_SIZE_MASK);
667     }
668     else
669     {
670         /* Turn off PREV_FREE flag in next block */
671         char *pNext = (char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK);
672         if (pNext < (char *)subheap->base + subheap->size)
673             *(DWORD *)pNext &= ~ARENA_FLAG_PREV_FREE;
674     }
675 }
676
677
678 /***********************************************************************
679  *           allocate_large_block
680  */
681 static void *allocate_large_block( HEAP *heap, DWORD flags, SIZE_T size )
682 {
683     ARENA_LARGE *arena;
684     SIZE_T block_size = sizeof(*arena) + ROUND_SIZE(size) + HEAP_TAIL_EXTRA_SIZE(flags);
685     LPVOID address = NULL;
686
687     if (block_size < size) return NULL;  /* overflow */
688     if (NtAllocateVirtualMemory( NtCurrentProcess(), &address, 5,
689                                  &block_size, MEM_COMMIT, get_protection_type( flags ) ))
690     {
691         WARN("Could not allocate block for %08lx bytes\n", size );
692         return NULL;
693     }
694     arena = address;
695     arena->data_size = size;
696     arena->block_size = block_size;
697     arena->size = ARENA_LARGE_SIZE;
698     arena->magic = ARENA_LARGE_MAGIC;
699     mark_block_tail( (char *)(arena + 1) + size, block_size - sizeof(*arena) - size, flags );
700     list_add_tail( &heap->large_list, &arena->entry );
701     notify_alloc( arena + 1, size, flags & HEAP_ZERO_MEMORY );
702     return arena + 1;
703 }
704
705
706 /***********************************************************************
707  *           free_large_block
708  */
709 static void free_large_block( HEAP *heap, DWORD flags, void *ptr )
710 {
711     ARENA_LARGE *arena = (ARENA_LARGE *)ptr - 1;
712     LPVOID address = arena;
713     SIZE_T size = 0;
714
715     list_remove( &arena->entry );
716     NtFreeVirtualMemory( NtCurrentProcess(), &address, &size, MEM_RELEASE );
717 }
718
719
720 /***********************************************************************
721  *           realloc_large_block
722  */
723 static void *realloc_large_block( HEAP *heap, DWORD flags, void *ptr, SIZE_T size )
724 {
725     ARENA_LARGE *arena = (ARENA_LARGE *)ptr - 1;
726     void *new_ptr;
727
728     if (arena->block_size - sizeof(*arena) >= size)
729     {
730         SIZE_T unused = arena->block_size - sizeof(*arena) - size;
731
732         /* FIXME: we could remap zero-pages instead */
733         if (size > arena->data_size)
734             initialize_block( (char *)ptr + arena->data_size, size - arena->data_size, unused, flags );
735         else
736             mark_block_tail( (char *)ptr + size, unused, flags );
737         arena->data_size = size;
738         return ptr;
739     }
740     if (flags & HEAP_REALLOC_IN_PLACE_ONLY) return NULL;
741     if (!(new_ptr = allocate_large_block( heap, flags, size )))
742     {
743         WARN("Could not allocate block for %08lx bytes\n", size );
744         return NULL;
745     }
746     memcpy( new_ptr, ptr, arena->data_size );
747     free_large_block( heap, flags, ptr );
748     return new_ptr;
749 }
750
751
752 /***********************************************************************
753  *           find_large_block
754  */
755 static ARENA_LARGE *find_large_block( HEAP *heap, const void *ptr )
756 {
757     ARENA_LARGE *arena;
758
759     LIST_FOR_EACH_ENTRY( arena, &heap->large_list, ARENA_LARGE, entry )
760         if (ptr == arena + 1) return arena;
761
762     return NULL;
763 }
764
765
766 /***********************************************************************
767  *           validate_large_arena
768  */
769 static BOOL validate_large_arena( HEAP *heap, const ARENA_LARGE *arena, BOOL quiet )
770 {
771     DWORD flags = heap->flags;
772
773     if ((ULONG_PTR)arena % getpagesize())
774     {
775         if (quiet == NOISY)
776         {
777             ERR( "Heap %p: invalid large arena pointer %p\n", heap, arena );
778             if (TRACE_ON(heap)) HEAP_Dump( heap );
779         }
780         else if (WARN_ON(heap))
781         {
782             WARN( "Heap %p: unaligned arena pointer %p\n", heap, arena );
783             if (TRACE_ON(heap)) HEAP_Dump( heap );
784         }
785         return FALSE;
786     }
787     if (arena->size != ARENA_LARGE_SIZE || arena->magic != ARENA_LARGE_MAGIC)
788     {
789         if (quiet == NOISY)
790         {
791             ERR( "Heap %p: invalid large arena %p values %x/%x\n",
792                  heap, arena, arena->size, arena->magic );
793             if (TRACE_ON(heap)) HEAP_Dump( heap );
794         }
795         else if (WARN_ON(heap))
796         {
797             WARN( "Heap %p: invalid large arena %p values %x/%x\n",
798                   heap, arena, arena->size, arena->magic );
799             if (TRACE_ON(heap)) HEAP_Dump( heap );
800         }
801         return FALSE;
802     }
803     if (arena->data_size > arena->block_size - sizeof(*arena))
804     {
805         ERR( "Heap %p: invalid large arena %p size %lx/%lx\n",
806              heap, arena, arena->data_size, arena->block_size );
807         return FALSE;
808     }
809     if (flags & HEAP_TAIL_CHECKING_ENABLED)
810     {
811         SIZE_T i, unused = arena->block_size - sizeof(*arena) - arena->data_size;
812         const unsigned char *data = (const unsigned char *)(arena + 1) + arena->data_size;
813
814         for (i = 0; i < unused; i++)
815         {
816             if (data[i] == ARENA_TAIL_FILLER) continue;
817             ERR("Heap %p: block %p tail overwritten at %p (byte %lu/%lu == 0x%02x)\n",
818                 heap, arena + 1, data + i, i, unused, data[i] );
819             return FALSE;
820         }
821     }
822     return TRUE;
823 }
824
825
826 /***********************************************************************
827  *           HEAP_CreateSubHeap
828  */
829 static SUBHEAP *HEAP_CreateSubHeap( HEAP *heap, LPVOID address, DWORD flags,
830                                     SIZE_T commitSize, SIZE_T totalSize )
831 {
832     SUBHEAP *subheap;
833     FREE_LIST_ENTRY *pEntry;
834     unsigned int i;
835
836     if (!address)
837     {
838         /* round-up sizes on a 64K boundary */
839         totalSize  = (totalSize + 0xffff) & 0xffff0000;
840         commitSize = (commitSize + 0xffff) & 0xffff0000;
841         if (!commitSize) commitSize = 0x10000;
842         totalSize = min( totalSize, 0xffff0000 );  /* don't allow a heap larger than 4Gb */
843         if (totalSize < commitSize) totalSize = commitSize;
844         if (flags & HEAP_SHARED) commitSize = totalSize;  /* always commit everything in a shared heap */
845
846         /* allocate the memory block */
847         if (NtAllocateVirtualMemory( NtCurrentProcess(), &address, 0, &totalSize,
848                                      MEM_RESERVE, get_protection_type( flags ) ))
849         {
850             WARN("Could not allocate %08lx bytes\n", totalSize );
851             return NULL;
852         }
853         if (NtAllocateVirtualMemory( NtCurrentProcess(), &address, 0,
854                                      &commitSize, MEM_COMMIT, get_protection_type( flags ) ))
855         {
856             WARN("Could not commit %08lx bytes for sub-heap %p\n", commitSize, address );
857             return NULL;
858         }
859     }
860
861     if (heap)
862     {
863         /* If this is a secondary subheap, insert it into list */
864
865         subheap = address;
866         subheap->base       = address;
867         subheap->heap       = heap;
868         subheap->size       = totalSize;
869         subheap->min_commit = 0x10000;
870         subheap->commitSize = commitSize;
871         subheap->magic      = SUBHEAP_MAGIC;
872         subheap->headerSize = ROUND_SIZE( sizeof(SUBHEAP) );
873         list_add_head( &heap->subheap_list, &subheap->entry );
874     }
875     else
876     {
877         /* If this is a primary subheap, initialize main heap */
878
879         heap = address;
880         heap->flags         = flags;
881         heap->magic         = HEAP_MAGIC;
882         heap->grow_size     = max( HEAP_DEF_SIZE, totalSize );
883         list_init( &heap->subheap_list );
884         list_init( &heap->large_list );
885
886         subheap = &heap->subheap;
887         subheap->base       = address;
888         subheap->heap       = heap;
889         subheap->size       = totalSize;
890         subheap->min_commit = commitSize;
891         subheap->commitSize = commitSize;
892         subheap->magic      = SUBHEAP_MAGIC;
893         subheap->headerSize = ROUND_SIZE( sizeof(HEAP) );
894         list_add_head( &heap->subheap_list, &subheap->entry );
895
896         /* Build the free lists */
897
898         heap->freeList = (FREE_LIST_ENTRY *)((char *)heap + subheap->headerSize);
899         subheap->headerSize += HEAP_NB_FREE_LISTS * sizeof(FREE_LIST_ENTRY);
900         list_init( &heap->freeList[0].arena.entry );
901         for (i = 0, pEntry = heap->freeList; i < HEAP_NB_FREE_LISTS; i++, pEntry++)
902         {
903             pEntry->arena.size = 0 | ARENA_FLAG_FREE;
904             pEntry->arena.magic = ARENA_FREE_MAGIC;
905             if (i) list_add_after( &pEntry[-1].arena.entry, &pEntry->arena.entry );
906         }
907
908         /* Initialize critical section */
909
910         if (!processHeap)  /* do it by hand to avoid memory allocations */
911         {
912             heap->critSection.DebugInfo      = &process_heap_critsect_debug;
913             heap->critSection.LockCount      = -1;
914             heap->critSection.RecursionCount = 0;
915             heap->critSection.OwningThread   = 0;
916             heap->critSection.LockSemaphore  = 0;
917             heap->critSection.SpinCount      = 0;
918             process_heap_critsect_debug.CriticalSection = &heap->critSection;
919         }
920         else
921         {
922             RtlInitializeCriticalSection( &heap->critSection );
923             heap->critSection.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": HEAP.critSection");
924         }
925
926         if (flags & HEAP_SHARED)
927         {
928             /* let's assume that only one thread at a time will try to do this */
929             HANDLE sem = heap->critSection.LockSemaphore;
930             if (!sem) NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 );
931
932             NtDuplicateObject( NtCurrentProcess(), sem, NtCurrentProcess(), &sem, 0, 0,
933                                DUP_HANDLE_MAKE_GLOBAL | DUP_HANDLE_SAME_ACCESS | DUP_HANDLE_CLOSE_SOURCE );
934             heap->critSection.LockSemaphore = sem;
935             RtlFreeHeap( processHeap, 0, heap->critSection.DebugInfo );
936             heap->critSection.DebugInfo = NULL;
937         }
938     }
939
940     /* Create the first free block */
941
942     HEAP_CreateFreeBlock( subheap, (LPBYTE)subheap->base + subheap->headerSize,
943                           subheap->size - subheap->headerSize );
944
945     return subheap;
946 }
947
948
949 /***********************************************************************
950  *           HEAP_FindFreeBlock
951  *
952  * Find a free block at least as large as the requested size, and make sure
953  * the requested size is committed.
954  */
955 static ARENA_FREE *HEAP_FindFreeBlock( HEAP *heap, SIZE_T size,
956                                        SUBHEAP **ppSubHeap )
957 {
958     SUBHEAP *subheap;
959     struct list *ptr;
960     SIZE_T total_size;
961     FREE_LIST_ENTRY *pEntry = heap->freeList + get_freelist_index( size + sizeof(ARENA_INUSE) );
962
963     /* Find a suitable free list, and in it find a block large enough */
964
965     ptr = &pEntry->arena.entry;
966     while ((ptr = list_next( &heap->freeList[0].arena.entry, ptr )))
967     {
968         ARENA_FREE *pArena = LIST_ENTRY( ptr, ARENA_FREE, entry );
969         SIZE_T arena_size = (pArena->size & ARENA_SIZE_MASK) +
970                             sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
971         if (arena_size >= size)
972         {
973             subheap = HEAP_FindSubHeap( heap, pArena );
974             if (!HEAP_Commit( subheap, (ARENA_INUSE *)pArena, size )) return NULL;
975             *ppSubHeap = subheap;
976             return pArena;
977         }
978     }
979
980     /* If no block was found, attempt to grow the heap */
981
982     if (!(heap->flags & HEAP_GROWABLE))
983     {
984         WARN("Not enough space in heap %p for %08lx bytes\n", heap, size );
985         return NULL;
986     }
987     /* make sure that we have a big enough size *committed* to fit another
988      * last free arena in !
989      * So just one heap struct, one first free arena which will eventually
990      * get used, and a second free arena that might get assigned all remaining
991      * free space in HEAP_ShrinkBlock() */
992     total_size = size + ROUND_SIZE(sizeof(SUBHEAP)) + sizeof(ARENA_INUSE) + sizeof(ARENA_FREE);
993     if (total_size < size) return NULL;  /* overflow */
994
995     if ((subheap = HEAP_CreateSubHeap( heap, NULL, heap->flags, total_size,
996                                        max( heap->grow_size, total_size ) )))
997     {
998         if (heap->grow_size < 128 * 1024 * 1024) heap->grow_size *= 2;
999     }
1000     else while (!subheap)  /* shrink the grow size again if we are running out of space */
1001     {
1002         if (heap->grow_size <= total_size || heap->grow_size <= 4 * 1024 * 1024) return NULL;
1003         heap->grow_size /= 2;
1004         subheap = HEAP_CreateSubHeap( heap, NULL, heap->flags, total_size,
1005                                       max( heap->grow_size, total_size ) );
1006     }
1007
1008     TRACE("created new sub-heap %p of %08lx bytes for heap %p\n",
1009           subheap, subheap->size, heap );
1010
1011     *ppSubHeap = subheap;
1012     return (ARENA_FREE *)((char *)subheap->base + subheap->headerSize);
1013 }
1014
1015
1016 /***********************************************************************
1017  *           HEAP_IsValidArenaPtr
1018  *
1019  * Check that the pointer is inside the range possible for arenas.
1020  */
1021 static BOOL HEAP_IsValidArenaPtr( const HEAP *heap, const ARENA_FREE *ptr )
1022 {
1023     unsigned int i;
1024     const SUBHEAP *subheap = HEAP_FindSubHeap( heap, ptr );
1025     if (!subheap) return FALSE;
1026     if ((const char *)ptr >= (const char *)subheap->base + subheap->headerSize) return TRUE;
1027     if (subheap != &heap->subheap) return FALSE;
1028     for (i = 0; i < HEAP_NB_FREE_LISTS; i++)
1029         if (ptr == &heap->freeList[i].arena) return TRUE;
1030     return FALSE;
1031 }
1032
1033
1034 /***********************************************************************
1035  *           HEAP_ValidateFreeArena
1036  */
1037 static BOOL HEAP_ValidateFreeArena( SUBHEAP *subheap, ARENA_FREE *pArena )
1038 {
1039     DWORD flags = subheap->heap->flags;
1040     SIZE_T size;
1041     ARENA_FREE *prev, *next;
1042     char *heapEnd = (char *)subheap->base + subheap->size;
1043
1044     /* Check for unaligned pointers */
1045     if ((ULONG_PTR)pArena % ALIGNMENT != ARENA_OFFSET)
1046     {
1047         ERR("Heap %p: unaligned arena pointer %p\n", subheap->heap, pArena );
1048         return FALSE;
1049     }
1050
1051     /* Check magic number */
1052     if (pArena->magic != ARENA_FREE_MAGIC)
1053     {
1054         ERR("Heap %p: invalid free arena magic %08x for %p\n", subheap->heap, pArena->magic, pArena );
1055         return FALSE;
1056     }
1057     /* Check size flags */
1058     if (!(pArena->size & ARENA_FLAG_FREE) ||
1059         (pArena->size & ARENA_FLAG_PREV_FREE))
1060     {
1061         ERR("Heap %p: bad flags %08x for free arena %p\n",
1062             subheap->heap, pArena->size & ~ARENA_SIZE_MASK, pArena );
1063         return FALSE;
1064     }
1065     /* Check arena size */
1066     size = pArena->size & ARENA_SIZE_MASK;
1067     if ((char *)(pArena + 1) + size > heapEnd)
1068     {
1069         ERR("Heap %p: bad size %08lx for free arena %p\n", subheap->heap, size, pArena );
1070         return FALSE;
1071     }
1072     /* Check that next pointer is valid */
1073     next = LIST_ENTRY( pArena->entry.next, ARENA_FREE, entry );
1074     if (!HEAP_IsValidArenaPtr( subheap->heap, next ))
1075     {
1076         ERR("Heap %p: bad next ptr %p for arena %p\n",
1077             subheap->heap, next, pArena );
1078         return FALSE;
1079     }
1080     /* Check that next arena is free */
1081     if (!(next->size & ARENA_FLAG_FREE) || (next->magic != ARENA_FREE_MAGIC))
1082     {
1083         ERR("Heap %p: next arena %p invalid for %p\n",
1084             subheap->heap, next, pArena );
1085         return FALSE;
1086     }
1087     /* Check that prev pointer is valid */
1088     prev = LIST_ENTRY( pArena->entry.prev, ARENA_FREE, entry );
1089     if (!HEAP_IsValidArenaPtr( subheap->heap, prev ))
1090     {
1091         ERR("Heap %p: bad prev ptr %p for arena %p\n",
1092             subheap->heap, prev, pArena );
1093         return FALSE;
1094     }
1095     /* Check that prev arena is free */
1096     if (!(prev->size & ARENA_FLAG_FREE) || (prev->magic != ARENA_FREE_MAGIC))
1097     {
1098         /* this often means that the prev arena got overwritten
1099          * by a memory write before that prev arena */
1100         ERR("Heap %p: prev arena %p invalid for %p\n",
1101             subheap->heap, prev, pArena );
1102         return FALSE;
1103     }
1104     /* Check that next block has PREV_FREE flag */
1105     if ((char *)(pArena + 1) + size < heapEnd)
1106     {
1107         if (!(*(DWORD *)((char *)(pArena + 1) + size) & ARENA_FLAG_PREV_FREE))
1108         {
1109             ERR("Heap %p: free arena %p next block has no PREV_FREE flag\n",
1110                 subheap->heap, pArena );
1111             return FALSE;
1112         }
1113         /* Check next block back pointer */
1114         if (*((ARENA_FREE **)((char *)(pArena + 1) + size) - 1) != pArena)
1115         {
1116             ERR("Heap %p: arena %p has wrong back ptr %p\n",
1117                 subheap->heap, pArena,
1118                 *((ARENA_FREE **)((char *)(pArena+1) + size) - 1));
1119             return FALSE;
1120         }
1121     }
1122     if (flags & HEAP_FREE_CHECKING_ENABLED)
1123     {
1124         DWORD *ptr = (DWORD *)(pArena + 1);
1125         char *end = (char *)(pArena + 1) + size;
1126
1127         if (end >= heapEnd) end = (char *)subheap->base + subheap->commitSize;
1128         while (ptr < (DWORD *)end - 1)
1129         {
1130             if (*ptr != ARENA_FREE_FILLER)
1131             {
1132                 ERR("Heap %p: free block %p overwritten at %p by %08x\n",
1133                     subheap->heap, (ARENA_INUSE *)pArena + 1, ptr, *ptr );
1134                 return FALSE;
1135             }
1136             ptr++;
1137         }
1138     }
1139     return TRUE;
1140 }
1141
1142
1143 /***********************************************************************
1144  *           HEAP_ValidateInUseArena
1145  */
1146 static BOOL HEAP_ValidateInUseArena( const SUBHEAP *subheap, const ARENA_INUSE *pArena, BOOL quiet )
1147 {
1148     SIZE_T size;
1149     DWORD i, flags = subheap->heap->flags;
1150     const char *heapEnd = (const char *)subheap->base + subheap->size;
1151
1152     /* Check for unaligned pointers */
1153     if ((ULONG_PTR)pArena % ALIGNMENT != ARENA_OFFSET)
1154     {
1155         if ( quiet == NOISY )
1156         {
1157             ERR( "Heap %p: unaligned arena pointer %p\n", subheap->heap, pArena );
1158             if ( TRACE_ON(heap) )
1159                 HEAP_Dump( subheap->heap );
1160         }
1161         else if ( WARN_ON(heap) )
1162         {
1163             WARN( "Heap %p: unaligned arena pointer %p\n", subheap->heap, pArena );
1164             if ( TRACE_ON(heap) )
1165                 HEAP_Dump( subheap->heap );
1166         }
1167         return FALSE;
1168     }
1169
1170     /* Check magic number */
1171     if (pArena->magic != ARENA_INUSE_MAGIC)
1172     {
1173         if (quiet == NOISY) {
1174             ERR("Heap %p: invalid in-use arena magic %08x for %p\n", subheap->heap, pArena->magic, pArena );
1175             if (TRACE_ON(heap))
1176                HEAP_Dump( subheap->heap );
1177         }  else if (WARN_ON(heap)) {
1178             WARN("Heap %p: invalid in-use arena magic %08x for %p\n", subheap->heap, pArena->magic, pArena );
1179             if (TRACE_ON(heap))
1180                HEAP_Dump( subheap->heap );
1181         }
1182         return FALSE;
1183     }
1184     /* Check size flags */
1185     if (pArena->size & ARENA_FLAG_FREE)
1186     {
1187         ERR("Heap %p: bad flags %08x for in-use arena %p\n",
1188             subheap->heap, pArena->size & ~ARENA_SIZE_MASK, pArena );
1189         return FALSE;
1190     }
1191     /* Check arena size */
1192     size = pArena->size & ARENA_SIZE_MASK;
1193     if ((const char *)(pArena + 1) + size > heapEnd ||
1194         (const char *)(pArena + 1) + size < (const char *)(pArena + 1))
1195     {
1196         ERR("Heap %p: bad size %08lx for in-use arena %p\n", subheap->heap, size, pArena );
1197         return FALSE;
1198     }
1199     /* Check next arena PREV_FREE flag */
1200     if (((const char *)(pArena + 1) + size < heapEnd) &&
1201         (*(const DWORD *)((const char *)(pArena + 1) + size) & ARENA_FLAG_PREV_FREE))
1202     {
1203         ERR("Heap %p: in-use arena %p next block %p has PREV_FREE flag %x\n",
1204             subheap->heap, pArena, (const char *)(pArena + 1) + size,*(const DWORD *)((const char *)(pArena + 1) + size) );
1205         return FALSE;
1206     }
1207     /* Check prev free arena */
1208     if (pArena->size & ARENA_FLAG_PREV_FREE)
1209     {
1210         const ARENA_FREE *pPrev = *((const ARENA_FREE * const*)pArena - 1);
1211         /* Check prev pointer */
1212         if (!HEAP_IsValidArenaPtr( subheap->heap, pPrev ))
1213         {
1214             ERR("Heap %p: bad back ptr %p for arena %p\n",
1215                 subheap->heap, pPrev, pArena );
1216             return FALSE;
1217         }
1218         /* Check that prev arena is free */
1219         if (!(pPrev->size & ARENA_FLAG_FREE) ||
1220             (pPrev->magic != ARENA_FREE_MAGIC))
1221         {
1222             ERR("Heap %p: prev arena %p invalid for in-use %p\n",
1223                 subheap->heap, pPrev, pArena );
1224             return FALSE;
1225         }
1226         /* Check that prev arena is really the previous block */
1227         if ((const char *)(pPrev + 1) + (pPrev->size & ARENA_SIZE_MASK) != (const char *)pArena)
1228         {
1229             ERR("Heap %p: prev arena %p is not prev for in-use %p\n",
1230                 subheap->heap, pPrev, pArena );
1231             return FALSE;
1232         }
1233     }
1234     /* Check unused size */
1235     if (pArena->unused_bytes > size)
1236     {
1237         ERR("Heap %p: invalid unused size %08x/%08lx\n", subheap->heap, pArena->unused_bytes, size );
1238         return FALSE;
1239     }
1240     /* Check unused bytes */
1241     if (flags & HEAP_TAIL_CHECKING_ENABLED)
1242     {
1243         const unsigned char *data = (const unsigned char *)(pArena + 1) + size - pArena->unused_bytes;
1244
1245         for (i = 0; i < pArena->unused_bytes; i++)
1246         {
1247             if (data[i] == ARENA_TAIL_FILLER) continue;
1248             ERR("Heap %p: block %p tail overwritten at %p (byte %u/%u == 0x%02x)\n",
1249                 subheap->heap, pArena + 1, data + i, i, pArena->unused_bytes, data[i] );
1250             return FALSE;
1251         }
1252     }
1253     return TRUE;
1254 }
1255
1256
1257 /***********************************************************************
1258  *           HEAP_IsRealArena  [Internal]
1259  * Validates a block is a valid arena.
1260  *
1261  * RETURNS
1262  *      TRUE: Success
1263  *      FALSE: Failure
1264  */
1265 static BOOL HEAP_IsRealArena( HEAP *heapPtr,   /* [in] ptr to the heap */
1266               DWORD flags,   /* [in] Bit flags that control access during operation */
1267               LPCVOID block, /* [in] Optional pointer to memory block to validate */
1268               BOOL quiet )   /* [in] Flag - if true, HEAP_ValidateInUseArena
1269                               *             does not complain    */
1270 {
1271     SUBHEAP *subheap;
1272     BOOL ret = TRUE;
1273     const ARENA_LARGE *large_arena;
1274
1275     flags &= HEAP_NO_SERIALIZE;
1276     flags |= heapPtr->flags;
1277     /* calling HeapLock may result in infinite recursion, so do the critsect directly */
1278     if (!(flags & HEAP_NO_SERIALIZE))
1279         RtlEnterCriticalSection( &heapPtr->critSection );
1280
1281     if (block)  /* only check this single memory block */
1282     {
1283         const ARENA_INUSE *arena = (const ARENA_INUSE *)block - 1;
1284
1285         if (!(subheap = HEAP_FindSubHeap( heapPtr, arena )) ||
1286             ((const char *)arena < (char *)subheap->base + subheap->headerSize))
1287         {
1288             if (!(large_arena = find_large_block( heapPtr, block )))
1289             {
1290                 if (quiet == NOISY)
1291                     ERR("Heap %p: block %p is not inside heap\n", heapPtr, block );
1292                 else if (WARN_ON(heap))
1293                     WARN("Heap %p: block %p is not inside heap\n", heapPtr, block );
1294                 ret = FALSE;
1295             }
1296             else
1297                 ret = validate_large_arena( heapPtr, large_arena, quiet );
1298         } else
1299             ret = HEAP_ValidateInUseArena( subheap, arena, quiet );
1300
1301         if (!(flags & HEAP_NO_SERIALIZE))
1302             RtlLeaveCriticalSection( &heapPtr->critSection );
1303         return ret;
1304     }
1305
1306     LIST_FOR_EACH_ENTRY( subheap, &heapPtr->subheap_list, SUBHEAP, entry )
1307     {
1308         char *ptr = (char *)subheap->base + subheap->headerSize;
1309         while (ptr < (char *)subheap->base + subheap->size)
1310         {
1311             if (*(DWORD *)ptr & ARENA_FLAG_FREE)
1312             {
1313                 if (!HEAP_ValidateFreeArena( subheap, (ARENA_FREE *)ptr )) {
1314                     ret = FALSE;
1315                     break;
1316                 }
1317                 ptr += sizeof(ARENA_FREE) + (*(DWORD *)ptr & ARENA_SIZE_MASK);
1318             }
1319             else
1320             {
1321                 if (!HEAP_ValidateInUseArena( subheap, (ARENA_INUSE *)ptr, NOISY )) {
1322                     ret = FALSE;
1323                     break;
1324                 }
1325                 ptr += sizeof(ARENA_INUSE) + (*(DWORD *)ptr & ARENA_SIZE_MASK);
1326             }
1327         }
1328         if (!ret) break;
1329     }
1330
1331     LIST_FOR_EACH_ENTRY( large_arena, &heapPtr->large_list, ARENA_LARGE, entry )
1332         if (!(ret = validate_large_arena( heapPtr, large_arena, quiet ))) break;
1333
1334     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1335     return ret;
1336 }
1337
1338
1339 /***********************************************************************
1340  *           validate_block_pointer
1341  *
1342  * Minimum validation needed to catch bad parameters in heap functions.
1343  */
1344 static BOOL validate_block_pointer( HEAP *heap, SUBHEAP **ret_subheap, const ARENA_INUSE *arena )
1345 {
1346     SUBHEAP *subheap;
1347     BOOL ret = FALSE;
1348
1349     if (!(*ret_subheap = subheap = HEAP_FindSubHeap( heap, arena )))
1350     {
1351         ARENA_LARGE *large_arena = find_large_block( heap, arena + 1 );
1352
1353         if (!large_arena)
1354         {
1355             WARN( "Heap %p: pointer %p is not inside heap\n", heap, arena + 1 );
1356             return FALSE;
1357         }
1358         if ((heap->flags & HEAP_VALIDATE) && !validate_large_arena( heap, large_arena, QUIET ))
1359             return FALSE;
1360         return TRUE;
1361     }
1362
1363     if ((char *)arena < (char *)subheap->base + subheap->headerSize)
1364         WARN( "Heap %p: pointer %p is inside subheap %p header\n", subheap->heap, arena + 1, subheap );
1365     else if (subheap->heap->flags & HEAP_VALIDATE)  /* do the full validation */
1366         ret = HEAP_ValidateInUseArena( subheap, arena, QUIET );
1367     else if ((ULONG_PTR)arena % ALIGNMENT != ARENA_OFFSET)
1368         WARN( "Heap %p: unaligned arena pointer %p\n", subheap->heap, arena );
1369     else if (arena->magic != ARENA_INUSE_MAGIC)
1370         WARN( "Heap %p: invalid in-use arena magic %08x for %p\n", subheap->heap, arena->magic, arena );
1371     else if (arena->size & ARENA_FLAG_FREE)
1372         ERR( "Heap %p: bad flags %08x for in-use arena %p\n",
1373              subheap->heap, arena->size & ~ARENA_SIZE_MASK, arena );
1374     else if ((const char *)(arena + 1) + (arena->size & ARENA_SIZE_MASK) > (const char *)subheap->base + subheap->size ||
1375              (const char *)(arena + 1) + (arena->size & ARENA_SIZE_MASK) < (const char *)(arena + 1))
1376         ERR( "Heap %p: bad size %08x for in-use arena %p\n",
1377              subheap->heap, arena->size & ARENA_SIZE_MASK, arena );
1378     else
1379         ret = TRUE;
1380
1381     return ret;
1382 }
1383
1384
1385 /***********************************************************************
1386  *           heap_set_debug_flags
1387  */
1388 void heap_set_debug_flags( HANDLE handle )
1389 {
1390     HEAP *heap = HEAP_GetPtr( handle );
1391     ULONG global_flags = RtlGetNtGlobalFlags();
1392     ULONG flags = 0;
1393
1394     if (TRACE_ON(heap)) global_flags |= FLG_HEAP_VALIDATE_ALL;
1395     if (WARN_ON(heap)) global_flags |= FLG_HEAP_VALIDATE_PARAMETERS;
1396
1397     if (global_flags & FLG_HEAP_ENABLE_TAIL_CHECK) flags |= HEAP_TAIL_CHECKING_ENABLED;
1398     if (global_flags & FLG_HEAP_ENABLE_FREE_CHECK) flags |= HEAP_FREE_CHECKING_ENABLED;
1399     if (global_flags & FLG_HEAP_DISABLE_COALESCING) flags |= HEAP_DISABLE_COALESCE_ON_FREE;
1400     if (global_flags & FLG_HEAP_PAGE_ALLOCS) flags |= HEAP_PAGE_ALLOCS | HEAP_GROWABLE;
1401
1402     if (global_flags & FLG_HEAP_VALIDATE_PARAMETERS)
1403         flags |= HEAP_VALIDATE | HEAP_VALIDATE_PARAMS |
1404                  HEAP_TAIL_CHECKING_ENABLED | HEAP_FREE_CHECKING_ENABLED;
1405     if (global_flags & FLG_HEAP_VALIDATE_ALL)
1406         flags |= HEAP_VALIDATE | HEAP_VALIDATE_ALL |
1407                  HEAP_TAIL_CHECKING_ENABLED | HEAP_FREE_CHECKING_ENABLED;
1408
1409     if (RUNNING_ON_VALGRIND) flags = 0; /* no sense in validating since Valgrind catches accesses */
1410
1411     heap->flags |= flags;
1412     heap->force_flags |= flags & ~(HEAP_VALIDATE | HEAP_DISABLE_COALESCE_ON_FREE);
1413
1414     if (flags & (HEAP_FREE_CHECKING_ENABLED | HEAP_TAIL_CHECKING_ENABLED))  /* fix existing blocks */
1415     {
1416         SUBHEAP *subheap;
1417         ARENA_LARGE *large;
1418
1419         LIST_FOR_EACH_ENTRY( subheap, &heap->subheap_list, SUBHEAP, entry )
1420         {
1421             char *ptr = (char *)subheap->base + subheap->headerSize;
1422             char *end = (char *)subheap->base + subheap->commitSize;
1423             while (ptr < end)
1424             {
1425                 ARENA_INUSE *arena = (ARENA_INUSE *)ptr;
1426                 SIZE_T size = arena->size & ARENA_SIZE_MASK;
1427                 if (arena->size & ARENA_FLAG_FREE)
1428                 {
1429                     SIZE_T count = size;
1430
1431                     ptr += sizeof(ARENA_FREE) + size;
1432                     if (ptr > end) count = end - (char *)((ARENA_FREE *)arena + 1);
1433                     else count -= sizeof(DWORD);
1434                     mark_block_free( (ARENA_FREE *)arena + 1, count, flags );
1435                 }
1436                 else
1437                 {
1438                     mark_block_tail( (char *)(arena + 1) + size - arena->unused_bytes,
1439                                      arena->unused_bytes, flags );
1440                     ptr += sizeof(ARENA_INUSE) + size;
1441                 }
1442             }
1443         }
1444
1445         LIST_FOR_EACH_ENTRY( large, &heap->large_list, ARENA_LARGE, entry )
1446             mark_block_tail( (char *)(large + 1) + large->data_size,
1447                              large->block_size - sizeof(*large) - large->data_size, flags );
1448     }
1449 }
1450
1451
1452 /***********************************************************************
1453  *           RtlCreateHeap   (NTDLL.@)
1454  *
1455  * Create a new Heap.
1456  *
1457  * PARAMS
1458  *  flags      [I] HEAP_ flags from "winnt.h"
1459  *  addr       [I] Desired base address
1460  *  totalSize  [I] Total size of the heap, or 0 for a growable heap
1461  *  commitSize [I] Amount of heap space to commit
1462  *  unknown    [I] Not yet understood
1463  *  definition [I] Heap definition
1464  *
1465  * RETURNS
1466  *  Success: A HANDLE to the newly created heap.
1467  *  Failure: a NULL HANDLE.
1468  */
1469 HANDLE WINAPI RtlCreateHeap( ULONG flags, PVOID addr, SIZE_T totalSize, SIZE_T commitSize,
1470                              PVOID unknown, PRTL_HEAP_DEFINITION definition )
1471 {
1472     SUBHEAP *subheap;
1473
1474     /* Allocate the heap block */
1475
1476     if (!totalSize)
1477     {
1478         totalSize = HEAP_DEF_SIZE;
1479         flags |= HEAP_GROWABLE;
1480     }
1481
1482     if (!(subheap = HEAP_CreateSubHeap( NULL, addr, flags, commitSize, totalSize ))) return 0;
1483
1484     /* link it into the per-process heap list */
1485     if (processHeap)
1486     {
1487         HEAP *heapPtr = subheap->heap;
1488         RtlEnterCriticalSection( &processHeap->critSection );
1489         list_add_head( &processHeap->entry, &heapPtr->entry );
1490         RtlLeaveCriticalSection( &processHeap->critSection );
1491     }
1492     else if (!addr)
1493     {
1494         processHeap = subheap->heap;  /* assume the first heap we create is the process main heap */
1495         list_init( &processHeap->entry );
1496     }
1497
1498     heap_set_debug_flags( subheap->heap );
1499     return subheap->heap;
1500 }
1501
1502
1503 /***********************************************************************
1504  *           RtlDestroyHeap   (NTDLL.@)
1505  *
1506  * Destroy a Heap created with RtlCreateHeap().
1507  *
1508  * PARAMS
1509  *  heap [I] Heap to destroy.
1510  *
1511  * RETURNS
1512  *  Success: A NULL HANDLE, if heap is NULL or it was destroyed
1513  *  Failure: The Heap handle, if heap is the process heap.
1514  */
1515 HANDLE WINAPI RtlDestroyHeap( HANDLE heap )
1516 {
1517     HEAP *heapPtr = HEAP_GetPtr( heap );
1518     SUBHEAP *subheap, *next;
1519     ARENA_LARGE *arena, *arena_next;
1520     SIZE_T size;
1521     void *addr;
1522
1523     TRACE("%p\n", heap );
1524     if (!heapPtr) return heap;
1525
1526     if (heap == processHeap) return heap; /* cannot delete the main process heap */
1527
1528     /* remove it from the per-process list */
1529     RtlEnterCriticalSection( &processHeap->critSection );
1530     list_remove( &heapPtr->entry );
1531     RtlLeaveCriticalSection( &processHeap->critSection );
1532
1533     heapPtr->critSection.DebugInfo->Spare[0] = 0;
1534     RtlDeleteCriticalSection( &heapPtr->critSection );
1535
1536     LIST_FOR_EACH_ENTRY_SAFE( arena, arena_next, &heapPtr->large_list, ARENA_LARGE, entry )
1537     {
1538         list_remove( &arena->entry );
1539         size = 0;
1540         addr = arena;
1541         NtFreeVirtualMemory( NtCurrentProcess(), &addr, &size, MEM_RELEASE );
1542     }
1543     LIST_FOR_EACH_ENTRY_SAFE( subheap, next, &heapPtr->subheap_list, SUBHEAP, entry )
1544     {
1545         if (subheap == &heapPtr->subheap) continue;  /* do this one last */
1546         subheap_notify_free_all(subheap);
1547         list_remove( &subheap->entry );
1548         size = 0;
1549         addr = subheap->base;
1550         NtFreeVirtualMemory( NtCurrentProcess(), &addr, &size, MEM_RELEASE );
1551     }
1552     subheap_notify_free_all(&heapPtr->subheap);
1553     size = 0;
1554     addr = heapPtr->subheap.base;
1555     NtFreeVirtualMemory( NtCurrentProcess(), &addr, &size, MEM_RELEASE );
1556     return 0;
1557 }
1558
1559
1560 /***********************************************************************
1561  *           RtlAllocateHeap   (NTDLL.@)
1562  *
1563  * Allocate a memory block from a Heap.
1564  *
1565  * PARAMS
1566  *  heap  [I] Heap to allocate block from
1567  *  flags [I] HEAP_ flags from "winnt.h"
1568  *  size  [I] Size of the memory block to allocate
1569  *
1570  * RETURNS
1571  *  Success: A pointer to the newly allocated block
1572  *  Failure: NULL.
1573  *
1574  * NOTES
1575  *  This call does not SetLastError().
1576  */
1577 PVOID WINAPI RtlAllocateHeap( HANDLE heap, ULONG flags, SIZE_T size )
1578 {
1579     ARENA_FREE *pArena;
1580     ARENA_INUSE *pInUse;
1581     SUBHEAP *subheap;
1582     HEAP *heapPtr = HEAP_GetPtr( heap );
1583     SIZE_T rounded_size;
1584
1585     /* Validate the parameters */
1586
1587     if (!heapPtr) return NULL;
1588     flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY;
1589     flags |= heapPtr->flags;
1590     rounded_size = ROUND_SIZE(size) + HEAP_TAIL_EXTRA_SIZE( flags );
1591     if (rounded_size < size)  /* overflow */
1592     {
1593         if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
1594         return NULL;
1595     }
1596     if (rounded_size < HEAP_MIN_DATA_SIZE) rounded_size = HEAP_MIN_DATA_SIZE;
1597
1598     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1599
1600     if (rounded_size >= HEAP_MIN_LARGE_BLOCK_SIZE && (flags & HEAP_GROWABLE))
1601     {
1602         void *ret = allocate_large_block( heap, flags, size );
1603         if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1604         if (!ret && (flags & HEAP_GENERATE_EXCEPTIONS)) RtlRaiseStatus( STATUS_NO_MEMORY );
1605         TRACE("(%p,%08x,%08lx): returning %p\n", heap, flags, size, ret );
1606         return ret;
1607     }
1608
1609     /* Locate a suitable free block */
1610
1611     if (!(pArena = HEAP_FindFreeBlock( heapPtr, rounded_size, &subheap )))
1612     {
1613         TRACE("(%p,%08x,%08lx): returning NULL\n",
1614                   heap, flags, size  );
1615         if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1616         if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
1617         return NULL;
1618     }
1619
1620     /* Remove the arena from the free list */
1621
1622     list_remove( &pArena->entry );
1623
1624     /* Build the in-use arena */
1625
1626     pInUse = (ARENA_INUSE *)pArena;
1627
1628     /* in-use arena is smaller than free arena,
1629      * so we have to add the difference to the size */
1630     pInUse->size  = (pInUse->size & ~ARENA_FLAG_FREE) + sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
1631     pInUse->magic = ARENA_INUSE_MAGIC;
1632
1633     /* Shrink the block */
1634
1635     HEAP_ShrinkBlock( subheap, pInUse, rounded_size );
1636     pInUse->unused_bytes = (pInUse->size & ARENA_SIZE_MASK) - size;
1637
1638     notify_alloc( pInUse + 1, size, flags & HEAP_ZERO_MEMORY );
1639     initialize_block( pInUse + 1, size, pInUse->unused_bytes, flags );
1640
1641     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1642
1643     TRACE("(%p,%08x,%08lx): returning %p\n", heap, flags, size, pInUse + 1 );
1644     return pInUse + 1;
1645 }
1646
1647
1648 /***********************************************************************
1649  *           RtlFreeHeap   (NTDLL.@)
1650  *
1651  * Free a memory block allocated with RtlAllocateHeap().
1652  *
1653  * PARAMS
1654  *  heap  [I] Heap that block was allocated from
1655  *  flags [I] HEAP_ flags from "winnt.h"
1656  *  ptr   [I] Block to free
1657  *
1658  * RETURNS
1659  *  Success: TRUE, if ptr is NULL or was freed successfully.
1660  *  Failure: FALSE.
1661  */
1662 BOOLEAN WINAPI RtlFreeHeap( HANDLE heap, ULONG flags, PVOID ptr )
1663 {
1664     ARENA_INUSE *pInUse;
1665     SUBHEAP *subheap;
1666     HEAP *heapPtr;
1667
1668     /* Validate the parameters */
1669
1670     if (!ptr) return TRUE;  /* freeing a NULL ptr isn't an error in Win2k */
1671
1672     heapPtr = HEAP_GetPtr( heap );
1673     if (!heapPtr)
1674     {
1675         RtlSetLastWin32ErrorAndNtStatusFromNtStatus( STATUS_INVALID_HANDLE );
1676         return FALSE;
1677     }
1678
1679     flags &= HEAP_NO_SERIALIZE;
1680     flags |= heapPtr->flags;
1681     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1682
1683     /* Inform valgrind we are trying to free memory, so it can throw up an error message */
1684     notify_free( ptr );
1685
1686     /* Some sanity checks */
1687     pInUse  = (ARENA_INUSE *)ptr - 1;
1688     if (!validate_block_pointer( heapPtr, &subheap, pInUse )) goto error;
1689
1690     if (!subheap)
1691         free_large_block( heapPtr, flags, ptr );
1692     else
1693         HEAP_MakeInUseBlockFree( subheap, pInUse );
1694
1695     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1696     TRACE("(%p,%08x,%p): returning TRUE\n", heap, flags, ptr );
1697     return TRUE;
1698
1699 error:
1700     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1701     RtlSetLastWin32ErrorAndNtStatusFromNtStatus( STATUS_INVALID_PARAMETER );
1702     TRACE("(%p,%08x,%p): returning FALSE\n", heap, flags, ptr );
1703     return FALSE;
1704 }
1705
1706
1707 /***********************************************************************
1708  *           RtlReAllocateHeap   (NTDLL.@)
1709  *
1710  * Change the size of a memory block allocated with RtlAllocateHeap().
1711  *
1712  * PARAMS
1713  *  heap  [I] Heap that block was allocated from
1714  *  flags [I] HEAP_ flags from "winnt.h"
1715  *  ptr   [I] Block to resize
1716  *  size  [I] Size of the memory block to allocate
1717  *
1718  * RETURNS
1719  *  Success: A pointer to the resized block (which may be different).
1720  *  Failure: NULL.
1721  */
1722 PVOID WINAPI RtlReAllocateHeap( HANDLE heap, ULONG flags, PVOID ptr, SIZE_T size )
1723 {
1724     ARENA_INUSE *pArena;
1725     HEAP *heapPtr;
1726     SUBHEAP *subheap;
1727     SIZE_T oldBlockSize, oldActualSize, rounded_size;
1728     void *ret;
1729
1730     if (!ptr) return NULL;
1731     if (!(heapPtr = HEAP_GetPtr( heap )))
1732     {
1733         RtlSetLastWin32ErrorAndNtStatusFromNtStatus( STATUS_INVALID_HANDLE );
1734         return NULL;
1735     }
1736
1737     /* Validate the parameters */
1738
1739     flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY |
1740              HEAP_REALLOC_IN_PLACE_ONLY;
1741     flags |= heapPtr->flags;
1742     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1743
1744     rounded_size = ROUND_SIZE(size) + HEAP_TAIL_EXTRA_SIZE(flags);
1745     if (rounded_size < size) goto oom;  /* overflow */
1746     if (rounded_size < HEAP_MIN_DATA_SIZE) rounded_size = HEAP_MIN_DATA_SIZE;
1747
1748     pArena = (ARENA_INUSE *)ptr - 1;
1749     if (!validate_block_pointer( heapPtr, &subheap, pArena )) goto error;
1750     if (!subheap)
1751     {
1752         if (!(ret = realloc_large_block( heapPtr, flags, ptr, size ))) goto oom;
1753         notify_free( ptr );
1754         goto done;
1755     }
1756
1757     /* Check if we need to grow the block */
1758
1759     oldBlockSize = (pArena->size & ARENA_SIZE_MASK);
1760     oldActualSize = (pArena->size & ARENA_SIZE_MASK) - pArena->unused_bytes;
1761     if (rounded_size > oldBlockSize)
1762     {
1763         char *pNext = (char *)(pArena + 1) + oldBlockSize;
1764
1765         if (rounded_size >= HEAP_MIN_LARGE_BLOCK_SIZE && (flags & HEAP_GROWABLE))
1766         {
1767             if (flags & HEAP_REALLOC_IN_PLACE_ONLY) goto oom;
1768             if (!(ret = allocate_large_block( heapPtr, flags, size ))) goto oom;
1769             memcpy( ret, pArena + 1, oldActualSize );
1770             notify_free( pArena + 1 );
1771             HEAP_MakeInUseBlockFree( subheap, pArena );
1772             goto done;
1773         }
1774         if ((pNext < (char *)subheap->base + subheap->size) &&
1775             (*(DWORD *)pNext & ARENA_FLAG_FREE) &&
1776             (oldBlockSize + (*(DWORD *)pNext & ARENA_SIZE_MASK) + sizeof(ARENA_FREE) >= rounded_size))
1777         {
1778             /* The next block is free and large enough */
1779             ARENA_FREE *pFree = (ARENA_FREE *)pNext;
1780             list_remove( &pFree->entry );
1781             pArena->size += (pFree->size & ARENA_SIZE_MASK) + sizeof(*pFree);
1782             if (!HEAP_Commit( subheap, pArena, rounded_size )) goto oom;
1783             notify_free( pArena + 1 );
1784             HEAP_ShrinkBlock( subheap, pArena, rounded_size );
1785             notify_alloc( pArena + 1, size, FALSE );
1786             /* FIXME: this is wrong as we may lose old VBits settings */
1787             mark_block_initialized( pArena + 1, oldActualSize );
1788         }
1789         else  /* Do it the hard way */
1790         {
1791             ARENA_FREE *pNew;
1792             ARENA_INUSE *pInUse;
1793             SUBHEAP *newsubheap;
1794
1795             if ((flags & HEAP_REALLOC_IN_PLACE_ONLY) ||
1796                 !(pNew = HEAP_FindFreeBlock( heapPtr, rounded_size, &newsubheap )))
1797                 goto oom;
1798
1799             /* Build the in-use arena */
1800
1801             list_remove( &pNew->entry );
1802             pInUse = (ARENA_INUSE *)pNew;
1803             pInUse->size = (pInUse->size & ~ARENA_FLAG_FREE)
1804                            + sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
1805             pInUse->magic = ARENA_INUSE_MAGIC;
1806             HEAP_ShrinkBlock( newsubheap, pInUse, rounded_size );
1807
1808             mark_block_initialized( pInUse + 1, oldActualSize );
1809             notify_alloc( pInUse + 1, size, FALSE );
1810             memcpy( pInUse + 1, pArena + 1, oldActualSize );
1811
1812             /* Free the previous block */
1813
1814             notify_free( pArena + 1 );
1815             HEAP_MakeInUseBlockFree( subheap, pArena );
1816             subheap = newsubheap;
1817             pArena  = pInUse;
1818         }
1819     }
1820     else
1821     {
1822         /* Shrink the block */
1823         notify_free( pArena + 1 );
1824         HEAP_ShrinkBlock( subheap, pArena, rounded_size );
1825         notify_alloc( pArena + 1, size, FALSE );
1826         /* FIXME: this is wrong as we may lose old VBits settings */
1827         mark_block_initialized( pArena + 1, size );
1828     }
1829
1830     pArena->unused_bytes = (pArena->size & ARENA_SIZE_MASK) - size;
1831
1832     /* Clear the extra bytes if needed */
1833
1834     if (size > oldActualSize)
1835         initialize_block( (char *)(pArena + 1) + oldActualSize, size - oldActualSize,
1836                           pArena->unused_bytes, flags );
1837     else
1838         mark_block_tail( (char *)(pArena + 1) + size, pArena->unused_bytes, flags );
1839
1840     /* Return the new arena */
1841
1842     ret = pArena + 1;
1843 done:
1844     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1845     TRACE("(%p,%08x,%p,%08lx): returning %p\n", heap, flags, ptr, size, ret );
1846     return ret;
1847
1848 oom:
1849     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1850     if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
1851     RtlSetLastWin32ErrorAndNtStatusFromNtStatus( STATUS_NO_MEMORY );
1852     TRACE("(%p,%08x,%p,%08lx): returning NULL\n", heap, flags, ptr, size );
1853     return NULL;
1854
1855 error:
1856     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1857     RtlSetLastWin32ErrorAndNtStatusFromNtStatus( STATUS_INVALID_PARAMETER );
1858     TRACE("(%p,%08x,%p,%08lx): returning NULL\n", heap, flags, ptr, size );
1859     return NULL;
1860 }
1861
1862
1863 /***********************************************************************
1864  *           RtlCompactHeap   (NTDLL.@)
1865  *
1866  * Compact the free space in a Heap.
1867  *
1868  * PARAMS
1869  *  heap  [I] Heap that block was allocated from
1870  *  flags [I] HEAP_ flags from "winnt.h"
1871  *
1872  * RETURNS
1873  *  The number of bytes compacted.
1874  *
1875  * NOTES
1876  *  This function is a harmless stub.
1877  */
1878 ULONG WINAPI RtlCompactHeap( HANDLE heap, ULONG flags )
1879 {
1880     static BOOL reported;
1881     if (!reported++) FIXME( "(%p, 0x%x) stub\n", heap, flags );
1882     return 0;
1883 }
1884
1885
1886 /***********************************************************************
1887  *           RtlLockHeap   (NTDLL.@)
1888  *
1889  * Lock a Heap.
1890  *
1891  * PARAMS
1892  *  heap  [I] Heap to lock
1893  *
1894  * RETURNS
1895  *  Success: TRUE. The Heap is locked.
1896  *  Failure: FALSE, if heap is invalid.
1897  */
1898 BOOLEAN WINAPI RtlLockHeap( HANDLE heap )
1899 {
1900     HEAP *heapPtr = HEAP_GetPtr( heap );
1901     if (!heapPtr) return FALSE;
1902     RtlEnterCriticalSection( &heapPtr->critSection );
1903     return TRUE;
1904 }
1905
1906
1907 /***********************************************************************
1908  *           RtlUnlockHeap   (NTDLL.@)
1909  *
1910  * Unlock a Heap.
1911  *
1912  * PARAMS
1913  *  heap  [I] Heap to unlock
1914  *
1915  * RETURNS
1916  *  Success: TRUE. The Heap is unlocked.
1917  *  Failure: FALSE, if heap is invalid.
1918  */
1919 BOOLEAN WINAPI RtlUnlockHeap( HANDLE heap )
1920 {
1921     HEAP *heapPtr = HEAP_GetPtr( heap );
1922     if (!heapPtr) return FALSE;
1923     RtlLeaveCriticalSection( &heapPtr->critSection );
1924     return TRUE;
1925 }
1926
1927
1928 /***********************************************************************
1929  *           RtlSizeHeap   (NTDLL.@)
1930  *
1931  * Get the actual size of a memory block allocated from a Heap.
1932  *
1933  * PARAMS
1934  *  heap  [I] Heap that block was allocated from
1935  *  flags [I] HEAP_ flags from "winnt.h"
1936  *  ptr   [I] Block to get the size of
1937  *
1938  * RETURNS
1939  *  Success: The size of the block.
1940  *  Failure: -1, heap or ptr are invalid.
1941  *
1942  * NOTES
1943  *  The size may be bigger than what was passed to RtlAllocateHeap().
1944  */
1945 SIZE_T WINAPI RtlSizeHeap( HANDLE heap, ULONG flags, const void *ptr )
1946 {
1947     SIZE_T ret;
1948     const ARENA_INUSE *pArena;
1949     SUBHEAP *subheap;
1950     HEAP *heapPtr = HEAP_GetPtr( heap );
1951
1952     if (!heapPtr)
1953     {
1954         RtlSetLastWin32ErrorAndNtStatusFromNtStatus( STATUS_INVALID_HANDLE );
1955         return ~0UL;
1956     }
1957     flags &= HEAP_NO_SERIALIZE;
1958     flags |= heapPtr->flags;
1959     if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
1960
1961     pArena = (const ARENA_INUSE *)ptr - 1;
1962     if (!validate_block_pointer( heapPtr, &subheap, pArena ))
1963     {
1964         RtlSetLastWin32ErrorAndNtStatusFromNtStatus( STATUS_INVALID_PARAMETER );
1965         ret = ~0UL;
1966     }
1967     else if (!subheap)
1968     {
1969         const ARENA_LARGE *large_arena = (const ARENA_LARGE *)ptr - 1;
1970         ret = large_arena->data_size;
1971     }
1972     else
1973     {
1974         ret = (pArena->size & ARENA_SIZE_MASK) - pArena->unused_bytes;
1975     }
1976     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
1977
1978     TRACE("(%p,%08x,%p): returning %08lx\n", heap, flags, ptr, ret );
1979     return ret;
1980 }
1981
1982
1983 /***********************************************************************
1984  *           RtlValidateHeap   (NTDLL.@)
1985  *
1986  * Determine if a block is a valid allocation from a heap.
1987  *
1988  * PARAMS
1989  *  heap  [I] Heap that block was allocated from
1990  *  flags [I] HEAP_ flags from "winnt.h"
1991  *  ptr   [I] Block to check
1992  *
1993  * RETURNS
1994  *  Success: TRUE. The block was allocated from heap.
1995  *  Failure: FALSE, if heap is invalid or ptr was not allocated from it.
1996  */
1997 BOOLEAN WINAPI RtlValidateHeap( HANDLE heap, ULONG flags, LPCVOID ptr )
1998 {
1999     HEAP *heapPtr = HEAP_GetPtr( heap );
2000     if (!heapPtr) return FALSE;
2001     return HEAP_IsRealArena( heapPtr, flags, ptr, QUIET );
2002 }
2003
2004
2005 /***********************************************************************
2006  *           RtlWalkHeap    (NTDLL.@)
2007  *
2008  * FIXME
2009  *  The PROCESS_HEAP_ENTRY flag values seem different between this
2010  *  function and HeapWalk(). To be checked.
2011  */
2012 NTSTATUS WINAPI RtlWalkHeap( HANDLE heap, PVOID entry_ptr )
2013 {
2014     LPPROCESS_HEAP_ENTRY entry = entry_ptr; /* FIXME */
2015     HEAP *heapPtr = HEAP_GetPtr(heap);
2016     SUBHEAP *sub, *currentheap = NULL;
2017     NTSTATUS ret;
2018     char *ptr;
2019     int region_index = 0;
2020
2021     if (!heapPtr || !entry) return STATUS_INVALID_PARAMETER;
2022
2023     if (!(heapPtr->flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
2024
2025     /* FIXME: enumerate large blocks too */
2026
2027     /* set ptr to the next arena to be examined */
2028
2029     if (!entry->lpData) /* first call (init) ? */
2030     {
2031         TRACE("begin walking of heap %p.\n", heap);
2032         currentheap = &heapPtr->subheap;
2033         ptr = (char*)currentheap->base + currentheap->headerSize;
2034     }
2035     else
2036     {
2037         ptr = entry->lpData;
2038         LIST_FOR_EACH_ENTRY( sub, &heapPtr->subheap_list, SUBHEAP, entry )
2039         {
2040             if ((ptr >= (char *)sub->base) &&
2041                 (ptr < (char *)sub->base + sub->size))
2042             {
2043                 currentheap = sub;
2044                 break;
2045             }
2046             region_index++;
2047         }
2048         if (currentheap == NULL)
2049         {
2050             ERR("no matching subheap found, shouldn't happen !\n");
2051             ret = STATUS_NO_MORE_ENTRIES;
2052             goto HW_end;
2053         }
2054
2055         if (((ARENA_INUSE *)ptr - 1)->magic == ARENA_INUSE_MAGIC)
2056         {
2057             ARENA_INUSE *pArena = (ARENA_INUSE *)ptr - 1;
2058             ptr += pArena->size & ARENA_SIZE_MASK;
2059         }
2060         else if (((ARENA_FREE *)ptr - 1)->magic == ARENA_FREE_MAGIC)
2061         {
2062             ARENA_FREE *pArena = (ARENA_FREE *)ptr - 1;
2063             ptr += pArena->size & ARENA_SIZE_MASK;
2064         }
2065         else
2066             ptr += entry->cbData; /* point to next arena */
2067
2068         if (ptr > (char *)currentheap->base + currentheap->size - 1)
2069         {   /* proceed with next subheap */
2070             struct list *next = list_next( &heapPtr->subheap_list, &currentheap->entry );
2071             if (!next)
2072             {  /* successfully finished */
2073                 TRACE("end reached.\n");
2074                 ret = STATUS_NO_MORE_ENTRIES;
2075                 goto HW_end;
2076             }
2077             currentheap = LIST_ENTRY( next, SUBHEAP, entry );
2078             ptr = (char *)currentheap->base + currentheap->headerSize;
2079         }
2080     }
2081
2082     entry->wFlags = 0;
2083     if (*(DWORD *)ptr & ARENA_FLAG_FREE)
2084     {
2085         ARENA_FREE *pArena = (ARENA_FREE *)ptr;
2086
2087         /*TRACE("free, magic: %04x\n", pArena->magic);*/
2088
2089         entry->lpData = pArena + 1;
2090         entry->cbData = pArena->size & ARENA_SIZE_MASK;
2091         entry->cbOverhead = sizeof(ARENA_FREE);
2092         entry->wFlags = PROCESS_HEAP_UNCOMMITTED_RANGE;
2093     }
2094     else
2095     {
2096         ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
2097
2098         /*TRACE("busy, magic: %04x\n", pArena->magic);*/
2099
2100         entry->lpData = pArena + 1;
2101         entry->cbData = pArena->size & ARENA_SIZE_MASK;
2102         entry->cbOverhead = sizeof(ARENA_INUSE);
2103         entry->wFlags = PROCESS_HEAP_ENTRY_BUSY;
2104         /* FIXME: can't handle PROCESS_HEAP_ENTRY_MOVEABLE
2105         and PROCESS_HEAP_ENTRY_DDESHARE yet */
2106     }
2107
2108     entry->iRegionIndex = region_index;
2109
2110     /* first element of heap ? */
2111     if (ptr == (char *)currentheap->base + currentheap->headerSize)
2112     {
2113         entry->wFlags |= PROCESS_HEAP_REGION;
2114         entry->u.Region.dwCommittedSize = currentheap->commitSize;
2115         entry->u.Region.dwUnCommittedSize =
2116                 currentheap->size - currentheap->commitSize;
2117         entry->u.Region.lpFirstBlock = /* first valid block */
2118                 (char *)currentheap->base + currentheap->headerSize;
2119         entry->u.Region.lpLastBlock  = /* first invalid block */
2120                 (char *)currentheap->base + currentheap->size;
2121     }
2122     ret = STATUS_SUCCESS;
2123     if (TRACE_ON(heap)) HEAP_DumpEntry(entry);
2124
2125 HW_end:
2126     if (!(heapPtr->flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
2127     return ret;
2128 }
2129
2130
2131 /***********************************************************************
2132  *           RtlGetProcessHeaps    (NTDLL.@)
2133  *
2134  * Get the Heaps belonging to the current process.
2135  *
2136  * PARAMS
2137  *  count [I] size of heaps
2138  *  heaps [O] Destination array for heap HANDLE's
2139  *
2140  * RETURNS
2141  *  Success: The number of Heaps allocated by the process.
2142  *  Failure: 0.
2143  */
2144 ULONG WINAPI RtlGetProcessHeaps( ULONG count, HANDLE *heaps )
2145 {
2146     ULONG total = 1;  /* main heap */
2147     struct list *ptr;
2148
2149     RtlEnterCriticalSection( &processHeap->critSection );
2150     LIST_FOR_EACH( ptr, &processHeap->entry ) total++;
2151     if (total <= count)
2152     {
2153         *heaps++ = processHeap;
2154         LIST_FOR_EACH( ptr, &processHeap->entry )
2155             *heaps++ = LIST_ENTRY( ptr, HEAP, entry );
2156     }
2157     RtlLeaveCriticalSection( &processHeap->critSection );
2158     return total;
2159 }
2160
2161 /***********************************************************************
2162  *           RtlQueryHeapInformation    (NTDLL.@)
2163  */
2164 NTSTATUS WINAPI RtlQueryHeapInformation( HANDLE heap, HEAP_INFORMATION_CLASS info_class,
2165                                          PVOID info, SIZE_T size_in, PSIZE_T size_out)
2166 {
2167     switch (info_class)
2168     {
2169     case HeapCompatibilityInformation:
2170         if (size_out) *size_out = sizeof(ULONG);
2171
2172         if (size_in < sizeof(ULONG))
2173             return STATUS_BUFFER_TOO_SMALL;
2174
2175         *(ULONG *)info = 0; /* standard heap */
2176         return STATUS_SUCCESS;
2177
2178     default:
2179         FIXME("Unknown heap information class %u\n", info_class);
2180         return STATUS_INVALID_INFO_CLASS;
2181     }
2182 }