From a99b0eeff3202d63f55f252ceb7cf364549d812c Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Mon, 10 Nov 2014 04:05:38 +0100
Subject: winedevice: Avoid invalid memory access when relocation block
 addresses memory outside of the current page.

---
 programs/winedevice/device.c | 49 ++++++++++++++++++++++++++++++++++++++------
 1 file changed, 43 insertions(+), 6 deletions(-)

diff --git a/programs/winedevice/device.c b/programs/winedevice/device.c
index 94132ed..6bcc2b2 100644
--- a/programs/winedevice/device.c
+++ b/programs/winedevice/device.c
@@ -63,6 +63,39 @@ static LDR_MODULE *find_ldr_module( HMODULE module )
     return ldr;
 }
 
+/* change permissions of a specific memory range, save original permissions */
+static void virtual_protect_save( void *addr, SIZE_T size, ULONG new_prot, ULONG *old_prot )
+{
+    SYSTEM_BASIC_INFORMATION info;
+    UINT i = 0;
+
+    NtQuerySystemInformation( SystemBasicInformation, &info, sizeof(info), NULL );
+    while (size)
+    {
+        SIZE_T block_size = min( size, info.PageSize - ((UINT_PTR)addr & (info.PageSize - 1)) );
+        VirtualProtect( addr, block_size, new_prot, &old_prot[i++] );
+        addr  = (void *)((char *)addr + block_size);
+        size -= block_size;
+    }
+}
+
+/* restore permissions for a specific memory range */
+static void virtual_protect_load( void *addr, SIZE_T size, ULONG *old_prot )
+{
+    SYSTEM_BASIC_INFORMATION info;
+    DWORD dummy;
+    UINT i = 0;
+
+    NtQuerySystemInformation( SystemBasicInformation, &info, sizeof(info), NULL );
+    while (size)
+    {
+        SIZE_T block_size = min( size, info.PageSize - ((UINT_PTR)addr & (info.PageSize - 1)) );
+        VirtualProtect( addr, block_size, old_prot[i++], &dummy );
+        addr  = (void *)((char *)addr + block_size);
+        size -= block_size;
+    }
+}
+
 /* load the driver module file */
 static HMODULE load_driver_module( const WCHAR *name )
 {
@@ -86,7 +119,7 @@ static HMODULE load_driver_module( const WCHAR *name )
     if (nt->OptionalHeader.SectionAlignment < info.PageSize ||
         !(nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
     {
-        DWORD old;
+        DWORD old_prot[3];
         IMAGE_BASE_RELOCATION *rel, *end;
 
         if ((rel = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &size )))
@@ -94,20 +127,24 @@ static HMODULE load_driver_module( const WCHAR *name )
             WINE_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)
+            while (rel < end - 1 && rel->SizeOfBlock)
             {
                 void *page = (char *)module + rel->VirtualAddress;
-                VirtualProtect( page, info.PageSize, PAGE_EXECUTE_READWRITE, &old );
+                /* LdrProcessRelocationBlock can access the memory range from page - (page + 0xfff + 8), so
+                 * changing permissions of a single page is not sufficient. We assume here that the minimum
+                 * page size is 0x1000, so we have to save/restore two or three pages, depending on the
+                 * virtual address. */
+                virtual_protect_save( page, 0xfff + 8, PAGE_EXECUTE_READWRITE, old_prot );
                 rel = LdrProcessRelocationBlock( page, (rel->SizeOfBlock - sizeof(*rel)) / sizeof(USHORT),
                                                  (USHORT *)(rel + 1), delta );
-                if (old != PAGE_EXECUTE_READWRITE) VirtualProtect( page, info.PageSize, old, &old );
+                virtual_protect_load( page, 0xfff + 8, old_prot );
                 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 );
+            VirtualProtect( nt, size, PAGE_READWRITE, &old_prot[0] );
             nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0;
-            VirtualProtect( nt, size, old, &old );
+            VirtualProtect( nt, size, old_prot[0], &old_prot[0] );
         }
     }
 
-- 
2.7.1

