ntdll: Avoid inter-process APCs when called for the process itself.

This commit is contained in:
Alexandre Julliard 2007-01-18 15:41:05 +01:00
parent 9d09e699d6
commit f3cb4f7d94
5 changed files with 188 additions and 158 deletions

View File

@ -652,6 +652,151 @@ static int wait_reply( void *cookie )
}
/***********************************************************************
* invoke_apc
*
* Invoke a single APC. Return TRUE if a user APC has been run.
*/
static BOOL invoke_apc( const apc_call_t *call, apc_result_t *result )
{
BOOL user_apc = FALSE;
memset( result, 0, sizeof(*result) );
switch (call->type)
{
case APC_USER:
call->user.func( call->user.args[0], call->user.args[1], call->user.args[2] );
user_apc = TRUE;
break;
case APC_TIMER:
{
LARGE_INTEGER time;
/* convert sec/usec to NT time */
RtlSecondsSince1970ToTime( call->timer.time.sec, &time );
time.QuadPart += call->timer.time.usec * 10;
call->timer.func( call->timer.arg, time.u.LowPart, time.u.HighPart );
user_apc = TRUE;
break;
}
case APC_ASYNC_IO:
NtCurrentTeb()->num_async_io--;
call->async_io.func( call->async_io.user, call->async_io.sb, call->async_io.status );
break;
case APC_VIRTUAL_ALLOC:
result->type = call->type;
result->virtual_alloc.addr = call->virtual_alloc.addr;
result->virtual_alloc.size = call->virtual_alloc.size;
result->virtual_alloc.status = NtAllocateVirtualMemory( NtCurrentProcess(),
&result->virtual_alloc.addr,
call->virtual_alloc.zero_bits,
&result->virtual_alloc.size,
call->virtual_alloc.op_type,
call->virtual_alloc.prot );
break;
case APC_VIRTUAL_FREE:
result->type = call->type;
result->virtual_free.addr = call->virtual_free.addr;
result->virtual_free.size = call->virtual_free.size;
result->virtual_free.status = NtFreeVirtualMemory( NtCurrentProcess(),
&result->virtual_free.addr,
&result->virtual_free.size,
call->virtual_free.op_type );
break;
case APC_VIRTUAL_QUERY:
{
MEMORY_BASIC_INFORMATION info;
result->type = call->type;
result->virtual_query.status = NtQueryVirtualMemory( NtCurrentProcess(),
call->virtual_query.addr,
MemoryBasicInformation, &info,
sizeof(info), NULL );
if (result->virtual_query.status == STATUS_SUCCESS)
{
result->virtual_query.base = info.BaseAddress;
result->virtual_query.alloc_base = info.AllocationBase;
result->virtual_query.size = info.RegionSize;
result->virtual_query.state = info.State;
result->virtual_query.prot = info.Protect;
result->virtual_query.alloc_prot = info.AllocationProtect;
result->virtual_query.alloc_type = info.Type;
}
break;
}
case APC_VIRTUAL_PROTECT:
result->type = call->type;
result->virtual_protect.addr = call->virtual_protect.addr;
result->virtual_protect.size = call->virtual_protect.size;
result->virtual_protect.status = NtProtectVirtualMemory( NtCurrentProcess(),
&result->virtual_protect.addr,
&result->virtual_protect.size,
call->virtual_protect.prot,
&result->virtual_protect.prot );
break;
case APC_VIRTUAL_FLUSH:
result->type = call->type;
result->virtual_flush.addr = call->virtual_flush.addr;
result->virtual_flush.size = call->virtual_flush.size;
result->virtual_flush.status = NtFlushVirtualMemory( NtCurrentProcess(),
&result->virtual_flush.addr,
&result->virtual_flush.size, 0 );
break;
case APC_VIRTUAL_LOCK:
result->type = call->type;
result->virtual_lock.addr = call->virtual_lock.addr;
result->virtual_lock.size = call->virtual_lock.size;
result->virtual_lock.status = NtLockVirtualMemory( NtCurrentProcess(),
&result->virtual_lock.addr,
&result->virtual_lock.size, 0 );
break;
case APC_VIRTUAL_UNLOCK:
result->type = call->type;
result->virtual_unlock.addr = call->virtual_unlock.addr;
result->virtual_unlock.size = call->virtual_unlock.size;
result->virtual_unlock.status = NtUnlockVirtualMemory( NtCurrentProcess(),
&result->virtual_unlock.addr,
&result->virtual_unlock.size, 0 );
break;
case APC_MAP_VIEW:
{
LARGE_INTEGER offset;
result->type = call->type;
result->map_view.addr = call->map_view.addr;
result->map_view.size = call->map_view.size;
offset.u.LowPart = call->map_view.offset_low;
offset.u.HighPart = call->map_view.offset_high;
result->map_view.status = NtMapViewOfSection( call->map_view.handle, NtCurrentProcess(),
&result->map_view.addr, call->map_view.zero_bits,
0, &offset, &result->map_view.size, ViewShare,
call->map_view.alloc_type, call->map_view.prot );
NtClose( call->map_view.handle );
break;
}
case APC_UNMAP_VIEW:
result->type = call->type;
result->unmap_view.status = NtUnmapViewOfSection( NtCurrentProcess(), call->unmap_view.addr );
break;
case APC_CREATE_THREAD:
{
CLIENT_ID id;
result->type = call->type;
result->create_thread.status = RtlCreateUserThread( NtCurrentProcess(), NULL,
call->create_thread.suspend, NULL,
call->create_thread.reserve,
call->create_thread.commit,
call->create_thread.func,
call->create_thread.arg,
&result->create_thread.handle, &id );
result->create_thread.tid = (thread_id_t)id.UniqueThread;
break;
}
default:
server_protocol_error( "get_apc_request: bad type %d\n", call->type );
break;
}
return user_apc;
}
/***********************************************************************
* call_apcs
*
@ -684,140 +829,7 @@ static BOOL call_apcs( BOOL alertable )
if (ret) return user_apc; /* no more APCs */
memset( &result, 0, sizeof(result) );
switch (call.type)
{
case APC_USER:
call.user.func( call.user.args[0], call.user.args[1], call.user.args[2] );
user_apc = TRUE;
break;
case APC_TIMER:
{
LARGE_INTEGER time;
/* convert sec/usec to NT time */
RtlSecondsSince1970ToTime( call.timer.time.sec, &time );
time.QuadPart += call.timer.time.usec * 10;
call.timer.func( call.timer.arg, time.u.LowPart, time.u.HighPart );
user_apc = TRUE;
break;
}
case APC_ASYNC_IO:
NtCurrentTeb()->num_async_io--;
call.async_io.func( call.async_io.user, call.async_io.sb, call.async_io.status );
break;
case APC_VIRTUAL_ALLOC:
result.type = call.type;
result.virtual_alloc.addr = call.virtual_alloc.addr;
result.virtual_alloc.size = call.virtual_alloc.size;
result.virtual_alloc.status = NtAllocateVirtualMemory( NtCurrentProcess(),
&result.virtual_alloc.addr,
call.virtual_alloc.zero_bits,
&result.virtual_alloc.size,
call.virtual_alloc.op_type,
call.virtual_alloc.prot );
break;
case APC_VIRTUAL_FREE:
result.type = call.type;
result.virtual_free.addr = call.virtual_free.addr;
result.virtual_free.size = call.virtual_free.size;
result.virtual_free.status = NtFreeVirtualMemory( NtCurrentProcess(),
&result.virtual_free.addr,
&result.virtual_free.size,
call.virtual_free.op_type );
break;
case APC_VIRTUAL_QUERY:
{
MEMORY_BASIC_INFORMATION info;
result.type = call.type;
result.virtual_query.status = NtQueryVirtualMemory( NtCurrentProcess(),
call.virtual_query.addr,
MemoryBasicInformation, &info,
sizeof(info), NULL );
if (result.virtual_query.status == STATUS_SUCCESS)
{
result.virtual_query.base = info.BaseAddress;
result.virtual_query.alloc_base = info.AllocationBase;
result.virtual_query.size = info.RegionSize;
result.virtual_query.state = info.State;
result.virtual_query.prot = info.Protect;
result.virtual_query.alloc_prot = info.AllocationProtect;
result.virtual_query.alloc_type = info.Type;
}
break;
}
case APC_VIRTUAL_PROTECT:
result.type = call.type;
result.virtual_protect.addr = call.virtual_protect.addr;
result.virtual_protect.size = call.virtual_protect.size;
result.virtual_protect.status = NtProtectVirtualMemory( NtCurrentProcess(),
&result.virtual_protect.addr,
&result.virtual_protect.size,
call.virtual_protect.prot,
&result.virtual_protect.prot );
break;
case APC_VIRTUAL_FLUSH:
result.type = call.type;
result.virtual_flush.addr = call.virtual_flush.addr;
result.virtual_flush.size = call.virtual_flush.size;
result.virtual_flush.status = NtFlushVirtualMemory( NtCurrentProcess(),
&result.virtual_flush.addr,
&result.virtual_flush.size, 0 );
break;
case APC_VIRTUAL_LOCK:
result.type = call.type;
result.virtual_lock.addr = call.virtual_lock.addr;
result.virtual_lock.size = call.virtual_lock.size;
result.virtual_lock.status = NtLockVirtualMemory( NtCurrentProcess(),
&result.virtual_lock.addr,
&result.virtual_lock.size, 0 );
break;
case APC_VIRTUAL_UNLOCK:
result.type = call.type;
result.virtual_unlock.addr = call.virtual_unlock.addr;
result.virtual_unlock.size = call.virtual_unlock.size;
result.virtual_unlock.status = NtUnlockVirtualMemory( NtCurrentProcess(),
&result.virtual_unlock.addr,
&result.virtual_unlock.size, 0 );
break;
case APC_MAP_VIEW:
{
LARGE_INTEGER offset;
result.type = call.type;
result.map_view.addr = call.map_view.addr;
result.map_view.size = call.map_view.size;
offset.u.LowPart = call.map_view.offset_low;
offset.u.HighPart = call.map_view.offset_high;
result.map_view.status = NtMapViewOfSection( call.map_view.handle, NtCurrentProcess(),
&result.map_view.addr, call.map_view.zero_bits,
0, &offset, &result.map_view.size, ViewShare,
call.map_view.alloc_type, call.map_view.prot );
NtClose( call.map_view.handle );
break;
}
case APC_UNMAP_VIEW:
result.type = call.type;
result.unmap_view.status = NtUnmapViewOfSection( NtCurrentProcess(),
call.unmap_view.addr );
break;
case APC_CREATE_THREAD:
{
CLIENT_ID id;
result.type = call.type;
result.create_thread.status = RtlCreateUserThread( NtCurrentProcess(), NULL,
call.create_thread.suspend, NULL,
call.create_thread.reserve,
call.create_thread.commit,
call.create_thread.func,
call.create_thread.arg,
&result.create_thread.handle, &id );
result.create_thread.tid = (thread_id_t)id.UniqueThread;
break;
}
default:
server_protocol_error( "get_apc_request: bad type %d\n", call.type );
break;
}
user_apc = invoke_apc( &call, &result );
}
}
@ -831,28 +843,39 @@ NTSTATUS NTDLL_queue_process_apc( HANDLE process, const apc_call_t *call, apc_re
{
NTSTATUS ret;
HANDLE handle = 0;
BOOL self = FALSE;
SERVER_START_REQ( queue_apc )
{
req->process = process;
req->call = *call;
if (!(ret = wine_server_call( req ))) handle = reply->handle;
if (!(ret = wine_server_call( req )))
{
handle = reply->handle;
self = reply->self;
}
}
SERVER_END_REQ;
if (ret != STATUS_SUCCESS) return ret;
if (!handle) return ret;
NtWaitForSingleObject( handle, FALSE, NULL );
SERVER_START_REQ( get_apc_result )
if (self)
{
req->handle = handle;
if (!(ret = wine_server_call( req ))) *result = reply->result;
invoke_apc( call, result );
}
SERVER_END_REQ;
else
{
NtWaitForSingleObject( handle, FALSE, NULL );
if (!ret && result->type == APC_NONE) continue; /* APC didn't run, try again */
if (ret) NtClose( handle );
SERVER_START_REQ( get_apc_result )
{
req->handle = handle;
if (!(ret = wine_server_call( req ))) *result = reply->result;
}
SERVER_END_REQ;
if (!ret && result->type == APC_NONE) continue; /* APC didn't run, try again */
if (ret) NtClose( handle );
}
return ret;
}
}

View File

@ -714,6 +714,7 @@ struct queue_apc_reply
{
struct reply_header __header;
obj_handle_t handle;
int self;
};
@ -4624,6 +4625,6 @@ union generic_reply
struct query_symlink_reply query_symlink_reply;
};
#define SERVER_PROTOCOL_VERSION 273
#define SERVER_PROTOCOL_VERSION 274
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */

View File

@ -635,6 +635,7 @@ typedef union
apc_call_t call; /* call arguments */
@REPLY
obj_handle_t handle; /* APC handle */
int self; /* run APC in caller itself? */
@END

View File

@ -1172,7 +1172,7 @@ DECL_HANDLER(queue_apc)
break;
case APC_MAP_VIEW:
process = get_process_from_handle( req->process, PROCESS_VM_OPERATION );
if (process)
if (process && process != current->process)
{
/* duplicate the handle into the target process */
obj_handle_t handle = duplicate_handle( current->process, apc->call.map_view.handle,
@ -1200,18 +1200,22 @@ DECL_HANDLER(queue_apc)
}
else if (process)
{
obj_handle_t handle = alloc_handle( current->process, apc, SYNCHRONIZE, 0 );
if (handle)
reply->self = (process == current->process);
if (!reply->self)
{
if (queue_apc( process, NULL, apc ))
obj_handle_t handle = alloc_handle( current->process, apc, SYNCHRONIZE, 0 );
if (handle)
{
apc->caller = (struct thread *)grab_object( current );
reply->handle = handle;
}
else
{
close_handle( current->process, handle );
set_error( STATUS_PROCESS_IS_TERMINATING );
if (queue_apc( process, NULL, apc ))
{
apc->caller = (struct thread *)grab_object( current );
reply->handle = handle;
}
else
{
close_handle( current->process, handle );
set_error( STATUS_PROCESS_IS_TERMINATING );
}
}
}
release_object( process );

View File

@ -995,7 +995,8 @@ static void dump_queue_apc_request( const struct queue_apc_request *req )
static void dump_queue_apc_reply( const struct queue_apc_reply *req )
{
fprintf( stderr, " handle=%p", req->handle );
fprintf( stderr, " handle=%p,", req->handle );
fprintf( stderr, " self=%d", req->self );
}
static void dump_get_apc_request( const struct get_apc_request *req )