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