/*++ 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