diff --git a/dlls/kernel32/tests/heap.c b/dlls/kernel32/tests/heap.c
index 56f9744..eb79476 100644
--- a/dlls/kernel32/tests/heap.c
+++ b/dlls/kernel32/tests/heap.c
@@ -24,6 +24,7 @@
 
 #include "windef.h"
 #include "winbase.h"
+#include "winternl.h"
 #include "wine/test.h"
 
 #define MAGIC_DEAD 0xdeadbeef
@@ -467,6 +468,142 @@ static void test_HeapQueryInformation(void)
     ok(info == 0 || info == 1 || info == 2, "expected 0, 1 or 2, got %u\n", info);
 }
 
+static void test_heap_tail_check_ex(size_t bufsize1, size_t bufsize2, size_t overrun, int extra_alloc)
+{
+    char *p, *q, *r;
+    BOOL valid;
+    SIZE_T size;
+    HANDLE heap;
+
+    heap = HeapCreate(HEAP_GROWABLE|HEAP_FREE_CHECKING_ENABLED, 0, 0);
+    ok(heap != NULL, "can't allocate heap?\n");
+
+    p = HeapAlloc(heap, 0, bufsize1);
+    ok(p != NULL, "RtlAllocateHeap failed?\n");
+
+    valid = HeapValidate(heap, 0, p);
+    ok(valid, "Validate failed before overrun?\n");
+
+    size = HeapSize(heap, 0, p);
+    ok(size == bufsize1, "Size failed before overrun?\n");
+
+    if (extra_alloc) {
+        /* Allocating extra block so resize can't be done in place */
+        r = HeapAlloc(heap, 0, 1);
+    }
+
+    q = HeapReAlloc(heap, 0, p, bufsize2);
+    ok(q != NULL, "ReAllocate failed before overrun?\n");
+
+    valid = HeapValidate(heap, 0, q);
+    ok(valid, "Validate failed before overrun?\n");
+
+    size = HeapSize(heap, 0, q);
+    ok(size == bufsize2, "Size failed before overrun? (was %d, wanted %d)\n", (int)size, bufsize2);
+
+    trace("test_heap_tail_check_ex(%x, %x, %d, %d); writing to %p, expect one valgrind error and four corrupt arena warnings.\n", 
+          bufsize1, bufsize2, overrun, extra_alloc, q+(bufsize2+overrun-1));
+    q[bufsize2 + overrun - 1] = 0;
+
+    p = HeapReAlloc(heap, 0, q, bufsize2+1);
+    todo_wine
+    ok(p == NULL, "ReAllocate did not fail after overrun? (on realloc from %x to %x, %d byte overrun)\n", bufsize2, bufsize2+1, overrun);
+
+    valid = HeapValidate(heap, 0, q);
+    todo_wine
+    ok(!valid, "Validate did not fail after overrun?\n");
+
+    size = HeapSize(heap, 0, p);
+    todo_wine
+    ok(size == -1, "Size did not fail after overrun?\n");
+
+    valid = HeapFree(heap, 0, q);
+    todo_wine
+    ok(!valid, "Free did not fail after overrun?\n");
+
+    if (extra_alloc) {
+        valid = HeapFree(heap, 0, r);
+        ok(valid, "Free of extra block failed?\n");
+    }
+
+    ok(HeapDestroy(heap), "Can't destroy heap?\n");
+}
+
+static void test_heap_tail_check(size_t bufsize1, size_t bufsize2, size_t overrun)
+{
+    test_heap_tail_check_ex(bufsize1, bufsize2, overrun, 0);
+}
+
+static ULONG    (WINAPI * pRtlGetNtGlobalFlags)(void);
+
+static HMODULE hntdll;
+
+#define NTDLL_GET_PROC(func) \
+    p ## func = (void*)GetProcAddress(hntdll, #func); \
+    if(!p ## func) { \
+        trace("GetProcAddress(%s) failed\n", #func); \
+        FreeLibrary(hntdll); \
+        return FALSE; \
+    }
+
+static BOOL InitFunctionPtrs(void)
+{
+    hntdll = LoadLibraryA("ntdll.dll");
+    if(!hntdll) {
+        trace("Could not load ntdll.dll\n");
+        return FALSE;
+    }
+    NTDLL_GET_PROC(RtlGetNtGlobalFlags)
+    return TRUE;
+}
+
+#undef NTDLL_GET_PROC
+
+static void test_free_overrun(void)
+{
+    DWORD mask = FLG_HEAP_ENABLE_FREE_CHECK;
+    if (broken((pRtlGetNtGlobalFlags() & mask) == 0)) {
+        skip("\
+Debug heap not enabled, skipping heap free padding overrun test.  To enable,\n\
+run in windbg; use event filters to avoid breaking on each error.\n\
+This is only needed on Windows; on Wine, we can enable debug heap in the test.\n");
+        return;
+    }
+
+    /* Use odd sizes (or sizes under 16) so there will be some padding,
+     * so overruns can be detected.
+     */
+
+    /* Small arenas */
+    test_heap_tail_check(0, 0, 1);
+    test_heap_tail_check(1, 0, 1);
+    test_heap_tail_check(0, 1, 1);
+    test_heap_tail_check(1, 1, 1);
+
+    /* Small arena resize downwards and upwards */
+    test_heap_tail_check(127, 35, 1);
+    test_heap_tail_check(35, 127, 1);
+    /* Ditto, but force 'hard' realloc case; needed to cover all non-failure RtlReAllocateHeap code paths in wine. */
+    test_heap_tail_check_ex(35, 127, 1, 1);
+
+    if (broken(1)) {
+        skip("windows does not support guard bytes for large arenas?\n");
+    } else {
+        /* Large arenas */
+        test_heap_tail_check(0x7f001, 0x7f001, 1);
+
+        /* Large arena resize downwards and upwards */
+        test_heap_tail_check(0x7f001, 0x7f101, 1);
+        test_heap_tail_check(0x7f101, 0x7f001, 1);
+        test_heap_tail_check(0x100001, 0x200001, 1);
+        test_heap_tail_check(0x200001, 0x100001, 1);
+
+        /* Large vs. small resize */
+        test_heap_tail_check(0x7f001, 1, 1);
+        test_heap_tail_check(1, 0x7f001, 1);
+    }
+}
+
 START_TEST(heap)
 {
     test_heap();
@@ -480,4 +617,10 @@ START_TEST(heap)
     test_sized_HeapReAlloc((1 << 20), (2 << 20));
     test_sized_HeapReAlloc((1 << 20), 1);
     test_HeapQueryInformation();
+
+    /* Test overrun behavior */
+    if (!InitFunctionPtrs())
+        return;
+    test_free_overrun();
 }
+
diff --git a/dlls/ntdll/heap.c b/dlls/ntdll/heap.c
index adaf693..89b4d22 100644
--- a/dlls/ntdll/heap.c
+++ b/dlls/ntdll/heap.c
@@ -85,6 +85,7 @@ typedef struct
 
 #define ARENA_INUSE_FILLER     0x55
 #define ARENA_FREE_FILLER      0xaa
+#define ARENA_PAD_FILLER(odd) ((odd) ? 0xee : 0xfe)
 
 /* everything is aligned on 8 byte boundaries (16 for Win64) */
 #define ALIGNMENT              (2*sizeof(void*))
@@ -159,10 +160,9 @@ static HEAP *processHeap;  /* main process heap */
 
 static BOOL HEAP_IsRealArena( HEAP *heapPtr, DWORD flags, LPCVOID block, BOOL quiet );
 
-/* mark a block of memory as free for debugging purposes */
-static inline void mark_block_free( void *ptr, SIZE_T size )
+/* mark a block of memory as inaccessible for debugging purposes */
+static inline void mark_block_noaccess( void *ptr, SIZE_T size )
 {
-    if (TRACE_ON(heap) || WARN_ON(heap)) memset( ptr, ARENA_FREE_FILLER, size );
 #if defined(VALGRIND_MAKE_MEM_NOACCESS)
     VALGRIND_DISCARD( VALGRIND_MAKE_MEM_NOACCESS( ptr, size ));
 #elif defined( VALGRIND_MAKE_NOACCESS)
@@ -200,6 +200,16 @@ static inline void mark_block_uninitialized( void *ptr, SIZE_T size )
     }
 }
 
+/* mark a block of memory as free for debugging purposes */
+static inline void mark_block_free( void *ptr, SIZE_T size )
+{
+    if (TRACE_ON(heap) || WARN_ON(heap)) {
+        mark_block_uninitialized( ptr, size );
+        memset( ptr, ARENA_FREE_FILLER, size );
+    }
+    mark_block_noaccess( ptr, size );
+}
+
 /* clear contents of a block of memory */
 static inline void clear_block( void *ptr, SIZE_T size )
 {
@@ -273,6 +283,30 @@ static RTL_CRITICAL_SECTION_DEBUG process_heap_critsect_debug =
       0, 0, { (DWORD_PTR)(__FILE__ ": main process heap section") }
 };
 
+/***********************************************************************
+ *           HEAP_FillTail
+ * If heap checking enabled, fill tail with appropriate guard pattern. 
+ * Mark tail as inaccessible to valgrind.
+ *
+ * PARAMS
+ *  heap  [I] Heap this block is associated with
+ *  p     [I] the block whose tail to fill
+ *  size  [I] Payload size of the memory block
+ *  filler_size [I] Unused bytes at end of block
+ */
+static void HEAP_FillTail( HEAP *heap, PVOID p, SIZE_T size, SIZE_T filler_size )
+{
+    unsigned char *tail = ((unsigned char *)p) + size;
+    SIZE_T j;
+
+    if (heap->flags & HEAP_FREE_CHECKING_ENABLED) {
+        mark_block_uninitialized(tail, filler_size);
+        for (j=0; j<filler_size && j < 4096; j++) {
+            tail[j] = ARENA_PAD_FILLER((size ^ j) & 1);
+        }
+        mark_block_noaccess(tail, j);
+    }
+}
 
 /***********************************************************************
  *           HEAP_Dump
@@ -654,6 +688,7 @@ static void *allocate_large_block( HEAP *heap, DWORD flags, SIZE_T size )
         return NULL;
     }
     arena = address;
+    HEAP_FillTail( heap, arena + 1, size, block_size - sizeof(*arena) - size );
     arena->data_size = size;
     arena->block_size = block_size;
     arena->size = ARENA_LARGE_SIZE;
@@ -691,6 +726,7 @@ static void *realloc_large_block( HEAP *heap, DWORD flags, void *ptr, SIZE_T siz
         if ((flags & HEAP_ZERO_MEMORY) && size > arena->data_size)
             memset( (char *)ptr + arena->data_size, 0, size - arena->data_size );
         arena->data_size = size;
+        HEAP_FillTail( heap, arena + 1, size, arena->block_size - sizeof(*arena) - size );
         return ptr;
     }
     if (flags & HEAP_REALLOC_IN_PLACE_ONLY) return NULL;
@@ -1256,6 +1292,16 @@ HANDLE WINAPI RtlCreateHeap( ULONG flags, PVOID addr, SIZE_T totalSize, SIZE_T c
 
     /* Allocate the heap block */
 
+    /* Enable debug heap if user requests it.  (Don't enable it for
+     * the virtual heap, which is created before NtCurrentTab() can
+     * be called.
+     * FIXME: figure out better way to detect virtual heap creation
+     * than comparing flags to HEAP_NO_SERIALIZE.
+     */
+    if ((flags != HEAP_NO_SERIALIZE) &&  
+        (NtCurrentTeb()->Peb->NtGlobalFlag & FLG_HEAP_ENABLE_FREE_CHECK)) {
+        flags |= HEAP_FREE_CHECKING_ENABLED;
+    }
     if (!totalSize)
     {
         totalSize = HEAP_DEF_SIZE;
@@ -1421,12 +1467,10 @@ PVOID WINAPI RtlAllocateHeap( HANDLE heap, ULONG flags, SIZE_T size )
     notify_alloc( pInUse + 1, size, flags & HEAP_ZERO_MEMORY );
 
     if (flags & HEAP_ZERO_MEMORY)
-    {
         clear_block( pInUse + 1, size );
-        mark_block_uninitialized( (char *)(pInUse + 1) + size, pInUse->unused_bytes );
-    }
     else
-        mark_block_uninitialized( pInUse + 1, pInUse->size & ARENA_SIZE_MASK );
+        mark_block_uninitialized( pInUse + 1, size );
+    HEAP_FillTail( heap, pInUse + 1, size, pInUse->unused_bytes );
 
     if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
 
@@ -1635,14 +1679,12 @@ PVOID WINAPI RtlReAllocateHeap( HANDLE heap, ULONG flags, PVOID ptr, SIZE_T size
     if (size > oldActualSize)
     {
         if (flags & HEAP_ZERO_MEMORY)
-        {
             clear_block( (char *)(pArena + 1) + oldActualSize, size - oldActualSize );
-            mark_block_uninitialized( (char *)(pArena + 1) + size, pArena->unused_bytes );
-        }
         else
             mark_block_uninitialized( (char *)(pArena + 1) + oldActualSize,
-                                      (pArena->size & ARENA_SIZE_MASK) - oldActualSize );
+                                      (pArena->size & ARENA_SIZE_MASK) - pArena->unused_bytes - oldActualSize );
     }
+    HEAP_FillTail( heapPtr, pArena + 1, size, pArena->unused_bytes );
 
     /* Return the new arena */
 
