From f3483229e702bed0791a35666237e8583f5d47b0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Fri, 5 May 2017 05:40:50 +0200
Subject: [PATCH] ntdll: Create thread to update user_shared_data time values
 when necessary.

---
 dlls/ntdll/loader.c     | 31 ++++++++++++++++++++++
 dlls/ntdll/ntdll_misc.h |  3 +++
 dlls/ntdll/thread.c     | 70 ++++++++++++++++++++++++++++++++++++++++++++-----
 dlls/ntdll/virtual.c    | 17 ++++++++++++
 4 files changed, 114 insertions(+), 7 deletions(-)

diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c
index 8073434..0546478 100644
--- a/dlls/ntdll/loader.c
+++ b/dlls/ntdll/loader.c
@@ -3340,6 +3340,36 @@ PIMAGE_NT_HEADERS WINAPI RtlImageNtHeader(HMODULE hModule)
 }
 
 
+/***********************************************************************
+ *           user_shared_data_init
+ *
+ * Initializes a user shared
+ */
+static void user_shared_data_init(void)
+{
+    void *addr = user_shared_data_external;
+    SIZE_T data_size = page_size;
+    ULONG old_prot;
+
+    /* initialize time fields */
+    __wine_user_shared_data();
+
+    /* invalidate high times to prevent race conditions */
+    user_shared_data->SystemTime.High2Time = 0;
+    user_shared_data->SystemTime.High1Time = -1;
+
+    user_shared_data->InterruptTime.High2Time = 0;
+    user_shared_data->InterruptTime.High1Time = -1;
+
+    user_shared_data->u.TickCount.High2Time  = 0;
+    user_shared_data->u.TickCount.High1Time  = -1;
+
+    /* copy to correct address and make it non accessible */
+    memcpy(user_shared_data_external, user_shared_data, sizeof(*user_shared_data));
+    NtProtectVirtualMemory( NtCurrentProcess(), &addr, &data_size, PAGE_NOACCESS, &old_prot );
+}
+
+
 /******************************************************************
  *		LdrInitializeThunk (NTDLL.@)
  *
@@ -3669,6 +3699,7 @@ void __wine_process_init(void)
     NtCurrentTeb()->Peb->LoaderLock = &loader_section;
     update_user_process_params( &wm->ldr.FullDllName );
     version_init( wm->ldr.FullDllName.Buffer );
+    user_shared_data_init();
     hidden_exports_init( wm->ldr.FullDllName.Buffer );
     virtual_set_large_address_space();
 
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index 0dba878..47abfba 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -194,6 +194,9 @@ extern void virtual_set_large_address_space(void) DECLSPEC_HIDDEN;
 extern void virtual_fill_image_information( const pe_image_info_t *pe_info,
                                             SECTION_IMAGE_INFORMATION *info ) DECLSPEC_HIDDEN;
 extern struct _KUSER_SHARED_DATA *user_shared_data DECLSPEC_HIDDEN;
+extern struct _KUSER_SHARED_DATA *user_shared_data_external DECLSPEC_HIDDEN;
+extern void create_user_shared_data_thread(void) DECLSPEC_HIDDEN;
+extern BYTE* CDECL __wine_user_shared_data(void);
 
 /* completion */
 extern NTSTATUS NTDLL_AddCompletion( HANDLE hFile, ULONG_PTR CompletionValue,
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c
index 0a11cfe..1c09344 100644
--- a/dlls/ntdll/thread.c
+++ b/dlls/ntdll/thread.c
@@ -44,6 +44,7 @@
 #include "wine/library.h"
 #include "wine/server.h"
 #include "wine/debug.h"
+#include "winbase.h"
 #include "ntdll_misc.h"
 #include "ddk/wdm.h"
 #include "wine/exception.h"
@@ -54,7 +55,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(thread);
 #define PTHREAD_STACK_MIN 16384
 #endif
 
-struct _KUSER_SHARED_DATA *user_shared_data = NULL;
+static struct _KUSER_SHARED_DATA user_shared_data_internal;
+struct _KUSER_SHARED_DATA *user_shared_data_external;
+struct _KUSER_SHARED_DATA *user_shared_data = &user_shared_data_internal;
 static const WCHAR default_windirW[] = {'C',':','\\','w','i','n','d','o','w','s',0};
 
 void (WINAPI *kernel32_start_process)(LPTHREAD_START_ROUTINE,void*) = NULL;
@@ -263,7 +266,7 @@ void thread_init(void)
         MESSAGE( "wine: failed to map the shared user data: %08x\n", status );
         exit(1);
     }
-    user_shared_data = addr;
+	user_shared_data_external = addr;
     memcpy( user_shared_data->NtSystemRoot, default_windirW, sizeof(default_windirW) );
 
     /* allocate and initialize the PEB */
@@ -355,18 +358,71 @@ void thread_init(void)
  */
 BYTE* CDECL __wine_user_shared_data(void)
 {
+    static int spinlock;
+    ULARGE_INTEGER interrupt;
     LARGE_INTEGER now;
+    
+    while (interlocked_cmpxchg( &spinlock, 1, 0 ) != 0);
+    
     NtQuerySystemTime( &now );
-    user_shared_data->SystemTime.LowPart = now.u.LowPart;
-    user_shared_data->SystemTime.High1Time = user_shared_data->SystemTime.High2Time = now.u.HighPart;
-    user_shared_data->u.TickCountQuad = (now.QuadPart - server_start_time) / 10000;
-    user_shared_data->u.TickCount.High2Time = user_shared_data->u.TickCount.High1Time;
-    user_shared_data->TickCountLowDeprecated = user_shared_data->u.TickCount.LowPart;
+    user_shared_data->SystemTime.High2Time = now.u.HighPart;
+    user_shared_data->SystemTime.LowPart   = now.u.LowPart;
+    user_shared_data->SystemTime.High1Time = now.u.HighPart;
+
+    RtlQueryUnbiasedInterruptTime( &interrupt.QuadPart );
+    user_shared_data->InterruptTime.High2Time = interrupt.HighPart;
+    user_shared_data->InterruptTime.LowPart   = interrupt.LowPart;
+    user_shared_data->InterruptTime.High1Time = interrupt.HighPart;
+
+    interrupt.QuadPart /= 10000;
+    user_shared_data->u.TickCount.High2Time  = interrupt.HighPart;
+    user_shared_data->u.TickCount.LowPart    = interrupt.LowPart;
+    user_shared_data->u.TickCount.High1Time  = interrupt.HighPart;
+    user_shared_data->TickCountLowDeprecated = interrupt.LowPart;
     user_shared_data->TickCountMultiplier = 1 << 24;
+    
+    spinlock = 0;
     return (BYTE *)user_shared_data;
 }
 
 
+static void *user_shared_data_thread(void *arg)
+{
+    struct timeval tv;
+
+    while (TRUE)
+    {
+        __wine_user_shared_data();
+
+        tv.tv_sec = 0;
+        tv.tv_usec = 15600;
+        select(0, NULL, NULL, NULL, &tv);
+    }
+    return NULL;
+}
+
+
+void create_user_shared_data_thread(void)
+{
+    static int thread_created;
+    pthread_attr_t attr;
+    pthread_t thread;
+
+    if (interlocked_cmpxchg(&thread_created, 1, 0) != 0)
+        return;
+
+    FIXME("Creating user shared data update thread.\n");
+
+    user_shared_data = user_shared_data_external;
+    __wine_user_shared_data();
+
+    pthread_attr_init(&attr);
+    pthread_attr_setstacksize(&attr, 0x10000);
+    pthread_create(&thread, &attr, user_shared_data_thread, NULL);
+    pthread_attr_destroy(&attr);
+}
+
+
 /***********************************************************************
  *           free_thread_data
  */
diff --git a/dlls/ntdll/virtual.c b/dlls/ntdll/virtual.c
index da40aab..de63460 100644
--- a/dlls/ntdll/virtual.c
+++ b/dlls/ntdll/virtual.c
@@ -2013,6 +2013,7 @@ NTSTATUS virtual_handle_fault( LPCVOID addr, DWORD err, BOOL on_signal_stack )
 {
     NTSTATUS ret = STATUS_ACCESS_VIOLATION;
     void *page = ROUND_ADDR( addr, page_mask );
+    BOOL update_shared_data = FALSE;
     sigset_t sigset;
     BYTE vprot;
 
@@ -2038,7 +2039,23 @@ NTSTATUS virtual_handle_fault( LPCVOID addr, DWORD err, BOOL on_signal_stack )
                 ret = STATUS_SUCCESS;
         }
     }
+    else if (!err && page == user_shared_data_external)
+    {
+        if (!(vprot & VPROT_READ))
+        {
+            set_page_vprot_bits( page, page_size, VPROT_READ | VPROT_WRITE, 0 );
+            mprotect_range( page, page_size, 0, 0 );
+            update_shared_data = TRUE;
+        }
+        /* ignore fault if page is readable now */
+        if (VIRTUAL_GetUnixProt( get_page_vprot( page )) & PROT_READ) ret = STATUS_SUCCESS;
+        else update_shared_data = FALSE;
+    }
     server_leave_uninterrupted_section( &csVirtual, &sigset );
+
+    if (update_shared_data)
+        create_user_shared_data_thread();
+
     return ret;
 }
 
-- 
1.9.1

