Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions Lib/test/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -2325,6 +2325,62 @@ def test_unlink_removes_junction(self):
os.unlink(self.junction)
self.assertFalse(os.path.exists(self.junction))

@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests")
class Win32NtTests(unittest.TestCase):
def setUp(self):
from test import support
self.nt = support.import_module('nt')
pass

def tearDown(self):
pass

def test_getfinalpathname_handles(self):
try:
import ctypes, ctypes.wintypes
except ImportError:
raise unittest.SkipTest('ctypes module is required for this test')

kernel = ctypes.WinDLL('Kernel32.dll', use_last_error=True)
kernel.GetCurrentProcess.restype = ctypes.wintypes.HANDLE

kernel.GetProcessHandleCount.restype = ctypes.wintypes.BOOL
kernel.GetProcessHandleCount.argtypes = (ctypes.wintypes.HANDLE,
ctypes.wintypes.LPDWORD)

# This is a pseudo-handle that doesn't need to be closed
hproc = kernel.GetCurrentProcess()

handle_count = ctypes.wintypes.DWORD()
ok = kernel.GetProcessHandleCount(hproc, ctypes.byref(handle_count))
self.assertEqual(1, ok)

before_count = handle_count.value

# The first two test the error path, __file__ tests the success path
filenames = [ r'\\?\C:',
r'\\?\NUL',
r'\\?\CONIN',
__file__ ]

for i in range(10):
for name in filenames:
try:
tmp = self.nt._getfinalpathname(name)
except:
# Failure is expected
pass
try:
tmp = os.stat(name)
except:
pass

ok = kernel.GetProcessHandleCount(hproc, ctypes.byref(handle_count))
self.assertEqual(1, ok)

handle_delta = handle_count.value - before_count

self.assertEqual(0, handle_delta)

@support.skip_unless_symlink
class NonLocalSymlinkTests(unittest.TestCase):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix handle leaks in os.stat on Windows.
18 changes: 10 additions & 8 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1640,11 +1640,6 @@ get_target_path(HANDLE hdl, wchar_t **target_path)
return FALSE;
}

if(!CloseHandle(hdl)) {
PyMem_RawFree(buf);
return FALSE;
}

buf[result_length] = 0;

*target_path = buf;
Expand Down Expand Up @@ -1702,9 +1697,10 @@ win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result,
return -1;
}
if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
if (!win32_get_reparse_tag(hFile, &reparse_tag))
if (!win32_get_reparse_tag(hFile, &reparse_tag)) {
CloseHandle(hFile);
Comment thread
thebecwar marked this conversation as resolved.
return -1;

}
/* Close the outer open file handle now that we're about to
reopen it with different flags. */
if (!CloseHandle(hFile))
Expand All @@ -1721,8 +1717,14 @@ win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result,
if (hFile2 == INVALID_HANDLE_VALUE)
return -1;

if (!get_target_path(hFile2, &target_path))
if (!get_target_path(hFile2, &target_path)) {
CloseHandle(hFile2);
return -1;
}

if (!CloseHandle(hFile2)) {
return -1;
}

code = win32_xstat_impl(target_path, result, FALSE);
PyMem_RawFree(target_path);
Expand Down