809 lines
16 KiB
C
809 lines
16 KiB
C
/*++
|
||
|
||
Copyright (c) 1998 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
context.c
|
||
|
||
Abstract:
|
||
|
||
This module contains SPUD context management routines.
|
||
|
||
Request contexts are referenced via a request handle. This necessary
|
||
to validate requests incoming from user-mode. (We can't trust user-
|
||
mode code to give us valid pointers, so instead of storing a pointer
|
||
to our request context structure in the user-mode structure, we store
|
||
a request handle.)
|
||
|
||
Request handles identify a SPUD_HANDLE_ENTRY structure in a lookup
|
||
table. Each SPUD_HANDLE_ENTRY contains a copy of the request handle
|
||
(for validation) and a pointer to a SPUD_AFD_REQ_CONTEXT structure.
|
||
Free handle entries are linked together. As handles are allocated,
|
||
they are removed from the free list. Once the free list becomes empty,
|
||
the lookup table is grown appropriately.
|
||
|
||
Author:
|
||
|
||
Keith Moore (keithmo) 01-Oct-1997
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
|
||
#include "spudp.h"
|
||
|
||
|
||
//
|
||
// Private constants.
|
||
//
|
||
|
||
#define SPUD_HANDLE_TABLE_GROWTH 32 // entries
|
||
|
||
#define LOCK_HANDLE_TABLE() \
|
||
if( TRUE ) { \
|
||
KeEnterCriticalRegion(); \
|
||
ExAcquireResourceExclusiveLite( \
|
||
&SpudNonpagedData->ReqHandleTableLock, \
|
||
TRUE \
|
||
); \
|
||
} else
|
||
|
||
#define UNLOCK_HANDLE_TABLE() \
|
||
if( TRUE ) { \
|
||
ExReleaseResourceLite( \
|
||
&SpudNonpagedData->ReqHandleTableLock \
|
||
); \
|
||
KeLeaveCriticalRegion(); \
|
||
} else
|
||
|
||
|
||
//
|
||
// Private types.
|
||
//
|
||
|
||
typedef union _SPUD_HANDLE_ENTRY {
|
||
LIST_ENTRY FreeListEntry;
|
||
struct {
|
||
PVOID ReqHandle;
|
||
PVOID Context;
|
||
};
|
||
} SPUD_HANDLE_ENTRY, *PSPUD_HANDLE_ENTRY;
|
||
|
||
|
||
//
|
||
// Private globals.
|
||
//
|
||
|
||
PSPUD_HANDLE_ENTRY SpudpHandleTable;
|
||
LONG SpudpHandleTableSize;
|
||
LIST_ENTRY SpudpFreeList;
|
||
|
||
|
||
//
|
||
// Private prototypes.
|
||
//
|
||
|
||
PVOID
|
||
SpudpAllocateReqHandle(
|
||
IN PSPUD_AFD_REQ_CONTEXT SpudReqContext
|
||
);
|
||
|
||
VOID
|
||
SpudpFreeReqHandle(
|
||
IN PVOID ReqHandle
|
||
);
|
||
|
||
PSPUD_AFD_REQ_CONTEXT
|
||
SpudpGetReqHandleContext(
|
||
IN PVOID ReqHandle
|
||
);
|
||
|
||
PSPUD_HANDLE_ENTRY
|
||
SpudpMapHandleToEntry(
|
||
IN PVOID ReqHandle
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( INIT, SpudInitializeContextManager )
|
||
#pragma alloc_text( PAGE, SpudTerminateContextManager )
|
||
#pragma alloc_text( PAGE, SpudAllocateRequestContext )
|
||
#pragma alloc_text( PAGE, SpudFreeRequestContext )
|
||
#pragma alloc_text( PAGE, SpudGetRequestContext )
|
||
#pragma alloc_text( PAGE, SpudpAllocateReqHandle )
|
||
#pragma alloc_text( PAGE, SpudpFreeReqHandle )
|
||
#pragma alloc_text( PAGE, SpudpGetReqHandleContext )
|
||
#pragma alloc_text( PAGE, SpudpMapHandleToEntry )
|
||
#endif // ALLOC_PRAGMA
|
||
|
||
//
|
||
// Public functions.
|
||
//
|
||
|
||
|
||
NTSTATUS
|
||
SpudInitializeContextManager(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Performs global initialization for the context manager package.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Completion status.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// Sanity check.
|
||
//
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Note that the resource protecting the handle table is initialized
|
||
// in SpudInitializeData().
|
||
//
|
||
|
||
SpudpHandleTable = NULL;
|
||
SpudpHandleTableSize = 0;
|
||
InitializeListHead( &SpudpFreeList );
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // SpudInitializeContextManager
|
||
|
||
|
||
VOID
|
||
SpudTerminateContextManager(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Performs global termination for the context manager package.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// Sanity check.
|
||
//
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Free the handle table (if allocated), reset the other globals.
|
||
//
|
||
|
||
if( SpudpHandleTable != NULL ) {
|
||
SPUD_FREE_POOL( SpudpHandleTable );
|
||
}
|
||
|
||
SpudpHandleTable = NULL;
|
||
SpudpHandleTableSize = 0;
|
||
InitializeListHead( &SpudpFreeList );
|
||
|
||
} // SpudTerminateContextManager
|
||
|
||
|
||
NTSTATUS
|
||
SpudAllocateRequestContext(
|
||
OUT PSPUD_AFD_REQ_CONTEXT *SpudReqContext,
|
||
IN PSPUD_REQ_CONTEXT ReqContext,
|
||
IN PAFD_RECV_INFO RecvInfo OPTIONAL,
|
||
IN PIRP Irp,
|
||
IN PIO_STATUS_BLOCK IoStatusBlock OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Allocates & initializes a new SPUD_AFD_REQ_CONTEXT structure.
|
||
|
||
Arguments:
|
||
|
||
SpudReqContext - If successful, receives a pointer to the newly
|
||
allocated SPUD_AFD_REQ_CONTEXT structure.
|
||
|
||
ReqContext - Pointer to the user-mode SPUD_REQ_CONTEXT structure.
|
||
The newly allocated context structure will be associated with
|
||
this user-mode context.
|
||
|
||
RecvInfo - An optional pointer to a AFD_RECV_INFO structure describing
|
||
a future receive operation.
|
||
|
||
Irp - Pointer to an IO request packet to associate with the new context
|
||
structure.
|
||
|
||
IoStatusBlock - An optional pointer to an IO_STATUS_BLOCK used to
|
||
initialize one of the fields in the new context structure.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Completion status.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSPUD_AFD_REQ_CONTEXT spudReqContext;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// Sanity check.
|
||
//
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Try to allocate a new structure.
|
||
//
|
||
|
||
spudReqContext = ExAllocateFromNPagedLookasideList(
|
||
&SpudNonpagedData->ReqContextList
|
||
);
|
||
|
||
if( spudReqContext == NULL ) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlZeroMemory(
|
||
spudReqContext,
|
||
sizeof(*spudReqContext)
|
||
);
|
||
|
||
//
|
||
// Try to allocate a request handle for the context structure.
|
||
//
|
||
|
||
spudReqContext->ReqHandle = SpudpAllocateReqHandle( spudReqContext );
|
||
|
||
if( spudReqContext->ReqHandle == SPUD_INVALID_REQ_HANDLE ) {
|
||
SpudFreeRequestContext( spudReqContext );
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
spudReqContext->Signature = SPUD_REQ_CONTEXT_SIGNATURE;
|
||
spudReqContext->Irp = Irp;
|
||
spudReqContext->AtqContext = ReqContext;
|
||
|
||
try {
|
||
|
||
ReqContext->KernelReqInfo = spudReqContext->ReqHandle;
|
||
|
||
if( IoStatusBlock != NULL ) {
|
||
spudReqContext->IoStatus1 = *IoStatusBlock;
|
||
}
|
||
|
||
//
|
||
// If we got an AFD_RECV_INFO structure, then save the buffer
|
||
// length and create a MDL describing the buffer.
|
||
//
|
||
|
||
if( RecvInfo != NULL ) {
|
||
|
||
spudReqContext->IoStatus2.Information = RecvInfo->BufferArray->len;
|
||
spudReqContext->Mdl = IoAllocateMdl(
|
||
RecvInfo->BufferArray->buf,
|
||
RecvInfo->BufferArray->len,
|
||
FALSE,
|
||
FALSE,
|
||
NULL
|
||
);
|
||
|
||
if( spudReqContext->Mdl == NULL ) {
|
||
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
MmProbeAndLockPages(
|
||
spudReqContext->Mdl,
|
||
UserMode,
|
||
IoWriteAccess
|
||
);
|
||
|
||
}
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
//
|
||
// Bad news. This is either due to a) an AV trying to access
|
||
// the user-mode request context structure, or b) an exception
|
||
// trying to lock down the receive buffer. If we managed to
|
||
// allocate a MDL for the context, then we know the problem
|
||
// was because the pages could not be locked, so we'll need to
|
||
// free the MDL before continuing. In any case, we'll need to
|
||
// free the newly allocated request context.
|
||
//
|
||
|
||
status = GetExceptionCode();
|
||
|
||
if( spudReqContext->Mdl != NULL ) {
|
||
IoFreeMdl( spudReqContext->Mdl );
|
||
spudReqContext->Mdl = NULL;
|
||
}
|
||
|
||
SpudFreeRequestContext( spudReqContext );
|
||
spudReqContext = NULL;
|
||
}
|
||
|
||
*SpudReqContext = spudReqContext;
|
||
return status;
|
||
|
||
} // SpudAllocateRequestContext
|
||
|
||
|
||
VOID
|
||
SpudFreeRequestContext(
|
||
IN PSPUD_AFD_REQ_CONTEXT SpudReqContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Frees a SPUD_AFD_REQ_CONTEXT structure allocated above.
|
||
|
||
Arguments:
|
||
|
||
SpudReqContext - A context structure allocated above.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PMDL mdl, nextMdl;
|
||
|
||
//
|
||
// Sanity check.
|
||
//
|
||
|
||
PAGED_CODE();
|
||
ASSERT( SpudReqContext != NULL );
|
||
ASSERT( SpudReqContext->Signature == SPUD_REQ_CONTEXT_SIGNATURE );
|
||
|
||
//
|
||
// Unlock & free any MDL chain still attached to the
|
||
// request context.
|
||
//
|
||
|
||
mdl = SpudReqContext->Mdl;
|
||
SpudReqContext->Mdl = NULL;
|
||
|
||
while( mdl != NULL ) {
|
||
nextMdl = mdl->Next;
|
||
MmUnlockPages( mdl );
|
||
IoFreeMdl( mdl );
|
||
mdl = nextMdl;
|
||
}
|
||
|
||
//
|
||
// Free the handle if we managed to allocate one.
|
||
//
|
||
|
||
if( SpudReqContext->ReqHandle != SPUD_INVALID_REQ_HANDLE ) {
|
||
ASSERT( SpudpGetReqHandleContext( SpudReqContext->ReqHandle ) == SpudReqContext );
|
||
SpudpFreeReqHandle( SpudReqContext->ReqHandle );
|
||
SpudReqContext->ReqHandle = SPUD_INVALID_REQ_HANDLE;
|
||
}
|
||
|
||
//
|
||
// Free the context.
|
||
//
|
||
|
||
SpudReqContext->Signature = SPUD_REQ_CONTEXT_SIGNATURE_X;
|
||
SpudReqContext->Irp = NULL;
|
||
|
||
ExFreeToNPagedLookasideList(
|
||
&SpudNonpagedData->ReqContextList,
|
||
SpudReqContext
|
||
);
|
||
|
||
} // SpudFreeRequestContext
|
||
|
||
|
||
PSPUD_AFD_REQ_CONTEXT
|
||
SpudGetRequestContext(
|
||
IN PSPUD_REQ_CONTEXT ReqContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Retrieves the SPUD_AFD_REQ_CONTEXT structure associated with the
|
||
incoming user-mode SPUD_REQ_CONTEXT.
|
||
|
||
Arguments:
|
||
|
||
ReqContext - The incoming user-mode SPUD_REQ_CONTEXT structure.
|
||
|
||
Return Value:
|
||
|
||
PSPUD_AFD_REQ_CONTEXT - Pointer to the associated context structure
|
||
if successful, NULL otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSPUD_AFD_REQ_CONTEXT spudReqContext = NULL;
|
||
PVOID reqHandle;
|
||
|
||
//
|
||
// Sanity check.
|
||
//
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Snag the kernel-mode request context handle from the user-mode
|
||
// context.
|
||
//
|
||
|
||
try {
|
||
reqHandle = ReqContext->KernelReqInfo;
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
reqHandle = SPUD_INVALID_REQ_HANDLE;
|
||
}
|
||
|
||
if( reqHandle != SPUD_INVALID_REQ_HANDLE ) {
|
||
|
||
//
|
||
// Map the handle.
|
||
//
|
||
|
||
spudReqContext = SpudpGetReqHandleContext( reqHandle );
|
||
|
||
}
|
||
|
||
return spudReqContext;
|
||
|
||
} // SpudGetRequestContext
|
||
|
||
|
||
//
|
||
// Private functions.
|
||
//
|
||
|
||
|
||
PVOID
|
||
SpudpAllocateReqHandle(
|
||
IN PSPUD_AFD_REQ_CONTEXT SpudReqContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Allocates a new request handle for the specified context.
|
||
|
||
Arguments:
|
||
|
||
SpudReqContext - A context structure.
|
||
|
||
Return Value:
|
||
|
||
PVOID - The new request handle if successful, SPUD_INVALID_REQ_HANDLE
|
||
otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
LONG groupValue;
|
||
PSPUD_HANDLE_ENTRY handleEntry;
|
||
PSPUD_HANDLE_ENTRY newHandleTable;
|
||
LONG newHandleTableSize;
|
||
LONG i;
|
||
PLIST_ENTRY listEntry;
|
||
PVOID reqHandle;
|
||
|
||
//
|
||
// Sanity check.
|
||
//
|
||
|
||
PAGED_CODE();
|
||
ASSERT( SpudReqContext != NULL );
|
||
|
||
//
|
||
// See if there's room at the inn.
|
||
//
|
||
|
||
LOCK_HANDLE_TABLE();
|
||
|
||
if( IsListEmpty( &SpudpFreeList ) ) {
|
||
|
||
//
|
||
// No room, we'll need to create/expand the table.
|
||
//
|
||
|
||
newHandleTableSize = SpudpHandleTableSize + SPUD_HANDLE_TABLE_GROWTH;
|
||
|
||
newHandleTable = SPUD_ALLOCATE_POOL(
|
||
PagedPool,
|
||
newHandleTableSize * sizeof(SPUD_HANDLE_ENTRY),
|
||
SPUD_HANDLE_TABLE_POOL_TAG
|
||
);
|
||
|
||
if( newHandleTable == NULL ) {
|
||
UNLOCK_HANDLE_TABLE();
|
||
return NULL;
|
||
}
|
||
|
||
if( SpudpHandleTable == NULL ) {
|
||
|
||
//
|
||
// This is the initial table allocation, so reserve the
|
||
// first entry. (NULL is not a valid request handle.)
|
||
//
|
||
|
||
newHandleTable[0].ReqHandle = NULL;
|
||
newHandleTable[0].Context = NULL;
|
||
|
||
SpudpHandleTableSize++;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Copy the old table into the new table, then free the
|
||
// old table.
|
||
//
|
||
|
||
RtlCopyMemory(
|
||
newHandleTable,
|
||
SpudpHandleTable,
|
||
SpudpHandleTableSize * sizeof(SPUD_HANDLE_ENTRY)
|
||
);
|
||
|
||
SPUD_FREE_POOL( SpudpHandleTable );
|
||
|
||
}
|
||
|
||
//
|
||
// Add the new entries to the free list.
|
||
//
|
||
|
||
for( i = newHandleTableSize - 1 ; i >= SpudpHandleTableSize ; i-- ) {
|
||
|
||
InsertHeadList(
|
||
&SpudpFreeList,
|
||
&newHandleTable[i].FreeListEntry
|
||
);
|
||
|
||
}
|
||
|
||
SpudpHandleTable = newHandleTable;
|
||
SpudpHandleTableSize = newHandleTableSize;
|
||
|
||
}
|
||
|
||
//
|
||
// Pull the next free entry off the list.
|
||
//
|
||
|
||
ASSERT( !IsListEmpty( &SpudpFreeList ) );
|
||
|
||
listEntry = RemoveHeadList( &SpudpFreeList );
|
||
|
||
handleEntry = CONTAINING_RECORD(
|
||
listEntry,
|
||
SPUD_HANDLE_ENTRY,
|
||
FreeListEntry
|
||
);
|
||
|
||
//
|
||
// Compute the handle and initialize the new handle entry.
|
||
//
|
||
|
||
reqHandle = (PVOID)( handleEntry - SpudpHandleTable );
|
||
ASSERT( reqHandle != SPUD_INVALID_REQ_HANDLE );
|
||
|
||
handleEntry->ReqHandle = reqHandle;
|
||
handleEntry->Context = SpudReqContext;
|
||
|
||
UNLOCK_HANDLE_TABLE();
|
||
return reqHandle;
|
||
|
||
} // SpudpAllocateReqHandle
|
||
|
||
|
||
VOID
|
||
SpudpFreeReqHandle(
|
||
IN PVOID ReqHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Frees a request handle.
|
||
|
||
Arguments:
|
||
|
||
ReqHandle - The request handle to free.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSPUD_HANDLE_ENTRY handleEntry;
|
||
|
||
//
|
||
// Sanity check.
|
||
//
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Map the handle to the table entry. If successful, put the
|
||
// entry onto the free list.
|
||
//
|
||
|
||
handleEntry = SpudpMapHandleToEntry( ReqHandle );
|
||
|
||
if( handleEntry != NULL ) {
|
||
|
||
InsertTailList(
|
||
&SpudpFreeList,
|
||
&handleEntry->FreeListEntry
|
||
);
|
||
|
||
UNLOCK_HANDLE_TABLE();
|
||
|
||
}
|
||
|
||
} // SpudpFreeReqHandle
|
||
|
||
|
||
PSPUD_AFD_REQ_CONTEXT
|
||
SpudpGetReqHandleContext(
|
||
IN PVOID ReqHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Retrieves the context value associated with the given request handle.
|
||
|
||
Arguments:
|
||
|
||
ReqHandle - The request handle to retrieve.
|
||
|
||
Return Value:
|
||
|
||
PSPUD_AFD_REQ_CONTEXT - Pointer to the context if successful,
|
||
NULL otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSPUD_HANDLE_ENTRY handleEntry;
|
||
PSPUD_AFD_REQ_CONTEXT spudReqContext;
|
||
|
||
//
|
||
// Sanity check.
|
||
//
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Map the handle to the table entry. If successful, retrieve
|
||
// the context.
|
||
//
|
||
|
||
handleEntry = SpudpMapHandleToEntry( ReqHandle );
|
||
|
||
if( handleEntry != NULL ) {
|
||
|
||
spudReqContext = handleEntry->Context;
|
||
ASSERT( spudReqContext != NULL );
|
||
|
||
UNLOCK_HANDLE_TABLE();
|
||
return spudReqContext;
|
||
|
||
}
|
||
|
||
return NULL;
|
||
|
||
} // SpudpGetReqHandleContext
|
||
|
||
|
||
PSPUD_HANDLE_ENTRY
|
||
SpudpMapHandleToEntry(
|
||
IN PVOID ReqHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Maps the given request handle to the corresponding SPUD_HANDLE_ENTRY
|
||
structure.
|
||
|
||
N.B. This routine returns with the handle table lock held if successful.
|
||
|
||
Arguments:
|
||
|
||
ReqHandle - The handle to map.
|
||
|
||
Return Value:
|
||
|
||
PSPUD_HANDLE_ENTRY - The entry corresponding to the request handle if
|
||
successful, NULL otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSPUD_HANDLE_ENTRY handleEntry;
|
||
|
||
//
|
||
// Sanity check.
|
||
//
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Validate the handle.
|
||
//
|
||
|
||
LOCK_HANDLE_TABLE();
|
||
|
||
if( (LONG_PTR)ReqHandle > 0 && (LONG_PTR)ReqHandle < (LONG_PTR)SpudpHandleTableSize ) {
|
||
|
||
handleEntry = SpudpHandleTable + (ULONG_PTR)ReqHandle;
|
||
|
||
//
|
||
// The handle is within legal range, ensure it's in use.
|
||
//
|
||
|
||
if( handleEntry->ReqHandle == ReqHandle ) {
|
||
return handleEntry;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Invalid handle, fail it.
|
||
//
|
||
|
||
UNLOCK_HANDLE_TABLE();
|
||
return NULL;
|
||
|
||
} // SpudpMapHandleToEntry
|
||
|