Commit 73c72390 authored by Alexandre Julliard's avatar Alexandre Julliard

Fixed Get/SetThreadContext to work properly on suspended threads.

Added a test case.
parent 9f9fd408
......@@ -607,6 +607,44 @@ static VOID test_GetThreadExitCode(void)
ok(CloseHandle(thread)!=0,"Error closing thread handle\n");
}
#ifdef __i386__
static int test_value = 0;
static void set_test_val( int val )
{
test_value += val;
}
static DWORD WINAPI threadFunc6(LPVOID p)
{
test_value *= (int)p;
return 0;
}
static void test_SetThreadContext(void)
{
CONTEXT ctx;
int *stack;
HANDLE thread = CreateThread( NULL, 0, threadFunc6, (void *)2, CREATE_SUSPENDED, NULL );
ctx.ContextFlags = CONTEXT_FULL;
ok( GetThreadContext( thread, &ctx ), "GetThreadContext failed\n" );
/* simulate a call to set_test_val(10) */
stack = (int *)ctx.Esp;
stack[-1] = 10;
stack[-2] = ctx.Eip;
ctx.Esp -= 2 * sizeof(int *);
ctx.Eip = (DWORD)set_test_val;
ok( SetThreadContext( thread, &ctx ), "SetThreadContext failed\n" );
ResumeThread( thread );
WaitForSingleObject( thread, INFINITE );
ok( test_value == 20, "test_value %d instead of 20\n", test_value );
}
#endif /* __i386__ */
START_TEST(thread)
{
HINSTANCE lib;
......@@ -628,4 +666,7 @@ START_TEST(thread)
test_GetThreadTimes();
test_thread_processor();
test_GetThreadExitCode();
#ifdef __i386__
test_SetThreadContext();
#endif
}
......@@ -143,6 +143,43 @@ extern DWORD EXC_CallHandler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_R
#endif
/**********************************************************************
* wait_suspend
*
* Wait until the thread is no longer suspended.
*/
void wait_suspend( CONTEXT *context )
{
LARGE_INTEGER timeout;
/* store the context we got at suspend time */
SERVER_START_REQ( set_thread_context )
{
req->handle = GetCurrentThread();
req->flags = CONTEXT_FULL;
req->suspend = 1;
wine_server_add_data( req, context, sizeof(*context) );
wine_server_call( req );
}
SERVER_END_REQ;
/* wait with 0 timeout, will only return once the thread is no longer suspended */
timeout.QuadPart = 0;
NTDLL_wait_for_multiple_objects( 0, NULL, 0, &timeout, 0 );
/* retrieve the new context */
SERVER_START_REQ( get_thread_context )
{
req->handle = GetCurrentThread();
req->flags = CONTEXT_FULL;
req->suspend = 1;
wine_server_set_reply( req, context, sizeof(*context) );
wine_server_call( req );
}
SERVER_END_REQ;
}
/**********************************************************************
* send_debug_event
*
* Send an EXCEPTION_DEBUG_EVENT event to the debugger.
......
......@@ -30,6 +30,8 @@
#define MAX_NT_PATH_LENGTH 277
/* exceptions */
extern void wait_suspend( CONTEXT *context );
extern void WINAPI __regs_RtlRaiseException( PEXCEPTION_RECORD, PCONTEXT );
/* debug helper */
......
......@@ -85,7 +85,7 @@ typedef struct
unsigned long i387;
unsigned long oldmask;
unsigned long cr2;
} SIGCONTEXT;
} volatile SIGCONTEXT;
#define HANDLER_DEF(name) void name( int __signal, SIGCONTEXT __context )
#define HANDLER_CONTEXT (&__context)
......@@ -641,12 +641,24 @@ typedef void (WINAPI *raise_func)( EXCEPTION_RECORD *rec, CONTEXT *context );
*
* Handler initialization when the full context is not needed.
*/
static void *init_handler( const SIGCONTEXT *sigcontext )
inline static void *init_handler( const SIGCONTEXT *sigcontext, WORD *fs, WORD *gs )
{
void *stack = (void *)ESP_sig(sigcontext);
TEB *teb = get_current_teb();
struct ntdll_thread_data *thread_data = (struct ntdll_thread_data *)teb->SystemReserved2;
/* get %fs and %gs at time of the fault */
#ifdef FS_sig
*fs = LOWORD(FS_sig(sigcontext));
#else
*fs = wine_get_fs();
#endif
#ifdef GS_sig
*gs = LOWORD(GS_sig(sigcontext));
#else
*gs = wine_get_gs();
#endif
wine_set_fs( thread_data->teb_sel );
/* now restore a proper %gs for the fault handler */
......@@ -718,6 +730,67 @@ inline static void restore_fpu( CONTEXT *context )
/***********************************************************************
* save_context
*
* Build a context structure from the signal info.
*/
inline static void save_context( CONTEXT *context, const SIGCONTEXT *sigcontext, WORD fs, WORD gs )
{
context->ContextFlags = CONTEXT_FULL;
context->Eax = EAX_sig(sigcontext);
context->Ebx = EBX_sig(sigcontext);
context->Ecx = ECX_sig(sigcontext);
context->Edx = EDX_sig(sigcontext);
context->Esi = ESI_sig(sigcontext);
context->Edi = EDI_sig(sigcontext);
context->Ebp = EBP_sig(sigcontext);
context->EFlags = EFL_sig(sigcontext);
context->Eip = EIP_sig(sigcontext);
context->Esp = ESP_sig(sigcontext);
context->SegCs = LOWORD(CS_sig(sigcontext));
context->SegDs = LOWORD(DS_sig(sigcontext));
context->SegEs = LOWORD(ES_sig(sigcontext));
context->SegFs = fs;
context->SegGs = gs;
context->SegSs = LOWORD(SS_sig(sigcontext));
}
/***********************************************************************
* restore_context
*
* Restore the signal info from the context.
*/
inline static void restore_context( const CONTEXT *context, SIGCONTEXT *sigcontext )
{
EAX_sig(sigcontext) = context->Eax;
EBX_sig(sigcontext) = context->Ebx;
ECX_sig(sigcontext) = context->Ecx;
EDX_sig(sigcontext) = context->Edx;
ESI_sig(sigcontext) = context->Esi;
EDI_sig(sigcontext) = context->Edi;
EBP_sig(sigcontext) = context->Ebp;
EFL_sig(sigcontext) = context->EFlags;
EIP_sig(sigcontext) = context->Eip;
ESP_sig(sigcontext) = context->Esp;
CS_sig(sigcontext) = context->SegCs;
DS_sig(sigcontext) = context->SegDs;
ES_sig(sigcontext) = context->SegEs;
SS_sig(sigcontext) = context->SegSs;
#ifdef GS_sig
GS_sig(sigcontext) = context->SegGs;
#else
wine_set_gs( context->SegGs );
#endif
#ifdef FS_sig
FS_sig(sigcontext) = context->SegFs;
#else
wine_set_fs( context->SegFs );
#endif
}
/***********************************************************************
* setup_exception
*
* Setup a proper stack frame for the raise function, and modify the
......@@ -740,19 +813,7 @@ static EXCEPTION_RECORD *setup_exception( SIGCONTEXT *sigcontext, raise_func fun
WORD fs, gs;
/* get %fs and %gs at time of the fault */
#ifdef FS_sig
fs = LOWORD(FS_sig(sigcontext));
#else
fs = wine_get_fs();
#endif
#ifdef GS_sig
gs = LOWORD(GS_sig(sigcontext));
#else
gs = wine_get_gs();
#endif
stack = init_handler( sigcontext );
stack = init_handler( sigcontext, &fs, &gs );
/* stack sanity checks */
......@@ -792,23 +853,7 @@ static EXCEPTION_RECORD *setup_exception( SIGCONTEXT *sigcontext, raise_func fun
stack->rec.ExceptionAddress = (LPVOID)EIP_sig(sigcontext);
stack->rec.NumberParameters = 0;
stack->context.ContextFlags = CONTEXT_FULL;
stack->context.Eax = EAX_sig(sigcontext);
stack->context.Ebx = EBX_sig(sigcontext);
stack->context.Ecx = ECX_sig(sigcontext);
stack->context.Edx = EDX_sig(sigcontext);
stack->context.Esi = ESI_sig(sigcontext);
stack->context.Edi = EDI_sig(sigcontext);
stack->context.Ebp = EBP_sig(sigcontext);
stack->context.EFlags = EFL_sig(sigcontext);
stack->context.Eip = EIP_sig(sigcontext);
stack->context.Esp = ESP_sig(sigcontext);
stack->context.SegCs = LOWORD(CS_sig(sigcontext));
stack->context.SegDs = LOWORD(DS_sig(sigcontext));
stack->context.SegEs = LOWORD(ES_sig(sigcontext));
stack->context.SegFs = fs;
stack->context.SegGs = gs;
stack->context.SegSs = LOWORD(SS_sig(sigcontext));
save_context( &stack->context, sigcontext, fs, gs );
/* now modify the sigcontext to return to the raise function */
ESP_sig(sigcontext) = (DWORD)stack;
......@@ -1108,10 +1153,13 @@ static HANDLER_DEF(fpe_handler)
* int_handler
*
* Handler for SIGINT.
*
* FIXME: should not be calling external functions on the signal stack.
*/
static HANDLER_DEF(int_handler)
{
init_handler( HANDLER_CONTEXT );
WORD fs, gs;
init_handler( HANDLER_CONTEXT, &fs, &gs );
if (!dispatch_signal(SIGINT))
{
EXCEPTION_RECORD *rec = setup_exception( HANDLER_CONTEXT, __regs_RtlRaiseException );
......@@ -1139,7 +1187,8 @@ static HANDLER_DEF(abrt_handler)
*/
static HANDLER_DEF(term_handler)
{
init_handler( HANDLER_CONTEXT );
WORD fs, gs;
init_handler( HANDLER_CONTEXT, &fs, &gs );
server_abort_thread(0);
}
......@@ -1151,12 +1200,13 @@ static HANDLER_DEF(term_handler)
*/
static HANDLER_DEF(usr1_handler)
{
LARGE_INTEGER timeout;
WORD fs, gs;
CONTEXT context;
init_handler( HANDLER_CONTEXT );
/* wait with 0 timeout, will only return once the thread is no longer suspended */
timeout.QuadPart = 0;
NTDLL_wait_for_multiple_objects( 0, NULL, 0, &timeout, 0 );
init_handler( HANDLER_CONTEXT, &fs, &gs );
save_context( &context, HANDLER_CONTEXT, fs, gs );
wait_suspend( &context );
restore_context( &context, HANDLER_CONTEXT );
}
......@@ -1198,6 +1248,7 @@ static int set_handler( int sig, int have_sigaltstack, void (*func)() )
sig_act.ksa_handler = func;
sig_act.ksa_flags = SA_RESTART;
sig_act.ksa_mask = (1 << (SIGINT-1)) |
(1 << (SIGUSR1-1)) |
(1 << (SIGUSR2-1));
/* point to the top of the signal stack */
sig_act.ksa_restorer = (char *)get_signal_stack() + signal_stack_size;
......@@ -1207,6 +1258,7 @@ static int set_handler( int sig, int have_sigaltstack, void (*func)() )
sig_act.sa_handler = func;
sigemptyset( &sig_act.sa_mask );
sigaddset( &sig_act.sa_mask, SIGINT );
sigaddset( &sig_act.sa_mask, SIGUSR1 );
sigaddset( &sig_act.sa_mask, SIGUSR2 );
#if defined(linux) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
......
......@@ -577,11 +577,11 @@ static HANDLER_DEF(term_handler)
*/
static HANDLER_DEF(usr1_handler)
{
LARGE_INTEGER timeout;
CONTEXT context;
/* wait with 0 timeout, will only return once the thread is no longer suspended */
timeout.QuadPart = 0;
NTDLL_wait_for_multiple_objects( 0, NULL, 0, &timeout, 0 );
save_context( &context, HANDLER_CONTEXT );
wait_suspend( &context );
restore_context( &context, HANDLER_CONTEXT );
}
......
......@@ -384,11 +384,11 @@ static HANDLER_DEF(term_handler)
*/
static HANDLER_DEF(usr1_handler)
{
LARGE_INTEGER timeout;
CONTEXT context;
/* wait with 0 timeout, will only return once the thread is no longer suspended */
timeout.QuadPart = 0;
NTDLL_wait_for_multiple_objects( 0, NULL, 0, &timeout, 0 );
save_context( &context, HANDLER_CONTEXT );
wait_suspend( &context );
restore_context( &context, HANDLER_CONTEXT );
}
......
......@@ -408,11 +408,11 @@ static HANDLER_DEF(term_handler)
*/
static HANDLER_DEF(usr1_handler)
{
LARGE_INTEGER timeout;
CONTEXT context;
/* wait with 0 timeout, will only return once the thread is no longer suspended */
timeout.QuadPart = 0;
NTDLL_wait_for_multiple_objects( 0, NULL, 0, &timeout, 0 );
save_context( &context, HANDLER_CONTEXT );
wait_suspend( &context );
restore_context( &context, HANDLER_CONTEXT );
}
......
......@@ -485,15 +485,41 @@ NTSTATUS WINAPI NtQueueApcThread( HANDLE handle, PNTAPCFUNC func, ULONG_PTR arg1
NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context )
{
NTSTATUS ret;
DWORD dummy, i;
SERVER_START_REQ( set_thread_context )
{
req->handle = handle;
req->flags = context->ContextFlags;
req->handle = handle;
req->flags = context->ContextFlags;
req->suspend = 0;
wine_server_add_data( req, context, sizeof(*context) );
ret = wine_server_call( req );
}
SERVER_END_REQ;
if (ret == STATUS_PENDING)
{
if (NtSuspendThread( handle, &dummy ) == STATUS_SUCCESS)
{
for (i = 0; i < 100; i++)
{
SERVER_START_REQ( set_thread_context )
{
req->handle = handle;
req->flags = context->ContextFlags;
req->suspend = 0;
wine_server_add_data( req, context, sizeof(*context) );
ret = wine_server_call( req );
}
SERVER_END_REQ;
if (ret != STATUS_PENDING) break;
NtYieldExecution();
}
NtResumeThread( handle, &dummy );
}
}
if (ret == STATUS_PENDING) ret = STATUS_ACCESS_DENIED;
return ret;
}
......@@ -731,16 +757,42 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context )
{
NTSTATUS ret;
CONTEXT ctx;
DWORD dummy, i;
SERVER_START_REQ( get_thread_context )
{
req->handle = handle;
req->flags = context->ContextFlags;
req->handle = handle;
req->flags = context->ContextFlags;
req->suspend = 0;
wine_server_set_reply( req, &ctx, sizeof(ctx) );
ret = wine_server_call( req );
}
SERVER_END_REQ;
if (ret == STATUS_PENDING)
{
if (NtSuspendThread( handle, &dummy ) == STATUS_SUCCESS)
{
for (i = 0; i < 100; i++)
{
SERVER_START_REQ( get_thread_context )
{
req->handle = handle;
req->flags = context->ContextFlags;
req->suspend = 0;
wine_server_set_reply( req, &ctx, sizeof(ctx) );
ret = wine_server_call( req );
}
SERVER_END_REQ;
if (ret != STATUS_PENDING) break;
NtYieldExecution();
}
NtResumeThread( handle, &dummy );
}
}
if (ret == STATUS_SUCCESS) copy_context( context, &ctx, context->ContextFlags );
else if (ret == STATUS_PENDING) ret = STATUS_ACCESS_DENIED;
return ret;
}
......
......@@ -1572,7 +1572,6 @@ struct get_exception_status_request
struct get_exception_status_reply
{
struct reply_header __header;
int status;
/* VARARG(context,context); */
};
......@@ -1952,6 +1951,7 @@ struct get_thread_context_request
struct request_header __header;
obj_handle_t handle;
unsigned int flags;
int suspend;
};
struct get_thread_context_reply
{
......@@ -1966,6 +1966,7 @@ struct set_thread_context_request
struct request_header __header;
obj_handle_t handle;
unsigned int flags;
int suspend;
/* VARARG(context,context); */
};
struct set_thread_context_reply
......@@ -4207,6 +4208,6 @@ union generic_reply
struct set_mailslot_info_reply set_mailslot_info_reply;
};
#define SERVER_PROTOCOL_VERSION 196
#define SERVER_PROTOCOL_VERSION 197
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */
......@@ -1401,6 +1401,7 @@ enum char_info_mode
@REQ(get_thread_context)
obj_handle_t handle; /* thread handle */
unsigned int flags; /* context flags */
int suspend; /* if getting context during suspend */
@REPLY
VARARG(context,context); /* thread context */
@END
......@@ -1410,6 +1411,7 @@ enum char_info_mode
@REQ(set_thread_context)
obj_handle_t handle; /* thread handle */
unsigned int flags; /* context flags */
int suspend; /* if setting context during suspend */
VARARG(context,context); /* thread context */
@END
......
......@@ -118,6 +118,7 @@ inline static void init_thread_structure( struct thread *thread )
thread->unix_pid = -1; /* not known yet */
thread->unix_tid = -1; /* not known yet */
thread->context = NULL;
thread->suspend_context = NULL;
thread->teb = NULL;
thread->debug_ctx = NULL;
thread->debug_event = NULL;
......@@ -212,6 +213,7 @@ static void cleanup_thread( struct thread *thread )
if (thread->request_fd) release_object( thread->request_fd );
if (thread->reply_fd) release_object( thread->reply_fd );
if (thread->wait_fd) release_object( thread->wait_fd );
if (thread->suspend_context) free( thread->suspend_context );
free_msg_queue( thread );
cleanup_clipboard_thread(thread);
destroy_thread_windows( thread );
......@@ -229,19 +231,19 @@ static void cleanup_thread( struct thread *thread )
thread->request_fd = NULL;
thread->reply_fd = NULL;
thread->wait_fd = NULL;
thread->context = NULL;
thread->suspend_context = NULL;
thread->desktop = 0;
}
/* destroy a thread when its refcount is 0 */
static void destroy_thread( struct object *obj )
{
struct thread_apc *apc;
struct thread *thread = (struct thread *)obj;
assert( obj->ops == &thread_ops );
assert( !thread->debug_ctx ); /* cannot still be debugging something */
list_remove( &thread->entry );
while ((apc = thread_dequeue_apc( thread, 0 ))) free( apc );
cleanup_thread( thread );
release_object( thread->process );
if (thread->id) free_ptid( thread->id );
......@@ -1053,7 +1055,27 @@ DECL_HANDLER(get_thread_context)
}
if (!(thread = get_thread_from_handle( req->handle, THREAD_GET_CONTEXT ))) return;
if ((data = set_reply_data_size( sizeof(CONTEXT) )))
if (req->suspend)
{
if (thread != current || !thread->suspend_context)
{
/* not suspended, shouldn't happen */
set_error( STATUS_INVALID_PARAMETER );
}
else
{
if (thread->context == thread->suspend_context) thread->context = NULL;
set_reply_data_ptr( thread->suspend_context, sizeof(CONTEXT) );
thread->suspend_context = NULL;
}
}
else if (thread != current && !thread->context)
{
/* thread is not suspended, retry (if it's still running) */
if (thread->state != RUNNING) set_error( STATUS_ACCESS_DENIED );
else set_error( STATUS_PENDING );
}
else if ((data = set_reply_data_size( sizeof(CONTEXT) )))
{
memset( data, 0, sizeof(CONTEXT) );
get_thread_context( thread, data, req->flags );
......@@ -1071,11 +1093,32 @@ DECL_HANDLER(set_thread_context)
set_error( STATUS_INVALID_PARAMETER );
return;
}
if ((thread = get_thread_from_handle( req->handle, THREAD_SET_CONTEXT )))
if (!(thread = get_thread_from_handle( req->handle, THREAD_SET_CONTEXT ))) return;
if (req->suspend)
{
if (thread != current || thread->context)
{
/* nested suspend or exception, shouldn't happen */
set_error( STATUS_INVALID_PARAMETER );
}
else if ((thread->suspend_context = mem_alloc( sizeof(CONTEXT) )))
{
memcpy( thread->suspend_context, get_req_data(), sizeof(CONTEXT) );
thread->context = thread->suspend_context;
}
}
else if (thread != current && !thread->context)
{
/* thread is not suspended, retry (if it's still running) */
if (thread->state != RUNNING) set_error( STATUS_ACCESS_DENIED );
else set_error( STATUS_PENDING );
}
else
{
set_thread_context( thread, get_req_data(), req->flags );
release_object( thread );
}
release_object( thread );
}
/* fetch a selector entry for a thread */
......
......@@ -78,6 +78,7 @@ struct thread
int unix_pid; /* Unix pid of client */
int unix_tid; /* Unix tid of client */
CONTEXT *context; /* current context if in an exception handler */
CONTEXT *suspend_context; /* current context if suspended */
void *teb; /* TEB address (in client address space) */
int priority; /* priority level */
int affinity; /* affinity mask */
......
......@@ -1577,7 +1577,6 @@ static void dump_get_exception_status_request( const struct get_exception_status
static void dump_get_exception_status_reply( const struct get_exception_status_reply *req )
{
fprintf( stderr, " status=%d,", req->status );
fprintf( stderr, " context=" );
dump_varargs_context( cur_size );
}
......@@ -1856,7 +1855,8 @@ static void dump_get_timer_info_reply( const struct get_timer_info_reply *req )
static void dump_get_thread_context_request( const struct get_thread_context_request *req )
{
fprintf( stderr, " handle=%p,", req->handle );
fprintf( stderr, " flags=%08x", req->flags );
fprintf( stderr, " flags=%08x,", req->flags );
fprintf( stderr, " suspend=%d", req->suspend );
}
static void dump_get_thread_context_reply( const struct get_thread_context_reply *req )
......@@ -1869,6 +1869,7 @@ static void dump_set_thread_context_request( const struct set_thread_context_req
{
fprintf( stderr, " handle=%p,", req->handle );
fprintf( stderr, " flags=%08x,", req->flags );
fprintf( stderr, " suspend=%d,", req->suspend );
fprintf( stderr, " context=" );
dump_varargs_context( cur_size );
}
......@@ -3740,6 +3741,7 @@ static const struct
{ "FILE_IS_A_DIRECTORY", STATUS_FILE_IS_A_DIRECTORY },
{ "FILE_LOCK_CONFLICT", STATUS_FILE_LOCK_CONFLICT },
{ "HANDLE_NOT_CLOSABLE", STATUS_HANDLE_NOT_CLOSABLE },
{ "INSTANCE_NOT_AVAILABLE", STATUS_INSTANCE_NOT_AVAILABLE },
{ "INVALID_CID", STATUS_INVALID_CID },
{ "INVALID_FILE_FOR_SECTION", STATUS_INVALID_FILE_FOR_SECTION },
{ "INVALID_HANDLE", STATUS_INVALID_HANDLE },
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment