# HG changeset patch # Parent 5f2ae82157af71456c4837ff2838d1f0f01e759e 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 @@ -4477,6 +4477,54 @@ class TestExceptions(unittest.TestCase): self.assertTrue(issubclass(socket.timeout, OSError)) @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 @@ -5303,6 +5351,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 @@ -1460,27 +1460,17 @@ getsockaddrarg(PySocketSockObject *s, Py addr = (struct sockaddr_un*)addr_ret; #ifdef linux - if (path.len > 0 && *(const char *)path.buf == 0) { - /* Linux abstract namespace extension */ - if ((size_t)path.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 ((size_t)path.len >= sizeof addr->sun_path) { - PyErr_SetString(PyExc_OSError, - "AF_UNIX path too long"); - goto unix_out; - } - addr->sun_path[path.len] = 0; + if ((size_t)path.len > sizeof(addr->sun_path)) { +#else + if ((size_t)path.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.buf, path.len); + memset(addr->sun_path + path.len, 0, + sizeof(addr->sun_path) - path.len); *len_ret = path.len + offsetof(struct sockaddr_un, sun_path); retval = 1; unix_out: