[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