ntoskrnl.exe: Fix handling relocations on page boundary.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=28254
Signed-off-by: Rafał Harabień <rafalh92@outlook.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Rafał Harabień 2019-05-04 13:55:53 +00:00 committed by Alexandre Julliard
parent 1daeef7332
commit 6c0a8c3592
1 changed files with 88 additions and 21 deletions

View File

@ -3674,6 +3674,84 @@ static LDR_MODULE *find_ldr_module( HMODULE module )
return ldr;
}
/* convert PE image VirtualAddress to Real Address */
static inline void *get_rva( HMODULE module, DWORD va )
{
return (void *)((char *)module + va);
}
/* Copied from ntdll with checks for page alignment and characteristics removed */
static NTSTATUS perform_relocations( void *module, SIZE_T len )
{
IMAGE_NT_HEADERS *nt;
char *base;
IMAGE_BASE_RELOCATION *rel, *end;
const IMAGE_DATA_DIRECTORY *relocs;
const IMAGE_SECTION_HEADER *sec;
INT_PTR delta;
ULONG protect_old[96], i;
nt = RtlImageNtHeader( module );
base = (char *)nt->OptionalHeader.ImageBase;
assert( module != base );
relocs = &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (nt->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
{
WARN( "Need to relocate module from %p to %p, but there are no relocation records\n",
base, module );
return STATUS_CONFLICTING_ADDRESSES;
}
if (!relocs->Size) return STATUS_SUCCESS;
if (!relocs->VirtualAddress) return STATUS_CONFLICTING_ADDRESSES;
if (nt->FileHeader.NumberOfSections > ARRAY_SIZE( protect_old ))
return STATUS_INVALID_IMAGE_FORMAT;
sec = (const IMAGE_SECTION_HEADER *)((const char *)&nt->OptionalHeader +
nt->FileHeader.SizeOfOptionalHeader);
for (i = 0; i < nt->FileHeader.NumberOfSections; i++)
{
void *addr = get_rva( module, sec[i].VirtualAddress );
SIZE_T size = sec[i].SizeOfRawData;
NtProtectVirtualMemory( NtCurrentProcess(), &addr,
&size, PAGE_READWRITE, &protect_old[i] );
}
TRACE( "relocating from %p-%p to %p-%p\n",
base, base + len, module, (char *)module + len );
rel = get_rva( module, relocs->VirtualAddress );
end = get_rva( module, relocs->VirtualAddress + relocs->Size );
delta = (char *)module - base;
while (rel < end - 1 && rel->SizeOfBlock)
{
if (rel->VirtualAddress >= len)
{
WARN( "invalid address %p in relocation %p\n", get_rva( module, rel->VirtualAddress ), rel );
return STATUS_ACCESS_VIOLATION;
}
rel = LdrProcessRelocationBlock( get_rva( module, rel->VirtualAddress ),
(rel->SizeOfBlock - sizeof(*rel)) / sizeof(USHORT),
(USHORT *)(rel + 1), delta );
if (!rel) return STATUS_INVALID_IMAGE_FORMAT;
}
for (i = 0; i < nt->FileHeader.NumberOfSections; i++)
{
void *addr = get_rva( module, sec[i].VirtualAddress );
SIZE_T size = sec[i].SizeOfRawData;
NtProtectVirtualMemory( NtCurrentProcess(), &addr,
&size, protect_old[i], &protect_old[i] );
}
return STATUS_SUCCESS;
}
/* load the driver module file */
static HMODULE load_driver_module( const WCHAR *name )
{
@ -3683,6 +3761,8 @@ static HMODULE load_driver_module( const WCHAR *name )
int i;
INT_PTR delta;
ULONG size;
DWORD old;
NTSTATUS status;
HMODULE module = LoadLibraryW( name );
if (!module) return NULL;
@ -3697,28 +3777,15 @@ static HMODULE load_driver_module( const WCHAR *name )
if (nt->OptionalHeader.SectionAlignment < info.PageSize ||
!(nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
{
DWORD old;
IMAGE_BASE_RELOCATION *rel, *end;
status = perform_relocations(module, nt->OptionalHeader.SizeOfImage);
if (status != STATUS_SUCCESS)
goto error;
if ((rel = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &size )))
{
TRACE( "%s: relocating from %p to %p\n", wine_dbgstr_w(name), (char *)module - delta, module );
end = (IMAGE_BASE_RELOCATION *)((char *)rel + size);
while (rel < end && rel->SizeOfBlock)
{
void *page = (char *)module + rel->VirtualAddress;
VirtualProtect( page, info.PageSize, PAGE_EXECUTE_READWRITE, &old );
rel = LdrProcessRelocationBlock( page, (rel->SizeOfBlock - sizeof(*rel)) / sizeof(USHORT),
(USHORT *)(rel + 1), delta );
if (old != PAGE_EXECUTE_READWRITE) VirtualProtect( page, info.PageSize, old, &old );
if (!rel) goto error;
}
/* make sure we don't try again */
size = FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + nt->FileHeader.SizeOfOptionalHeader;
VirtualProtect( nt, size, PAGE_READWRITE, &old );
nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0;
VirtualProtect( nt, size, old, &old );
}
/* make sure we don't try again */
size = FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + nt->FileHeader.SizeOfOptionalHeader;
VirtualProtect( nt, size, PAGE_READWRITE, &old );
nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = 0;
VirtualProtect( nt, size, old, &old );
}
/* make sure imports are relocated too */