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 #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