# HG changeset patch # Parent ffc1f9d1c8b374edc6d1d5de41d18f0d3631cbf7 Allow AF_UNIX pathnames up to the maximum 108 bytes on Linux, since it does not require sun_path to be null terminated. diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -4401,6 +4401,54 @@ class TestExceptions(unittest.TestCase): self.assertTrue(issubclass(socket.timeout, socket.error)) @unittest.skipUnless(sys.platform == 'linux', 'Linux specific test') +class TestLinuxPathLen(unittest.TestCase): + + # Test AF_UNIX path length limits on Linux. + + UNIX_PATH_MAX = 108 + + def setUp(self): + self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.to_unlink = [] + + def tearDown(self): + self.sock.close() + for name in self.to_unlink: + support.unlink(name) + + def pathEncodingArgs(self): + # Return the encoding and error handler used to encode/decode + # pathnames. + encoding = sys.getfilesystemencoding() + if encoding is None: + encoding = sys.getdefaultencoding() + return encoding, "surrogateescape" + + def pathname(self, length): + # Return a bytes pathname of the given length. + path = os.path.abspath(support.TESTFN) + path_bytes = path.encode(*self.pathEncodingArgs()) + return path_bytes + b"a" * (length - len(path_bytes)) + + def testPathTooLong(self): + # Check we can't bind to a path longer than the assumed maximum. + path = self.pathname(self.UNIX_PATH_MAX + 1) + with self.assertRaisesRegexp(socket.error, "AF_UNIX path too long"): + self.sock.bind(path) + self.to_unlink.append(path) + + def testMaxPathLen(self): + # Test binding to a path of the maximum length and reading the + # address back. In this case, sun_path is not null terminated, + # and makesockaddr() used to read past the end of it. + path = self.pathname(self.UNIX_PATH_MAX) + self.sock.bind(path) + self.to_unlink.append(path) + self.assertEqual(self.sock.getsockname(), + path.decode(*self.pathEncodingArgs())) + os.stat(path) + +@unittest.skipUnless(sys.platform == 'linux', 'Linux specific test') class TestLinuxAbstractNamespace(unittest.TestCase): UNIX_PATH_MAX = 108 @@ -4896,6 +4944,7 @@ def test_main(): ]) tests.append(BasicSocketPairTest) tests.append(TestUnixDomain) + tests.append(TestLinuxPathLen) tests.append(TestLinuxAbstractNamespace) tests.extend([TIPCTest, TIPCThreadableTest]) tests.extend([BasicCANTest, CANTest]) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1308,27 +1308,16 @@ getsockaddrarg(PySocketSockObject *s, Py addr = (struct sockaddr_un*)addr_ret; #ifdef linux - if (len > 0 && path[0] == 0) { - /* Linux abstract namespace extension */ - if (len > sizeof addr->sun_path) { - PyErr_SetString(PyExc_OSError, - "AF_UNIX path too long"); - goto unix_out; - } - } - else -#endif /* linux */ - { - /* regular NULL-terminated string */ - if (len >= sizeof addr->sun_path) { - PyErr_SetString(PyExc_OSError, - "AF_UNIX path too long"); - goto unix_out; - } - addr->sun_path[len] = 0; + if (len > sizeof(addr->sun_path)) { +#else + if (len >= sizeof(addr->sun_path)) { +#endif + PyErr_SetString(PyExc_OSError, "AF_UNIX path too long"); + goto unix_out; } addr->sun_family = s->sock_family; memcpy(addr->sun_path, path, len); + memset(addr->sun_path + len, 0, sizeof(addr->sun_path) - len); #if defined(PYOS_OS2) *len_ret = sizeof(*addr); #else