1 /*
2  * PROJECT:     ReactOS kernel-mode tests
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Kernel mode tests for object information querying
5  * COPYRIGHT:   Copyright 2023 George Bișoc <george.bisoc@reactos.org>
6  *              Copyright 2024 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
7  */
8 
9 #include <kmt_test.h>
10 
11 #define OBJ_WINSTA_DIRECTORY_NAME_INFO_SIZE (sizeof(UNICODE_STRING) + sizeof(L"\\Windows"))
12 #define OBJ_DIRECTORY_TYPE_INFO_SIZE (sizeof(OBJECT_TYPE_INFORMATION) + sizeof(L"Directory"))
13 
14 static
15 VOID
ObjectBasicInformationTests(VOID)16 ObjectBasicInformationTests(VOID)
17 {
18     NTSTATUS Status;
19     HANDLE WinStaDirHandle;
20     OBJECT_BASIC_INFORMATION BasicInfo;
21     ULONG ReturnLength;
22     OBJECT_ATTRIBUTES ObjectAttributes;
23     static UNICODE_STRING WinStaDir = RTL_CONSTANT_STRING(L"\\Windows");
24 
25     /* We must be in PASSIVE_LEVEL to do all of this stuff */
26     ok_irql(PASSIVE_LEVEL);
27 
28     /* Create a path to \Windows directory */
29     InitializeObjectAttributes(&ObjectAttributes,
30                                &WinStaDir,
31                                OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_KERNEL_HANDLE,
32                                NULL,
33                                NULL);
34     Status = ZwOpenDirectoryObject(&WinStaDirHandle,
35                                    DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
36                                    &ObjectAttributes);
37     if (!NT_SUCCESS(Status))
38     {
39         ok(FALSE, "Failed to open \\Windows directory (Status 0x%lx)\n", Status);
40         return;
41     }
42 
43     /* Give 0 as information length, this must fail */
44     Status = ZwQueryObject(WinStaDirHandle,
45                            ObjectBasicInformation,
46                            &BasicInfo,
47                            0,
48                            &ReturnLength);
49     ok_eq_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
50 
51     /* Do a proper query now */
52     Status = ZwQueryObject(WinStaDirHandle,
53                            ObjectBasicInformation,
54                            &BasicInfo,
55                            sizeof(BasicInfo),
56                            &ReturnLength);
57     ok_eq_hex(Status, STATUS_SUCCESS);
58 
59     /* \Windows is currently used */
60     ok(BasicInfo.HandleCount != 0, "\\Windows is in use but HandleCount is 0!\n");
61     ok(BasicInfo.PointerCount != 0, "\\Windows is in use but PointerCount is 0!\n");
62 
63     ok_eq_ulong(BasicInfo.NameInfoSize, OBJ_WINSTA_DIRECTORY_NAME_INFO_SIZE);
64     ok_eq_ulong(BasicInfo.TypeInfoSize, OBJ_DIRECTORY_TYPE_INFO_SIZE);
65 
66     ZwClose(WinStaDirHandle);
67 }
68 
69 /* Flags combination allowing all the read, write and delete share modes.
70  * Currently similar to FILE_SHARE_VALID_FLAGS. */
71 #define FILE_SHARE_ALL \
72     (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
73 
74 #define ok_ntstatus ok_eq_hex
75 
76 /* Adapted from apitests/ntdll/NtQueryObject.c!START_TEST(NtQueryObject).
77  * Please sync both tests in case you add or remove new features. */
78 static
79 VOID
ObjectNameInformationTests(VOID)80 ObjectNameInformationTests(VOID)
81 {
82     ULONG g_OsVersion =
83         SharedUserData->NtMajorVersion << 8 | SharedUserData->NtMinorVersion;
84 
85     NTSTATUS Status;
86     HANDLE DeviceHandle;
87     UNICODE_STRING DeviceName;
88     OBJECT_ATTRIBUTES ObjectAttributes;
89     IO_STATUS_BLOCK IoStatusBlock;
90 
91     ULONG BufferSize1, BufferSize2, BufferSize3;
92     struct { OBJECT_NAME_INFORMATION; WCHAR Buffer[MAX_PATH]; } ObjectNameBuffer;
93     PUNICODE_STRING ObjectName = &ObjectNameBuffer.Name;
94 
95     /* Test the drive containing SystemRoot */
96     WCHAR NtDeviceName[] = L"\\DosDevices\\?:";
97     NtDeviceName[sizeof("\\DosDevices\\")-1] = SharedUserData->NtSystemRoot[0];
98 
99     /* We must be in PASSIVE_LEVEL to do all of this stuff */
100     ok_irql(PASSIVE_LEVEL);
101 
102     /* Open a handle to the device */
103     RtlInitUnicodeString(&DeviceName, NtDeviceName);
104     InitializeObjectAttributes(&ObjectAttributes,
105                                &DeviceName,
106                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
107                                NULL,
108                                NULL);
109     Status = ZwOpenFile(&DeviceHandle,
110                         FILE_READ_ATTRIBUTES | SYNCHRONIZE,
111                         &ObjectAttributes,
112                         &IoStatusBlock,
113                         FILE_SHARE_ALL,
114                         FILE_SYNCHRONOUS_IO_NONALERT);
115     ok_ntstatus(Status, STATUS_SUCCESS);
116     if (skip(NT_SUCCESS(Status), "Device '%wZ': Opening failed\n", &DeviceName))
117         return;
118 
119     /* Invoke ObjectNameInformation that retrieves the canonical device name */
120     Status = ZwQueryObject(DeviceHandle,
121                            ObjectNameInformation,
122                            &ObjectNameBuffer,
123                            0,
124                            &BufferSize1);
125     ok_ntstatus(Status, STATUS_INFO_LENGTH_MISMATCH);
126 
127     Status = ZwQueryObject(DeviceHandle,
128                            ObjectNameInformation,
129                            &ObjectNameBuffer,
130                            sizeof(OBJECT_NAME_INFORMATION),
131                            &BufferSize2);
132     ok_ntstatus(Status, STATUS_BUFFER_OVERFLOW);
133 
134     Status = ZwQueryObject(DeviceHandle,
135                            ObjectNameInformation,
136                            &ObjectNameBuffer,
137                            sizeof(ObjectNameBuffer),
138                            &BufferSize3);
139     ok_ntstatus(Status, STATUS_SUCCESS);
140 
141     ZwClose(DeviceHandle);
142 
143     /* Compare the returned buffer sizes */
144 
145     /* The returned size behaviour changed (when ZwQueryObject()'s
146      * input Length is zero) between Windows <= 2003 and Vista+ */
147     if (g_OsVersion < _WIN32_WINNT_VISTA)
148         ok_eq_ulong(BufferSize1, sizeof(OBJECT_NAME_INFORMATION));
149     else
150         ok_eq_ulong(BufferSize1, sizeof(OBJECT_NAME_INFORMATION) + ObjectName->MaximumLength);
151 
152     ok_eq_ulong(BufferSize2, BufferSize3);
153     ok_eq_ulong(BufferSize3, sizeof(OBJECT_NAME_INFORMATION) + ObjectName->MaximumLength);
154 
155     /* Test the name buffer */
156     ok(ObjectName->Length > 0, "ObjectName->Length == %hu, expected > 0\n", ObjectName->Length);
157     ok_eq_uint(ObjectName->MaximumLength, ObjectName->Length + sizeof(WCHAR));
158     ok(ObjectName->Buffer[ObjectName->Length / sizeof(WCHAR)] == UNICODE_NULL,
159        "UNICODE_NULL not found at end of ObjectName->Buffer\n");
160     if (skip(ObjectName->Buffer[ObjectName->Length / sizeof(WCHAR)] == UNICODE_NULL,
161         "ObjectName->Buffer string length check skipped\n"))
162     {
163         return;
164     }
165     /* Verify that ObjectName->Length doesn't count extra NUL-terminators */
166     {
167     SIZE_T strLen = wcslen(ObjectName->Buffer) * sizeof(WCHAR);
168     ok_eq_size(strLen, (SIZE_T)ObjectName->Length);
169     }
170 }
171 
START_TEST(ObQuery)172 START_TEST(ObQuery)
173 {
174     ObjectBasicInformationTests();
175     ObjectNameInformationTests();
176 }
177