diff --git a/reactos/ntoskrnl/config/ntapi.c b/reactos/ntoskrnl/config/ntapi.c index 04a169fd5b3..993b2bb0d17 100644 --- a/reactos/ntoskrnl/config/ntapi.c +++ b/reactos/ntoskrnl/config/ntapi.c @@ -630,11 +630,11 @@ NtSetValueKey(IN HANDLE KeyHandle, Data = NULL; /* Probe and copy the data */ - if ((PreviousMode != KernelMode) && Data) + if ((PreviousMode != KernelMode) && (DataSize != 0)) { PVOID DataCopy = ExAllocatePoolWithTag(PagedPool, DataSize, TAG_CM); if (!DataCopy) - return STATUS_NO_MEMORY; + return STATUS_INSUFFICIENT_RESOURCES; _SEH2_TRY { ProbeForRead(Data, DataSize, 1); diff --git a/rostests/apitests/ntdll/CMakeLists.txt b/rostests/apitests/ntdll/CMakeLists.txt index dbe8f659da2..cbd19aa0d98 100644 --- a/rostests/apitests/ntdll/CMakeLists.txt +++ b/rostests/apitests/ntdll/CMakeLists.txt @@ -18,6 +18,7 @@ list(APPEND SOURCE NtQuerySystemEnvironmentValue.c NtQueryVolumeInformationFile.c NtSaveKey.c + NtSetValueKey.c RtlAllocateHeap.c RtlBitmap.c RtlCopyMappedMemory.c diff --git a/rostests/apitests/ntdll/NtSetValueKey.c b/rostests/apitests/ntdll/NtSetValueKey.c new file mode 100644 index 00000000000..6da3c9ea546 --- /dev/null +++ b/rostests/apitests/ntdll/NtSetValueKey.c @@ -0,0 +1,209 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory + * PURPOSE: Test for NtSetValueKey + * PROGRAMMER: Thomas Faber + */ + +#include + +#include +#define WIN32_NO_STATUS +#include +#include +#include +#include + +START_TEST(NtSetValueKey) +{ + NTSTATUS Status; + HANDLE ParentKeyHandle; + HANDLE KeyHandle; + UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"SOFTWARE\\ntdll-apitest-NtSetValueKey"); + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING ValueName; + WCHAR Default[] = L"Default"; + WCHAR Hello[] = L"Hello"; + WCHAR Empty[] = L""; + NTSTATUS QueryStatus; + PKEY_VALUE_PARTIAL_INFORMATION PartialInfo; + ULONG PartialInfoLength; + ULONG ResultLength; + const struct + { + ULONG Type; + PVOID Data; + ULONG DataSize; + NTSTATUS StatusExisting; + NTSTATUS StatusNew; + NTSTATUS StatusExisting2; + NTSTATUS StatusNew2; + } Tests[] = + { + { REG_NONE, NULL, 0, STATUS_SUCCESS, STATUS_SUCCESS }, /* Empty REG_NONE value */ + { REG_SZ, Hello, sizeof(Hello), STATUS_SUCCESS, STATUS_SUCCESS }, /* Regular string */ + { REG_SZ, Empty, sizeof(Empty), STATUS_SUCCESS, STATUS_SUCCESS }, /* Empty string */ + { REG_SZ, NULL, 0, STATUS_SUCCESS, STATUS_SUCCESS }, /* Zero length */ + { REG_SZ, Hello, 0, STATUS_SUCCESS, STATUS_SUCCESS }, /* Zero length, non-null data */ + { REG_SZ, (PVOID)(LONG_PTR)-4, 0, STATUS_SUCCESS, STATUS_SUCCESS }, /* Zero length, kernel data */ + { REG_SZ, NULL, 1, STATUS_ACCESS_VIOLATION, STATUS_ACCESS_VIOLATION }, /* Non-zero length (odd), null data */ + { REG_SZ, NULL, 2, STATUS_ACCESS_VIOLATION, STATUS_ACCESS_VIOLATION }, /* Non-zero length (even), null data */ + { REG_SZ, NULL, 4, STATUS_ACCESS_VIOLATION, STATUS_ACCESS_VIOLATION }, /* CM_KEY_VALUE_SMALL, null data */ + { REG_SZ, NULL, 5, STATUS_INVALID_PARAMETER, STATUS_ACCESS_VIOLATION, /* CM_KEY_VALUE_SMALL+1, null data */ + STATUS_ACCESS_VIOLATION, STATUS_INSUFFICIENT_RESOURCES }, /* win7 */ + { REG_SZ, NULL, 6, STATUS_INVALID_PARAMETER, STATUS_ACCESS_VIOLATION, /* CM_KEY_VALUE_SMALL+2, null data */ + STATUS_ACCESS_VIOLATION, STATUS_INSUFFICIENT_RESOURCES }, /* win7 */ + { REG_SZ, NULL, 0x7fff0000, STATUS_INVALID_PARAMETER, STATUS_INSUFFICIENT_RESOURCES, /* MI_USER_PROBE_ADDRESS, null data */ + STATUS_INSUFFICIENT_RESOURCES, STATUS_INSUFFICIENT_RESOURCES }, /* win7 */ + { REG_SZ, NULL, 0x7fff0001, STATUS_ACCESS_VIOLATION, STATUS_ACCESS_VIOLATION, /* MI_USER_PROBE_ADDRESS+1, null data */ + STATUS_INSUFFICIENT_RESOURCES, STATUS_INSUFFICIENT_RESOURCES }, /* win7 */ + { REG_SZ, NULL, 0x7fffffff, STATUS_ACCESS_VIOLATION, STATUS_ACCESS_VIOLATION, /* <2GB, null data */ + STATUS_INVALID_PARAMETER, STATUS_INVALID_PARAMETER }, /* win7 */ + { REG_SZ, NULL, 0x80000000, STATUS_ACCESS_VIOLATION, STATUS_ACCESS_VIOLATION, /* 2GB, null data */ + STATUS_INVALID_PARAMETER, STATUS_INVALID_PARAMETER }, /* win7 */ + { REG_BINARY, NULL, 5, STATUS_INVALID_PARAMETER, STATUS_ACCESS_VIOLATION, /* ROSTESTS-200 */ + STATUS_ACCESS_VIOLATION, STATUS_INSUFFICIENT_RESOURCES }, /* win7 */ + }; + ULONG i; + + Status = RtlOpenCurrentUser(READ_CONTROL, &ParentKeyHandle); + ok(Status == STATUS_SUCCESS, "RtlOpenCurrentUser returned %lx\n", Status); + if (!NT_SUCCESS(Status)) + { + skip("No user key handle\n"); + return; + } + + InitializeObjectAttributes(&ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + ParentKeyHandle, + NULL); + Status = NtCreateKey(&KeyHandle, + KEY_QUERY_VALUE | KEY_SET_VALUE | DELETE, + &ObjectAttributes, + 0, + NULL, + REG_OPTION_VOLATILE, + NULL); + ok(Status == STATUS_SUCCESS, "NtCreateKey returned %lx\n", Status); + if (!NT_SUCCESS(Status)) + { + NtClose(ParentKeyHandle); + skip("No key handle\n"); + return; + } + + PartialInfoLength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[128]); + PartialInfo = HeapAlloc(GetProcessHeap(), 0, PartialInfoLength); + if (PartialInfo == NULL) + { + NtDeleteKey(KeyHandle); + NtClose(KeyHandle); + NtClose(ParentKeyHandle); + skip("No key handle\n"); + return; + } + + for (i = 0; i < RTL_NUMBER_OF(Tests); i++) + { + /* + * Existing value + */ + /* Make sure it exists */ + RtlInitUnicodeString(&ValueName, L"ExistingValue"); + Status = NtSetValueKey(KeyHandle, &ValueName, 0, REG_SZ, Default, sizeof(Default)); + ok(Status == STATUS_SUCCESS, "[%lu] NtSetValueKey failed with %lx", i, Status); + + /* Set it */ + Status = NtSetValueKey(KeyHandle, &ValueName, 0, Tests[i].Type, Tests[i].Data, Tests[i].DataSize); + if (Status == Tests[i].StatusExisting2) + ok(Status == Tests[i].StatusExisting || Status == Tests[i].StatusExisting2, "[%lu, %p, %lu] NtSetValueKey returned %lx, expected %lx or %lx\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize, Status, Tests[i].StatusExisting, Tests[i].StatusExisting2); + else + ok(Status == Tests[i].StatusExisting, "[%lu, %p, %lu] NtSetValueKey returned %lx, expected %lx\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize, Status, Tests[i].StatusExisting); + + /* Check it */ + RtlZeroMemory(PartialInfo, PartialInfoLength); + QueryStatus = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation, PartialInfo, PartialInfoLength, &ResultLength); + ok(QueryStatus == STATUS_SUCCESS, "[%lu, %p, %lu] NtQueryValueKey failed with %lx\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize, QueryStatus); + if (NT_SUCCESS(QueryStatus)) + { + if (NT_SUCCESS(Status)) + { + ok(PartialInfo->TitleIndex == 0, "[%lu, %p, %lu] TitleIndex = %lu\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->TitleIndex); + ok(PartialInfo->Type == Tests[i].Type, "[%lu, %p, %lu] Type = %lu\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->Type); + ok(PartialInfo->DataLength == Tests[i].DataSize, "[%lu, %p, %lu] DataLength = %lu\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->DataLength); + ok(!memcmp(PartialInfo->Data, Tests[i].Data, Tests[i].DataSize), "[%lu, %p, %lu] Data does not match set value\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize); + } + else + { + ok(PartialInfo->TitleIndex == 0, "[%lu, %p, %lu] TitleIndex = %lu\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->TitleIndex); + ok(PartialInfo->Type == REG_SZ, "[%lu, %p, %lu] Type = %lu\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->Type); + ok(PartialInfo->DataLength == sizeof(Default), "[%lu, %p, %lu] DataLength = %lu\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->DataLength); + ok(!memcmp(PartialInfo->Data, Default, sizeof(Default)), "[%lu, %p, %lu] Data does not match default\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize); + } + } + + /* + * New value + */ + /* Make sure it doesn't exist */ + RtlInitUnicodeString(&ValueName, L"NewValue"); + Status = NtDeleteValueKey(KeyHandle, &ValueName); + ok(Status == STATUS_SUCCESS || Status == STATUS_OBJECT_NAME_NOT_FOUND, + "[%lu] NtDeleteValueKey failed with %lx", i, Status); + + /* Set it */ + Status = NtSetValueKey(KeyHandle, &ValueName, 0, Tests[i].Type, Tests[i].Data, Tests[i].DataSize); + if (Tests[i].StatusNew2) + ok(Status == Tests[i].StatusNew || Status == Tests[i].StatusNew2, "[%lu, %p, %lu] NtSetValueKey returned %lx, expected %lx or %lx\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize, Status, Tests[i].StatusNew, Tests[i].StatusNew2); + else + ok(Status == Tests[i].StatusNew, "[%lu, %p, %lu] NtSetValueKey returned %lx, expected %lx\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize, Status, Tests[i].StatusNew); + + /* Check it */ + RtlZeroMemory(PartialInfo, PartialInfoLength); + QueryStatus = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation, PartialInfo, PartialInfoLength, &ResultLength); + if (NT_SUCCESS(Status)) + { + ok(QueryStatus == STATUS_SUCCESS, "[%lu, %p, %lu] NtQueryValueKey failed with %lx\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize, QueryStatus); + if (NT_SUCCESS(QueryStatus)) + { + ok(PartialInfo->TitleIndex == 0, "[%lu, %p, %lu] TitleIndex = %lu\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->TitleIndex); + ok(PartialInfo->Type == Tests[i].Type, "[%lu, %p, %lu] Type = %lu\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->Type); + ok(PartialInfo->DataLength == Tests[i].DataSize, "[%lu, %p, %lu] DataLength = %lu\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize, PartialInfo->DataLength); + ok(!memcmp(PartialInfo->Data, Tests[i].Data, Tests[i].DataSize), "[%lu, %p, %lu] Data does not match set value\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize); + } + } + else + { + ok(QueryStatus == STATUS_OBJECT_NAME_NOT_FOUND, "[%lu, %p, %lu] QueryStatus = %lx\n", + Tests[i].Type, Tests[i].Data, Tests[i].DataSize, QueryStatus); + } + } + + HeapFree(GetProcessHeap(), 0, PartialInfo); + Status = NtDeleteKey(KeyHandle); + ok(Status == STATUS_SUCCESS, "NtDeleteKey returned %lx\n", Status); + Status = NtClose(KeyHandle); + ok(Status == STATUS_SUCCESS, "NtClose returned %lx\n", Status); + Status = NtClose(ParentKeyHandle); + ok(Status == STATUS_SUCCESS, "NtClose returned %lx\n", Status); +} diff --git a/rostests/apitests/ntdll/testlist.c b/rostests/apitests/ntdll/testlist.c index 1dadf0d7c4c..f13f249af0f 100644 --- a/rostests/apitests/ntdll/testlist.c +++ b/rostests/apitests/ntdll/testlist.c @@ -21,6 +21,7 @@ extern void func_NtQueryKey(void); extern void func_NtQuerySystemEnvironmentValue(void); extern void func_NtQueryVolumeInformationFile(void); extern void func_NtSaveKey(void); +extern void func_NtSetValueKey(void); extern void func_NtSystemInformation(void); extern void func_RtlAllocateHeap(void); extern void func_RtlBitmap(void); @@ -63,6 +64,7 @@ const struct test winetest_testlist[] = { "NtQuerySystemEnvironmentValue", func_NtQuerySystemEnvironmentValue }, { "NtQueryVolumeInformationFile", func_NtQueryVolumeInformationFile }, { "NtSaveKey", func_NtSaveKey}, + { "NtSetValueKey", func_NtSetValueKey}, { "NtSystemInformation", func_NtSystemInformation }, { "RtlAllocateHeap", func_RtlAllocateHeap }, { "RtlBitmapApi", func_RtlBitmap },