From: Antoine Pitrou Date: Tue, 23 Aug 2011 17:54:20 +0000 (+0200) Subject: Issue #11657: Fix sending file descriptors over 255 over a multiprocessing Pipe. X-Git-Tag: v2.7.3rc1~493 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a1a8da8bf5d6d27936702f9241c8726efd6b4b42;p=python Issue #11657: Fix sending file descriptors over 255 over a multiprocessing Pipe. Also added some tests. --- diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py index 6da4b17772..935e224d44 100644 --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -15,6 +15,7 @@ import array import socket import random import logging +import errno from test import test_support from StringIO import StringIO _multiprocessing = test_support.import_module('_multiprocessing') @@ -32,7 +33,7 @@ import multiprocessing.managers import multiprocessing.heap import multiprocessing.pool -from multiprocessing import util +from multiprocessing import util, reduction try: from multiprocessing.sharedctypes import Value, copy @@ -40,6 +41,11 @@ try: except ImportError: HAS_SHAREDCTYPES = False +try: + import msvcrt +except ImportError: + msvcrt = None + # # # @@ -68,6 +74,11 @@ HAVE_GETVALUE = not getattr(_multiprocessing, WIN32 = (sys.platform == "win32") +try: + MAXFD = os.sysconf("SC_OPEN_MAX") +except: + MAXFD = 256 + # # Some tests require ctypes # @@ -1481,6 +1492,76 @@ class _TestConnection(BaseTestCase): self.assertRaises(ValueError, a.send_bytes, msg, 4, -1) + @classmethod + def _is_fd_assigned(cls, fd): + try: + os.fstat(fd) + except OSError as e: + if e.errno == errno.EBADF: + return False + raise + else: + return True + + @classmethod + def _writefd(cls, conn, data, create_dummy_fds=False): + if create_dummy_fds: + for i in range(0, 256): + if not cls._is_fd_assigned(i): + os.dup2(conn.fileno(), i) + fd = reduction.recv_handle(conn) + if msvcrt: + fd = msvcrt.open_osfhandle(fd, os.O_WRONLY) + os.write(fd, data) + os.close(fd) + + def test_fd_transfer(self): + if self.TYPE != 'processes': + self.skipTest("only makes sense with processes") + conn, child_conn = self.Pipe(duplex=True) + + p = self.Process(target=self._writefd, args=(child_conn, b"foo")) + p.start() + with open(test_support.TESTFN, "wb") as f: + fd = f.fileno() + if msvcrt: + fd = msvcrt.get_osfhandle(fd) + reduction.send_handle(conn, fd, p.pid) + p.join() + with open(test_support.TESTFN, "rb") as f: + self.assertEqual(f.read(), b"foo") + + @unittest.skipIf(sys.platform == "win32", + "test semantics don't make sense on Windows") + @unittest.skipIf(MAXFD <= 256, + "largest assignable fd number is too small") + @unittest.skipUnless(hasattr(os, "dup2"), + "test needs os.dup2()") + def test_large_fd_transfer(self): + # With fd > 256 (issue #11657) + if self.TYPE != 'processes': + self.skipTest("only makes sense with processes") + conn, child_conn = self.Pipe(duplex=True) + + p = self.Process(target=self._writefd, args=(child_conn, b"bar", True)) + p.start() + with open(test_support.TESTFN, "wb") as f: + fd = f.fileno() + for newfd in range(256, MAXFD): + if not self._is_fd_assigned(newfd): + break + else: + self.fail("could not find an unassigned large file descriptor") + os.dup2(fd, newfd) + try: + reduction.send_handle(conn, newfd, p.pid) + finally: + os.close(newfd) + p.join() + with open(test_support.TESTFN, "rb") as f: + self.assertEqual(f.read(), b"bar") + + class _TestListenerClient(BaseTestCase): ALLOWED_TYPES = ('processes', 'threads') diff --git a/Misc/NEWS b/Misc/NEWS index a5bc4aeaa7..4be510d18b 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -40,6 +40,9 @@ Core and Builtins Library ------- +- Issue #11657: Fix sending file descriptors over 255 over a multiprocessing + Pipe. + - Issue #12213: Fix a buffering bug with interleaved reads and writes that could appear on io.BufferedRandom streams. diff --git a/Modules/_multiprocessing/multiprocessing.c b/Modules/_multiprocessing/multiprocessing.c index 93f1c488e3..5b23fc3c36 100644 --- a/Modules/_multiprocessing/multiprocessing.c +++ b/Modules/_multiprocessing/multiprocessing.c @@ -122,7 +122,7 @@ multiprocessing_sendfd(PyObject *self, PyObject *args) cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int)); msg.msg_controllen = cmsg->cmsg_len; - *CMSG_DATA(cmsg) = fd; + * (int *) CMSG_DATA(cmsg) = fd; Py_BEGIN_ALLOW_THREADS res = sendmsg(conn, &msg, 0); @@ -165,7 +165,7 @@ multiprocessing_recvfd(PyObject *self, PyObject *args) if (res < 0) return PyErr_SetFromErrno(PyExc_OSError); - fd = *CMSG_DATA(cmsg); + fd = * (int *) CMSG_DATA(cmsg); return Py_BuildValue("i", fd); }