This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author eryksun
Recipients eryksun, m3rc1fulcameron, paul.moore, steve.dower, tim.golden, zach.ware
Date 2019-09-17.06:35:55
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1568702155.41.0.669755809278.issue38188@roundup.psfhosted.org>
In-reply-to
Content
As far as I can tell, reduction.send_handle isn't used internally in the Windows implementation, and it's also not a documented API function. However, it is tested on Windows in test_fd_transfer in Lib/test/_test_multiprocessing.py. As it turns out, the bug that Cameron's proposed solution fixes slips under the radar. By coincidence, DUPLICATE_SAME_ACCESS (2) has the same value as the file access right FILE_WRITE_DATA (2), and test_fd_transfer only checks whether the child can write to a file handle.

I propose adding test_fd_transfer_windows to _TestConnection in Lib/test/_test_multiprocessing.py, which will test whether the parent and child are granted the same access to a kernel file object after the handle is sent to the child.

    @classmethod
    def _check_handle_access(cls, conn):
        handle = reduction.recv_handle(conn)
        conn.send(get_handle_info(handle).GrantedAccess)

    @unittest.skipUnless(HAS_REDUCTION, "test needs multiprocessing.reduction")
    @unittest.skipIf(sys.platform != "win32", "Windows-only test")
    def test_fd_transfer_windows(self):
        if self.TYPE != 'processes':
            self.skipTest("only makes sense with processes")
        conn, child_conn = self.Pipe(duplex=True)
        p = self.Process(target=self._check_handle_access, args=(child_conn,))
        p.daemon = True
        p.start()
        try:
            with open(test.support.TESTFN, "wb") as f:
                self.addCleanup(test.support.unlink, test.support.TESTFN)
                handle = msvcrt.get_osfhandle(f.fileno())
                parent_access = get_handle_info(handle).GrantedAccess
                reduction.send_handle(conn, handle, p.pid)
                child_access = conn.recv()
                self.assertEqual(parent_access, child_access)
        finally:
            p.join()

get_handle_info() and the required ctypes support definitions [1] would be defined at module scope as follows:

    if WIN32:
        from ctypes import (WinDLL, WinError, Structure, POINTER, 
                            byref, sizeof, c_void_p, c_ulong)
        ntdll = WinDLL('ntdll')
        
        ntdll.NtQueryObject.argtypes = (
            c_void_p, # Handle
            c_ulong,  # ObjectInformationClass
            c_void_p, # ObjectInformation
            c_ulong,  # ObjectInformationLength
            POINTER(c_ulong)) # ReturnLength

        ObjectBasicInformation = 0

        class PUBLIC_OBJECT_BASIC_INFORMATION(Structure):
            (r"https://docs.microsoft.com/en-us/windows/win32/api"
             r"/winternl/nf-winternl-ntqueryobject")
            _fields_ = (('Attributes', c_ulong),
                        ('GrantedAccess', c_ulong),
                        ('HandleCount', c_ulong),
                        ('PointerCount', c_ulong),
                        ('Reserved', c_ulong * 10))


        def get_handle_info(handle):
            info = PUBLIC_OBJECT_BASIC_INFORMATION()
            status = ntdll.NtQueryObject(handle, ObjectBasicInformation,
                        byref(info), sizeof(info), None)
            if status < 0:
                error = ntdll.RtlNtStatusToDosError(status)
                raise WinError(error)
            return info

[1] https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryobject
History
Date User Action Args
2019-09-17 06:35:55eryksunsetrecipients: + eryksun, paul.moore, tim.golden, zach.ware, steve.dower, m3rc1fulcameron
2019-09-17 06:35:55eryksunsetmessageid: <1568702155.41.0.669755809278.issue38188@roundup.psfhosted.org>
2019-09-17 06:35:55eryksunlinkissue38188 messages
2019-09-17 06:35:55eryksuncreate