SafeDisc-1 semi-cleaned note...

Laurent Pinchart laurent.pinchart at skynet.be
Mon May 27 17:36:52 CDT 2002


> i hope i am not asking to much by this, but is there
> any chance you could send me a copy of the diff's with
> some fixme comments so I know exactly which things
> need to be cleaned up?  I know that you said the
> server suspension code needs to be checked, and the
> 0x7ffe0000 page needs to be properly implemented, but
> im not sure what all would need to be changed to get
> this done.

Ok, here's a first version of safedisc.safe.diff with comments. I don't think 
it's usefull to comment safedisc.dmca.diff for now, but if you need some 
information about it let me know.

--- memory/emulate.c	Thu Jan  1 01:00:00 1970
+++ memory/emulate.c	Thu May  9 17:35:27 2002
@@ -0,0 +1,53 @@
+/*
+ * Emulation of privileged memory
+ *
+ * Copyright 2002 Laurent Pinchart
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "windef.h"
+#include "wingdi.h"
+#include "wine/winuser16.h"
+#include "module.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(virtual);
+
+#ifdef __i386__
+
+/***********************************************************************
+ *           MEMORY_SharedUserData
+ *
+ * Handles exceptions for the SharedUserData access.
+ */
+BOOL MEMORY_SharedUserData( LPVOID arg, LPCVOID addr )
+{
+    DWORD dwProtection;
+    BOOL ret;
+
+    TRACE( "MEMORY_SharedUserData\n" );
+
+    ret = VirtualProtect( (LPVOID)0x7ffe0000, 0x10000, PAGE_READWRITE, 
&dwProtection );
+    if ( ret )
+    {
+	*(LPDWORD)0x7ffe0000 = 0x12345678;
+	ret = VirtualProtect( (LPVOID)0x7ffe0000, 0x10000, PAGE_READONLY, 
&dwProtection );
+    }
+
+    return ret;
+}
+
+#endif  /* __i386__ */

This is the emulation of "SharedUserData". SharedUserData is a page of memory 
located right before the kernel space (0x80000000), so at address 0x7ffe0000. 
It contains miscelaneous information, and is read-only in user space and 
read-write in kernel space. You can find more information about it in ntddk.h 
(not included in wine).

We need 3 things here:
1) Write support for the other fields, not only TickCount (kernel timer). This 
is optional but quite easy for some fields.
2) Find a way to either remove the read rights after the read access, so that 
the next read access would trigger an exception again, and the information 
could be updated (this is IMHO *very* difficult), or periodically update the 
information contained in that page (mostly TickCount). The later should be 
easier, but implies a slow down. If would be nice to trigger that periodical 
update only after the first read of TickCount, so that programs which don't 
read it wouldn't suffer from that slow down. We could also remove the read 
rights after some time (instead of right after the read operation). That 
might be the best way, as it wouldn't imply a slow down (not periodical 
update).
3) Replace 0x12345678 with a correct value. Don't forget that secdrv.c needs 
to know that value (or at least a close enough value), so we need a portable 
and accurate way to set and retrieve it from inside wine.

Index: dlls/ntdll/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/Makefile.in,v
retrieving revision 1.27
diff -u -r1.27 Makefile.in
--- dlls/ntdll/Makefile.in	14 May 2002 20:55:01 -0000	1.27
+++ dlls/ntdll/Makefile.in	23 May 2002 18:54:33 -0000
@@ -32,6 +32,7 @@
 	$(TOPOBJDIR)/loader/ne/segment.c \
 	$(TOPOBJDIR)/memory/atom.c \
 	$(TOPOBJDIR)/memory/codepage.c \
+	$(TOPOBJDIR)/memory/emulate.c \
 	$(TOPOBJDIR)/memory/environ.c \
 	$(TOPOBJDIR)/memory/global.c \
 	$(TOPOBJDIR)/memory/heap.c \

When emulate.c will be applied this will be too :-)

Index: dlls/ntdll/signal_i386.c
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/signal_i386.c,v
retrieving revision 1.35
diff -u -r1.35 signal_i386.c
--- dlls/ntdll/signal_i386.c	16 May 2002 20:32:16 -0000	1.35
+++ dlls/ntdll/signal_i386.c	23 May 2002 18:54:40 -0000
@@ -679,6 +679,7 @@
 {
     EXCEPTION_RECORD rec;
     DWORD page_fault_code = EXCEPTION_ACCESS_VIOLATION;
+    DWORD priv_instr_code = EXCEPTION_PRIV_INSTRUCTION;
 
 #ifdef CR2_sig
     /* we want the page-fault case to be fast */
@@ -708,8 +709,8 @@
     case T_SEGNPFLT:  /* Segment not present exception */
     case T_PROTFLT:   /* General protection fault */
     case T_UNKNOWN:   /* Unknown fault code */
-        if (INSTR_EmulateInstruction( context )) return;
-        rec.ExceptionCode = EXCEPTION_PRIV_INSTRUCTION;
+        if (INSTR_EmulateInstruction( context, &priv_instr_code )) return;
+        rec.ExceptionCode = priv_instr_code;
         break;
     case T_PAGEFLT:  /* Page fault */
 #ifdef CR2_sig
Index: include/miscemu.h
===================================================================
RCS file: /home/wine/wine/include/miscemu.h,v
retrieving revision 1.46
diff -u -r1.46 miscemu.h
--- include/miscemu.h	10 Mar 2002 00:02:34 -0000	1.46
+++ include/miscemu.h	23 May 2002 18:54:49 -0000
@@ -167,7 +167,7 @@
 extern UINT DOSMEM_MapLinearToDos(LPVOID); /* linear Wine to DOS */
 
 /* memory/instr.c */
-extern BOOL INSTR_EmulateInstruction( CONTEXT86 *context );
+extern BOOL INSTR_EmulateInstruction( CONTEXT86 *context, DWORD 
*exception_code );
 
 /* msdos/interrupts.c */
 typedef void (WINAPI *INTPROC)(CONTEXT86*);
Index: memory/instr.c
===================================================================
RCS file: /home/wine/wine/memory/instr.c,v
retrieving revision 1.15
diff -u -r1.15 instr.c
--- memory/instr.c	11 May 2002 23:01:12 -0000	1.15
+++ memory/instr.c	23 May 2002 18:54:58 -0000
@@ -396,7 +396,7 @@
  *
  * Emulate a privileged instruction. Returns TRUE if emulation successful.
  */
-BOOL INSTR_EmulateInstruction( CONTEXT86 *context )
+BOOL INSTR_EmulateInstruction( CONTEXT86 *context, DWORD *exception_code )
 {
     int prefix, segprefix, prefixlen, len, repX, long_op, long_addr;
     SEGPTR gpHandler;
@@ -686,7 +686,9 @@
         case 0xcd: /* int <XX> */
             if (long_op)
             {
-                ERR("int xx from 32-bit code is not supported.\n");
+                ERR("int 0x%02x from 32-bit code is not supported.\n", 
instr[1]);
+                if (instr[1] == 0x01)
+                    *exception_code = EXCEPTION_ACCESS_VIOLATION;
                 break;  /* Unable to emulate it */
             }
             else

This probably hasn't been applied because it lacks testing. It seems that some 
instructions which are emulated by wine (int 0x01 is one of them) should 
generate an EXCEPTION_PRIV_INSTRUCTION instead of EXCEPTION_ACCESS_VIOLATION. 
Or maybe my code is not clean enough to commit. I'd like to hear comments 
from Alexandre about this specific part of the patch.

Index: include/wine/server_protocol.h
===================================================================
RCS file: /home/wine/wine/include/wine/server_protocol.h,v
retrieving revision 1.35
diff -u -r1.35 server_protocol.h
--- include/wine/server_protocol.h	25 Apr 2002 22:58:59 -0000	1.35
+++ include/wine/server_protocol.h	23 May 2002 18:54:56 -0000
@@ -296,6 +296,7 @@
 struct init_process_done_reply
 {
     struct reply_header __header;
+    int          suspended;
     int          debugged;
 };
 
@@ -3198,6 +3199,6 @@
     struct get_window_properties_reply get_window_properties_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 79
+#define SERVER_PROTOCOL_VERSION 80
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
Index: scheduler/process.c
===================================================================
RCS file: /home/wine/wine/scheduler/process.c,v
retrieving revision 1.182
diff -u -r1.182 process.c
--- scheduler/process.c	22 May 2002 21:32:50 -0000	1.182
+++ scheduler/process.c	23 May 2002 18:55:04 -0000
@@ -25,6 +25,7 @@
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <signal.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -43,6 +44,7 @@
 #include "wine/server.h"
 #include "options.h"
 #include "wine/debug.h"
+#include "global.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(process);
 WINE_DECLARE_DEBUG_CHANNEL(relay);
@@ -126,6 +128,9 @@
 /* scheduler/pthread.c */
 extern void PTHREAD_init_done(void);
 
+/* memory/emulate.c */
+extern BOOL MEMORY_SharedUserData(LPVOID, LPCVOID);
+
 extern BOOL MAIN_MainInit(void);
 
 typedef WORD (WINAPI *pUserSignalProc)( UINT, DWORD, DWORD, HMODULE16 );
@@ -416,6 +421,20 @@
 	SetStdHandle( STD_ERROR_HANDLE,  current_startupinfo.hStdError  );
     }
 
+    /* Reserves a PAGE_NOACCESS at 0x7ffe0000. That page is used as shared
+     * memory between kernel space and user space (see SharedUserData in
+     * ntddk.h). It looks like it is at 0xffdf0000 on NT5. That is still to
+     * be checked.
+     */
+    if ( VirtualAlloc( (LPVOID)0x7ffe0000, 0x10000, MEM_RESERVE|MEM_COMMIT, 
PAGE_NOACCESS ) == (LPCVOID)0x07ffe0000 )
+    {
+	VIRTUAL_SetFaultHandler( (LPCVOID)0x7ffe0000, MEMORY_SharedUserData, 0 );
+    }
+    else
+    {
+	WARN( "Unable to map SharedUserData, SafeDisc protection won't be 
supported\n" );
+    }
+
     /* Now we can use the pthreads routines */
     PTHREAD_init_done();
 
@@ -440,7 +459,7 @@
  */
 static void start_process(void)
 {
-    int debugged, console_app;
+    int suspended, debugged, console_app;
     LPTHREAD_START_ROUTINE entry;
     WINE_MODREF *wm;
     HFILE main_file = main_exe_file;
@@ -481,7 +500,8 @@
         req->gui      = !console_app;
         wine_server_add_data( req, main_exe_name, strlen(main_exe_name) );
         wine_server_call( req );
-        debugged = reply->debugged;
+        suspended = reply->suspended;
+        debugged  = reply->debugged;
     }
     SERVER_END_REQ;
 
@@ -513,6 +533,8 @@
     PROCESS_CallUserSignalProc( USIG_PROCESS_LOADED, 0 );
     /* Call UserSignalProc ( USIG_PROCESS_RUNNING ... ) only for non-GUI 
win32 apps */
     if (console_app) PROCESS_CallUserSignalProc( USIG_PROCESS_RUNNING, 0 );
+
+    if (suspended) raise( SIGSTOP );
 
     if (TRACE_ON(relay))
         DPRINTF( "%08lx:Starting process %s (entryproc=%p)\n",
Index: server/process.c
===================================================================
RCS file: /home/wine/wine/server/process.c,v
retrieving revision 1.83
diff -u -r1.83 process.c
--- server/process.c	26 Apr 2002 19:05:18 -0000	1.83
+++ server/process.c	23 May 2002 18:55:07 -0000
@@ -382,6 +382,14 @@
 }
 
 
+/* signal the parent that a process init is finished */
+void process_init_done( struct process *process )
+{
+    set_event( process->init_event );
+    release_object( process->init_event );
+    process->init_event = NULL;
+}
+
 /* get a process from an id (and increment the refcount) */
 struct process *get_process_from_id( void *id )
 {
@@ -896,13 +904,27 @@
     if ((process->exe.namelen = get_req_data_size()))
         process->exe.filename = memdup( get_req_data(), process->exe.namelen 
);
 
-    generate_startup_debug_events( current->process, req->entry );
-    set_event( process->init_event );
-    release_object( process->init_event );
-    process->init_event = NULL;
+    generate_startup_debug_events( process, req->entry );
     if (req->gui) process->idle_event = create_event( NULL, 0, 1, 0 );
-    if (current->suspend + current->process->suspend > 0) stop_thread( 
current );
-    reply->debugged = (current->process->debugger != 0);
+
+    reply->suspended = 0;
+    reply->debugged  = (current->process->debugger != 0);
+    if (current->suspend + current->process->suspend > 0)
+    {
+        /* temporarily clear the init event so that ptrace functions work */
+        struct event *init_event = process->init_event;
+        process->init_event = NULL;
+        stop_thread( current );
+        process->init_event = init_event;
+
+        if (current->attached)
+        {
+            reply->suspended = 1;
+            continue_thread( current );  /* it will suspend itself */
+            return;
+        }
+    }
+    process_init_done( process );
 }
 
 /* open a handle to a process */
Index: server/process.h
===================================================================
RCS file: /home/wine/wine/server/process.h,v
retrieving revision 1.27
diff -u -r1.27 process.h
--- server/process.h	22 Mar 2002 00:21:23 -0000	1.27
+++ server/process.h	23 May 2002 18:55:07 -0000
@@ -90,6 +90,7 @@
 extern struct thread *create_process( int fd );
 extern struct process *get_process_from_id( void *id );
 extern struct process *get_process_from_handle( handle_t handle, unsigned int 
access );
+extern void process_init_done( struct process *process );
 extern int process_set_debugger( struct process *process, struct thread 
*thread );
 extern int debugger_detach( struct process* process, struct thread* debugger 
);
 
Index: server/protocol.def
===================================================================
RCS file: /home/wine/wine/server/protocol.def,v
retrieving revision 1.36
diff -u -r1.36 protocol.def
--- server/protocol.def	25 Apr 2002 22:58:59 -0000	1.36
+++ server/protocol.def	23 May 2002 18:55:12 -0000
@@ -276,6 +276,7 @@
     int          gui;          /* is it a GUI process? */
     VARARG(filename,string);   /* file name of main exe */
 @REPLY
+    int          suspended;    /* created suspended? */
     int          debugged;     /* being debugged? */
 @END
 
Index: server/ptrace.c
===================================================================
RCS file: /home/wine/wine/server/ptrace.c,v
retrieving revision 1.14
diff -u -r1.14 ptrace.c
--- server/ptrace.c	10 Mar 2002 00:18:36 -0000	1.14
+++ server/ptrace.c	23 May 2002 18:55:13 -0000
@@ -79,7 +79,12 @@
         switch(sig)
         {
         case SIGSTOP:  /* continue at once if not suspended */
-            if (thread && (thread->process->suspend + thread->suspend)) 
break;
+            if (thread && (thread->process->suspend + thread->suspend))
+            {
+                /* check if this was the self-suspend upon process init */
+                if (thread->process->init_event) process_init_done( 
thread->process );
+                break;
+            }
             /* fall through */
         default:  /* ignore other signals for now */
             if (thread && get_thread_single_step( thread ))
Index: server/trace.c
===================================================================
RCS file: /home/wine/wine/server/trace.c,v
retrieving revision 1.132
diff -u -r1.132 trace.c
--- server/trace.c	26 Apr 2002 19:05:18 -0000	1.132
+++ server/trace.c	23 May 2002 18:55:20 -0000
@@ -441,6 +441,7 @@
 
 static void dump_init_process_done_reply( const struct 
init_process_done_reply *req )
 {
+    fprintf( stderr, " suspended=%d,", req->suspended );
     fprintf( stderr, " debugged=%d", req->debugged );
 }
 

There are two patches in there. The first one sets a fault handler for the 
SharedUserData. This looks fine to me, and will probably be applied when 
memory/emulate.c will be applied.

The second one has been written by Alexandre to fix the process suspension at 
creation time. Once again I'd like to hear from him to know what he doesn't 
like about the code, as he's the one who wrote it :-)

Hope this helps.

I'm available if you need more information

Laurent Pinchart




More information about the wine-devel mailing list