[PATCH] kernelbase: Mind OS version in the PE header when reporting OS version.

Paul Gofman pgofman at codeweavers.com
Mon Aug 30 07:52:45 CDT 2021


Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
 dlls/kernel32/tests/version.c | 175 ++++++++++++++++++++++++++++++++++
 dlls/kernelbase/version.c     |  34 ++++++-
 2 files changed, 205 insertions(+), 4 deletions(-)

diff --git a/dlls/kernel32/tests/version.c b/dlls/kernel32/tests/version.c
index 835c5398685..ad87bb38a16 100644
--- a/dlls/kernel32/tests/version.c
+++ b/dlls/kernel32/tests/version.c
@@ -918,13 +918,188 @@ static void test_PackageIdFromFullName(void)
     ok(ret == ERROR_INVALID_PARAMETER, "Got unexpected ret %u.\n", ret);
 }
 
+#define TEST_VERSION_WIN7   1
+#define TEST_VERSION_WIN8   2
+#define TEST_VERSION_WIN8_1 4
+#define TEST_VERSION_WIN10  8
+
+static const struct
+{
+    unsigned int pe_version_major, pe_version_minor;
+    unsigned int manifest_versions;
+    unsigned int expected_major, expected_minor;
+}
+test_pe_os_version_tests[] =
+{
+    { 4, 0,                         0,  6, 2},
+    { 4, 0,        TEST_VERSION_WIN10, 10, 0},
+    { 6, 3,         TEST_VERSION_WIN8,  6, 2},
+    {10, 0,                         0, 10, 0},
+    { 6, 3,                         0,  6, 3},
+    { 6, 4,                         0,  6, 3},
+    { 9, 0,                         0,  6, 3},
+    {11, 0,                         0, 10, 0},
+    {10, 0,
+            TEST_VERSION_WIN7 | TEST_VERSION_WIN8 | TEST_VERSION_WIN8_1,
+                                        6, 3},
+};
+
+static void test_pe_os_version_child(unsigned int test)
+{
+    OSVERSIONINFOEXA info;
+    BOOL ret;
+
+    info.dwOSVersionInfoSize = sizeof(info);
+    ret = GetVersionExA((OSVERSIONINFOA *)&info);
+    ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+    ok(info.dwMajorVersion == test_pe_os_version_tests[test].expected_major,
+            "Test %u, expected major version %u, got %u.\n", test, test_pe_os_version_tests[test].expected_major,
+            info.dwMajorVersion);
+    ok(info.dwMinorVersion == test_pe_os_version_tests[test].expected_minor,
+            "Test %u, expected minor version %u, got %u.\n", test, test_pe_os_version_tests[test].expected_minor,
+            info.dwMinorVersion);
+}
+
+static void test_pe_os_version(void)
+{
+    static const char manifest_header[] =
+            "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+            "<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-com:asm.v1\""
+                    " xmlns:asmv3=\"urn:schemas-microsoft-com:asm.v3\">\n"
+                "\t<compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n"
+                    "\t\t<application>\n";
+    static const char manifest_footer[] =
+                    "\t\t</application>\n"
+                "\t</compatibility>\n"
+            "</assembly>\n";
+    static const char *version_guids[] =
+    {
+        "{35138b9a-5d96-4fbd-8e2d-a2440225f93a}",
+        "{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}",
+        "{1f676c76-80e1-4239-95bb-83d0f6d0da78}",
+        "{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}",
+    };
+    LONG hdr_offset, offset_major, offset_minor;
+    char str[MAX_PATH], tmp_exe_name[9];
+    RTL_OSVERSIONINFOEXW rtlinfo;
+    STARTUPINFOA si = { 0 };
+    PROCESS_INFORMATION pi;
+    DWORD result, code;
+    unsigned int i, j;
+    HANDLE file;
+    char **argv;
+    DWORD size;
+    BOOL ret;
+
+    winetest_get_mainargs( &argv );
+
+    if (!pRtlGetVersion)
+    {
+        win_skip("RtlGetVersion is not supported, skipping tests.\n");
+        return;
+    }
+
+    rtlinfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
+    ok(!pRtlGetVersion(&rtlinfo), "RtlGetVersion failed.\n");
+    if (rtlinfo.dwMajorVersion < 10)
+    {
+        skip("Too old Windows version %u.%u, skipping tests.\n", rtlinfo.dwMajorVersion, rtlinfo.dwMinorVersion);
+        return;
+    }
+
+    file = CreateFileA(argv[0], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+    ok(file != INVALID_HANDLE_VALUE, "CreateFile failed, GetLastError() %u.\n", GetLastError());
+    SetFilePointer(file, 0x3c, NULL, FILE_BEGIN);
+    ReadFile(file, &hdr_offset, sizeof(hdr_offset), &size, NULL);
+    CloseHandle(file);
+
+    offset_major = hdr_offset + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader)
+            + FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, MajorOperatingSystemVersion);
+    offset_minor = hdr_offset + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader)
+            + FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, MinorOperatingSystemVersion);
+
+    si.cb = sizeof(si);
+
+    for (i = 0; i < ARRAY_SIZE(test_pe_os_version_tests); ++i)
+    {
+        sprintf(tmp_exe_name, "tmp%u.exe", i);
+        ret = CopyFileA(argv[0], tmp_exe_name, FALSE);
+        ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+
+        file = CreateFileA(tmp_exe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+        ok(file != INVALID_HANDLE_VALUE, "CreateFile failed, GetLastError() %u.\n", GetLastError());
+
+        SetFilePointer(file, offset_major, NULL, FILE_BEGIN);
+        WriteFile(file, &test_pe_os_version_tests[i].pe_version_major,
+                sizeof(test_pe_os_version_tests[i].pe_version_major), &size, NULL);
+        SetFilePointer(file, offset_minor, NULL, FILE_BEGIN);
+        WriteFile(file, &test_pe_os_version_tests[i].pe_version_minor,
+                sizeof(test_pe_os_version_tests[i].pe_version_minor), &size, NULL);
+
+        CloseHandle(file);
+
+        sprintf(str, "%s.manifest", tmp_exe_name);
+        file = CreateFileA(str, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+        ok(file != INVALID_HANDLE_VALUE, "CreateFile failed, GetLastError() %u.\n", GetLastError());
+
+        WriteFile(file, manifest_header, strlen(manifest_header), &size, NULL);
+        for (j = 0; j < ARRAY_SIZE(version_guids); ++j)
+        {
+            if (test_pe_os_version_tests[i].manifest_versions & (1 << j))
+            {
+                sprintf(str, "\t\t\t<supportedOS Id=\"%s\"/>\n", version_guids[j]);
+                WriteFile(file, str, strlen(str), &size, NULL);
+            }
+        }
+        WriteFile(file, manifest_footer, strlen(manifest_footer), &size, NULL);
+
+        CloseHandle(file);
+
+        sprintf(str, "%s version pe_os_version %u", tmp_exe_name, i);
+
+        ret = CreateProcessA(NULL, str, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
+        ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+        CloseHandle(pi.hThread);
+        result = WaitForSingleObject(pi.hProcess, 10000);
+        ok(result == WAIT_OBJECT_0, "Got unexpected result %#x.\n", result);
+
+        ret = GetExitCodeProcess(pi.hProcess, &code);
+        ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
+        ok(!code, "Test %u failed.\n", i);
+
+        CloseHandle(pi.hProcess);
+
+        DeleteFileA(tmp_exe_name);
+        sprintf(str, "%s.manifest", tmp_exe_name);
+        DeleteFileA(str);
+    }
+}
+
 START_TEST(version)
 {
+    char **argv;
+    int argc;
+
+    argc = winetest_get_mainargs( &argv );
+
     init_function_pointers();
 
+    if (argc >= 4)
+    {
+        if (!strcmp(argv[2], "pe_os_version"))
+        {
+            unsigned int test;
+
+            test = atoi(argv[3]);
+            test_pe_os_version_child(test);
+        }
+        return;
+    }
+
     test_GetProductInfo();
     test_GetVersionEx();
     test_VerifyVersionInfo();
+    test_pe_os_version();
     test_GetSystemFirmwareTable();
     test_PackageIdFromFullName();
 }
diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c
index a5a1f67087f..d670df27690 100644
--- a/dlls/kernelbase/version.c
+++ b/dlls/kernelbase/version.c
@@ -127,10 +127,17 @@ typedef struct
 
 
 /***********************************************************************
- * Win8 info, reported if app doesn't provide compat GUID in manifest.
+ * Win8 info, reported if the app doesn't provide compat GUID in the manifest and
+ * doesn't have higher OS version in PE header.
  */
 static const struct version_info windows8_version_info = { 6, 2, 0x23f0 };
 
+/***********************************************************************
+ * Win8.1 info, reported if the app doesn't provide compat GUID in the manifest and
+ * OS version in PE header is 8.1 or higher but below 10.
+ */
+static const struct version_info windows8_1_version_info = { 6, 3, 0x2580 };
+
 
 /***********************************************************************
  * Windows versions that need compatibility GUID specified in manifest
@@ -161,7 +168,8 @@ static const struct
  * Initialize the current_version variable.
  *
  * For compatibility, Windows 8.1 and later report Win8 version unless the app
- * has a manifest that confirms its compatibility with newer versions of Windows.
+ * has a manifest or higher OS version in the PE optional header
+ * that confirms its compatibility with newer versions of Windows.
  *
  */
 static RTL_OSVERSIONINFOEXW current_version;
@@ -173,7 +181,9 @@ static BOOL CALLBACK init_current_version(PINIT_ONCE init_once, PVOID parameter,
         DWORD ElementCount;
         COMPATIBILITY_CONTEXT_ELEMENT Elements[1];
     } *acci;
+    BOOL have_os_compat_elements = FALSE;
     const struct version_info *ver;
+    IMAGE_NT_HEADERS *nt;
     SIZE_T req;
     int idx;
 
@@ -209,8 +219,12 @@ static BOOL CALLBACK init_current_version(PINIT_ONCE init_once, PVOID parameter,
 
             for (i = 0; i < acci->ElementCount; i++)
             {
-                if (acci->Elements[i].Type == ACTCTX_COMPATIBILITY_ELEMENT_TYPE_OS &&
-                    IsEqualGUID(&acci->Elements[i].Id, &version_data[idx].guid))
+                if (acci->Elements[i].Type != ACTCTX_COMPATIBILITY_ELEMENT_TYPE_OS)
+                    continue;
+
+                have_os_compat_elements = TRUE;
+
+                if (IsEqualGUID(&acci->Elements[i].Id, &version_data[idx].guid))
                 {
                     ver = &version_data[idx].info;
 
@@ -227,6 +241,18 @@ static BOOL CALLBACK init_current_version(PINIT_ONCE init_once, PVOID parameter,
     HeapFree(GetProcessHeap(), 0, acci);
 
 done:
+    if (!have_os_compat_elements && current_version.dwMajorVersion >= 10
+            && (nt = RtlImageNtHeader(NtCurrentTeb()->Peb->ImageBaseAddress))
+            && (nt->OptionalHeader.MajorOperatingSystemVersion > 6
+            || (nt->OptionalHeader.MajorOperatingSystemVersion == 6
+            && nt->OptionalHeader.MinorOperatingSystemVersion >= 3)))
+    {
+        if (current_version.dwMajorVersion > 10)
+            FIXME("Unsupported current_version.dwMajorVersion %u.\n", current_version.dwMajorVersion);
+
+        ver = nt->OptionalHeader.MajorOperatingSystemVersion >= 10 ? NULL : &windows8_1_version_info;
+    }
+
     if (ver)
     {
         current_version.dwMajorVersion = ver->major;
-- 
2.31.1




More information about the wine-devel mailing list