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 user-mode support routines
5  * COPYRIGHT:   Copyright 2011-2020 Thomas Faber <thomas.faber@reactos.org>
6  *              Copyright 2013 Nikolay Borisov <nib9@aber.ac.uk>
7  *              Copyright 2018 Serge Gautherie <reactos-git_serge_171003@gautherie.fr>
8  */
9 
10 #include <kmt_test.h>
11 
12 #include "kmtest.h"
13 #include <kmt_public.h>
14 
15 #include <assert.h>
16 #include <debug.h>
17 
18 extern HANDLE KmtestHandle;
19 
20 /**
21  * @name KmtUserCallbackThread
22  *
23  * Thread routine which awaits callback requests from kernel-mode
24  *
25  * @return Win32 error code
26  */
27 DWORD
28 WINAPI
KmtUserCallbackThread(PVOID Parameter)29 KmtUserCallbackThread(
30     PVOID Parameter)
31 {
32     DWORD Error = ERROR_SUCCESS;
33     /* TODO: RequestPacket? */
34     KMT_CALLBACK_REQUEST_PACKET RequestPacket;
35     KMT_RESPONSE Response;
36     DWORD BytesReturned;
37     HANDLE LocalKmtHandle;
38 
39     UNREFERENCED_PARAMETER(Parameter);
40 
41     /* concurrent IoCtls on the same (non-overlapped) handle aren't possible,
42      * so open a separate one.
43      * For more info http://www.osronline.com/showthread.cfm?link=230782 */
44     LocalKmtHandle = CreateFile(KMTEST_DEVICE_PATH, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
45     if (LocalKmtHandle == INVALID_HANDLE_VALUE)
46         error_goto(Error, cleanup);
47 
48     while (1)
49     {
50         if (!DeviceIoControl(LocalKmtHandle, IOCTL_KMTEST_USERMODE_AWAIT_REQ, NULL, 0, &RequestPacket, sizeof(RequestPacket), &BytesReturned, NULL))
51             error_goto(Error, cleanup);
52         ASSERT(BytesReturned == sizeof(RequestPacket));
53 
54         switch (RequestPacket.OperationClass)
55         {
56             case QueryVirtualMemory:
57             {
58                 SIZE_T InfoBufferSize = VirtualQuery(RequestPacket.Parameters, &Response.MemInfo, sizeof(Response.MemInfo));
59                 /* FIXME: an error is a valid result. That should go as a response to kernel mode instead of terminating the thread */
60                 if (InfoBufferSize == 0)
61                     error_goto(Error, cleanup);
62 
63                 if (!DeviceIoControl(LocalKmtHandle, IOCTL_KMTEST_USERMODE_SEND_RESPONSE, &RequestPacket.RequestId, sizeof(RequestPacket.RequestId), &Response, sizeof(Response), &BytesReturned, NULL))
64                     error_goto(Error, cleanup);
65                 ASSERT(BytesReturned == 0);
66 
67                 break;
68             }
69             default:
70                 DPRINT1("Unrecognized user-mode callback request\n");
71                 break;
72         }
73     }
74 
75 cleanup:
76     if (LocalKmtHandle != INVALID_HANDLE_VALUE)
77         CloseHandle(LocalKmtHandle);
78 
79     DPRINT("Callback handler dying! Error code %lu", Error);
80     return Error;
81 }
82 
83 
84 /**
85  * @name KmtRunKernelTest
86  *
87  * Run the specified kernel-mode test part
88  *
89  * @param TestName
90  *        Name of the test to run
91  *
92  * @return Win32 error code as returned by DeviceIoControl
93  */
94 DWORD
KmtRunKernelTest(IN PCSTR TestName)95 KmtRunKernelTest(
96     IN PCSTR TestName)
97 {
98     HANDLE CallbackThread;
99     DWORD Error = ERROR_SUCCESS;
100     DWORD BytesRead;
101 
102     CallbackThread = CreateThread(NULL, 0, KmtUserCallbackThread, NULL, 0, NULL);
103 
104     if (!DeviceIoControl(KmtestHandle, IOCTL_KMTEST_RUN_TEST, (PVOID)TestName, (DWORD)strlen(TestName), NULL, 0, &BytesRead, NULL))
105         error(Error);
106 
107     if (CallbackThread != NULL)
108          CloseHandle(CallbackThread);
109 
110     return Error;
111 }
112 
113 static WCHAR TestServiceName[MAX_PATH];
114 static SC_HANDLE TestServiceHandle;
115 static HANDLE TestDeviceHandle;
116 
117 /**
118  * @name KmtLoadDriver
119  *
120  * Load the specified special-purpose driver (create/start the service)
121  *
122  * @param ServiceName
123  *        Name of the driver service (Kmtest- prefix will be added automatically)
124  * @param RestartIfRunning
125  *        TRUE to stop and restart the service if it is already running
126  */
127 DWORD
KmtLoadDriver(IN PCWSTR ServiceName,IN BOOLEAN RestartIfRunning)128 KmtLoadDriver(
129     IN PCWSTR ServiceName,
130     IN BOOLEAN RestartIfRunning)
131 {
132     WCHAR ServicePath[MAX_PATH];
133 
134     StringCbCopyW(ServicePath, sizeof(ServicePath), ServiceName);
135     StringCbCatW(ServicePath, sizeof(ServicePath), L"_drv.sys");
136 
137     StringCbCopyW(TestServiceName, sizeof(TestServiceName), L"Kmtest-");
138     StringCbCatW(TestServiceName, sizeof(TestServiceName), ServiceName);
139 
140     return KmtCreateAndStartService(TestServiceName, ServicePath, NULL, &TestServiceHandle, RestartIfRunning);
141 }
142 
143 /**
144  * @name KmtUnloadDriverKeepService
145  *
146  * Unload special-purpose driver (stop the service only)
147  */
148 VOID
KmtUnloadDriverKeepService(VOID)149 KmtUnloadDriverKeepService(VOID)
150 {
151     DWORD Error;
152 
153     Error = KmtStopService(TestServiceName, &TestServiceHandle);
154 
155     if (Error)
156     {
157         fprintf(stderr, "Failed to stop %ls service with error 0x%lx\n", TestServiceName, Error);
158     }
159 }
160 
161 /**
162  * @name KmtUnloadDriver
163  *
164  * Unload special-purpose driver (stop and delete the service)
165  */
166 VOID
KmtUnloadDriver(VOID)167 KmtUnloadDriver(VOID)
168 {
169     DWORD Error;
170 
171     Error = KmtStopService(TestServiceName, &TestServiceHandle);
172 
173     if (Error)
174     {
175         fprintf(stderr, "Failed to stop %ls service with error 0x%lx\n", TestServiceName, Error);
176     }
177 
178     Error = KmtDeleteService(TestServiceName, &TestServiceHandle);
179 
180     if (Error)
181     {
182         fprintf(stderr, "Failed to delete %ls service with error 0x%lx\n", TestServiceName, Error);
183     }
184 }
185 
186 /**
187  * @name KmtOpenDriver
188  *
189  * Open special-purpose driver (acquire a device handle)
190  */
191 DWORD
KmtOpenDriver(VOID)192 KmtOpenDriver(VOID)
193 {
194     DWORD Error = ERROR_SUCCESS;
195     WCHAR DevicePath[MAX_PATH];
196 
197     StringCbCopyW(DevicePath, sizeof(DevicePath), L"\\\\.\\Global\\GLOBALROOT\\Device\\");
198     StringCbCatW(DevicePath, sizeof(DevicePath), TestServiceName);
199 
200     TestDeviceHandle = CreateFile(DevicePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
201     if (TestDeviceHandle == INVALID_HANDLE_VALUE)
202         error(Error);
203 
204     return Error;
205 }
206 
207 /**
208  * @name KmtOpenDriver
209  *
210  * Load and open special-purpose driver (acquire a device handle)
211  */
212 DWORD
KmtLoadAndOpenDriver(IN PCWSTR ServiceName,IN BOOLEAN RestartIfRunning)213 KmtLoadAndOpenDriver(
214     IN PCWSTR ServiceName,
215     IN BOOLEAN RestartIfRunning)
216 {
217     DWORD Error;
218 
219     Error = KmtLoadDriver(ServiceName, RestartIfRunning);
220     if (Error)
221         return Error;
222 
223     Error = KmtOpenDriver();
224     if (Error)
225         return Error;
226 
227     return ERROR_SUCCESS;
228 }
229 
230 /**
231  * @name KmtCloseDriver
232  *
233  * Close special-purpose driver (close device handle)
234  */
235 VOID
KmtCloseDriver(VOID)236 KmtCloseDriver(VOID)
237 {
238     DWORD Error = ERROR_SUCCESS;
239 
240     if (TestDeviceHandle && !CloseHandle(TestDeviceHandle))
241         error(Error);
242 
243     if (Error)
244     {
245         DPRINT1("CloseHandle failed: 0x%lx\n", Error);
246     }
247 }
248 
249 /**
250  * @name KmtSendToDriver
251  *
252  * Send an I/O control message with no arguments to the driver opened with KmtOpenDriver
253  *
254  * @param ControlCode
255  *
256  * @return Win32 error code as returned by DeviceIoControl
257  */
258 DWORD
KmtSendToDriver(IN DWORD ControlCode)259 KmtSendToDriver(
260     IN DWORD ControlCode)
261 {
262     DWORD BytesRead;
263 
264     assert(ControlCode < 0x400);
265 
266     if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), NULL, 0, NULL, 0, &BytesRead, NULL))
267         return GetLastError();
268 
269     return ERROR_SUCCESS;
270 }
271 
272 /**
273  * @name KmtSendStringToDriver
274  *
275  * Send an I/O control message with a string argument to the driver opened with KmtOpenDriver
276  *
277  * @param ControlCode
278  * @param String
279  *
280  * @return Win32 error code as returned by DeviceIoControl
281  */
282 DWORD
KmtSendStringToDriver(IN DWORD ControlCode,IN PCSTR String)283 KmtSendStringToDriver(
284     IN DWORD ControlCode,
285     IN PCSTR String)
286 {
287     DWORD BytesRead;
288 
289     assert(ControlCode < 0x400);
290 
291     if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), (PVOID)String, (DWORD)strlen(String), NULL, 0, &BytesRead, NULL))
292         return GetLastError();
293 
294     return ERROR_SUCCESS;
295 }
296 
297 /**
298  * @name KmtSendWStringToDriver
299  *
300  * Send an I/O control message with a wide string argument to the driver opened with KmtOpenDriver
301  *
302  * @param ControlCode
303  * @param String
304  *
305  * @return Win32 error code as returned by DeviceIoControl
306  */
307 DWORD
KmtSendWStringToDriver(IN DWORD ControlCode,IN PCWSTR String)308 KmtSendWStringToDriver(
309     IN DWORD ControlCode,
310     IN PCWSTR String)
311 {
312     DWORD BytesRead;
313 
314     assert(ControlCode < 0x400);
315 
316     if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), (PVOID)String, (DWORD)wcslen(String) * sizeof(WCHAR), NULL, 0, &BytesRead, NULL))
317         return GetLastError();
318 
319     return ERROR_SUCCESS;
320 }
321 
322 /**
323  * @name KmtSendUlongToDriver
324  *
325  * Send an I/O control message with an integer argument to the driver opened with KmtOpenDriver
326  *
327  * @param ControlCode
328  * @param Value
329  *
330  * @return Win32 error code as returned by DeviceIoControl
331  */
332 DWORD
KmtSendUlongToDriver(IN DWORD ControlCode,IN DWORD Value)333 KmtSendUlongToDriver(
334     IN DWORD ControlCode,
335     IN DWORD Value)
336 {
337     DWORD BytesRead;
338 
339     assert(ControlCode < 0x400);
340 
341     if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), &Value, sizeof(Value), NULL, 0, &BytesRead, NULL))
342         return GetLastError();
343 
344     return ERROR_SUCCESS;
345 }
346 
347 /**
348  * @name KmtSendBufferToDriver
349  *
350  * Send an I/O control message with the specified arguments to the driver opened with KmtOpenDriver
351  *
352  * @param ControlCode
353  * @param Buffer
354  * @param InLength
355  * @param OutLength
356  *
357  * @return Win32 error code as returned by DeviceIoControl
358  */
359 DWORD
KmtSendBufferToDriver(IN DWORD ControlCode,IN OUT PVOID Buffer OPTIONAL,IN DWORD InLength,IN OUT PDWORD OutLength)360 KmtSendBufferToDriver(
361     IN DWORD ControlCode,
362     IN OUT PVOID Buffer OPTIONAL,
363     IN DWORD InLength,
364     IN OUT PDWORD OutLength)
365 {
366     assert(OutLength);
367     assert(Buffer || (!InLength && !*OutLength));
368     assert(ControlCode < 0x400);
369 
370     if (!DeviceIoControl(TestDeviceHandle, KMT_MAKE_CODE(ControlCode), Buffer, InLength, Buffer, *OutLength, OutLength, NULL))
371         return GetLastError();
372 
373     return ERROR_SUCCESS;
374 }
375