ntdll: Find heap block region from its base offset.

This commit is contained in:
Rémi Bernon 2022-11-25 11:17:12 +01:00 committed by Alexandre Julliard
parent fd8e52b4e7
commit 2f6b763fa9

View file

@ -257,6 +257,14 @@ static inline void block_set_type( struct block *block, UINT type )
block->block_type = type;
}
static inline SUBHEAP *block_get_subheap( const struct heap *heap, const struct block *block )
{
char *offset = ROUND_ADDR( block, REGION_ALIGN - 1 );
void *base = offset - block->base_offset * REGION_ALIGN;
if (base != (void *)heap) return base;
else return (SUBHEAP *)&heap->subheap;
}
static inline UINT block_get_overhead( const struct block *block )
{
if (block_get_flags( block ) & BLOCK_FLAG_FREE) return sizeof(*block) + sizeof(struct list);
@ -741,10 +749,11 @@ static void create_free_block( struct heap *heap, ULONG flags, SUBHEAP *subheap,
}
static void free_used_block( struct heap *heap, ULONG flags, SUBHEAP *subheap, struct block *block )
static void free_used_block( struct heap *heap, ULONG flags, struct block *block )
{
struct entry *entry;
SIZE_T block_size;
SUBHEAP *subheap;
if (heap->pending_free)
{
@ -753,7 +762,7 @@ static void free_used_block( struct heap *heap, ULONG flags, SUBHEAP *subheap, s
heap->pending_pos = (heap->pending_pos + 1) % MAX_FREE_PENDING;
block_set_type( block, BLOCK_TYPE_DEAD );
mark_block_free( block + 1, block_get_size( block ) - sizeof(*block), flags );
if (!(block = tmp) || !(subheap = find_subheap( heap, block, FALSE ))) return;
if (!(block = tmp)) return;
}
block_size = block_get_size( block );
@ -767,6 +776,7 @@ static void free_used_block( struct heap *heap, ULONG flags, SUBHEAP *subheap, s
}
else entry = (struct entry *)block;
subheap = block_get_subheap( heap, block );
create_free_block( heap, flags, subheap, block, block_size );
if (next_block( subheap, block )) return; /* not the last block */
@ -788,9 +798,11 @@ static void free_used_block( struct heap *heap, ULONG flags, SUBHEAP *subheap, s
}
static inline void shrink_used_block( struct heap *heap, ULONG flags, SUBHEAP *subheap, struct block *block,
static inline void shrink_used_block( struct heap *heap, ULONG flags, struct block *block,
SIZE_T old_block_size, SIZE_T block_size, SIZE_T size )
{
SUBHEAP *subheap = block_get_subheap( heap, block );
if (old_block_size >= block_size + HEAP_MIN_BLOCK_SIZE)
{
block_set_size( block, block_size );
@ -1033,12 +1045,13 @@ static SUBHEAP *HEAP_CreateSubHeap( struct heap **heap_ptr, LPVOID address, DWOR
}
static struct block *find_free_block( struct heap *heap, ULONG flags, SIZE_T block_size, SUBHEAP **subheap )
static struct block *find_free_block( struct heap *heap, ULONG flags, SIZE_T block_size )
{
struct list *ptr = &find_free_list( heap, block_size, FALSE )->entry;
struct entry *entry;
struct block *block;
SIZE_T total_size;
SUBHEAP *subheap;
/* Find a suitable free list, and in it find a block large enough */
@ -1049,8 +1062,7 @@ static struct block *find_free_block( struct heap *heap, ULONG flags, SIZE_T blo
if (block_get_flags( block ) == BLOCK_FLAG_FREE_LINK) continue;
if (block_get_size( block ) >= block_size)
{
*subheap = find_subheap( heap, block, FALSE );
if (!subheap_commit( heap, *subheap, block, block_size )) return NULL;
if (!subheap_commit( heap, block_get_subheap( heap, block ), block, block_size )) return NULL;
list_remove( &entry->entry );
return block;
}
@ -1068,22 +1080,22 @@ static struct block *find_free_block( struct heap *heap, ULONG flags, SIZE_T blo
total_size = sizeof(SUBHEAP) + block_size + sizeof(struct entry);
if (total_size < block_size) return NULL; /* overflow */
if ((*subheap = HEAP_CreateSubHeap( &heap, NULL, flags, total_size,
if ((subheap = HEAP_CreateSubHeap( &heap, NULL, flags, total_size,
max( heap->grow_size, total_size ) )))
{
if (heap->grow_size <= HEAP_MAX_FREE_BLOCK_SIZE / 2) heap->grow_size *= 2;
}
else while (!*subheap) /* shrink the grow size again if we are running out of space */
else while (!subheap) /* shrink the grow size again if we are running out of space */
{
if (heap->grow_size <= total_size || heap->grow_size <= 4 * 1024 * 1024) return NULL;
heap->grow_size /= 2;
*subheap = HEAP_CreateSubHeap( &heap, NULL, flags, total_size,
subheap = HEAP_CreateSubHeap( &heap, NULL, flags, total_size,
max( heap->grow_size, total_size ) );
}
TRACE( "created new sub-heap %p of %#Ix bytes for heap %p\n", *subheap, subheap_size( *subheap ), heap );
TRACE( "created new sub-heap %p of %#Ix bytes for heap %p\n", subheap, subheap_size( subheap ), heap );
entry = first_block( *subheap );
entry = first_block( subheap );
list_remove( &entry->entry );
return &entry->block;
}
@ -1207,11 +1219,12 @@ static BOOL validate_used_block( const struct heap *heap, const SUBHEAP *subheap
}
static BOOL heap_validate_ptr( const struct heap *heap, const void *ptr, SUBHEAP **subheap )
static BOOL heap_validate_ptr( const struct heap *heap, const void *ptr )
{
const struct block *block = (struct block *)ptr - 1;
const SUBHEAP *subheap;
if (!(*subheap = find_subheap( heap, block, FALSE )))
if (!(subheap = find_subheap( heap, block, FALSE )))
{
if (!find_large_block( heap, block ))
{
@ -1222,7 +1235,7 @@ static BOOL heap_validate_ptr( const struct heap *heap, const void *ptr, SUBHEAP
return validate_large_block( heap, block );
}
return validate_used_block( heap, *subheap, block );
return validate_used_block( heap, subheap, block );
}
static BOOL heap_validate( const struct heap *heap )
@ -1259,36 +1272,38 @@ static BOOL heap_validate( const struct heap *heap )
return TRUE;
}
static inline struct block *unsafe_block_from_ptr( const struct heap *heap, const void *ptr, SUBHEAP **subheap )
static inline struct block *unsafe_block_from_ptr( const struct heap *heap, const void *ptr )
{
struct block *block = (struct block *)ptr - 1;
const char *err = NULL, *base, *commit_end;
const char *err = NULL;
SUBHEAP *subheap;
if (heap->flags & HEAP_VALIDATE)
{
if (!heap_validate_ptr( heap, ptr, subheap )) return NULL;
if (!heap_validate_ptr( heap, ptr )) return NULL;
return block;
}
if ((*subheap = find_subheap( heap, block, FALSE )))
if ((ULONG_PTR)ptr % BLOCK_ALIGN)
err = "invalid ptr alignment";
else if ((subheap = block_get_subheap( heap, block )) >= (SUBHEAP *)block)
err = "invalid base offset";
else if (block_get_type( block ) == BLOCK_TYPE_USED)
{
base = subheap_base( *subheap );
commit_end = subheap_commit_end( *subheap );
const char *base = subheap_base( subheap ), *commit_end = subheap_commit_end( subheap );
if (!contains( base, commit_end - base, block, block_get_size( block ) )) err = "invalid block size";
}
if (!*subheap)
else if (block_get_type( block ) == BLOCK_TYPE_LARGE)
{
if (find_large_block( heap, block )) return block;
err = "block region not found";
ARENA_LARGE *large = subheap_base( subheap );
if (block != &large->block) err = "invalid large block";
}
else if ((ULONG_PTR)ptr % BLOCK_ALIGN)
err = "invalid ptr BLOCK_ALIGN";
else if (block_get_type( block ) == BLOCK_TYPE_DEAD || (block_get_flags( block ) & BLOCK_FLAG_FREE))
else if (block_get_type( block ) == BLOCK_TYPE_DEAD)
err = "delayed freed block";
else if (block_get_type( block ) == BLOCK_TYPE_FREE)
err = "already freed block";
else if (block_get_type( block ) != BLOCK_TYPE_USED)
err = "invalid block header";
else if (!contains( base, commit_end - base, block, block_get_size( block ) ))
err = "invalid block size";
else
err = "invalid block type";
if (err) WARN( "heap %p, block %p: %s\n", heap, block, err );
return err ? NULL : block;
@ -1465,8 +1480,7 @@ HANDLE WINAPI RtlDestroyHeap( HANDLE handle )
{
heap->pending_free = NULL;
for (tmp = pending; *tmp && tmp != pending + MAX_FREE_PENDING; ++tmp)
if ((subheap = find_subheap( heap, *tmp, FALSE )))
free_used_block( heap, heap->flags, subheap, *tmp );
free_used_block( heap, heap->flags, *tmp );
RtlFreeHeap( handle, 0, pending );
}
@ -1527,7 +1541,6 @@ static NTSTATUS heap_allocate( struct heap *heap, ULONG flags, SIZE_T block_size
{
SIZE_T old_block_size;
struct block *block;
SUBHEAP *subheap;
if (block_size >= HEAP_MIN_LARGE_BLOCK_SIZE)
{
@ -1539,13 +1552,13 @@ static NTSTATUS heap_allocate( struct heap *heap, ULONG flags, SIZE_T block_size
/* Locate a suitable free block */
if (!(block = find_free_block( heap, flags, block_size, &subheap ))) return STATUS_NO_MEMORY;
if (!(block = find_free_block( heap, flags, block_size ))) return STATUS_NO_MEMORY;
/* read the free block size, changing block type or flags may alter it */
old_block_size = block_get_size( block );
block_set_type( block, BLOCK_TYPE_USED );
block_set_flags( block, ~0, BLOCK_USER_FLAGS( flags ) );
shrink_used_block( heap, flags, subheap, block, old_block_size, block_size, size );
shrink_used_block( heap, flags, block, old_block_size, block_size, size );
initialize_block( block, 0, size, flags );
mark_block_tail( block, flags );
@ -1586,11 +1599,10 @@ void *WINAPI DECLSPEC_HOTPATCH RtlAllocateHeap( HANDLE handle, ULONG flags, SIZE
static NTSTATUS heap_free( struct heap *heap, ULONG flags, void *ptr )
{
struct block *block;
SUBHEAP *subheap;
if (!(block = unsafe_block_from_ptr( heap, ptr, &subheap ))) return STATUS_INVALID_PARAMETER;
if (!(block = unsafe_block_from_ptr( heap, ptr ))) return STATUS_INVALID_PARAMETER;
if (block_get_flags( block ) & BLOCK_FLAG_LARGE) free_large_block( heap, block );
else free_used_block( heap, flags, subheap, block );
else free_used_block( heap, flags, block );
return STATUS_SUCCESS;
}
@ -1628,10 +1640,9 @@ static NTSTATUS heap_reallocate( struct heap *heap, ULONG flags, void *ptr,
{
SIZE_T old_block_size, old_size;
struct block *next, *block;
SUBHEAP *subheap;
NTSTATUS status;
if (!(block = unsafe_block_from_ptr( heap, ptr, &subheap ))) return STATUS_INVALID_PARAMETER;
if (!(block = unsafe_block_from_ptr( heap, ptr ))) return STATUS_INVALID_PARAMETER;
if (block_get_flags( block ) & BLOCK_FLAG_LARGE)
{
if (!(block = realloc_large_block( heap, flags, block, size ))) return STATUS_NO_MEMORY;
@ -1646,6 +1657,8 @@ static NTSTATUS heap_reallocate( struct heap *heap, ULONG flags, void *ptr,
old_size = old_block_size - block_get_overhead( block );
if (block_size > old_block_size)
{
SUBHEAP *subheap = block_get_subheap( heap, block );
if ((next = next_block( subheap, block )) && (block_get_flags( next ) & BLOCK_FLAG_FREE) &&
block_size < HEAP_MIN_LARGE_BLOCK_SIZE && block_size <= old_block_size + block_get_size( next ))
{
@ -1663,14 +1676,14 @@ static NTSTATUS heap_reallocate( struct heap *heap, ULONG flags, void *ptr,
memcpy( *ret, block + 1, old_size );
if (flags & HEAP_ZERO_MEMORY) memset( (char *)*ret + old_size, 0, size - old_size );
valgrind_notify_free( ptr );
free_used_block( heap, flags, subheap, block );
free_used_block( heap, flags, block );
return STATUS_SUCCESS;
}
}
valgrind_notify_resize( block + 1, old_size, size );
block_set_flags( block, BLOCK_FLAG_USER_MASK, BLOCK_USER_FLAGS( flags ) );
shrink_used_block( heap, flags, subheap, block, old_block_size, block_size, size );
shrink_used_block( heap, flags, block, old_block_size, block_size, size );
initialize_block( block, old_size, size, flags );
mark_block_tail( block, flags );
@ -1779,9 +1792,8 @@ BOOLEAN WINAPI RtlUnlockHeap( HANDLE handle )
static NTSTATUS heap_size( const struct heap *heap, const void *ptr, SIZE_T *size )
{
const struct block *block;
SUBHEAP *subheap;
if (!(block = unsafe_block_from_ptr( heap, ptr, &subheap ))) return STATUS_INVALID_PARAMETER;
if (!(block = unsafe_block_from_ptr( heap, ptr ))) return STATUS_INVALID_PARAMETER;
if (block_get_flags( block ) & BLOCK_FLAG_LARGE)
{
const ARENA_LARGE *large_arena = CONTAINING_RECORD( block, ARENA_LARGE, block );
@ -1823,7 +1835,6 @@ SIZE_T WINAPI RtlSizeHeap( HANDLE handle, ULONG flags, const void *ptr )
BOOLEAN WINAPI RtlValidateHeap( HANDLE handle, ULONG flags, const void *ptr )
{
struct heap *heap;
SUBHEAP *subheap;
ULONG heap_flags;
BOOLEAN ret;
@ -1832,7 +1843,7 @@ BOOLEAN WINAPI RtlValidateHeap( HANDLE handle, ULONG flags, const void *ptr )
else
{
heap_lock( heap, heap_flags );
if (ptr) ret = heap_validate_ptr( heap, ptr, &subheap );
if (ptr) ret = heap_validate_ptr( heap, ptr );
else ret = heap_validate( heap );
heap_unlock( heap, heap_flags );
}
@ -2042,7 +2053,6 @@ BOOLEAN WINAPI RtlGetUserInfoHeap( HANDLE handle, ULONG flags, void *ptr, void *
NTSTATUS status = STATUS_SUCCESS;
struct block *block;
struct heap *heap;
SUBHEAP *subheap;
ULONG heap_flags;
char *tmp;
@ -2054,7 +2064,7 @@ BOOLEAN WINAPI RtlGetUserInfoHeap( HANDLE handle, ULONG flags, void *ptr, void *
if (!(heap = unsafe_heap_from_handle( handle, flags, &heap_flags ))) return TRUE;
heap_lock( heap, heap_flags );
if (!(block = unsafe_block_from_ptr( heap, ptr, &subheap )))
if (!(block = unsafe_block_from_ptr( heap, ptr )))
{
WARN( "Failed to find block %p in heap %p\n", ptr, handle );
status = STATUS_INVALID_PARAMETER;
@ -2089,7 +2099,6 @@ BOOLEAN WINAPI RtlSetUserValueHeap( HANDLE handle, ULONG flags, void *ptr, void
struct block *block;
BOOLEAN ret = FALSE;
struct heap *heap;
SUBHEAP *subheap;
ULONG heap_flags;
char *tmp;
@ -2098,7 +2107,7 @@ BOOLEAN WINAPI RtlSetUserValueHeap( HANDLE handle, ULONG flags, void *ptr, void
if (!(heap = unsafe_heap_from_handle( handle, flags, &heap_flags ))) return TRUE;
heap_lock( heap, heap_flags );
if (!(block = unsafe_block_from_ptr( heap, ptr, &subheap )))
if (!(block = unsafe_block_from_ptr( heap, ptr )))
WARN( "Failed to find block %p in heap %p\n", ptr, handle );
else if (!(block_get_flags( block ) & BLOCK_FLAG_USER_INFO))
WARN( "Block %p wasn't allocated with user info\n", ptr );
@ -2128,7 +2137,6 @@ BOOLEAN WINAPI RtlSetUserFlagsHeap( HANDLE handle, ULONG flags, void *ptr, ULONG
struct block *block;
BOOLEAN ret = FALSE;
struct heap *heap;
SUBHEAP *subheap;
ULONG heap_flags;
TRACE( "handle %p, flags %#lx, ptr %p, clear %#lx, set %#lx.\n", handle, flags, ptr, clear, set );
@ -2142,7 +2150,7 @@ BOOLEAN WINAPI RtlSetUserFlagsHeap( HANDLE handle, ULONG flags, void *ptr, ULONG
if (!(heap = unsafe_heap_from_handle( handle, flags, &heap_flags ))) return TRUE;
heap_lock( heap, heap_flags );
if (!(block = unsafe_block_from_ptr( heap, ptr, &subheap )))
if (!(block = unsafe_block_from_ptr( heap, ptr )))
WARN( "Failed to find block %p in heap %p\n", ptr, handle );
else if (!(block_get_flags( block ) & BLOCK_FLAG_USER_INFO))
WARN( "Block %p wasn't allocated with user info\n", ptr );