1 /*
2 * PROJECT: ReactOS API Tests
3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4 * PURPOSE: Test for NtSetValueKey
5 * COPYRIGHT: Copyright 2016-2019 Thomas Faber (thomas.faber@reactos.org)
6 */
7
8 #include "precomp.h"
9
10 #include <winreg.h>
11
START_TEST(NtSetValueKey)12 START_TEST(NtSetValueKey)
13 {
14 NTSTATUS Status;
15 HANDLE ParentKeyHandle;
16 HANDLE KeyHandle;
17 UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"SOFTWARE\\ntdll-apitest-NtSetValueKey");
18 OBJECT_ATTRIBUTES ObjectAttributes;
19 UNICODE_STRING ValueName;
20 WCHAR Default[] = L"Default";
21 WCHAR Hello[] = L"Hello";
22 WCHAR Empty[] = L"";
23 NTSTATUS QueryStatus;
24 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo;
25 ULONG PartialInfoLength;
26 ULONG ResultLength;
27 ULONG DataLength;
28 PWCHAR LargeBuffer;
29 ULONG LargeBufferLength;
30 const struct
31 {
32 ULONG Type;
33 PVOID Data;
34 ULONG DataSize;
35 NTSTATUS StatusExisting;
36 NTSTATUS StatusNew;
37 NTSTATUS StatusExisting2;
38 NTSTATUS StatusNew2;
39 } Tests[] =
40 {
41 { REG_NONE, NULL, 0, STATUS_SUCCESS, STATUS_SUCCESS }, /* Empty REG_NONE value */
42 { REG_SZ, Hello, sizeof(Hello), STATUS_SUCCESS, STATUS_SUCCESS }, /* Regular string */
43 { REG_SZ, Empty, sizeof(Empty), STATUS_SUCCESS, STATUS_SUCCESS }, /* Empty string */
44 { REG_SZ, NULL, 0, STATUS_SUCCESS, STATUS_SUCCESS }, /* Zero length */
45 { REG_SZ, Hello, 0, STATUS_SUCCESS, STATUS_SUCCESS }, /* Zero length, non-null data */
46 { REG_SZ, (PVOID)(LONG_PTR)-4, 0, STATUS_SUCCESS, STATUS_SUCCESS }, /* Zero length, kernel data */
47 { REG_SZ, NULL, 1, STATUS_ACCESS_VIOLATION, STATUS_ACCESS_VIOLATION }, /* Non-zero length (odd), null data */
48 { REG_SZ, NULL, 2, STATUS_ACCESS_VIOLATION, STATUS_ACCESS_VIOLATION }, /* Non-zero length (even), null data */
49 { REG_SZ, NULL, 4, STATUS_ACCESS_VIOLATION, STATUS_ACCESS_VIOLATION }, /* CM_KEY_VALUE_SMALL, null data */
50 { REG_SZ, NULL, 5, STATUS_INVALID_PARAMETER, STATUS_ACCESS_VIOLATION, /* CM_KEY_VALUE_SMALL+1, null data */
51 STATUS_ACCESS_VIOLATION, STATUS_INSUFFICIENT_RESOURCES }, /* win7 */
52 { REG_SZ, NULL, 6, STATUS_INVALID_PARAMETER, STATUS_ACCESS_VIOLATION, /* CM_KEY_VALUE_SMALL+2, null data */
53 STATUS_ACCESS_VIOLATION, STATUS_INSUFFICIENT_RESOURCES }, /* win7 */
54 { REG_SZ, NULL, 0x7fff0000, STATUS_INVALID_PARAMETER, STATUS_INSUFFICIENT_RESOURCES, /* MI_USER_PROBE_ADDRESS, null data */
55 STATUS_INSUFFICIENT_RESOURCES, STATUS_INSUFFICIENT_RESOURCES }, /* win7 */
56 { REG_SZ, NULL, 0x7fff0001, STATUS_ACCESS_VIOLATION, STATUS_ACCESS_VIOLATION, /* MI_USER_PROBE_ADDRESS+1, null data */
57 STATUS_INSUFFICIENT_RESOURCES, STATUS_INSUFFICIENT_RESOURCES }, /* win7 */
58 { REG_SZ, NULL, 0x7fffffff, STATUS_ACCESS_VIOLATION, STATUS_ACCESS_VIOLATION, /* <2GB, null data */
59 STATUS_INVALID_PARAMETER, STATUS_INVALID_PARAMETER }, /* win7 */
60 { REG_SZ, NULL, 0x80000000, STATUS_ACCESS_VIOLATION, STATUS_ACCESS_VIOLATION, /* 2GB, null data */
61 STATUS_INVALID_PARAMETER, STATUS_INVALID_PARAMETER }, /* win7 */
62 { REG_BINARY, NULL, 5, STATUS_INVALID_PARAMETER, STATUS_ACCESS_VIOLATION, /* ROSTESTS-200 */
63 STATUS_ACCESS_VIOLATION, STATUS_INSUFFICIENT_RESOURCES }, /* win7 */
64 };
65 ULONG i;
66
67 #if defined(_M_AMD64)
68 if (!winetest_interactive)
69 {
70 skip("ROSTESTS-365: Skipping ntdll_apitest:NtSetValueKey because it hangs on Windows Server 2003 x64-Testbot. Set winetest_interactive to run it anyway.\n");
71 return;
72 }
73 #endif
74
75 Status = RtlOpenCurrentUser(READ_CONTROL, &ParentKeyHandle);
76 ok(Status == STATUS_SUCCESS, "RtlOpenCurrentUser returned %lx\n", Status);
77 if (!NT_SUCCESS(Status))
78 {
79 skip("No user key handle\n");
80 return;
81 }
82
83 InitializeObjectAttributes(&ObjectAttributes,
84 &KeyName,
85 OBJ_CASE_INSENSITIVE,
86 ParentKeyHandle,
87 NULL);
88 Status = NtCreateKey(&KeyHandle,
89 KEY_QUERY_VALUE | KEY_SET_VALUE | DELETE,
90 &ObjectAttributes,
91 0,
92 NULL,
93 REG_OPTION_VOLATILE,
94 NULL);
95 ok(Status == STATUS_SUCCESS, "NtCreateKey returned %lx\n", Status);
96 if (!NT_SUCCESS(Status))
97 {
98 NtClose(ParentKeyHandle);
99 skip("No key handle\n");
100 return;
101 }
102
103 LargeBufferLength = 0x20000;
104 LargeBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, LargeBufferLength);
105
106 PartialInfoLength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[LargeBufferLength]);
107 PartialInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, PartialInfoLength);
108
109 if (LargeBuffer == NULL || PartialInfo == NULL)
110 {
111 RtlFreeHeap(GetProcessHeap(), 0, LargeBuffer);
112 RtlFreeHeap(GetProcessHeap(), 0, PartialInfo);
113 NtDeleteKey(KeyHandle);
114 NtClose(KeyHandle);
115 NtClose(ParentKeyHandle);
116 skip("Could not allocate buffers\n");
117 return;
118 }
119
120 for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
121 {
122 /*
123 * Existing value
124 */
125 /* Make sure it exists */
126 RtlInitUnicodeString(&ValueName, L"ExistingValue");
127 Status = NtSetValueKey(KeyHandle, &ValueName, 0, REG_SZ, Default, sizeof(Default));
128 ok(Status == STATUS_SUCCESS, "[%lu] NtSetValueKey failed with %lx", i, Status);
129
130 /* Set it */
131 Status = NtSetValueKey(KeyHandle, &ValueName, 0, Tests[i].Type, Tests[i].Data, Tests[i].DataSize);
132 if (Status == Tests[i].StatusExisting2)
133 ok(Status == Tests[i].StatusExisting || Status == Tests[i].StatusExisting2, "[%lu, %p, %lu] NtSetValueKey returned %lx, expected %lx or %lx\n",
134 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, Status, Tests[i].StatusExisting, Tests[i].StatusExisting2);
135 else
136 ok(Status == Tests[i].StatusExisting, "[%lu, %p, %lu] NtSetValueKey returned %lx, expected %lx\n",
137 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, Status, Tests[i].StatusExisting);
138
139 /* Check it */
140 RtlZeroMemory(PartialInfo, PartialInfoLength);
141 QueryStatus = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation, PartialInfo, PartialInfoLength, &ResultLength);
142 ok(QueryStatus == STATUS_SUCCESS, "[%lu, %p, %lu] NtQueryValueKey failed with %lx\n",
143 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, QueryStatus);
144 if (NT_SUCCESS(QueryStatus))
145 {
146 if (NT_SUCCESS(Status))
147 {
148 ok(PartialInfo->TitleIndex == 0, "[%lu, %p, %lu] TitleIndex = %lu\n",
149 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->TitleIndex);
150 ok(PartialInfo->Type == Tests[i].Type, "[%lu, %p, %lu] Type = %lu\n",
151 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->Type);
152 ok(PartialInfo->DataLength == Tests[i].DataSize, "[%lu, %p, %lu] DataLength = %lu\n",
153 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->DataLength);
154 ok(!memcmp(PartialInfo->Data, Tests[i].Data, Tests[i].DataSize), "[%lu, %p, %lu] Data does not match set value\n",
155 Tests[i].Type, Tests[i].Data, Tests[i].DataSize);
156 }
157 else
158 {
159 ok(PartialInfo->TitleIndex == 0, "[%lu, %p, %lu] TitleIndex = %lu\n",
160 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->TitleIndex);
161 ok(PartialInfo->Type == REG_SZ, "[%lu, %p, %lu] Type = %lu\n",
162 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->Type);
163 ok(PartialInfo->DataLength == sizeof(Default), "[%lu, %p, %lu] DataLength = %lu\n",
164 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->DataLength);
165 ok(!memcmp(PartialInfo->Data, Default, sizeof(Default)), "[%lu, %p, %lu] Data does not match default\n",
166 Tests[i].Type, Tests[i].Data, Tests[i].DataSize);
167 }
168 }
169
170 /*
171 * New value
172 */
173 /* Make sure it doesn't exist */
174 RtlInitUnicodeString(&ValueName, L"NewValue");
175 Status = NtDeleteValueKey(KeyHandle, &ValueName);
176 ok(Status == STATUS_SUCCESS || Status == STATUS_OBJECT_NAME_NOT_FOUND,
177 "[%lu] NtDeleteValueKey failed with %lx", i, Status);
178
179 /* Set it */
180 Status = NtSetValueKey(KeyHandle, &ValueName, 0, Tests[i].Type, Tests[i].Data, Tests[i].DataSize);
181 if (Tests[i].StatusNew2)
182 ok(Status == Tests[i].StatusNew || Status == Tests[i].StatusNew2, "[%lu, %p, %lu] NtSetValueKey returned %lx, expected %lx or %lx\n",
183 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, Status, Tests[i].StatusNew, Tests[i].StatusNew2);
184 else
185 ok(Status == Tests[i].StatusNew, "[%lu, %p, %lu] NtSetValueKey returned %lx, expected %lx\n",
186 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, Status, Tests[i].StatusNew);
187
188 /* Check it */
189 RtlZeroMemory(PartialInfo, PartialInfoLength);
190 QueryStatus = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation, PartialInfo, PartialInfoLength, &ResultLength);
191 if (NT_SUCCESS(Status))
192 {
193 ok(QueryStatus == STATUS_SUCCESS, "[%lu, %p, %lu] NtQueryValueKey failed with %lx\n",
194 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, QueryStatus);
195 if (NT_SUCCESS(QueryStatus))
196 {
197 ok(PartialInfo->TitleIndex == 0, "[%lu, %p, %lu] TitleIndex = %lu\n",
198 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->TitleIndex);
199 ok(PartialInfo->Type == Tests[i].Type, "[%lu, %p, %lu] Type = %lu\n",
200 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->Type);
201 ok(PartialInfo->DataLength == Tests[i].DataSize, "[%lu, %p, %lu] DataLength = %lu\n",
202 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->DataLength);
203 ok(!memcmp(PartialInfo->Data, Tests[i].Data, Tests[i].DataSize), "[%lu, %p, %lu] Data does not match set value\n",
204 Tests[i].Type, Tests[i].Data, Tests[i].DataSize);
205 }
206 }
207 else
208 {
209 ok(QueryStatus == STATUS_OBJECT_NAME_NOT_FOUND, "[%lu, %p, %lu] QueryStatus = %lx\n",
210 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, QueryStatus);
211 }
212 }
213
214 /* String value larger than MAXUSHORT */
215 {
216 const ULONG DataLengths[] = { 0x10000, 0x10002, 0x20000 };
217
218 RtlInitUnicodeString(&ValueName, L"ExistingValue");
219 for (i = 0; i < RTL_NUMBER_OF(DataLengths); i++)
220 {
221 DataLength = DataLengths[i];
222 RtlFillMemoryUlong(LargeBuffer, DataLength, '\0B\0A');
223 LargeBuffer[DataLength / sizeof(WCHAR) - 2] = L'C';
224 LargeBuffer[DataLength / sizeof(WCHAR) - 1] = UNICODE_NULL;
225 Status = NtSetValueKey(KeyHandle, &ValueName, 0, REG_SZ, LargeBuffer, DataLength);
226 ok(Status == STATUS_SUCCESS, "[0x%lx] NtSetValueKey failed with %lx", DataLength, Status);
227
228 RtlZeroMemory(PartialInfo, PartialInfoLength);
229 Status = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation, PartialInfo, PartialInfoLength, &ResultLength);
230 ok(Status == STATUS_SUCCESS, "[0x%lx] NtQueryValueKey failed with %lx\n", DataLength, Status);
231 ok(PartialInfo->TitleIndex == 0, "[0x%lx] TitleIndex = %lu\n", DataLength, PartialInfo->TitleIndex);
232 ok(PartialInfo->Type == REG_SZ, "[0x%lx] Type = %lu\n", DataLength, PartialInfo->Type);
233 ok(PartialInfo->DataLength == DataLength, "[0x%lx] DataLength = %lu\n", DataLength, PartialInfo->DataLength);
234 ok(!memcmp(PartialInfo->Data, LargeBuffer, DataLength), "[0x%lx] Data does not match set value\n", DataLength);
235 }
236 }
237
238 RtlFreeHeap(GetProcessHeap(), 0, LargeBuffer);
239 RtlFreeHeap(GetProcessHeap(), 0, PartialInfo);
240 Status = NtDeleteKey(KeyHandle);
241 ok(Status == STATUS_SUCCESS, "NtDeleteKey returned %lx\n", Status);
242 Status = NtClose(KeyHandle);
243 ok(Status == STATUS_SUCCESS, "NtClose returned %lx\n", Status);
244 Status = NtClose(ParentKeyHandle);
245 ok(Status == STATUS_SUCCESS, "NtClose returned %lx\n", Status);
246 }
247