1 /*
2  * PROJECT:         ReactOS kernel-mode tests
3  * LICENSE:         GPLv2+ - See COPYING in the top level directory
4  * PURPOSE:         Kernel-Mode Test Suite Driver
5  * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
6  */
7 
8 #include <ntddk.h>
9 #include <ntifs.h>
10 #include <ndk/ketypes.h>
11 #include <ntstrsafe.h>
12 #include <limits.h>
13 #include <pseh/pseh2.h>
14 
15 #define NDEBUG
16 #include <debug.h>
17 
18 #include <kmt_public.h>
19 #define KMT_DEFINE_TEST_FUNCTIONS
20 #include <kmt_test.h>
21 
22 /* Usermode callback definitions */
23 typedef struct _KMT_USER_WORK_ENTRY
24 {
25     LIST_ENTRY ListEntry;
26     KEVENT WorkDoneEvent;
27     KMT_CALLBACK_REQUEST_PACKET Request;
28     PKMT_RESPONSE Response;
29 } KMT_USER_WORK_ENTRY, *PKMT_USER_WORK_ENTRY;
30 
31 typedef struct _KMT_USER_WORK_LIST
32 {
33     LIST_ENTRY ListHead;
34     FAST_MUTEX Lock;
35     KEVENT NewWorkEvent;
36 } KMT_USER_WORK_LIST, *PKMT_USER_WORK_LIST;
37 
38 /* Prototypes */
39 DRIVER_INITIALIZE DriverEntry;
40 static DRIVER_UNLOAD DriverUnload;
41 __drv_dispatchType(IRP_MJ_CREATE)
42 static DRIVER_DISPATCH DriverCreate;
43 __drv_dispatchType(IRP_MJ_CLEANUP)
44 static DRIVER_DISPATCH DriverCleanup;
45 __drv_dispatchType(IRP_MJ_CLOSE)
46 static DRIVER_DISPATCH DriverClose;
47 __drv_dispatchType(IRP_MJ_DEVICE_CONTROL)
48 static DRIVER_DISPATCH DriverIoControl;
49 static VOID KmtCleanUsermodeCallbacks(VOID);
50 
51 /* Globals */
52 static PDEVICE_OBJECT MainDeviceObject;
53 PDRIVER_OBJECT KmtDriverObject = NULL;
54 static KMT_USER_WORK_LIST WorkList;
55 static ULONG RequestId = 0;
56 
57 /* Entry */
58 /**
59  * @name DriverEntry
60  *
61  * Driver Entry point.
62  *
63  * @param DriverObject
64  *        Driver Object
65  * @param RegistryPath
66  *        Driver Registry Path
67  *
68  * @return Status
69  */
70 NTSTATUS
71 NTAPI
72 DriverEntry(
73     IN PDRIVER_OBJECT DriverObject,
74     IN PUNICODE_STRING RegistryPath)
75 {
76     NTSTATUS Status = STATUS_SUCCESS;
77     UNICODE_STRING DeviceName;
78     PKMT_DEVICE_EXTENSION DeviceExtension;
79     PKPRCB Prcb;
80 
81     PAGED_CODE();
82 
83     UNREFERENCED_PARAMETER(RegistryPath);
84 
85     DPRINT("DriverEntry\n");
86 
87     Prcb = KeGetCurrentPrcb();
88     KmtIsCheckedBuild = (Prcb->BuildType & PRCB_BUILD_DEBUG) != 0;
89     KmtIsMultiProcessorBuild = (Prcb->BuildType & PRCB_BUILD_UNIPROCESSOR) == 0;
90     KmtDriverObject = DriverObject;
91 
92     RtlInitUnicodeString(&DeviceName, KMTEST_DEVICE_DRIVER_PATH);
93     Status = IoCreateDevice(DriverObject, sizeof(KMT_DEVICE_EXTENSION),
94                             &DeviceName,
95                             FILE_DEVICE_UNKNOWN,
96                             FILE_DEVICE_SECURE_OPEN | FILE_READ_ONLY_DEVICE,
97                             FALSE, &MainDeviceObject);
98 
99     if (!NT_SUCCESS(Status))
100         goto cleanup;
101 
102     DPRINT("DriverEntry. Created DeviceObject %p. DeviceExtension %p\n",
103              MainDeviceObject, MainDeviceObject->DeviceExtension);
104     DeviceExtension = MainDeviceObject->DeviceExtension;
105     DeviceExtension->ResultBuffer = NULL;
106     DeviceExtension->Mdl = NULL;
107 
108     DriverObject->DriverUnload = DriverUnload;
109     DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverCreate;
110     DriverObject->MajorFunction[IRP_MJ_CLEANUP] = DriverCleanup;
111     DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverClose;
112     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverIoControl;
113 
114     ExInitializeFastMutex(&WorkList.Lock);
115     KeInitializeEvent(&WorkList.NewWorkEvent, NotificationEvent, FALSE);
116     InitializeListHead(&WorkList.ListHead);
117 
118 cleanup:
119     if (MainDeviceObject && !NT_SUCCESS(Status))
120     {
121         IoDeleteDevice(MainDeviceObject);
122         MainDeviceObject = NULL;
123     }
124 
125     return Status;
126 }
127 
128 /* Dispatch functions */
129 /**
130  * @name DriverUnload
131  *
132  * Driver cleanup funtion.
133  *
134  * @param DriverObject
135  *        Driver Object
136  */
137 static
138 VOID
139 NTAPI
140 DriverUnload(
141     IN PDRIVER_OBJECT DriverObject)
142 {
143     PAGED_CODE();
144 
145     UNREFERENCED_PARAMETER(DriverObject);
146 
147     DPRINT("DriverUnload\n");
148 
149     KmtCleanUsermodeCallbacks();
150 
151     if (MainDeviceObject)
152     {
153 #if DBG
154         PKMT_DEVICE_EXTENSION DeviceExtension = MainDeviceObject->DeviceExtension;
155         ASSERT(!DeviceExtension->Mdl);
156         ASSERT(!DeviceExtension->ResultBuffer);
157 #endif
158         ASSERT(!ResultBuffer);
159         IoDeleteDevice(MainDeviceObject);
160     }
161 }
162 
163 /**
164  * @name DriverCreate
165  *
166  * Driver Dispatch function for IRP_MJ_CREATE
167  *
168  * @param DeviceObject
169  *        Device Object
170  * @param Irp
171  *        I/O request packet
172  *
173  * @return Status
174  */
175 static
176 NTSTATUS
177 NTAPI
178 DriverCreate(
179     IN PDEVICE_OBJECT DeviceObject,
180     IN PIRP Irp)
181 {
182     NTSTATUS Status = STATUS_SUCCESS;
183     PIO_STACK_LOCATION IoStackLocation;
184 
185     PAGED_CODE();
186 
187     IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
188 
189     DPRINT("DriverCreate. DeviceObject=%p, RequestorMode=%d, FileObject=%p, FsContext=%p, FsContext2=%p\n",
190              DeviceObject, Irp->RequestorMode, IoStackLocation->FileObject,
191              IoStackLocation->FileObject->FsContext, IoStackLocation->FileObject->FsContext2);
192 
193     Irp->IoStatus.Status = Status;
194     Irp->IoStatus.Information = 0;
195 
196     IoCompleteRequest(Irp, IO_NO_INCREMENT);
197 
198     return Status;
199 }
200 
201 /**
202  * @name DriverCleanup
203  *
204  * Driver Dispatch function for IRP_MJ_CLEANUP
205  *
206  * @param DeviceObject
207  *        Device Object
208  * @param Irp
209  *        I/O request packet
210  *
211  * @return Status
212  */
213 static
214 NTSTATUS
215 NTAPI
216 DriverCleanup(
217     IN PDEVICE_OBJECT DeviceObject,
218     IN PIRP Irp)
219 {
220     NTSTATUS Status = STATUS_SUCCESS;
221     PIO_STACK_LOCATION IoStackLocation;
222     PKMT_DEVICE_EXTENSION DeviceExtension;
223 
224     PAGED_CODE();
225 
226     IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
227 
228     DPRINT("DriverCleanup. DeviceObject=%p, RequestorMode=%d, FileObject=%p, FsContext=%p, FsContext2=%p\n",
229              DeviceObject, Irp->RequestorMode, IoStackLocation->FileObject,
230              IoStackLocation->FileObject->FsContext, IoStackLocation->FileObject->FsContext2);
231 
232     ASSERT(IoStackLocation->FileObject->FsContext2 == NULL);
233     DeviceExtension = DeviceObject->DeviceExtension;
234     if (DeviceExtension->Mdl && IoStackLocation->FileObject->FsContext == DeviceExtension->Mdl)
235     {
236         MmUnlockPages(DeviceExtension->Mdl);
237         IoFreeMdl(DeviceExtension->Mdl);
238         DeviceExtension->Mdl = NULL;
239         ResultBuffer = DeviceExtension->ResultBuffer = NULL;
240     }
241     else
242     {
243         ASSERT(IoStackLocation->FileObject->FsContext == NULL);
244     }
245 
246     Irp->IoStatus.Status = Status;
247     Irp->IoStatus.Information = 0;
248 
249     IoCompleteRequest(Irp, IO_NO_INCREMENT);
250 
251     return Status;
252 }
253 
254 /**
255  * @name DriverClose
256  *
257  * Driver Dispatch function for IRP_MJ_CLOSE
258  *
259  * @param DeviceObject
260  *        Device Object
261  * @param Irp
262  *        I/O request packet
263  *
264  * @return Status
265  */
266 static
267 NTSTATUS
268 NTAPI
269 DriverClose(
270     IN PDEVICE_OBJECT DeviceObject,
271     IN PIRP Irp)
272 {
273     NTSTATUS Status = STATUS_SUCCESS;
274 
275     PAGED_CODE();
276 
277     DPRINT("DriverClose. DeviceObject=%p, RequestorMode=%d\n",
278              DeviceObject, Irp->RequestorMode);
279 
280     Irp->IoStatus.Status = Status;
281     Irp->IoStatus.Information = 0;
282 
283     IoCompleteRequest(Irp, IO_NO_INCREMENT);
284 
285     return Status;
286 }
287 
288 /**
289  * @name DriverIoControl
290  *
291  * Driver Dispatch function for IRP_MJ_DEVICE_CONTROL
292  *
293  * @param DeviceObject
294  *        Device Object
295  * @param Irp
296  *        I/O request packet
297  *
298  * @return Status
299  */
300 static
301 NTSTATUS
302 NTAPI
303 DriverIoControl(
304     IN PDEVICE_OBJECT DeviceObject,
305     IN PIRP Irp)
306 {
307     NTSTATUS Status = STATUS_SUCCESS;
308     PIO_STACK_LOCATION IoStackLocation;
309     SIZE_T Length = 0;
310 
311     PAGED_CODE();
312 
313     IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
314 
315     DPRINT("DriverIoControl. Code=0x%08X, DeviceObject=%p, FileObject=%p, FsContext=%p, FsContext2=%p\n",
316              IoStackLocation->Parameters.DeviceIoControl.IoControlCode,
317              DeviceObject, IoStackLocation->FileObject,
318              IoStackLocation->FileObject->FsContext, IoStackLocation->FileObject->FsContext2);
319 
320     switch (IoStackLocation->Parameters.DeviceIoControl.IoControlCode)
321     {
322         case IOCTL_KMTEST_GET_TESTS:
323         {
324             PCKMT_TEST TestEntry;
325             LPSTR OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
326             size_t Remaining = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
327 
328             DPRINT("DriverIoControl. IOCTL_KMTEST_GET_TESTS, outlen=%lu\n",
329                      IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
330 
331             for (TestEntry = TestList; TestEntry->TestName; ++TestEntry)
332             {
333                 RtlStringCbCopyExA(OutputBuffer, Remaining, TestEntry->TestName, &OutputBuffer, &Remaining, 0);
334                 if (Remaining)
335                 {
336                     *OutputBuffer++ = '\0';
337                     --Remaining;
338                 }
339             }
340             if (Remaining)
341             {
342                 *OutputBuffer++ = '\0';
343                 --Remaining;
344             }
345             Length = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength - Remaining;
346             break;
347         }
348         case IOCTL_KMTEST_RUN_TEST:
349         {
350             ANSI_STRING TestName;
351             PCKMT_TEST TestEntry;
352 
353             DPRINT("DriverIoControl. IOCTL_KMTEST_RUN_TEST, inlen=%lu, outlen=%lu\n",
354                      IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
355                      IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
356             TestName.Length = TestName.MaximumLength = (USHORT)min(IoStackLocation->Parameters.DeviceIoControl.InputBufferLength, USHRT_MAX);
357             TestName.Buffer = Irp->AssociatedIrp.SystemBuffer;
358             DPRINT("DriverIoControl. Run test: %Z\n", &TestName);
359 
360             for (TestEntry = TestList; TestEntry->TestName; ++TestEntry)
361             {
362                 ANSI_STRING EntryName;
363                 if (TestEntry->TestName[0] == '-')
364                     RtlInitAnsiString(&EntryName, TestEntry->TestName + 1);
365                 else
366                     RtlInitAnsiString(&EntryName, TestEntry->TestName);
367 
368                 if (!RtlCompareString(&TestName, &EntryName, FALSE))
369                 {
370                     DPRINT1("DriverIoControl. Starting test %Z\n", &EntryName);
371                     TestEntry->TestFunction();
372                     DPRINT1("DriverIoControl. Finished test %Z\n", &EntryName);
373                     break;
374                 }
375             }
376 
377             if (!TestEntry->TestName)
378                 Status = STATUS_OBJECT_NAME_INVALID;
379 
380             break;
381         }
382         case IOCTL_KMTEST_SET_RESULTBUFFER:
383         {
384             PKMT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
385 
386             DPRINT("DriverIoControl. IOCTL_KMTEST_SET_RESULTBUFFER, buffer=%p, inlen=%lu, outlen=%lu\n",
387                     IoStackLocation->Parameters.DeviceIoControl.Type3InputBuffer,
388                     IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
389                     IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
390 
391             if (DeviceExtension->Mdl)
392             {
393                 if (IoStackLocation->FileObject->FsContext != DeviceExtension->Mdl)
394                 {
395                     Status = STATUS_ACCESS_DENIED;
396                     break;
397                 }
398                 MmUnlockPages(DeviceExtension->Mdl);
399                 IoFreeMdl(DeviceExtension->Mdl);
400                 IoStackLocation->FileObject->FsContext = NULL;
401                 ResultBuffer = DeviceExtension->ResultBuffer = NULL;
402             }
403 
404             DeviceExtension->Mdl = IoAllocateMdl(IoStackLocation->Parameters.DeviceIoControl.Type3InputBuffer,
405                                                     IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
406                                                     FALSE, FALSE, NULL);
407             if (!DeviceExtension->Mdl)
408             {
409                 Status = STATUS_INSUFFICIENT_RESOURCES;
410                 break;
411             }
412 
413             _SEH2_TRY
414             {
415                 MmProbeAndLockPages(DeviceExtension->Mdl, UserMode, IoModifyAccess);
416             }
417             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
418             {
419                 Status = _SEH2_GetExceptionCode();
420                 IoFreeMdl(DeviceExtension->Mdl);
421                 DeviceExtension->Mdl = NULL;
422                 break;
423             } _SEH2_END;
424 
425             ResultBuffer = DeviceExtension->ResultBuffer = MmGetSystemAddressForMdlSafe(DeviceExtension->Mdl, NormalPagePriority);
426             IoStackLocation->FileObject->FsContext = DeviceExtension->Mdl;
427 
428             DPRINT("DriverIoControl. ResultBuffer: %ld %ld %ld %ld\n",
429                     ResultBuffer->Successes, ResultBuffer->Failures,
430                     ResultBuffer->LogBufferLength, ResultBuffer->LogBufferMaxLength);
431             break;
432         }
433         case IOCTL_KMTEST_USERMODE_AWAIT_REQ:
434         {
435             PLIST_ENTRY Entry;
436             PKMT_USER_WORK_ENTRY WorkItem;
437 
438             DPRINT("DriverIoControl. IOCTL_KMTEST_USERMODE_AWAIT_REQ, len=%lu\n",
439                     IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
440 
441             /* TODO: prevent multiple concurrent invocations */
442             Status = KeWaitForSingleObject(&WorkList.NewWorkEvent, UserRequest, UserMode, FALSE, NULL);
443             if (Status == STATUS_USER_APC || Status == STATUS_KERNEL_APC)
444                 break;
445 
446             if (IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KMT_CALLBACK_REQUEST_PACKET))
447             {
448                 Status = STATUS_INVALID_BUFFER_SIZE;
449                 break;
450             }
451 
452             ASSERT(!IsListEmpty(&WorkList.ListHead));
453 
454             Entry = WorkList.ListHead.Flink;
455             WorkItem = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
456 
457             Length = sizeof(WorkItem->Request);
458             RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, &WorkItem->Request, Length);
459             Status = STATUS_SUCCESS;
460 
461             KeResetEvent(&WorkList.NewWorkEvent);
462             break;
463 
464         }
465         case IOCTL_KMTEST_USERMODE_SEND_RESPONSE:
466         {
467             PLIST_ENTRY Entry;
468             PKMT_USER_WORK_ENTRY WorkEntry;
469             PVOID Response;
470             ULONG ResponseSize = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
471 
472             DPRINT("DriverIoControl. IOCTL_KMTEST_USERMODE_SEND_RESPONSE, inlen=%lu, outlen=%lu\n",
473                     IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
474                     IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
475 
476             if (IoStackLocation->Parameters.DeviceIoControl.InputBufferLength != sizeof(ULONG) || ResponseSize != sizeof(KMT_RESPONSE))
477             {
478                 Status = STATUS_INVALID_BUFFER_SIZE;
479                 break;
480             }
481 
482             /* FIXME: don't misuse the output buffer as an input! */
483             Response = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
484             if (Response == NULL)
485             {
486                 Status = STATUS_INSUFFICIENT_RESOURCES;
487                 break;
488             }
489 
490             ExAcquireFastMutex(&WorkList.Lock);
491 
492             Status = STATUS_OBJECTID_NOT_FOUND;
493 
494             Entry = WorkList.ListHead.Flink;
495             while (Entry != &WorkList.ListHead)
496             {
497                 WorkEntry = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
498                 if (WorkEntry->Request.RequestId == *(PULONG)Irp->AssociatedIrp.SystemBuffer)
499                 {
500                     WorkEntry->Response = ExAllocatePoolWithTag(PagedPool, sizeof(KMT_RESPONSE), 'pseR');
501                     if (WorkEntry->Response == NULL)
502                     {
503                         Status = STATUS_INSUFFICIENT_RESOURCES;
504                         break;
505                     }
506 
507                     RtlCopyMemory(WorkEntry->Response, Response, ResponseSize);
508                     KeSetEvent(&WorkEntry->WorkDoneEvent, IO_NO_INCREMENT, FALSE);
509                     Status = STATUS_SUCCESS;
510                     break;
511                 }
512 
513                 Entry = Entry->Flink;
514             }
515 
516             ExReleaseFastMutex(&WorkList.Lock);
517 
518             break;
519         }
520         default:
521             DPRINT1("DriverIoControl. Invalid IoCtl code 0x%08X\n",
522                      IoStackLocation->Parameters.DeviceIoControl.IoControlCode);
523             Status = STATUS_INVALID_DEVICE_REQUEST;
524             break;
525     }
526 
527     Irp->IoStatus.Status = Status;
528     Irp->IoStatus.Information = Length;
529 
530     IoCompleteRequest(Irp, IO_NO_INCREMENT);
531 
532     return Status;
533 }
534 
535 /**
536  * @name KmtUserModeCallback
537  *
538  * Enqueue a request to the usermode callback queue and blocks until the work
539  * is finished.
540  *
541  * @param Operation
542  *        TODO
543  * @param Parameters
544  *        TODO
545  *        TODO: why is this PVOID?
546  *
547  * @return Response from user mode
548  */
549 PKMT_RESPONSE
550 KmtUserModeCallback(
551     IN KMT_CALLBACK_INFORMATION_CLASS Operation,
552     IN PVOID Parameters)
553 {
554     PKMT_RESPONSE Result;
555     NTSTATUS Status;
556     PKMT_USER_WORK_ENTRY WorkEntry;
557     LARGE_INTEGER Timeout;
558 
559     PAGED_CODE();
560 
561     WorkEntry = ExAllocatePoolWithTag(PagedPool, sizeof(KMT_USER_WORK_ENTRY), 'ekrW');
562     if (WorkEntry == NULL)
563         return NULL;
564 
565     KeInitializeEvent(&WorkEntry->WorkDoneEvent, NotificationEvent, FALSE);
566     WorkEntry->Request.RequestId = RequestId++;
567     WorkEntry->Request.OperationClass = Operation;
568     WorkEntry->Request.Parameters = Parameters;
569     WorkEntry->Response = NULL;
570 
571     ExAcquireFastMutex(&WorkList.Lock);
572     InsertTailList(&WorkList.ListHead, &WorkEntry->ListEntry);
573     ExReleaseFastMutex(&WorkList.Lock);
574 
575     KeSetEvent(&WorkList.NewWorkEvent, IO_NO_INCREMENT, FALSE);
576 
577     Timeout.QuadPart = -10 * 1000 * 1000 * 10; //wait for 10 seconds
578     Status = KeWaitForSingleObject(&WorkEntry->WorkDoneEvent, Executive, UserMode, FALSE, &Timeout);
579 
580     if (Status == STATUS_USER_APC || Status == STATUS_KERNEL_APC || Status == STATUS_TIMEOUT)
581     {
582         DPRINT1("Unexpected callback abortion! Reason: %lx\n", Status);
583     }
584 
585     ExAcquireFastMutex(&WorkList.Lock);
586     RemoveEntryList(&WorkEntry->ListEntry);
587     ExReleaseFastMutex(&WorkList.Lock);
588 
589     Result = WorkEntry->Response;
590 
591     ExFreePoolWithTag(WorkEntry, 'ekrW');
592 
593     return Result;
594 }
595 
596 /**
597  * @name KmtFreeCallbackResponse
598  *
599  * TODO
600  *
601  * @param Response
602  *        TODO
603  */
604 VOID
605 KmtFreeCallbackResponse(
606     PKMT_RESPONSE Response)
607 {
608     PAGED_CODE();
609 
610     ExFreePoolWithTag(Response, 'pseR');
611 }
612 
613 /**
614  * @name KmtCleanUsermodeCallbacks
615  *
616  * TODO
617  */
618 static
619 VOID
620 KmtCleanUsermodeCallbacks(VOID)
621 {
622     PLIST_ENTRY Entry;
623 
624     PAGED_CODE();
625 
626     ExAcquireFastMutex(&WorkList.Lock);
627 
628     Entry = WorkList.ListHead.Flink;
629     while (Entry != &WorkList.ListHead)
630     {
631         PKMT_USER_WORK_ENTRY WorkEntry = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
632         if (WorkEntry->Response != NULL)
633         {
634             KmtFreeCallbackResponse(WorkEntry->Response);
635         }
636 
637         Entry = Entry->Flink;
638 
639         ExFreePoolWithTag(WorkEntry, 'ekrW');
640     }
641 
642     ExReleaseFastMutex(&WorkList.Lock);
643 }
644