Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

720 lines
23 KiB

/******************************Module*Header*******************************\
* Module Name: usersrv.cxx
*
* This module provides all the routines necessary for calling user
* side servers. The original user mode server is the font driver.
*
* Created: 28-May-1996
* Author: Kirk Olynyk [kirko]
*
* Copyright (c) 1996 Microsoft Corporation
*
\**************************************************************************/
#include "precomp.hxx"
BOOL GreGetMessage( GDIMSG* );
// A GDIMSG_INFO structure is associated with each server thread
// The pointer to this struture is stored in the TEB.
typedef struct _GDIMSG_INFO {
GDIMSG ClientMessage; // client message (kernel mode)
GDIMSG ServerMessage; // server message (user mode)
KEVENT ClientEvent; // client waits on this
KEVENT ServerEvent; // server waits on this
} GDIMSG_INFO;
// for accessing the pointer to the GDIMSG_INFO in the TEB
#define PPMSG(x) ((GDIMSG_INFO**)(&((x)->Spare2)))
#define MESSAGE_BLOCK_SIZE (PAGE_SIZE - 64)
#if DBG
int giUSrv = 0;
#define TRACE_USRV(n,str) { if (giUSrv >= (n)) { KdPrint(str); } }
#else
#define TRACE_USRV(n,str)
#endif
/**********************************************************************
* *
* gapfnServerInit =: array of pointers to functions returning BOOL *
* where each of these functions is a server *
* initialization routine *
* *
**********************************************************************/
BOOL (**gapfnUserServerInit)(GDIMSG*,void*) = 0;
/******************************Public*Routine******************************\
*
* Routine Name:
*
* NtGdiGetMessage
*
* Routine Description:
*
* This is the routine called by the user mode server to unload the
* returned data from the previous call and to load the data associated
* with this call.
*
* Arguments:
*
* pMsg a pointer to a GDIMSG structure residing in the user
* mode space of the server thread.
*
* Return Value:
*
* TRUE if successfull otherwist FALSE.
*
\**************************************************************************/
extern "C" BOOL APIENTRY NtGdiGetMessage( GDIMSG *pMsg )
{
GDIMSG Msg; // Kernel Side copy of GDIMSG structure
BOOL bRet; // success indicator
ASSERTGDI(
MM_LOWEST_USER_ADDRESS <= pMsg &&
pMsg <= MM_HIGHEST_USER_ADDRESS, "Invalid pMsg\n");
TRACE_USRV(1,(
"NtGdiGetMessage:\n pMsg = %x",
pMsg));
// Make a temporary kernel size copy of the server message
__try
{
ProbeForRead( pMsg, sizeof(*pMsg), sizeof(double) );
Msg = *pMsg; // make a copy of the user mode GDIMSG structure
bRet = TRUE; // indicate that the read was successful
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
KdPrint(("NtGdiGetMessage: Exception #1\n"));
bRet = FALSE;
}
if ( bRet ) // if the read of the user mode GDIMSG structure
{ // was successful then we can pass it on to the
// client thread to process it
if ( bRet = GreGetMessage( &Msg ))
{
// Now it is time to go to take the next message
// to the user mode server. We can not be sure
// that the pointer is valid so we surround
// the write with a try/except pair
__try
{
ProbeForWrite( pMsg, sizeof(*pMsg), sizeof(double) );
*pMsg = Msg;
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
KdPrint(("NtGdiGetMessage: Exception #2\n"));
bRet = FALSE;
}
}
}
return( bRet );
}
/******************************Public*Routine******************************\
*
* Routine Name:
*
* pMsgInfoInit
*
* Routine Description:
*
* This is the work horse routine called by NtGdiGetMessage.
*
* This is the routine that is called upon the very first call from
* a user mode server to get its first message. All initialization
* is done at this point.
*
* There initialization process can be partitioned into two classes:
*
* 1. Service Identification and Initialization
* 2. Client-Server memory allocation for message transport.
*
* Arguments:
*
*
* pMsg a pointer to the GDIMSG structure that was filled in
* by the user mode driver before calling to this routine.
* In this special case of initialization this GDIMSG
* structure contains a pointer to user mode memory
* allocated by the user mode server. As such, this memory
* cannot be trusted and must be accessed only when
* surrounded by try excepts.
*
* The memory provided by the user mode server contains
* information used to identify the type of service
* provided by the server.
*
* Return Value:
*
* a pointer to the GDIMSG_INFO structure that is to
* be associated with this server thread by placing
* a pointer to it in the TEB.
*
\**************************************************************************/
GDIMSG_INFO *pMsgInfoInit( GDIMSG *pMsg )
{
extern BOOL bInitializeUserModeServer( GDIMSG* );
GDIMSG_INFO *pInfo = 0;
TRACE_USRV(1,(
"pMsgInfoInit:\n"
"\tpMsg %x\n",
pMsg));
void *pvInfo = 0; // ptr to GDIMSG_INFO structure
if ( !bInitializeUserModeServer( pMsg ))
{
TRACE_USRV(1,(
"pMsgInfoInit:\n"
"\tbInitializeUserModeServer returned FALSE\n"));
}
else if (!( pvInfo = PALLOCMEM( sizeof(GDIMSG_INFO), 'igmG' )))
{
TRACE_USRV(1,(
"pMsgInfoInit:\n"
"\tFailed to allocated GDIMSG_INFO\n"));
}
else
{
TRACE_USRV(1,(
"pMsgInfoInit:\n"
"\tpvInfo %x\n",
pvInfo));
pInfo = (GDIMSG_INFO*) pvInfo;
// initialize server semaphore
KeInitializeEvent(
&( pInfo->ServerEvent ), // address of KEVENT structure
SynchronizationEvent, // auto-clearing
FALSE ); // initialize as Not-Signaled
// initialize client semaphore
KeInitializeEvent(
&( pInfo->ClientEvent ), // address of KEVENT structure
NotificationEvent, // requires explicit KeClearEvent
FALSE ); // initialize as Not-Signaled
}
if ( pInfo == 0 && pvInfo != 0 )
{
VFREEMEM( pvInfo );
}
return( pInfo );
}
/******************************Public*Routine******************************\
*
* Routine Name:
*
* GreGetMessage
*
* Routine Description:
*
* This is the workhorse routine for NtGdiGetMessage. A server will
* call this routine to place data in the kernel buffer, go to sleep
* for the next message, and then when wakened, copy data from the
* kernel buffer to user memory.
*
* Arguments:
*
* pMsg a pointer to a GDIMSG structure residing in
* kernel mode memory ( may contain pointers
* to user mode memory ).
*
* Return Value:
*
* TRUE upon success, FALSE otherwise.
*
\**************************************************************************/
BOOL GreGetMessage( GDIMSG *pMsg )
{
BOOL bRet;
NTSTATUS NtStatus;
TEB *pTeb; // a pointer to the TEB of this thread
GDIMSG_INFO *pInfo; // a pointer to the GDIMSG_INFO structure associated
// with this server thread. If this is the first
// call to get a message then no GDIMSG_INFO structure
// will be allocated yet. We must then allocate
// a GDIMSG_INFO structure and initialize it.
// Whether the structure existed or we had to allocate
// it, this is a pointer to that structure.
GDIMSG_INFO **ppInfo; // address in TEB that is to receive the
// GDIMSG_INFO pointer.
bRet = TRUE;
pTeb = NtCurrentTeb();
pInfo = 0;
// pMsg should point to a kernel address
ASSERTGDI( pMsg >= MM_LOWEST_SYSTEM_ADDRESS, "Invalid pMsg\n" );
TRACE_USRV(1,(
"GreGetMessage:\n"
"\tpMsg %x\n"
"\tpTeb %x\n",
pMsg, pTeb));
if ( !pTeb )
{
KdPrint(("GreGetMessage error: unable to get pTeb\n"));
return( FALSE );
}
ppInfo = PPMSG( pTeb ); // location of pointer in TEB
pInfo = *ppInfo; // value of pointer in TEB
if ( pInfo == 0 ) // First message?
{
*ppInfo = pInfo = pMsgInfoInit( pMsg );
}
TRACE_USRV(2,(
"GreGetMessage:\n"
"\tppInfo %x\n"
"\tpInfo %x\n",
ppInfo, pInfo));
pInfo->ServerMessage = *pMsg; // pMsg points to safe
TRACE_USRV(2,(
"GreGetMessage:\n"
"\tServerMessage: pjIn = %x, cjIn = %x, pjOut = %x, cjOut = %x\n"
"\tClientMessage: pjIn = %x, cjIn = %x, pjOut = %x, cjOut = %x\n",
pInfo->ServerMessage.pjIn, pInfo->ServerMessage.cjIn,
pInfo->ServerMessage.pjOut, pInfo->ServerMessage.pjOut,
pInfo->ClientMessage.pjIn, pInfo->ClientMessage.cjIn,
pInfo->ClientMessage.pjOut, pInfo->ClientMessage.pjOut
));
if (
pMsg->pjOut &&
pInfo->ClientMessage.pjIn &&
pMsg->cjOut &&
pMsg->cjOut <= pInfo->ClientMessage.cjIn
)
{ // Copy new server message
// kernel mode structure
__try
{
ProbeForRead( pMsg->pjOut, pMsg->cjOut, sizeof(double) );
RtlMoveMemory(
pInfo->ClientMessage.pjIn,
pMsg->pjOut,
pMsg->cjOut
);
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
KdPrint(("GreGetMessage exception #1\n"));
}
}
else
{
RIP("GreGetMessage:Inconsistent Server Message\n");
bRet = FALSE;
}
// Wake up the client thread so that it can process the message
// This has to be done even though the pInfo may be zero
TRACE_USRV(2,(
"GreGetMessage:\n"
"\tWake up client\n"
));
NtStatus =
KeSetEvent(
&pInfo->ClientEvent,// pointer to initialized event object
0, // set priority increment to zero
FALSE ); // don't append implicit KeWaitXxx
TRACE_USRV(2,(
"GreGetMessage:\n"
"\tNtStatus = %d\n",
NtStatus));
ASSERTGDI( NtStatus == 0,
"Client Event was in Signaled state\n");
// put the server thread to sleep and wait for the next call
// from the client
TRACE_USRV(2,(
"GreGetMessage:\n"
"\tSleep until next call from client\n"
));
NtStatus =
KeWaitForSingleObject(
&pInfo->ServerEvent,// address of initialized event
UserRequest, // this driver is doing work on behalf
// of a user and is running in the context
// of a User server thread
KernelMode, // we do not expect this to be asleep
// too long in the usual case
FALSE, // not alertable
0 ); // no time out limitation since we are
// waiting for the next call from an
// application and there is no telling
// when that will happen
TRACE_USRV(2,(
"GreGetMessage:\n"
"\tSleeping beauty awakes\n"
"\tNtStatus %d\n",
NtStatus
));
ASSERTGDI(NtStatus == STATUS_SUCCESS,
"Server Thread awoke unexpectedly\n");
// The server thread has been wakened by the client thread
// We must copy the data left by the client thread in
// the kernel buffer to the user mode buffer. No need
// for try-except's since the user mode memory is guranateed
// to be safe.
TRACE_USRV(2,(
"GreGetMessage:\n"
"\tServerMessage: pjIn = %x, cjIn = %x, pjOut = %x, cjOut = %x\n"
"\tClientMessage: pjIn = %x, cjIn = %x, pjOut = %x, cjOut = %x\n",
pInfo->ServerMessage.pjIn, pInfo->ServerMessage.cjIn,
pInfo->ServerMessage.pjOut, pInfo->ServerMessage.pjOut,
pInfo->ClientMessage.pjIn, pInfo->ClientMessage.cjIn,
pInfo->ClientMessage.pjOut, pInfo->ClientMessage.pjOut
));
if (
pInfo->ServerMessage.pjIn &&
pInfo->ClientMessage.pjOut &&
pInfo->ClientMessage.cjOut &&
pInfo->ClientMessage.cjOut <= pInfo->ServerMessage.cjOut
)
{
__try
{
ProbeForWrite(
pInfo->ServerMessage.pjIn,
pInfo->ClientMessage.cjOut,
sizeof(double) );
RtlMoveMemory(
pInfo->ServerMessage.pjIn,
pInfo->ClientMessage.pjOut,
pInfo->ClientMessage.cjOut );
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
KdPrint(("GreGetMessage exception #2\n"));
bRet = FALSE;
}
}
else
{
bRet = FALSE;
}
return( bRet );
}
/******************************Public*Routine******************************\
*
* Routine Name:
*
* bIntiializeUserModeServer
*
* Routine Description:
*
* This routine does the work to set up whatever is necessary to make
* use of the user mode server.
*
* Arguments:
*
* pMsg This is a pointer to a kenrel mode copy of the original
* GDIMSG structure that contains
* pointers to user mode memory allocated by the user
* mode server. This contains all the information necessary
* to initialize this user mode server. Since this memory
* is not guaranteed to be safe, we must surround all
* access to this user mode memory with __try __except
* pairs.
*
* Return Value:
*
* The allowed return values are TRUE upon successful initialization
* and FALSE upon failure.
*
\**************************************************************************/
BOOL bInitializeUserModeServer( GDIMSG *pMsg )
{
extern BOOL bInitServerWorkhorse( GDIMSG*, void* );
void *pv;
BOOL bRet = TRUE;
TRACE_USRV(2,(
"bInitializeUserModeServer:\n"
"\tpMsg %x",
pMsg
));
if ( pMsg == 0 )
{
KdPrint(("bInitializeUserModeServer Error: pMsg == 0\n"));
return( FALSE );
}
if ( ( pv = PALLOCMEM( pMsg->cjOut, 'pmtG' )) == 0 )
{
WARNING("bInitializeUserModeServer: PALLOCMEM failed\n");
return( FALSE );
}
__try
{
ProbeForRead( pMsg->pjOut, pMsg->cjOut, sizeof(double) );
RtlCopyMemory( pv, pMsg->pjOut, pMsg->cjOut );
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
KdPrint(("bInitializeUserModeServer exception #1\n"));
bRet = FALSE;
}
bRet = bRet && bInitServerWorkhorse( pMsg, pv );
VFREEMEM( pv );
return( bRet );
}
/******************************Public*Routine******************************\
*
* Routine Name:
*
* bInitServerWorkhorse
*
* Routine Description:
*
* This is the workhorse routine for server initialization. There
* are only a finite number of servers that Gdi recognizes. An argument
* to this routine is a pointer to a kernel mode copy of the
* original user mode buffer allocated and initialized by the user mode
* server. This pointer is passed to all of the routines that claim
* to know about user mode servers. The first routine that recognizes
* the buffer will process and intialize it.
*
* Arguments:
*
* pMsg a pointer to a kernel mode copy of the original server
* message. This may contain pointers to user mode data
*
* pv a pointer to a kernel mode copy of the input buffer
* of the original server message
*
* Return Value:
*
* TRUE if successful otherwise FALSE.
*
\**************************************************************************/
BOOL bInitServerWorkhorse( GDIMSG *pMsg, void *pv )
{
BOOL bRet = FALSE;
TRACE_USRV(1,(
"bInitServerWorkhorse:\n"
"\tpMsg %x\n"
"\tpv %x\n",
pMsg,
pv
));
SEMOBJ so(gpsemDriverMgmt); // Enter a critical section to
// protect the dispatch table
if ( gapfnUserServerInit )
{
BOOL (**ppfn)(GDIMSG*,void*);
for (ppfn = gapfnUserServerInit; *ppfn; ppfn++)
{
TRACE_USRV(2,(
"bInitServerWorkhorse:\n"
"\tCalling function at %x ...\n",
*ppfn
));
if ( bRet = (**ppfn)( pMsg, pv ))
{
TRACE_USRV(2,(
"bInitServerWorkhorse:\n"
"\tInitialization function returend TRUE!\n"
));
break;
}
TRACE_USRV(2,(
"bInitServerWorkhorse:\n"
"\tInitialization function returned FALSE.\n"
));
}
}
return( bRet );
}
/******************************Public*Routine******************************\
*
* Routine Name:
*
* bRegisterClient
*
* Routine Description:
*
* This is the way that the engine will register a client for a server.
* GDI registers a function that is used to instatiate a user mode
* server process the first time that it calls into the kernel.
*
* I anticipate that this will be called once to register the function
* that initializes font drivers. There may be other services that
* need to be placed in user mode, each of these servcice that GDI
* anticpates using, must be registered this way in order to use
* this client server message passing mechanism.
*
* Arguments:
*
* pfn A pointer to a boolean function that takes a GDIMSG pointer
* and a pointer to a buffer as an argument. Using this
* information, GDI will set up the data structures needed
* to take advantage of this user mode service.
*
* The reader is referred to the comments in bInitServerWorkhorse
* for a description of the arguments to these functions.
*
* Return Value:
*
* TRUE if successful otherwise FASLE.
*
\**************************************************************************/
BOOL bRegisterClient( BOOL (*pfn)(GDIMSG*,void*) )
{
static int iCurrent = 0; // index of current slot in gapfnUserServerInit
static int iOneTooMany = 0; // first invalid index in gapfnUserServerInit,
// also equal to the number of available
// elements in the the array pointed to by
// gapfnUserServerInit
static int iIncrement = 4; // number of entries that table should grow
// upon reallocation
BOOL bRet = FALSE; // return value for this routine
TRACE_USRV(2,(
"bRegisterClient:\n"
"\tpfn %x\n"
"\tgapfnUserServerInit %x\n"
"\tiOneTooMany %x\n"
"\tiIncrement %x\n",
pfn,
gapfnUserServerInit,
iOneTooMany,
iIncrement
));
if ( pfn == 0 ) // Did the caller passin a funcion pointer?
{ // No!
KdPrint(("bRegisterClient: pfn == 0\n"));
}
else
{
SEMOBJ so(gpsemDriverMgmt); // Enter a critical section to
// protect the dispatch table
// Q: Is there room in the table
if ( iCurrent < iOneTooMany ) // for one more entry?
{ // A: Yes. There is no no need
bRet = TRUE; // need to allocate a new table
}
else // A: No. The dispatch table
{ // is full and it is necessary
// to allocate a bigger table
// allocate iIncrement more slots
// than before (Note: PALLOCMEM
// zero's allocated memory)
void *pv =
PALLOCMEM(
sizeof( *gapfnUserServerInit ) * (iOneTooMany + iIncrement),
'isuG' );
if ( pv == 0 ) // Q: Was the allocation successful?
{ // A: No. We have a problem
KdPrint(("bRegisterClient: pv == 0\n"));
}
else
{ // A: Yes, successful allocation
TRACE_USRV(2,(
"bRegisterClient:\n"
"\tAllocated new dispatch table at %x\n",
pv
));
if ( gapfnUserServerInit ) // Q: Do we already have a table?
{ // A: Yes, copy the old dispatch table
TRACE_USRV(2,( // to the new and larger memory
"bRegisterClient:\n"
"\tCopying old dispatch table from %x\n",
gapfnUserServerInit
));
RtlCopyMemory( // Copy old table to the new table
pv,
gapfnUserServerInit,
iOneTooMany * sizeof( *gapfnUserServerInit ));
VFREEMEM( gapfnUserServerInit ); // free old table
}
// reset global pointer to dispatch
// table
gapfnUserServerInit = (BOOL (**) (GDIMSG*, void*)) pv;
iOneTooMany += iIncrement; // update out of bounds index
bRet = TRUE;
}
}
if ( bRet )
{
gapfnUserServerInit[iCurrent] = pfn; // set the lowest free entry
// in the dispatch table
iCurrent += 1; // increment pointer to next entry
}
// ~SEMOBJ is called here causing you to leave the critical section
}
return( bRet );
}