https://git.reactos.org/?p=reactos.git;a=commitdiff;h=774ef4e80e8fe150f854cd...
commit 774ef4e80e8fe150f854cd9c009253c434096add Author: Katayama Hirofumi MZ katayama.hirofumi.mz@gmail.com AuthorDate: Fri Apr 24 17:45:43 2020 +0900 Commit: GitHub noreply@github.com CommitDate: Fri Apr 24 17:45:43 2020 +0900
[SDK][RTL][NTDLL_APITEST] Add RtlMultipleAllocateHeap and RtlMultipleFreeHeap (#2641)
- Add RtlMultipleAllocateHeap and RtlMultipleFreeHeap functions (2k3+). - Add a testcase for two functions. CORE-12026 --- modules/rostests/apitests/ntdll/CMakeLists.txt | 1 + .../apitests/ntdll/RtlMultipleAllocateHeap.c | 286 +++++++++++++++++++++ modules/rostests/apitests/ntdll/testlist.c | 2 + sdk/include/ndk/rtlfuncs.h | 13 +- sdk/lib/rtl/heap.c | 62 ++++- 5 files changed, 351 insertions(+), 13 deletions(-)
diff --git a/modules/rostests/apitests/ntdll/CMakeLists.txt b/modules/rostests/apitests/ntdll/CMakeLists.txt index d03b29806c9..ac1804728fc 100644 --- a/modules/rostests/apitests/ntdll/CMakeLists.txt +++ b/modules/rostests/apitests/ntdll/CMakeLists.txt @@ -61,6 +61,7 @@ list(APPEND SOURCE RtlImageRvaToVa.c RtlIsNameLegalDOS8Dot3.c RtlMemoryStream.c + RtlMultipleAllocateHeap.c RtlNtPathNameToDosPathName.c RtlpEnsureBufferSize.c RtlQueryTimeZoneInfo.c diff --git a/modules/rostests/apitests/ntdll/RtlMultipleAllocateHeap.c b/modules/rostests/apitests/ntdll/RtlMultipleAllocateHeap.c new file mode 100644 index 00000000000..b2eb3132afa --- /dev/null +++ b/modules/rostests/apitests/ntdll/RtlMultipleAllocateHeap.c @@ -0,0 +1,286 @@ +/* + * PROJECT: ReactOS API Tests + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Test for RtlMultipleAllocateHeap and RtlMultipleFreeHeap + * COPYRIGHT: Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) + */ +#include "precomp.h" + +typedef ULONG (NTAPI *FN_RtlMultipleAllocateHeap)(IN PVOID, IN ULONG, IN SIZE_T, IN ULONG, OUT PVOID *); +typedef ULONG (NTAPI *FN_RtlMultipleFreeHeap)(IN PVOID, IN ULONG, IN ULONG, OUT PVOID *); + +static FN_RtlMultipleAllocateHeap g_alloc = NULL; +static FN_RtlMultipleFreeHeap g_free = NULL; + +#define TEST_ALLOC(ret_expected,err_expected,threw_excepted,HeapHandle,Flags,Size,Count,Array) \ + threw = 0; \ + SetLastError(-1); \ + _SEH2_TRY { \ + ret = g_alloc((HeapHandle), (Flags), (Size), (Count), (Array)); \ + err = GetLastError(); \ + } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \ + threw = _SEH2_GetExceptionCode(); \ + } \ + _SEH2_END \ + ok((ret) == (ret_expected), "ret excepted %d, but %d\n", (ret_expected), (ret)); \ + ok((err) == (err_expected), "err excepted %d, but %d\n", (err_expected), (err)); \ + ok((threw) == (threw_excepted), "threw excepted %d, but %d\n", (threw_excepted), (threw)); + +#define TEST_ALLOC_NO_RET(err_expected,threw_excepted,HeapHandle,Flags,Size,Count,Array) \ + threw = 0; \ + SetLastError(-1); \ + _SEH2_TRY { \ + ret = g_alloc((HeapHandle), (Flags), (Size), (Count), (Array)); \ + err = GetLastError(); \ + } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \ + threw = _SEH2_GetExceptionCode(); \ + } \ + _SEH2_END \ + ok((err) == (err_expected), "err excepted %d, but %d", (err_expected), (err)); \ + ok((threw) == (threw_excepted), "threw excepted %d, but %d\n", (threw_excepted), (threw)); + +#define TEST_FREE(ret_expected,err_expected,threw_excepted,HeapHandle,Flags,Count,Array) \ + threw = 0; \ + SetLastError(-1); \ + _SEH2_TRY { \ + ret = g_free((HeapHandle), (Flags), (Count), (Array)); \ + err = GetLastError(); \ + } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \ + threw = _SEH2_GetExceptionCode(); \ + } \ + _SEH2_END \ + ok((ret) == (ret_expected), "ret excepted %d, but %d\n", (ret_expected), (ret)); \ + ok((err) == (err_expected), "err excepted %d, but %d\n", (err_expected), (err)); \ + ok((threw) == (threw_excepted), "threw excepted %d, but %d\n", (threw_excepted), (threw)); + +#define ASSUME_ARRAY_ITEMS_ARE_NULL() \ + ok(Array[0] == NULL, "Array[0] is expected as NULL\n"); \ + ok(Array[1] == NULL, "Array[1] is expected as NULL\n"); \ + ok(Array[2] == NULL, "Array[2] is expected as NULL\n"); + +#define INT_EXPECTED(var,value) \ + ok((var) == (value), #var " expected %d, but %d\n", (value), (var)) + +static void +set_array(PVOID *array, PVOID p0, PVOID p1, PVOID p2) +{ + array[0] = p0; + array[1] = p1; + array[2] = p2; +} + +static void +MultiHeapAllocTest() +{ + INT ret, threw, err; + HANDLE HeapHandle = GetProcessHeap(); + PVOID Array[3] = {NULL, NULL, NULL}; + + // HeapHandle is non-NULL and array is NULL + TEST_ALLOC(0, -1, 0, HeapHandle, 0, 0, 0, NULL); + TEST_ALLOC(0, -1, 0xC0000005, HeapHandle, 0, 0, 1, NULL); + TEST_ALLOC(0, -1, 0, HeapHandle, 0, 1, 0, NULL); + TEST_ALLOC(0, -1, 0xC0000005, HeapHandle, 0, 1, 1, NULL); + + // Array is non-NULL and contents are NULL + set_array(Array, NULL, NULL, NULL); + TEST_ALLOC(0, -1, 0, HeapHandle, 0, 0, 0, Array); + ASSUME_ARRAY_ITEMS_ARE_NULL(); + + set_array(Array, NULL, NULL, NULL); + TEST_ALLOC(1, -1, 0, HeapHandle, 0, 0, 1, Array); + ok(Array[0] != NULL, "Array[0] is expected as non-NULL\n"); + ok(Array[1] == NULL, "Array[1] is expected as NULL\n"); + ok(Array[2] == NULL, "Array[2] is expected as NULL\n"); + + set_array(Array, NULL, NULL, NULL); + TEST_ALLOC(0, -1, 0, HeapHandle, 0, 1, 0, Array); + ASSUME_ARRAY_ITEMS_ARE_NULL(); + + set_array(Array, NULL, NULL, NULL); + TEST_ALLOC(1, -1, 0, HeapHandle, 0, 1, 1, Array); + ok(Array[0] != NULL, "Array[0] is expected as non-NULL\n"); + ok(Array[1] == NULL, "Array[1] is expected as NULL\n"); + ok(Array[2] == NULL, "Array[2] is expected as NULL\n"); + + set_array(Array, NULL, NULL, NULL); + TEST_ALLOC(2, -1, 0, HeapHandle, 0, 1, 2, Array); + ok(Array[0] != NULL, "Array[0] is expected as non-NULL\n"); + ok(Array[1] != NULL, "Array[1] is expected as non-NULL\n"); + ok(Array[2] == NULL, "Array[2] is expected as NULL\n"); + + set_array(Array, NULL, NULL, NULL); + TEST_ALLOC(3, -1, 0, HeapHandle, 0, 1, 3, Array); + ok(Array[0] != NULL, "Array[0] is expected as non-NULL\n"); + ok(Array[1] != NULL, "Array[1] is expected as non-NULL\n"); + ok(Array[2] != NULL, "Array[2] is expected as non-NULL\n"); + + // Array is non-NULL and contents are invalid pointers + set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3); + TEST_ALLOC(0, -1, 0, HeapHandle, 0, 0, 0, Array); + ok(Array[0] == (PVOID)1, "Array[0] is expected as 1\n"); + ok(Array[1] == (PVOID)2, "Array[1] is expected as 2\n"); + ok(Array[2] == (PVOID)3, "Array[2] is expected as 3\n"); + + set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3); + TEST_ALLOC(1, -1, 0, HeapHandle, 0, 0, 1, Array); + ok(Array[0] != NULL, "Array[0] is expected as non-NULL\n"); + ok(Array[1] == (PVOID)2, "Array[1] is expected as 2\n"); + ok(Array[2] == (PVOID)3, "Array[2] is expected as 3\n"); + + set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3); + TEST_ALLOC(0, -1, 0, HeapHandle, 0, 1, 0, Array); + ok(Array[0] == (PVOID)1, "Array[0] is expected as 1\n"); + ok(Array[1] == (PVOID)2, "Array[1] is expected as 2\n"); + ok(Array[2] == (PVOID)3, "Array[2] is expected as 3\n"); + + set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3); + TEST_ALLOC(1, -1, 0, HeapHandle, 0, 1, 1, Array); + ok(Array[0] != NULL, "Array[0] is expected as non-NULL\n"); + ok(Array[1] == (PVOID)2, "Array[1] is expected as non-NULL\n"); + ok(Array[2] == (PVOID)3, "Array[2] is expected as NULL\n"); + + set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3); + TEST_ALLOC(2, -1, 0, HeapHandle, 0, 1, 2, Array); + ok(Array[0] != NULL, "Array[0] is expected as non-NULL\n"); + ok(Array[1] != NULL, "Array[1] is expected as non-NULL\n"); + ok(Array[2] == (PVOID)3, "Array[2] is expected as 3\n"); + + set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3); + TEST_ALLOC(3, -1, 0, HeapHandle, 0, 1, 3, Array); + ok(Array[0] != NULL, "Array[0] is expected as non-NULL\n"); + ok(Array[1] != NULL, "Array[1] is expected as non-NULL\n"); + ok(Array[2] != NULL, "Array[2] is expected as non-NULL\n"); + + // Array is non-NULL and too large to allocate + set_array(Array, NULL, NULL, NULL); + TEST_ALLOC_NO_RET(ERROR_NOT_ENOUGH_MEMORY, 0, HeapHandle, 0, 0x5FFFFFFF, 3, Array); + ok(ret != 3, "excepted not allocated"); + set_array(Array, NULL, NULL, NULL); + TEST_ALLOC_NO_RET(ERROR_NOT_ENOUGH_MEMORY, 0xC0000017, HeapHandle, HEAP_GENERATE_EXCEPTIONS, 0x5FFFFFFF, 3, Array); + ok(ret != 3, "excepted not allocated"); +} + +static void +MultiHeapFreeTest() +{ + INT ret, threw, err; + HANDLE HeapHandle = GetProcessHeap(); + PVOID Array[3] = {NULL, NULL, NULL}; + + // HeapHandle is non-NULL and array is NULL + TEST_FREE(0, -1, 0, HeapHandle, 0, 0, NULL); + TEST_FREE(0, -1, 0, HeapHandle, 0, 0, NULL); + TEST_FREE(0, -1, 0xC0000005, HeapHandle, 0, 1, NULL); + TEST_FREE(0, -1, 0xC0000005, HeapHandle, 0, 2, NULL); + TEST_FREE(0, -1, 0xC0000005, HeapHandle, 0, 3, NULL); + + // Array is non-NULL and contents are NULL + set_array(Array, NULL, NULL, NULL); + TEST_FREE(0, -1, 0, HeapHandle, 0, 0, Array); + set_array(Array, NULL, NULL, NULL); + TEST_FREE(1, -1, 0, HeapHandle, 0, 1, Array); + set_array(Array, NULL, NULL, NULL); + TEST_FREE(2, -1, 0, HeapHandle, 0, 2, Array); + set_array(Array, NULL, NULL, NULL); + TEST_FREE(3, -1, 0, HeapHandle, 0, 3, Array); + + // Array is non-NULL and contents are invalid pointers + set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3); + TEST_FREE(0, -1, 0, HeapHandle, 0, 0, Array); + set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3); + TEST_FREE(0, ERROR_INVALID_PARAMETER, 0, HeapHandle, 0, 1, Array); + set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3); + TEST_FREE(0, ERROR_INVALID_PARAMETER, 0, HeapHandle, 0, 2, Array); + set_array(Array, (PVOID)1, (PVOID)2, (PVOID)3); + TEST_FREE(0, ERROR_INVALID_PARAMETER, 0, HeapHandle, 0, 3, Array); + + // Array is non-NULL and contents are 1 valid pointer and 2 NULLs + set_array(Array, NULL, NULL, NULL); + ret = g_alloc(HeapHandle, 0, 1, 1, Array); + INT_EXPECTED(ret, 1); + TEST_FREE(0, -1, 0, HeapHandle, 0, 0, Array); + + set_array(Array, NULL, NULL, NULL); + ret = g_alloc(HeapHandle, 0, 1, 1, Array); + INT_EXPECTED(ret, 1); + TEST_FREE(1, -1, 0, HeapHandle, 0, 1, Array); + + set_array(Array, NULL, NULL, NULL); + ret = g_alloc(HeapHandle, 0, 1, 1, Array); + INT_EXPECTED(ret, 1); + TEST_FREE(2, -1, 0, HeapHandle, 0, 2, Array); + + set_array(Array, NULL, NULL, NULL); + ret = g_alloc(HeapHandle, 0, 1, 1, Array); + INT_EXPECTED(ret, 1); + TEST_FREE(3, -1, 0, HeapHandle, 0, 3, Array); + + // Array is non-NULL and contents are 1 valid pointer and 2 invalids + set_array(Array, NULL, (PVOID)2, (PVOID)3); + ret = g_alloc(HeapHandle, 0, 1, 1, Array); + INT_EXPECTED(ret, 1); + TEST_FREE(0, -1, 0, HeapHandle, 0, 0, Array); + + set_array(Array, NULL, (PVOID)2, (PVOID)3); + ret = g_alloc(HeapHandle, 0, 1, 1, Array); + INT_EXPECTED(ret, 1); + TEST_FREE(1, -1, 0, HeapHandle, 0, 1, Array); + + set_array(Array, NULL, (PVOID)2, (PVOID)3); + ret = g_alloc(HeapHandle, 0, 1, 1, Array); + INT_EXPECTED(ret, 1); + TEST_FREE(1, ERROR_INVALID_PARAMETER, 0, HeapHandle, 0, 2, Array); + + set_array(Array, NULL, (PVOID)2, (PVOID)3); + ret = g_alloc(HeapHandle, 0, 1, 1, Array); + INT_EXPECTED(ret, 1); + TEST_FREE(1, ERROR_INVALID_PARAMETER, 0, HeapHandle, 0, 3, Array); + + // Array is non-NULL and contents are 1 valid pointer and 2 invalids (generate exceptions) + set_array(Array, NULL, (PVOID)2, (PVOID)3); + ret = g_alloc(HeapHandle, 0, 1, 1, Array); + INT_EXPECTED(ret, 1); + TEST_FREE(0, -1, 0, HeapHandle, HEAP_GENERATE_EXCEPTIONS, 0, Array); + + set_array(Array, NULL, (PVOID)2, (PVOID)3); + ret = g_alloc(HeapHandle, 0, 1, 1, Array); + INT_EXPECTED(ret, 1); + TEST_FREE(1, -1, 0, HeapHandle, HEAP_GENERATE_EXCEPTIONS, 1, Array); + + set_array(Array, NULL, (PVOID)2, (PVOID)3); + ret = g_alloc(HeapHandle, 0, 1, 1, Array); + INT_EXPECTED(ret, 1); + TEST_FREE(1, ERROR_INVALID_PARAMETER, 0, HeapHandle, HEAP_GENERATE_EXCEPTIONS, 2, Array); + + set_array(Array, NULL, (PVOID)2, (PVOID)3); + ret = g_alloc(HeapHandle, 0, 1, 1, Array); + INT_EXPECTED(ret, 1); + TEST_FREE(1, ERROR_INVALID_PARAMETER, 0, HeapHandle, HEAP_GENERATE_EXCEPTIONS, 3, Array); + + // Array is non-NULL and contents are 3 valid pointers + set_array(Array, NULL, NULL, NULL); + ret = g_alloc(HeapHandle, 0, 3, 3, Array); + INT_EXPECTED(ret, 3); + TEST_FREE(3, -1, 0, HeapHandle, 0, 3, Array); +} + +START_TEST(RtlMultipleAllocateHeap) +{ + HINSTANCE ntdll = LoadLibraryA("ntdll"); + + g_alloc = (FN_RtlMultipleAllocateHeap)GetProcAddress(ntdll, "RtlMultipleAllocateHeap"); + g_free = (FN_RtlMultipleFreeHeap)GetProcAddress(ntdll, "RtlMultipleFreeHeap"); + + if (!g_alloc || !g_free) + { + skip("RtlMultipleAllocateHeap or RtlMultipleFreeHeap not found\n"); + } + else + { + MultiHeapAllocTest(); + MultiHeapFreeTest(); + } + + FreeLibrary(ntdll); +} diff --git a/modules/rostests/apitests/ntdll/testlist.c b/modules/rostests/apitests/ntdll/testlist.c index 7b3155314eb..95efbb0f6d8 100644 --- a/modules/rostests/apitests/ntdll/testlist.c +++ b/modules/rostests/apitests/ntdll/testlist.c @@ -60,6 +60,7 @@ extern void func_RtlHandle(void); extern void func_RtlImageRvaToVa(void); extern void func_RtlIsNameLegalDOS8Dot3(void); extern void func_RtlMemoryStream(void); +extern void func_RtlMultipleAllocateHeap(void); extern void func_RtlNtPathNameToDosPathName(void); extern void func_RtlpEnsureBufferSize(void); extern void func_RtlQueryTimeZoneInformation(void); @@ -129,6 +130,7 @@ const struct test winetest_testlist[] = { "RtlImageRvaToVa", func_RtlImageRvaToVa }, { "RtlIsNameLegalDOS8Dot3", func_RtlIsNameLegalDOS8Dot3 }, { "RtlMemoryStream", func_RtlMemoryStream }, + { "RtlMultipleAllocateHeap", func_RtlMultipleAllocateHeap }, { "RtlNtPathNameToDosPathName", func_RtlNtPathNameToDosPathName }, { "RtlpEnsureBufferSize", func_RtlpEnsureBufferSize }, { "RtlQueryTimeZoneInformation", func_RtlQueryTimeZoneInformation }, diff --git a/sdk/include/ndk/rtlfuncs.h b/sdk/include/ndk/rtlfuncs.h index 1e386af2861..d8764771eb7 100644 --- a/sdk/include/ndk/rtlfuncs.h +++ b/sdk/include/ndk/rtlfuncs.h @@ -992,27 +992,26 @@ RtlLockHeap( _In_ HANDLE Heap );
-_Must_inspect_result_ NTSYSAPI -NTSTATUS +ULONG NTAPI -RtlMultipleAllocateHeap ( +RtlMultipleAllocateHeap( _In_ HANDLE HeapHandle, _In_ ULONG Flags, _In_ SIZE_T Size, _In_ ULONG Count, _Out_cap_(Count) _Deref_post_bytecap_(Size) PVOID * Array - ); +);
NTSYSAPI -NTSTATUS +ULONG NTAPI -RtlMultipleFreeHeap ( +RtlMultipleFreeHeap( _In_ HANDLE HeapHandle, _In_ ULONG Flags, _In_ ULONG Count, _In_count_(Count) /* _Deref_ _Post_invalid_ */ PVOID * Array - ); +);
NTSYSAPI NTSTATUS diff --git a/sdk/lib/rtl/heap.c b/sdk/lib/rtl/heap.c index ccdb7d1ef29..ad4f6a1513d 100644 --- a/sdk/lib/rtl/heap.c +++ b/sdk/lib/rtl/heap.c @@ -4,6 +4,7 @@ * FILE: lib/rtl/heap.c * PURPOSE: RTL Heap backend allocator * PROGRAMMERS: Copyright 2010 Aleksey Bragin + * Copyright 2020 Katayama Hirofumi MZ */
/* Useful references: @@ -3972,7 +3973,8 @@ RtlQueryHeapInformation(HANDLE HeapHandle, return STATUS_UNSUCCESSFUL; }
-NTSTATUS +/* @implemented */ +ULONG NTAPI RtlMultipleAllocateHeap(IN PVOID HeapHandle, IN ULONG Flags, @@ -3980,19 +3982,67 @@ RtlMultipleAllocateHeap(IN PVOID HeapHandle, IN ULONG Count, OUT PVOID *Array) { - UNIMPLEMENTED; - return 0; + ULONG Index; + EXCEPTION_RECORD ExceptionRecord; + + for (Index = 0; Index < Count; ++Index) + { + Array[Index] = RtlAllocateHeap(HeapHandle, Flags, Size); + if (Array[Index] == NULL) + { + /* ERROR_NOT_ENOUGH_MEMORY */ + RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_NO_MEMORY); + + if (Flags & HEAP_GENERATE_EXCEPTIONS) + { + ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY; + ExceptionRecord.ExceptionRecord = NULL; + ExceptionRecord.NumberParameters = 0; + ExceptionRecord.ExceptionFlags = 0; + + RtlRaiseException(&ExceptionRecord); + } + break; + } + } + + return Index; }
-NTSTATUS +/* @implemented */ +ULONG NTAPI RtlMultipleFreeHeap(IN PVOID HeapHandle, IN ULONG Flags, IN ULONG Count, OUT PVOID *Array) { - UNIMPLEMENTED; - return 0; + ULONG Index; + + for (Index = 0; Index < Count; ++Index) + { + if (Array[Index] == NULL) + continue; + + _SEH2_TRY + { + if (!RtlFreeHeap(HeapHandle, Flags, Array[Index])) + { + /* ERROR_INVALID_PARAMETER */ + RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER); + break; + } + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + /* ERROR_INVALID_PARAMETER */ + RtlSetLastWin32ErrorAndNtStatusFromNtStatus(STATUS_INVALID_PARAMETER); + break; + } + _SEH2_END; + } + + return Index; }
/*