Source code of Windows XP (NT5)
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.

808 lines
16 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. context.c
  5. Abstract:
  6. This module contains SPUD context management routines.
  7. Request contexts are referenced via a request handle. This necessary
  8. to validate requests incoming from user-mode. (We can't trust user-
  9. mode code to give us valid pointers, so instead of storing a pointer
  10. to our request context structure in the user-mode structure, we store
  11. a request handle.)
  12. Request handles identify a SPUD_HANDLE_ENTRY structure in a lookup
  13. table. Each SPUD_HANDLE_ENTRY contains a copy of the request handle
  14. (for validation) and a pointer to a SPUD_AFD_REQ_CONTEXT structure.
  15. Free handle entries are linked together. As handles are allocated,
  16. they are removed from the free list. Once the free list becomes empty,
  17. the lookup table is grown appropriately.
  18. Author:
  19. Keith Moore (keithmo) 01-Oct-1997
  20. Revision History:
  21. --*/
  22. #include "spudp.h"
  23. //
  24. // Private constants.
  25. //
  26. #define SPUD_HANDLE_TABLE_GROWTH 32 // entries
  27. #define LOCK_HANDLE_TABLE() \
  28. if( TRUE ) { \
  29. KeEnterCriticalRegion(); \
  30. ExAcquireResourceExclusiveLite( \
  31. &SpudNonpagedData->ReqHandleTableLock, \
  32. TRUE \
  33. ); \
  34. } else
  35. #define UNLOCK_HANDLE_TABLE() \
  36. if( TRUE ) { \
  37. ExReleaseResourceLite( \
  38. &SpudNonpagedData->ReqHandleTableLock \
  39. ); \
  40. KeLeaveCriticalRegion(); \
  41. } else
  42. //
  43. // Private types.
  44. //
  45. typedef union _SPUD_HANDLE_ENTRY {
  46. LIST_ENTRY FreeListEntry;
  47. struct {
  48. PVOID ReqHandle;
  49. PVOID Context;
  50. };
  51. } SPUD_HANDLE_ENTRY, *PSPUD_HANDLE_ENTRY;
  52. //
  53. // Private globals.
  54. //
  55. PSPUD_HANDLE_ENTRY SpudpHandleTable;
  56. LONG SpudpHandleTableSize;
  57. LIST_ENTRY SpudpFreeList;
  58. //
  59. // Private prototypes.
  60. //
  61. PVOID
  62. SpudpAllocateReqHandle(
  63. IN PSPUD_AFD_REQ_CONTEXT SpudReqContext
  64. );
  65. VOID
  66. SpudpFreeReqHandle(
  67. IN PVOID ReqHandle
  68. );
  69. PSPUD_AFD_REQ_CONTEXT
  70. SpudpGetReqHandleContext(
  71. IN PVOID ReqHandle
  72. );
  73. PSPUD_HANDLE_ENTRY
  74. SpudpMapHandleToEntry(
  75. IN PVOID ReqHandle
  76. );
  77. #ifdef ALLOC_PRAGMA
  78. #pragma alloc_text( INIT, SpudInitializeContextManager )
  79. #pragma alloc_text( PAGE, SpudTerminateContextManager )
  80. #pragma alloc_text( PAGE, SpudAllocateRequestContext )
  81. #pragma alloc_text( PAGE, SpudFreeRequestContext )
  82. #pragma alloc_text( PAGE, SpudGetRequestContext )
  83. #pragma alloc_text( PAGE, SpudpAllocateReqHandle )
  84. #pragma alloc_text( PAGE, SpudpFreeReqHandle )
  85. #pragma alloc_text( PAGE, SpudpGetReqHandleContext )
  86. #pragma alloc_text( PAGE, SpudpMapHandleToEntry )
  87. #endif // ALLOC_PRAGMA
  88. //
  89. // Public functions.
  90. //
  91. NTSTATUS
  92. SpudInitializeContextManager(
  93. VOID
  94. )
  95. /*++
  96. Routine Description:
  97. Performs global initialization for the context manager package.
  98. Arguments:
  99. None.
  100. Return Value:
  101. NTSTATUS - Completion status.
  102. --*/
  103. {
  104. //
  105. // Sanity check.
  106. //
  107. PAGED_CODE();
  108. //
  109. // Note that the resource protecting the handle table is initialized
  110. // in SpudInitializeData().
  111. //
  112. SpudpHandleTable = NULL;
  113. SpudpHandleTableSize = 0;
  114. InitializeListHead( &SpudpFreeList );
  115. return STATUS_SUCCESS;
  116. } // SpudInitializeContextManager
  117. VOID
  118. SpudTerminateContextManager(
  119. VOID
  120. )
  121. /*++
  122. Routine Description:
  123. Performs global termination for the context manager package.
  124. Arguments:
  125. None.
  126. Return Value:
  127. None.
  128. --*/
  129. {
  130. //
  131. // Sanity check.
  132. //
  133. PAGED_CODE();
  134. //
  135. // Free the handle table (if allocated), reset the other globals.
  136. //
  137. if( SpudpHandleTable != NULL ) {
  138. SPUD_FREE_POOL( SpudpHandleTable );
  139. }
  140. SpudpHandleTable = NULL;
  141. SpudpHandleTableSize = 0;
  142. InitializeListHead( &SpudpFreeList );
  143. } // SpudTerminateContextManager
  144. NTSTATUS
  145. SpudAllocateRequestContext(
  146. OUT PSPUD_AFD_REQ_CONTEXT *SpudReqContext,
  147. IN PSPUD_REQ_CONTEXT ReqContext,
  148. IN PAFD_RECV_INFO RecvInfo OPTIONAL,
  149. IN PIRP Irp,
  150. IN PIO_STATUS_BLOCK IoStatusBlock OPTIONAL
  151. )
  152. /*++
  153. Routine Description:
  154. Allocates & initializes a new SPUD_AFD_REQ_CONTEXT structure.
  155. Arguments:
  156. SpudReqContext - If successful, receives a pointer to the newly
  157. allocated SPUD_AFD_REQ_CONTEXT structure.
  158. ReqContext - Pointer to the user-mode SPUD_REQ_CONTEXT structure.
  159. The newly allocated context structure will be associated with
  160. this user-mode context.
  161. RecvInfo - An optional pointer to a AFD_RECV_INFO structure describing
  162. a future receive operation.
  163. Irp - Pointer to an IO request packet to associate with the new context
  164. structure.
  165. IoStatusBlock - An optional pointer to an IO_STATUS_BLOCK used to
  166. initialize one of the fields in the new context structure.
  167. Return Value:
  168. NTSTATUS - Completion status.
  169. --*/
  170. {
  171. PSPUD_AFD_REQ_CONTEXT spudReqContext;
  172. NTSTATUS status = STATUS_SUCCESS;
  173. //
  174. // Sanity check.
  175. //
  176. PAGED_CODE();
  177. //
  178. // Try to allocate a new structure.
  179. //
  180. spudReqContext = ExAllocateFromNPagedLookasideList(
  181. &SpudNonpagedData->ReqContextList
  182. );
  183. if( spudReqContext == NULL ) {
  184. return STATUS_INSUFFICIENT_RESOURCES;
  185. }
  186. RtlZeroMemory(
  187. spudReqContext,
  188. sizeof(*spudReqContext)
  189. );
  190. //
  191. // Try to allocate a request handle for the context structure.
  192. //
  193. spudReqContext->ReqHandle = SpudpAllocateReqHandle( spudReqContext );
  194. if( spudReqContext->ReqHandle == SPUD_INVALID_REQ_HANDLE ) {
  195. SpudFreeRequestContext( spudReqContext );
  196. return STATUS_INSUFFICIENT_RESOURCES;
  197. }
  198. spudReqContext->Signature = SPUD_REQ_CONTEXT_SIGNATURE;
  199. spudReqContext->Irp = Irp;
  200. spudReqContext->AtqContext = ReqContext;
  201. try {
  202. ReqContext->KernelReqInfo = spudReqContext->ReqHandle;
  203. if( IoStatusBlock != NULL ) {
  204. spudReqContext->IoStatus1 = *IoStatusBlock;
  205. }
  206. //
  207. // If we got an AFD_RECV_INFO structure, then save the buffer
  208. // length and create a MDL describing the buffer.
  209. //
  210. if( RecvInfo != NULL ) {
  211. spudReqContext->IoStatus2.Information = RecvInfo->BufferArray->len;
  212. spudReqContext->Mdl = IoAllocateMdl(
  213. RecvInfo->BufferArray->buf,
  214. RecvInfo->BufferArray->len,
  215. FALSE,
  216. FALSE,
  217. NULL
  218. );
  219. if( spudReqContext->Mdl == NULL ) {
  220. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  221. }
  222. MmProbeAndLockPages(
  223. spudReqContext->Mdl,
  224. UserMode,
  225. IoWriteAccess
  226. );
  227. }
  228. } except( EXCEPTION_EXECUTE_HANDLER ) {
  229. //
  230. // Bad news. This is either due to a) an AV trying to access
  231. // the user-mode request context structure, or b) an exception
  232. // trying to lock down the receive buffer. If we managed to
  233. // allocate a MDL for the context, then we know the problem
  234. // was because the pages could not be locked, so we'll need to
  235. // free the MDL before continuing. In any case, we'll need to
  236. // free the newly allocated request context.
  237. //
  238. status = GetExceptionCode();
  239. if( spudReqContext->Mdl != NULL ) {
  240. IoFreeMdl( spudReqContext->Mdl );
  241. spudReqContext->Mdl = NULL;
  242. }
  243. SpudFreeRequestContext( spudReqContext );
  244. spudReqContext = NULL;
  245. }
  246. *SpudReqContext = spudReqContext;
  247. return status;
  248. } // SpudAllocateRequestContext
  249. VOID
  250. SpudFreeRequestContext(
  251. IN PSPUD_AFD_REQ_CONTEXT SpudReqContext
  252. )
  253. /*++
  254. Routine Description:
  255. Frees a SPUD_AFD_REQ_CONTEXT structure allocated above.
  256. Arguments:
  257. SpudReqContext - A context structure allocated above.
  258. Return Value:
  259. None.
  260. --*/
  261. {
  262. PMDL mdl, nextMdl;
  263. //
  264. // Sanity check.
  265. //
  266. PAGED_CODE();
  267. ASSERT( SpudReqContext != NULL );
  268. ASSERT( SpudReqContext->Signature == SPUD_REQ_CONTEXT_SIGNATURE );
  269. //
  270. // Unlock & free any MDL chain still attached to the
  271. // request context.
  272. //
  273. mdl = SpudReqContext->Mdl;
  274. SpudReqContext->Mdl = NULL;
  275. while( mdl != NULL ) {
  276. nextMdl = mdl->Next;
  277. MmUnlockPages( mdl );
  278. IoFreeMdl( mdl );
  279. mdl = nextMdl;
  280. }
  281. //
  282. // Free the handle if we managed to allocate one.
  283. //
  284. if( SpudReqContext->ReqHandle != SPUD_INVALID_REQ_HANDLE ) {
  285. ASSERT( SpudpGetReqHandleContext( SpudReqContext->ReqHandle ) == SpudReqContext );
  286. SpudpFreeReqHandle( SpudReqContext->ReqHandle );
  287. SpudReqContext->ReqHandle = SPUD_INVALID_REQ_HANDLE;
  288. }
  289. //
  290. // Free the context.
  291. //
  292. SpudReqContext->Signature = SPUD_REQ_CONTEXT_SIGNATURE_X;
  293. SpudReqContext->Irp = NULL;
  294. ExFreeToNPagedLookasideList(
  295. &SpudNonpagedData->ReqContextList,
  296. SpudReqContext
  297. );
  298. } // SpudFreeRequestContext
  299. PSPUD_AFD_REQ_CONTEXT
  300. SpudGetRequestContext(
  301. IN PSPUD_REQ_CONTEXT ReqContext
  302. )
  303. /*++
  304. Routine Description:
  305. Retrieves the SPUD_AFD_REQ_CONTEXT structure associated with the
  306. incoming user-mode SPUD_REQ_CONTEXT.
  307. Arguments:
  308. ReqContext - The incoming user-mode SPUD_REQ_CONTEXT structure.
  309. Return Value:
  310. PSPUD_AFD_REQ_CONTEXT - Pointer to the associated context structure
  311. if successful, NULL otherwise.
  312. --*/
  313. {
  314. PSPUD_AFD_REQ_CONTEXT spudReqContext = NULL;
  315. PVOID reqHandle;
  316. //
  317. // Sanity check.
  318. //
  319. PAGED_CODE();
  320. //
  321. // Snag the kernel-mode request context handle from the user-mode
  322. // context.
  323. //
  324. try {
  325. reqHandle = ReqContext->KernelReqInfo;
  326. } except( EXCEPTION_EXECUTE_HANDLER ) {
  327. reqHandle = SPUD_INVALID_REQ_HANDLE;
  328. }
  329. if( reqHandle != SPUD_INVALID_REQ_HANDLE ) {
  330. //
  331. // Map the handle.
  332. //
  333. spudReqContext = SpudpGetReqHandleContext( reqHandle );
  334. }
  335. return spudReqContext;
  336. } // SpudGetRequestContext
  337. //
  338. // Private functions.
  339. //
  340. PVOID
  341. SpudpAllocateReqHandle(
  342. IN PSPUD_AFD_REQ_CONTEXT SpudReqContext
  343. )
  344. /*++
  345. Routine Description:
  346. Allocates a new request handle for the specified context.
  347. Arguments:
  348. SpudReqContext - A context structure.
  349. Return Value:
  350. PVOID - The new request handle if successful, SPUD_INVALID_REQ_HANDLE
  351. otherwise.
  352. --*/
  353. {
  354. LONG groupValue;
  355. PSPUD_HANDLE_ENTRY handleEntry;
  356. PSPUD_HANDLE_ENTRY newHandleTable;
  357. LONG newHandleTableSize;
  358. LONG i;
  359. PLIST_ENTRY listEntry;
  360. PVOID reqHandle;
  361. //
  362. // Sanity check.
  363. //
  364. PAGED_CODE();
  365. ASSERT( SpudReqContext != NULL );
  366. //
  367. // See if there's room at the inn.
  368. //
  369. LOCK_HANDLE_TABLE();
  370. if( IsListEmpty( &SpudpFreeList ) ) {
  371. //
  372. // No room, we'll need to create/expand the table.
  373. //
  374. newHandleTableSize = SpudpHandleTableSize + SPUD_HANDLE_TABLE_GROWTH;
  375. newHandleTable = SPUD_ALLOCATE_POOL(
  376. PagedPool,
  377. newHandleTableSize * sizeof(SPUD_HANDLE_ENTRY),
  378. SPUD_HANDLE_TABLE_POOL_TAG
  379. );
  380. if( newHandleTable == NULL ) {
  381. UNLOCK_HANDLE_TABLE();
  382. return NULL;
  383. }
  384. if( SpudpHandleTable == NULL ) {
  385. //
  386. // This is the initial table allocation, so reserve the
  387. // first entry. (NULL is not a valid request handle.)
  388. //
  389. newHandleTable[0].ReqHandle = NULL;
  390. newHandleTable[0].Context = NULL;
  391. SpudpHandleTableSize++;
  392. } else {
  393. //
  394. // Copy the old table into the new table, then free the
  395. // old table.
  396. //
  397. RtlCopyMemory(
  398. newHandleTable,
  399. SpudpHandleTable,
  400. SpudpHandleTableSize * sizeof(SPUD_HANDLE_ENTRY)
  401. );
  402. SPUD_FREE_POOL( SpudpHandleTable );
  403. }
  404. //
  405. // Add the new entries to the free list.
  406. //
  407. for( i = newHandleTableSize - 1 ; i >= SpudpHandleTableSize ; i-- ) {
  408. InsertHeadList(
  409. &SpudpFreeList,
  410. &newHandleTable[i].FreeListEntry
  411. );
  412. }
  413. SpudpHandleTable = newHandleTable;
  414. SpudpHandleTableSize = newHandleTableSize;
  415. }
  416. //
  417. // Pull the next free entry off the list.
  418. //
  419. ASSERT( !IsListEmpty( &SpudpFreeList ) );
  420. listEntry = RemoveHeadList( &SpudpFreeList );
  421. handleEntry = CONTAINING_RECORD(
  422. listEntry,
  423. SPUD_HANDLE_ENTRY,
  424. FreeListEntry
  425. );
  426. //
  427. // Compute the handle and initialize the new handle entry.
  428. //
  429. reqHandle = (PVOID)( handleEntry - SpudpHandleTable );
  430. ASSERT( reqHandle != SPUD_INVALID_REQ_HANDLE );
  431. handleEntry->ReqHandle = reqHandle;
  432. handleEntry->Context = SpudReqContext;
  433. UNLOCK_HANDLE_TABLE();
  434. return reqHandle;
  435. } // SpudpAllocateReqHandle
  436. VOID
  437. SpudpFreeReqHandle(
  438. IN PVOID ReqHandle
  439. )
  440. /*++
  441. Routine Description:
  442. Frees a request handle.
  443. Arguments:
  444. ReqHandle - The request handle to free.
  445. Return Value:
  446. None.
  447. --*/
  448. {
  449. PSPUD_HANDLE_ENTRY handleEntry;
  450. //
  451. // Sanity check.
  452. //
  453. PAGED_CODE();
  454. //
  455. // Map the handle to the table entry. If successful, put the
  456. // entry onto the free list.
  457. //
  458. handleEntry = SpudpMapHandleToEntry( ReqHandle );
  459. if( handleEntry != NULL ) {
  460. InsertTailList(
  461. &SpudpFreeList,
  462. &handleEntry->FreeListEntry
  463. );
  464. UNLOCK_HANDLE_TABLE();
  465. }
  466. } // SpudpFreeReqHandle
  467. PSPUD_AFD_REQ_CONTEXT
  468. SpudpGetReqHandleContext(
  469. IN PVOID ReqHandle
  470. )
  471. /*++
  472. Routine Description:
  473. Retrieves the context value associated with the given request handle.
  474. Arguments:
  475. ReqHandle - The request handle to retrieve.
  476. Return Value:
  477. PSPUD_AFD_REQ_CONTEXT - Pointer to the context if successful,
  478. NULL otherwise.
  479. --*/
  480. {
  481. PSPUD_HANDLE_ENTRY handleEntry;
  482. PSPUD_AFD_REQ_CONTEXT spudReqContext;
  483. //
  484. // Sanity check.
  485. //
  486. PAGED_CODE();
  487. //
  488. // Map the handle to the table entry. If successful, retrieve
  489. // the context.
  490. //
  491. handleEntry = SpudpMapHandleToEntry( ReqHandle );
  492. if( handleEntry != NULL ) {
  493. spudReqContext = handleEntry->Context;
  494. ASSERT( spudReqContext != NULL );
  495. UNLOCK_HANDLE_TABLE();
  496. return spudReqContext;
  497. }
  498. return NULL;
  499. } // SpudpGetReqHandleContext
  500. PSPUD_HANDLE_ENTRY
  501. SpudpMapHandleToEntry(
  502. IN PVOID ReqHandle
  503. )
  504. /*++
  505. Routine Description:
  506. Maps the given request handle to the corresponding SPUD_HANDLE_ENTRY
  507. structure.
  508. N.B. This routine returns with the handle table lock held if successful.
  509. Arguments:
  510. ReqHandle - The handle to map.
  511. Return Value:
  512. PSPUD_HANDLE_ENTRY - The entry corresponding to the request handle if
  513. successful, NULL otherwise.
  514. --*/
  515. {
  516. PSPUD_HANDLE_ENTRY handleEntry;
  517. //
  518. // Sanity check.
  519. //
  520. PAGED_CODE();
  521. //
  522. // Validate the handle.
  523. //
  524. LOCK_HANDLE_TABLE();
  525. if( (LONG_PTR)ReqHandle > 0 && (LONG_PTR)ReqHandle < (LONG_PTR)SpudpHandleTableSize ) {
  526. handleEntry = SpudpHandleTable + (ULONG_PTR)ReqHandle;
  527. //
  528. // The handle is within legal range, ensure it's in use.
  529. //
  530. if( handleEntry->ReqHandle == ReqHandle ) {
  531. return handleEntry;
  532. }
  533. }
  534. //
  535. // Invalid handle, fail it.
  536. //
  537. UNLOCK_HANDLE_TABLE();
  538. return NULL;
  539. } // SpudpMapHandleToEntry