[NTDLL_APITEST] Show that registry strings can be longer than MAXUSHORT.
[reactos.git] / modules / rostests / apitests / ntdll / NtSetValueKey.c
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
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 Status = RtlOpenCurrentUser(READ_CONTROL, &ParentKeyHandle);
68 ok(Status == STATUS_SUCCESS, "RtlOpenCurrentUser returned %lx\n", Status);
69 if (!NT_SUCCESS(Status))
70 {
71 skip("No user key handle\n");
72 return;
73 }
74
75 InitializeObjectAttributes(&ObjectAttributes,
76 &KeyName,
77 OBJ_CASE_INSENSITIVE,
78 ParentKeyHandle,
79 NULL);
80 Status = NtCreateKey(&KeyHandle,
81 KEY_QUERY_VALUE | KEY_SET_VALUE | DELETE,
82 &ObjectAttributes,
83 0,
84 NULL,
85 REG_OPTION_VOLATILE,
86 NULL);
87 ok(Status == STATUS_SUCCESS, "NtCreateKey returned %lx\n", Status);
88 if (!NT_SUCCESS(Status))
89 {
90 NtClose(ParentKeyHandle);
91 skip("No key handle\n");
92 return;
93 }
94
95 LargeBufferLength = 0x20000;
96 LargeBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, LargeBufferLength);
97
98 PartialInfoLength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[LargeBufferLength]);
99 PartialInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, PartialInfoLength);
100
101 if (LargeBuffer == NULL || PartialInfo == NULL)
102 {
103 RtlFreeHeap(GetProcessHeap(), 0, LargeBuffer);
104 RtlFreeHeap(GetProcessHeap(), 0, PartialInfo);
105 NtDeleteKey(KeyHandle);
106 NtClose(KeyHandle);
107 NtClose(ParentKeyHandle);
108 skip("Could not allocate buffers\n");
109 return;
110 }
111
112 for (i = 0; i < RTL_NUMBER_OF(Tests); i++)
113 {
114 /*
115 * Existing value
116 */
117 /* Make sure it exists */
118 RtlInitUnicodeString(&ValueName, L"ExistingValue");
119 Status = NtSetValueKey(KeyHandle, &ValueName, 0, REG_SZ, Default, sizeof(Default));
120 ok(Status == STATUS_SUCCESS, "[%lu] NtSetValueKey failed with %lx", i, Status);
121
122 /* Set it */
123 Status = NtSetValueKey(KeyHandle, &ValueName, 0, Tests[i].Type, Tests[i].Data, Tests[i].DataSize);
124 if (Status == Tests[i].StatusExisting2)
125 ok(Status == Tests[i].StatusExisting || Status == Tests[i].StatusExisting2, "[%lu, %p, %lu] NtSetValueKey returned %lx, expected %lx or %lx\n",
126 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, Status, Tests[i].StatusExisting, Tests[i].StatusExisting2);
127 else
128 ok(Status == Tests[i].StatusExisting, "[%lu, %p, %lu] NtSetValueKey returned %lx, expected %lx\n",
129 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, Status, Tests[i].StatusExisting);
130
131 /* Check it */
132 RtlZeroMemory(PartialInfo, PartialInfoLength);
133 QueryStatus = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation, PartialInfo, PartialInfoLength, &ResultLength);
134 ok(QueryStatus == STATUS_SUCCESS, "[%lu, %p, %lu] NtQueryValueKey failed with %lx\n",
135 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, QueryStatus);
136 if (NT_SUCCESS(QueryStatus))
137 {
138 if (NT_SUCCESS(Status))
139 {
140 ok(PartialInfo->TitleIndex == 0, "[%lu, %p, %lu] TitleIndex = %lu\n",
141 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->TitleIndex);
142 ok(PartialInfo->Type == Tests[i].Type, "[%lu, %p, %lu] Type = %lu\n",
143 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->Type);
144 ok(PartialInfo->DataLength == Tests[i].DataSize, "[%lu, %p, %lu] DataLength = %lu\n",
145 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->DataLength);
146 ok(!memcmp(PartialInfo->Data, Tests[i].Data, Tests[i].DataSize), "[%lu, %p, %lu] Data does not match set value\n",
147 Tests[i].Type, Tests[i].Data, Tests[i].DataSize);
148 }
149 else
150 {
151 ok(PartialInfo->TitleIndex == 0, "[%lu, %p, %lu] TitleIndex = %lu\n",
152 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->TitleIndex);
153 ok(PartialInfo->Type == REG_SZ, "[%lu, %p, %lu] Type = %lu\n",
154 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->Type);
155 ok(PartialInfo->DataLength == sizeof(Default), "[%lu, %p, %lu] DataLength = %lu\n",
156 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->DataLength);
157 ok(!memcmp(PartialInfo->Data, Default, sizeof(Default)), "[%lu, %p, %lu] Data does not match default\n",
158 Tests[i].Type, Tests[i].Data, Tests[i].DataSize);
159 }
160 }
161
162 /*
163 * New value
164 */
165 /* Make sure it doesn't exist */
166 RtlInitUnicodeString(&ValueName, L"NewValue");
167 Status = NtDeleteValueKey(KeyHandle, &ValueName);
168 ok(Status == STATUS_SUCCESS || Status == STATUS_OBJECT_NAME_NOT_FOUND,
169 "[%lu] NtDeleteValueKey failed with %lx", i, Status);
170
171 /* Set it */
172 Status = NtSetValueKey(KeyHandle, &ValueName, 0, Tests[i].Type, Tests[i].Data, Tests[i].DataSize);
173 if (Tests[i].StatusNew2)
174 ok(Status == Tests[i].StatusNew || Status == Tests[i].StatusNew2, "[%lu, %p, %lu] NtSetValueKey returned %lx, expected %lx or %lx\n",
175 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, Status, Tests[i].StatusNew, Tests[i].StatusNew2);
176 else
177 ok(Status == Tests[i].StatusNew, "[%lu, %p, %lu] NtSetValueKey returned %lx, expected %lx\n",
178 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, Status, Tests[i].StatusNew);
179
180 /* Check it */
181 RtlZeroMemory(PartialInfo, PartialInfoLength);
182 QueryStatus = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation, PartialInfo, PartialInfoLength, &ResultLength);
183 if (NT_SUCCESS(Status))
184 {
185 ok(QueryStatus == STATUS_SUCCESS, "[%lu, %p, %lu] NtQueryValueKey failed with %lx\n",
186 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, QueryStatus);
187 if (NT_SUCCESS(QueryStatus))
188 {
189 ok(PartialInfo->TitleIndex == 0, "[%lu, %p, %lu] TitleIndex = %lu\n",
190 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->TitleIndex);
191 ok(PartialInfo->Type == Tests[i].Type, "[%lu, %p, %lu] Type = %lu\n",
192 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->Type);
193 ok(PartialInfo->DataLength == Tests[i].DataSize, "[%lu, %p, %lu] DataLength = %lu\n",
194 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->DataLength);
195 ok(!memcmp(PartialInfo->Data, Tests[i].Data, Tests[i].DataSize), "[%lu, %p, %lu] Data does not match set value\n",
196 Tests[i].Type, Tests[i].Data, Tests[i].DataSize);
197 }
198 }
199 else
200 {
201 ok(QueryStatus == STATUS_OBJECT_NAME_NOT_FOUND, "[%lu, %p, %lu] QueryStatus = %lx\n",
202 Tests[i].Type, Tests[i].Data, Tests[i].DataSize, QueryStatus);
203 }
204 }
205
206 /* String value larger than MAXUSHORT */
207 {
208 const ULONG DataLengths[] = { 0x10000, 0x10002, 0x20000 };
209
210 RtlInitUnicodeString(&ValueName, L"ExistingValue");
211 for (i = 0; i < RTL_NUMBER_OF(DataLengths); i++)
212 {
213 DataLength = DataLengths[i];
214 RtlFillMemoryUlong(LargeBuffer, DataLength, '\0B\0A');
215 LargeBuffer[DataLength / sizeof(WCHAR) - 2] = L'C';
216 LargeBuffer[DataLength / sizeof(WCHAR) - 1] = UNICODE_NULL;
217 Status = NtSetValueKey(KeyHandle, &ValueName, 0, REG_SZ, LargeBuffer, DataLength);
218 ok(Status == STATUS_SUCCESS, "[0x%lx] NtSetValueKey failed with %lx", DataLength, Status);
219
220 RtlZeroMemory(PartialInfo, PartialInfoLength);
221 Status = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation, PartialInfo, PartialInfoLength, &ResultLength);
222 ok(Status == STATUS_SUCCESS, "[0x%lx] NtQueryValueKey failed with %lx\n", DataLength, Status);
223 ok(PartialInfo->TitleIndex == 0, "[0x%lx] TitleIndex = %lu\n", DataLength, PartialInfo->TitleIndex);
224 ok(PartialInfo->Type == REG_SZ, "[0x%lx] Type = %lu\n", DataLength, PartialInfo->Type);
225 ok(PartialInfo->DataLength == DataLength, "[0x%lx] DataLength = %lu\n", DataLength, PartialInfo->DataLength);
226 ok(!memcmp(PartialInfo->Data, LargeBuffer, DataLength), "[0x%lx] Data does not match set value\n", DataLength);
227 }
228 }
229
230 RtlFreeHeap(GetProcessHeap(), 0, LargeBuffer);
231 RtlFreeHeap(GetProcessHeap(), 0, PartialInfo);
232 Status = NtDeleteKey(KeyHandle);
233 ok(Status == STATUS_SUCCESS, "NtDeleteKey returned %lx\n", Status);
234 Status = NtClose(KeyHandle);
235 ok(Status == STATUS_SUCCESS, "NtClose returned %lx\n", Status);
236 Status = NtClose(ParentKeyHandle);
237 ok(Status == STATUS_SUCCESS, "NtClose returned %lx\n", Status);
238 }