From 74f4af7ac304baf8ea1eef95194b89982c3d683d Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 17 Sep 2016 17:27:48 -0700 Subject: [PATCH] Issue #27932: Prevent memory leak in win32_ver(). --- Doc/library/sys.rst | 27 +++++++---------- Lib/platform.py | 61 +------------------------------------- Misc/NEWS | 2 ++ PCbuild/pythoncore.vcxproj | 2 +- Python/sysmodule.c | 46 +++++++++++++++++++++++++--- 5 files changed, 57 insertions(+), 81 deletions(-) diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 6ee6c490dfd..55b744e19d6 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -552,26 +552,15 @@ always available. Return a named tuple describing the Windows version currently running. The named elements are *major*, *minor*, *build*, *platform*, *service_pack*, *service_pack_minor*, - *service_pack_major*, *suite_mask*, and *product_type*. - *service_pack* contains a string while all other values are + *service_pack_major*, *suite_mask*, *product_type* and + *platform_version*. *service_pack* contains a string, + *platform_version* a 3-tuple and all other values are integers. The components can also be accessed by name, so ``sys.getwindowsversion()[0]`` is equivalent to ``sys.getwindowsversion().major``. For compatibility with prior versions, only the first 5 elements are retrievable by indexing. - *platform* may be one of the following values: - - +-----------------------------------------+-------------------------+ - | Constant | Platform | - +=========================================+=========================+ - | :const:`0 (VER_PLATFORM_WIN32s)` | Win32s on Windows 3.1 | - +-----------------------------------------+-------------------------+ - | :const:`1 (VER_PLATFORM_WIN32_WINDOWS)` | Windows 95/98/ME | - +-----------------------------------------+-------------------------+ - | :const:`2 (VER_PLATFORM_WIN32_NT)` | Windows NT/2000/XP/x64 | - +-----------------------------------------+-------------------------+ - | :const:`3 (VER_PLATFORM_WIN32_CE)` | Windows CE | - +-----------------------------------------+-------------------------+ + *platform* will be :const:`2 (VER_PLATFORM_WIN32_NT)`. *product_type* may be one of the following values: @@ -587,17 +576,23 @@ always available. | | a domain controller. | +---------------------------------------+---------------------------------+ - This function wraps the Win32 :c:func:`GetVersionEx` function; see the Microsoft documentation on :c:func:`OSVERSIONINFOEX` for more information about these fields. + *platform_version* returns the accurate major version, minor version and + build number of the current operating system, rather than the version that + is being emulated for the process. It is intended for use in logging rather + than for feature detection. + Availability: Windows. .. versionchanged:: 3.2 Changed to a named tuple and added *service_pack_minor*, *service_pack_major*, *suite_mask*, and *product_type*. + .. versionchanged:: 3.6 + Added *platform_version* .. function:: get_coroutine_wrapper() diff --git a/Lib/platform.py b/Lib/platform.py index ee315fa3197..e48ad0b6e7b 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -497,65 +497,6 @@ _WIN32_SERVER_RELEASES = { (6, None): "post2012ServerR2", } -def _get_real_winver(maj, min, build): - if maj < 6 or (maj == 6 and min < 2): - return maj, min, build - - from ctypes import (c_buffer, POINTER, byref, create_unicode_buffer, - Structure, WinDLL) - from ctypes.wintypes import DWORD, HANDLE - - class VS_FIXEDFILEINFO(Structure): - _fields_ = [ - ("dwSignature", DWORD), - ("dwStrucVersion", DWORD), - ("dwFileVersionMS", DWORD), - ("dwFileVersionLS", DWORD), - ("dwProductVersionMS", DWORD), - ("dwProductVersionLS", DWORD), - ("dwFileFlagsMask", DWORD), - ("dwFileFlags", DWORD), - ("dwFileOS", DWORD), - ("dwFileType", DWORD), - ("dwFileSubtype", DWORD), - ("dwFileDateMS", DWORD), - ("dwFileDateLS", DWORD), - ] - - kernel32 = WinDLL('kernel32') - version = WinDLL('version') - - # We will immediately double the length up to MAX_PATH, but the - # path may be longer, so we retry until the returned string is - # shorter than our buffer. - name_len = actual_len = 130 - while actual_len == name_len: - name_len *= 2 - name = create_unicode_buffer(name_len) - actual_len = kernel32.GetModuleFileNameW(HANDLE(kernel32._handle), - name, len(name)) - if not actual_len: - return maj, min, build - - size = version.GetFileVersionInfoSizeW(name, None) - if not size: - return maj, min, build - - ver_block = c_buffer(size) - if (not version.GetFileVersionInfoW(name, None, size, ver_block) or - not ver_block): - return maj, min, build - - pvi = POINTER(VS_FIXEDFILEINFO)() - if not version.VerQueryValueW(ver_block, "", byref(pvi), byref(DWORD())): - return maj, min, build - - maj = pvi.contents.dwProductVersionMS >> 16 - min = pvi.contents.dwProductVersionMS & 0xFFFF - build = pvi.contents.dwProductVersionLS >> 16 - - return maj, min, build - def win32_ver(release='', version='', csd='', ptype=''): try: from sys import getwindowsversion @@ -567,7 +508,7 @@ def win32_ver(release='', version='', csd='', ptype=''): from _winreg import OpenKeyEx, QueryValueEx, CloseKey, HKEY_LOCAL_MACHINE winver = getwindowsversion() - maj, min, build = _get_real_winver(*winver[:3]) + maj, min, build = winver.platform_version or winver[:3] version = '{0}.{1}.{2}'.format(maj, min, build) release = (_WIN32_CLIENT_RELEASES.get((maj, min)) or diff --git a/Misc/NEWS b/Misc/NEWS index b7fa96933df..15a7e0e66ad 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -29,6 +29,8 @@ Core and Builtins Library ------- +- Issue #27932: Prevent memory leak in win32_ver(). + - Fix UnboundLocalError in socket._sendfile_use_sendfile. - Issue #28075: Check for ERROR_ACCESS_DENIED in Windows implementation of diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 5d957de7c85..769c6436572 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -69,7 +69,7 @@ _USRDLL;Py_BUILD_CORE;Py_ENABLE_SHARED;MS_DLL_ID="$(SysWinVer)";%(PreprocessorDefinitions) - shlwapi.lib;ws2_32.lib;%(AdditionalDependencies) + version.lib;shlwapi.lib;ws2_32.lib;%(AdditionalDependencies) 0x1e000000 diff --git a/Python/sysmodule.c b/Python/sysmodule.c index b7afe93ca5f..5dd0d108343 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -894,10 +894,11 @@ Return information about the running version of Windows as a named tuple.\n\ The members are named: major, minor, build, platform, service_pack,\n\ service_pack_major, service_pack_minor, suite_mask, and product_type. For\n\ backward compatibility, only the first 5 items are available by indexing.\n\ -All elements are numbers, except service_pack which is a string. Platform\n\ -may be 0 for win32s, 1 for Windows 9x/ME, 2 for Windows NT/2000/XP/Vista/7,\n\ -3 for Windows CE. Product_type may be 1 for a workstation, 2 for a domain\n\ -controller, 3 for a server." +All elements are numbers, except service_pack and platform_type which are\n\ +strings, and platform_version which is a 3-tuple. Platform is always 2.\n\ +Product_type may be 1 for a workstation, 2 for a domain controller, 3 for a\n\ +server. Platform_version is a 3-tuple containing a version number that is\n\ +intended for identifying the OS rather than feature detection." ); static PyTypeObject WindowsVersionType = {0, 0, 0, 0, 0, 0}; @@ -912,6 +913,7 @@ static PyStructSequence_Field windows_version_fields[] = { {"service_pack_minor", "Service Pack minor version number"}, {"suite_mask", "Bit mask identifying available product suites"}, {"product_type", "System product type"}, + {"platform_version", "Diagnostic version number"}, {0} }; @@ -936,6 +938,12 @@ sys_getwindowsversion(PyObject *self) PyObject *version; int pos = 0; OSVERSIONINFOEX ver; + DWORD realMajor, realMinor, realBuild; + HANDLE hKernel32; + wchar_t kernel32_path[MAX_PATH]; + LPVOID verblock; + DWORD verblock_size; + ver.dwOSVersionInfoSize = sizeof(ver); if (!GetVersionEx((OSVERSIONINFO*) &ver)) return PyErr_SetFromWindowsErr(0); @@ -954,10 +962,40 @@ sys_getwindowsversion(PyObject *self) PyStructSequence_SET_ITEM(version, pos++, PyLong_FromLong(ver.wSuiteMask)); PyStructSequence_SET_ITEM(version, pos++, PyLong_FromLong(ver.wProductType)); + realMajor = ver.dwMajorVersion; + realMinor = ver.dwMinorVersion; + realBuild = ver.dwBuildNumber; + + // GetVersion will lie if we are running in a compatibility mode. + // We need to read the version info from a system file resource + // to accurately identify the OS version. If we fail for any reason, + // just return whatever GetVersion said. + hKernel32 = GetModuleHandleW(L"kernel32.dll"); + if (hKernel32 && GetModuleFileNameW(hKernel32, kernel32_path, MAX_PATH) && + (verblock_size = GetFileVersionInfoSizeW(kernel32_path, NULL)) && + (verblock = PyMem_RawMalloc(verblock_size))) { + VS_FIXEDFILEINFO *ffi; + UINT ffi_len; + + if (GetFileVersionInfoW(kernel32_path, 0, verblock_size, verblock) && + VerQueryValueW(verblock, L"", (LPVOID)&ffi, &ffi_len)) { + realMajor = HIWORD(ffi->dwProductVersionMS); + realMinor = LOWORD(ffi->dwProductVersionMS); + realBuild = HIWORD(ffi->dwProductVersionLS); + } + PyMem_RawFree(verblock); + } + PyStructSequence_SET_ITEM(version, pos++, PyTuple_Pack(3, + PyLong_FromLong(realMajor), + PyLong_FromLong(realMinor), + PyLong_FromLong(realBuild) + )); + if (PyErr_Occurred()) { Py_DECREF(version); return NULL; } + return version; }