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 }
239