From 7c3a119d6deefb15aa1bdbe72a2a2d047e767030 Mon Sep 17 00:00:00 2001 From: Dmitry Borisov Date: Sat, 19 Apr 2025 11:22:29 +0600 Subject: [PATCH] [KMTESTS:HAL] Add a test for string I/O intrinsic functions CORE-20078 --- modules/rostests/kmtests/CMakeLists.txt | 3 +- modules/rostests/kmtests/hal/HalPortIo.c | 145 ++++++++++++++++++ .../rostests/kmtests/kmtest_drv/testlist.c | 6 + 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 modules/rostests/kmtests/hal/HalPortIo.c diff --git a/modules/rostests/kmtests/CMakeLists.txt b/modules/rostests/kmtests/CMakeLists.txt index 63d8bd469f7..6b5a9ea6fe6 100644 --- a/modules/rostests/kmtests/CMakeLists.txt +++ b/modules/rostests/kmtests/CMakeLists.txt @@ -124,7 +124,8 @@ if(ARCH STREQUAL "i386" OR ARCH STREQUAL "amd64") add_asm_files(KMTEST_DRV_ASM kmtest_drv/vm_detect.S) list(APPEND KMTEST_DRV_SOURCE - ${KMTEST_DRV_ASM}) + ${KMTEST_DRV_ASM} + hal/HalPortIo.c) endif() add_library(kmtest_drv MODULE ${KMTEST_DRV_SOURCE}) diff --git a/modules/rostests/kmtests/hal/HalPortIo.c b/modules/rostests/kmtests/hal/HalPortIo.c new file mode 100644 index 00000000000..ecaed0dfff9 --- /dev/null +++ b/modules/rostests/kmtests/hal/HalPortIo.c @@ -0,0 +1,145 @@ +/* + * PROJECT: ReactOS Kernel-Mode Tests + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Tests for string I/O intrinsic functions + * COPYRIGHT: Copyright 2025 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include + +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +/* Compile with the highest optimization level to trigger a bug (See CORE-20078) */ +#ifdef _MSC_VER +#pragma optimize("gst", on) +#pragma auto_inline(on) +#else +#pragma GCC optimize("O3") +#endif + +/* Used to isolate the effects of intrinsics from each other */ +typedef struct _TEST_CONTEXT +{ + PVOID OldBuffer; + UCHAR Pad[78]; + USHORT Port; + ULONG Size; + PVOID Buffer; +} TEST_CONTEXT, *PTEST_CONTEXT; + +/* TEST FUNCTIONS *************************************************************/ + +static +/* + * Isolate the effects of intrinsics from each other + * and hide the TEST_CONTEXT initialization from the optimizer. + */ +DECLSPEC_NOINLINE +VOID +TestWriteStringUshort( + _In_ PTEST_CONTEXT Context) +{ + __outwordstring(Context->Port, Context->Buffer, Context->Size / sizeof(USHORT)); + + /* + * The 'rep outsw' instruction increments or decrements the address in ESI by Size * 2. + * Test for CORE-20078: the ESI value should be preserved across calls. + */ + ok_eq_pointer(Context->Buffer, Context->OldBuffer); +} + +static +DECLSPEC_NOINLINE +VOID +TestWriteStringUlong( + _In_ PTEST_CONTEXT Context) +{ + __outdwordstring(Context->Port, Context->Buffer, Context->Size / sizeof(ULONG)); + + ok_eq_pointer(Context->Buffer, Context->OldBuffer); +} + +static +DECLSPEC_NOINLINE +VOID +TestWriteStringUchar( + _In_ PTEST_CONTEXT Context) +{ + __inbytestring(Context->Port, Context->Buffer, Context->Size); + + ok_eq_pointer(Context->Buffer, Context->OldBuffer); +} + +static +DECLSPEC_NOINLINE +VOID +TestReadStringUshort( + _In_ PTEST_CONTEXT Context) +{ + __inwordstring(Context->Port, Context->Buffer, Context->Size / sizeof(USHORT)); + + ok_eq_pointer(Context->Buffer, Context->OldBuffer); +} + +static +DECLSPEC_NOINLINE +VOID +TestReadStringUlong( + _In_ PTEST_CONTEXT Context) +{ + __outdwordstring(Context->Port, Context->Buffer, Context->Size / sizeof(ULONG)); + + ok_eq_pointer(Context->Buffer, Context->OldBuffer); +} + +static +DECLSPEC_NOINLINE +VOID +TestReadStringUchar( + _In_ PTEST_CONTEXT Context) +{ + __inbytestring(Context->Port, Context->Buffer, Context->Size); + + ok_eq_pointer(Context->Buffer, Context->OldBuffer); +} + +static +VOID +TestStringIo(VOID) +{ + TEST_CONTEXT Context; + UCHAR Buffer[20]; + + /* End of the x86 I/O range */ + Context.Port = 0xFFFF - sizeof(ULONG); + + Context.Buffer = Buffer; + Context.OldBuffer = Buffer; + Context.Size = sizeof(Buffer); + + TestReadStringUchar(&Context); + TestReadStringUshort(&Context); + TestReadStringUlong(&Context); + + /* + * Check whether the driver is running inside a virtual machine + * as it's not safe to write to I/O ports + * without having the port resources assigned. + */ + if (!skip(KmtIsVirtualMachine, "Please run those tests in a supported virtual machine\n")) + { + TestWriteStringUchar(&Context); + TestWriteStringUshort(&Context); + TestWriteStringUlong(&Context); + } +} + +START_TEST(HalPortIo) +{ + TestStringIo(); +} diff --git a/modules/rostests/kmtests/kmtest_drv/testlist.c b/modules/rostests/kmtests/kmtest_drv/testlist.c index b863bcf7b04..25db57346b7 100644 --- a/modules/rostests/kmtests/kmtest_drv/testlist.c +++ b/modules/rostests/kmtests/kmtest_drv/testlist.c @@ -26,6 +26,9 @@ KMT_TESTFUNC Test_FsRtlLegal; KMT_TESTFUNC Test_FsRtlMcb; KMT_TESTFUNC Test_FsRtlRemoveDotsFromPath; KMT_TESTFUNC Test_FsRtlTunnel; +#if defined(_M_IX86) || defined(_M_AMD64) +KMT_TESTFUNC Test_HalPortIo; +#endif KMT_TESTFUNC Test_HalSystemInfo; KMT_TESTFUNC Test_IoCreateFile; KMT_TESTFUNC Test_IoDeviceInterface; @@ -110,6 +113,9 @@ const KMT_TEST TestList[] = { "FsRtlMcb", Test_FsRtlMcb }, { "FsRtlRemoveDotsFromPath", Test_FsRtlRemoveDotsFromPath }, { "FsRtlTunnel", Test_FsRtlTunnel }, +#if defined(_M_IX86) || defined(_M_AMD64) + { "HalPortIo", Test_HalPortIo }, +#endif { "HalSystemInfo", Test_HalSystemInfo }, { "IoCreateFile", Test_IoCreateFile }, { "IoDeviceInterface", Test_IoDeviceInterface },