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