David Watson added the comment:
I've rebased the patches onto all the currently released
branches, but since there are now so many variations required,
I've bundled the pass-unterminated and test patches into a single
set (enable-unterminated-*), and the return-unterminated and
addrlen-makesockaddr patches into another (fix-overrun-*), which
applies on top.
The fix-overrun patches can be applied on their own, but don't
include any tests.
The 3.5 branch has some more substantial changes which stop the
patches applying - I haven't looked into those yet.
----------
Added file:
http://bugs.python.org/file39297/enable-unterminated-2.7-2015-05-05.diff
Added file: http://bugs.python.org/file39298/fix-overrun-2.7-2015-05-05.diff
Added file:
http://bugs.python.org/file39299/enable-unterminated-3.2-2015-05-05.diff
Added file: http://bugs.python.org/file39300/fix-overrun-3.2-2015-05-05.diff
Added file:
http://bugs.python.org/file39301/enable-unterminated-3.3-2015-05-05.diff
Added file: http://bugs.python.org/file39302/fix-overrun-3.3-2015-05-05.diff
Added file:
http://bugs.python.org/file39303/enable-unterminated-3.4-2015-05-05.diff
Added file: http://bugs.python.org/file39304/fix-overrun-3.4-2015-05-05.diff
_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue8372>
_______________________________________
# HG changeset patch
# Parent 376c2d81d0e2e8ec424d4aafabfbd75e42ea3804
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
@@ -1581,6 +1581,44 @@ class TestExceptions(unittest.TestCase):
self.assertTrue(issubclass(socket.gaierror, socket.error))
self.assertTrue(issubclass(socket.timeout, socket.error))
+@unittest.skipUnless(sys.platform.startswith('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:
+ test_support.unlink(name)
+
+ def pathname(self, length):
+ # Return a pathname of the given length.
+ path = os.path.abspath(test_support.TESTFN)
+ return path + "a" * (length - len(path))
+
+ 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)
+ os.stat(path)
+
@unittest.skipUnless(sys.platform == 'linux', 'Linux specific test')
class TestLinuxAbstractNamespace(unittest.TestCase):
@@ -1793,6 +1831,7 @@ def test_main():
NetworkConnectionBehaviourTest,
])
tests.append(BasicSocketPairTest)
+ tests.append(TestLinuxPathLen)
tests.append(TestLinuxAbstractNamespace)
tests.extend([TIPCTest, TIPCThreadableTest])
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -1251,27 +1251,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(socket_error,
- "AF_UNIX path too long");
- return 0;
- }
- }
- else
-#endif /* linux */
- {
- /* regular NULL-terminated string */
- if (len >= sizeof addr->sun_path) {
- PyErr_SetString(socket_error,
- "AF_UNIX path too long");
- return 0;
- }
- addr->sun_path[len] = 0;
+ if (len > sizeof(addr->sun_path)) {
+#else
+ if (len >= sizeof(addr->sun_path)) {
+#endif
+ PyErr_SetString(socket_error, "AF_UNIX path too long");
+ return 0;
}
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
# HG changeset patch
# Parent 55bdf133669c579118bbd19b1e20c23a431614ea
When parsing addresses returned by accept(), etc., do not assume
null termination of sun_path in AF_UNIX addresses: rely instead
on the returned address length. If this is longer then the
original buffer, ignore it and use the original length.
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -1027,13 +1027,22 @@ makebdaddr(bdaddr_t *bdaddr)
/*ARGSUSED*/
static PyObject *
-makesockaddr(int sockfd, struct sockaddr *addr, int addrlen, int proto)
+makesockaddr(int sockfd, struct sockaddr *addr, socklen_t addrlen,
+ socklen_t buflen, int proto)
{
if (addrlen == 0) {
/* No address -- may be recvfrom() from known socket */
Py_INCREF(Py_None);
return Py_None;
}
+ /* buflen is the length of the buffer containing the address, and
+ addrlen is either the same, or is the length returned by the OS
+ after writing an address into the buffer. Some systems return
+ the length they would have written if there had been space
+ (e.g. when an oversized AF_UNIX address has its sun_path
+ truncated). */
+ if (addrlen > buflen)
+ addrlen = buflen;
#ifdef __BEOS__
/* XXX: BeOS version of accept() doesn't set family correctly */
@@ -1058,19 +1067,27 @@ makesockaddr(int sockfd, struct sockaddr
#if defined(AF_UNIX)
case AF_UNIX:
{
+ Py_ssize_t len, splen;
struct sockaddr_un *a = (struct sockaddr_un *) addr;
+
+ if (addrlen < offsetof(struct sockaddr_un, sun_path))
+ Py_RETURN_NONE;
+ else
+ splen = addrlen - offsetof(struct sockaddr_un, sun_path);
#ifdef linux
- if (a->sun_path[0] == 0) { /* Linux abstract namespace */
- addrlen -= offsetof(struct sockaddr_un, sun_path);
- return PyString_FromStringAndSize(a->sun_path,
- addrlen);
+ if (splen > 0 && a->sun_path[0] == 0) {
+ /* Linux abstract namespace */
+ len = splen;
}
else
#endif /* linux */
{
- /* regular NULL-terminated string */
- return PyString_FromString(a->sun_path);
+ /* Path text can occupy all of sun_path[], and therefore
+ lack null termination */
+ for (len = 0; len < splen && a->sun_path[len] != 0; len++)
+ ;
}
+ return PyString_FromStringAndSize(a->sun_path, len);
}
#endif /* AF_UNIX */
@@ -1689,6 +1706,7 @@ sock_accept(PySocketSockObject *s)
sock_addr_t addrbuf;
SOCKET_T newfd;
socklen_t addrlen;
+ socklen_t buflen;
PyObject *sock = NULL;
PyObject *addr = NULL;
PyObject *res = NULL;
@@ -1696,6 +1714,7 @@ sock_accept(PySocketSockObject *s)
if (!getsockaddrlen(s, &addrlen))
return NULL;
+ buflen = addrlen;
memset(&addrbuf, 0, addrlen);
newfd = INVALID_SOCKET;
@@ -1731,7 +1750,7 @@ sock_accept(PySocketSockObject *s)
goto finally;
}
addr = makesockaddr(s->sock_fd, SAS2SA(&addrbuf),
- addrlen, s->sock_proto);
+ addrlen, buflen, s->sock_proto);
if (addr == NULL)
goto finally;
@@ -2229,16 +2248,18 @@ sock_getsockname(PySocketSockObject *s)
sock_addr_t addrbuf;
int res;
socklen_t addrlen;
+ socklen_t buflen;
if (!getsockaddrlen(s, &addrlen))
return NULL;
+ buflen = addrlen;
memset(&addrbuf, 0, addrlen);
Py_BEGIN_ALLOW_THREADS
res = getsockname(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
Py_END_ALLOW_THREADS
if (res < 0)
return s->errorhandler();
- return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen,
+ return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen, buflen,
s->sock_proto);
}
@@ -2258,16 +2279,18 @@ sock_getpeername(PySocketSockObject *s)
sock_addr_t addrbuf;
int res;
socklen_t addrlen;
+ socklen_t buflen;
if (!getsockaddrlen(s, &addrlen))
return NULL;
+ buflen = addrlen;
memset(&addrbuf, 0, addrlen);
Py_BEGIN_ALLOW_THREADS
res = getpeername(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
Py_END_ALLOW_THREADS
if (res < 0)
return s->errorhandler();
- return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen,
+ return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen, buflen,
s->sock_proto);
}
@@ -2598,11 +2621,13 @@ sock_recvfrom_guts(PySocketSockObject *s
int timeout;
ssize_t n = -1;
socklen_t addrlen;
+ socklen_t buflen;
*addr = NULL;
if (!getsockaddrlen(s, &addrlen))
return -1;
+ buflen = addrlen;
if (!IS_SELECTABLE(s)) {
select_error();
@@ -2640,7 +2665,7 @@ sock_recvfrom_guts(PySocketSockObject *s
}
if (!(*addr = makesockaddr(s->sock_fd, SAS2SA(&addrbuf),
- addrlen, s->sock_proto)))
+ addrlen, buflen, s->sock_proto)))
return -1;
return n;
@@ -4202,7 +4227,8 @@ socket_getaddrinfo(PyObject *self, PyObj
goto err;
for (res = res0; res; res = res->ai_next) {
PyObject *addr =
- makesockaddr(-1, res->ai_addr, res->ai_addrlen, protocol);
+ makesockaddr(-1, res->ai_addr, res->ai_addrlen, res->ai_addrlen,
+ protocol);
if (addr == NULL)
goto err;
single = Py_BuildValue("iiisO", res->ai_family,
# HG changeset patch
# Parent 91096d27c802859992c11016864e3b22a0fbd141
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
@@ -1876,6 +1876,53 @@ class TestExceptions(unittest.TestCase):
self.assertTrue(issubclass(socket.gaierror, socket.error))
self.assertTrue(issubclass(socket.timeout, socket.error))
+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)
+
class TestLinuxAbstractNamespace(unittest.TestCase):
UNIX_PATH_MAX = 108
@@ -2194,6 +2241,7 @@ def test_main():
tests.append(BasicSocketPairTest)
if sys.platform == 'linux2':
tests.append(TestLinuxAbstractNamespace)
+ tests.append(TestLinuxPathLen)
if isTipcAvailable():
tests.append(TIPCTest)
tests.append(TIPCThreadableTest)
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -1226,27 +1226,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(socket_error,
- "AF_UNIX path too long");
- return 0;
- }
- }
- else
-#endif /* linux */
- {
- /* regular NULL-terminated string */
- if (len >= sizeof addr->sun_path) {
- PyErr_SetString(socket_error,
- "AF_UNIX path too long");
- return 0;
- }
- addr->sun_path[len] = 0;
+ if (len > sizeof(addr->sun_path)) {
+#else
+ if (len >= sizeof(addr->sun_path)) {
+#endif
+ PyErr_SetString(socket_error, "AF_UNIX path too long");
+ return 0;
}
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
# HG changeset patch
# Parent 9e29c3d6767ce72e5bc902c47f66e896dd0f402f
When parsing addresses returned by accept(), etc., do not assume
null termination of sun_path in AF_UNIX addresses: rely instead
on the returned address length. If this is longer then the
original buffer, ignore it and use the original length.
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -1008,13 +1008,22 @@ makebdaddr(bdaddr_t *bdaddr)
/*ARGSUSED*/
static PyObject *
-makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto)
+makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, socklen_t addrlen,
+ socklen_t buflen, int proto)
{
if (addrlen == 0) {
/* No address -- may be recvfrom() from known socket */
Py_INCREF(Py_None);
return Py_None;
}
+ /* buflen is the length of the buffer containing the address, and
+ addrlen is either the same, or is the length returned by the OS
+ after writing an address into the buffer. Some systems return
+ the length they would have written if there had been space
+ (e.g. when an oversized AF_UNIX address has its sun_path
+ truncated). */
+ if (addrlen > buflen)
+ addrlen = buflen;
switch (addr->sa_family) {
@@ -1034,18 +1043,28 @@ makesockaddr(SOCKET_T sockfd, struct soc
#if defined(AF_UNIX)
case AF_UNIX:
{
+ Py_ssize_t len, splen;
struct sockaddr_un *a = (struct sockaddr_un *) addr;
+
+ if (addrlen < offsetof(struct sockaddr_un, sun_path))
+ Py_RETURN_NONE;
+ else
+ splen = addrlen - offsetof(struct sockaddr_un, sun_path);
#ifdef linux
- if (a->sun_path[0] == 0) { /* Linux abstract namespace */
- addrlen -= offsetof(struct sockaddr_un, sun_path);
- return PyBytes_FromStringAndSize(a->sun_path, addrlen);
+ /* Backwards compatibility: return empty addresses as bytes */
+ if (splen == 0 || (splen > 0 && a->sun_path[0] == 0)) {
+ /* Linux abstract namespace */
+ return PyBytes_FromStringAndSize(a->sun_path, splen);
}
else
#endif /* linux */
{
- /* regular NULL-terminated string */
- return PyUnicode_FromString(a->sun_path);
+ /* Path text can occupy all of sun_path[], and therefore
+ lack null termination */
+ for (len = 0; len < splen && a->sun_path[len] != 0; len++)
+ ;
}
+ return PyUnicode_FromStringAndSize(a->sun_path, len);
}
#endif /* AF_UNIX */
@@ -1664,12 +1683,14 @@ sock_accept(PySocketSockObject *s)
sock_addr_t addrbuf;
SOCKET_T newfd = INVALID_SOCKET;
socklen_t addrlen;
+ socklen_t buflen;
PyObject *sock = NULL;
PyObject *addr = NULL;
PyObject *res = NULL;
int timeout;
if (!getsockaddrlen(s, &addrlen))
return NULL;
+ buflen = addrlen;
memset(&addrbuf, 0, addrlen);
if (!IS_SELECTABLE(s))
@@ -1699,7 +1720,7 @@ sock_accept(PySocketSockObject *s)
}
addr = makesockaddr(s->sock_fd, SAS2SA(&addrbuf),
- addrlen, s->sock_proto);
+ addrlen, buflen, s->sock_proto);
if (addr == NULL)
goto finally;
@@ -2149,16 +2170,18 @@ sock_getsockname(PySocketSockObject *s)
sock_addr_t addrbuf;
int res;
socklen_t addrlen;
+ socklen_t buflen;
if (!getsockaddrlen(s, &addrlen))
return NULL;
+ buflen = addrlen;
memset(&addrbuf, 0, addrlen);
Py_BEGIN_ALLOW_THREADS
res = getsockname(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
Py_END_ALLOW_THREADS
if (res < 0)
return s->errorhandler();
- return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen,
+ return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen, buflen,
s->sock_proto);
}
@@ -2178,16 +2201,18 @@ sock_getpeername(PySocketSockObject *s)
sock_addr_t addrbuf;
int res;
socklen_t addrlen;
+ socklen_t buflen;
if (!getsockaddrlen(s, &addrlen))
return NULL;
+ buflen = addrlen;
memset(&addrbuf, 0, addrlen);
Py_BEGIN_ALLOW_THREADS
res = getpeername(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
Py_END_ALLOW_THREADS
if (res < 0)
return s->errorhandler();
- return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen,
+ return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen, buflen,
s->sock_proto);
}
@@ -2456,11 +2481,13 @@ sock_recvfrom_guts(PySocketSockObject *s
int timeout;
Py_ssize_t n = -1;
socklen_t addrlen;
+ socklen_t buflen;
*addr = NULL;
if (!getsockaddrlen(s, &addrlen))
return -1;
+ buflen = addrlen;
if (!IS_SELECTABLE(s)) {
select_error();
@@ -2498,7 +2525,7 @@ sock_recvfrom_guts(PySocketSockObject *s
}
if (!(*addr = makesockaddr(s->sock_fd, SAS2SA(&addrbuf),
- addrlen, s->sock_proto)))
+ addrlen, buflen, s->sock_proto)))
return -1;
return n;
@@ -4063,7 +4090,8 @@ socket_getaddrinfo(PyObject *self, PyObj
for (res = res0; res; res = res->ai_next) {
PyObject *single;
PyObject *addr =
- makesockaddr(-1, res->ai_addr, res->ai_addrlen, protocol);
+ makesockaddr(-1, res->ai_addr, res->ai_addrlen, res->ai_addrlen,
+ protocol);
if (addr == NULL)
goto err;
single = Py_BuildValue("iiisO", res->ai_family,
# 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
# HG changeset patch
# Parent 920816bbee6a0f9e0d2a3b86d08b7c61784818a7
When parsing addresses returned by accept(), etc., do not assume
null termination of sun_path in AF_UNIX addresses: rely instead
on the returned address length. If this is longer then the
original buffer, ignore it and use the original length.
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -1044,13 +1044,22 @@ makebdaddr(bdaddr_t *bdaddr)
/*ARGSUSED*/
static PyObject *
-makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto)
+makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, socklen_t addrlen,
+ socklen_t buflen, int proto)
{
if (addrlen == 0) {
/* No address -- may be recvfrom() from known socket */
Py_INCREF(Py_None);
return Py_None;
}
+ /* buflen is the length of the buffer containing the address, and
+ addrlen is either the same, or is the length returned by the OS
+ after writing an address into the buffer. Some systems return
+ the length they would have written if there had been space
+ (e.g. when an oversized AF_UNIX address has its sun_path
+ truncated). */
+ if (addrlen > buflen)
+ addrlen = buflen;
switch (addr->sa_family) {
@@ -1070,18 +1079,28 @@ makesockaddr(SOCKET_T sockfd, struct soc
#if defined(AF_UNIX)
case AF_UNIX:
{
+ Py_ssize_t len, splen;
struct sockaddr_un *a = (struct sockaddr_un *) addr;
+
+ if (addrlen < offsetof(struct sockaddr_un, sun_path))
+ Py_RETURN_NONE;
+ else
+ splen = addrlen - offsetof(struct sockaddr_un, sun_path);
#ifdef linux
- if (a->sun_path[0] == 0) { /* Linux abstract namespace */
- addrlen -= offsetof(struct sockaddr_un, sun_path);
- return PyBytes_FromStringAndSize(a->sun_path, addrlen);
+ /* Backwards compatibility: return empty addresses as bytes */
+ if (splen == 0 || (splen > 0 && a->sun_path[0] == 0)) {
+ /* Linux abstract namespace */
+ return PyBytes_FromStringAndSize(a->sun_path, splen);
}
else
#endif /* linux */
{
- /* regular NULL-terminated string */
- return PyUnicode_DecodeFSDefault(a->sun_path);
+ /* Path text can occupy all of sun_path[], and therefore
+ lack null termination */
+ for (len = 0; len < splen && a->sun_path[len] != 0; len++)
+ ;
}
+ return PyUnicode_DecodeFSDefaultAndSize(a->sun_path, len);
}
#endif /* AF_UNIX */
@@ -2001,12 +2020,14 @@ sock_accept(PySocketSockObject *s)
sock_addr_t addrbuf;
SOCKET_T newfd = INVALID_SOCKET;
socklen_t addrlen;
+ socklen_t buflen;
PyObject *sock = NULL;
PyObject *addr = NULL;
PyObject *res = NULL;
int timeout;
if (!getsockaddrlen(s, &addrlen))
return NULL;
+ buflen = addrlen;
memset(&addrbuf, 0, addrlen);
if (!IS_SELECTABLE(s))
@@ -2036,7 +2057,7 @@ sock_accept(PySocketSockObject *s)
}
addr = makesockaddr(s->sock_fd, SAS2SA(&addrbuf),
- addrlen, s->sock_proto);
+ addrlen, buflen, s->sock_proto);
if (addr == NULL)
goto finally;
@@ -2486,16 +2507,18 @@ sock_getsockname(PySocketSockObject *s)
sock_addr_t addrbuf;
int res;
socklen_t addrlen;
+ socklen_t buflen;
if (!getsockaddrlen(s, &addrlen))
return NULL;
+ buflen = addrlen;
memset(&addrbuf, 0, addrlen);
Py_BEGIN_ALLOW_THREADS
res = getsockname(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
Py_END_ALLOW_THREADS
if (res < 0)
return s->errorhandler();
- return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen,
+ return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen, buflen,
s->sock_proto);
}
@@ -2515,16 +2538,18 @@ sock_getpeername(PySocketSockObject *s)
sock_addr_t addrbuf;
int res;
socklen_t addrlen;
+ socklen_t buflen;
if (!getsockaddrlen(s, &addrlen))
return NULL;
+ buflen = addrlen;
memset(&addrbuf, 0, addrlen);
Py_BEGIN_ALLOW_THREADS
res = getpeername(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
Py_END_ALLOW_THREADS
if (res < 0)
return s->errorhandler();
- return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen,
+ return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen, buflen,
s->sock_proto);
}
@@ -2793,11 +2818,13 @@ sock_recvfrom_guts(PySocketSockObject *s
int timeout;
Py_ssize_t n = -1;
socklen_t addrlen;
+ socklen_t buflen;
*addr = NULL;
if (!getsockaddrlen(s, &addrlen))
return -1;
+ buflen = addrlen;
if (!IS_SELECTABLE(s)) {
select_error();
@@ -2835,7 +2862,7 @@ sock_recvfrom_guts(PySocketSockObject *s
}
if (!(*addr = makesockaddr(s->sock_fd, SAS2SA(&addrbuf),
- addrlen, s->sock_proto)))
+ addrlen, buflen, s->sock_proto)))
return -1;
return n;
@@ -3071,8 +3098,7 @@ sock_recvmsg_guts(PySocketSockObject *s,
cmsg_list,
(int)msg.msg_flags,
makesockaddr(s->sock_fd, SAS2SA(&addrbuf),
- ((msg.msg_namelen > addrbuflen) ?
- addrbuflen : msg.msg_namelen),
+ msg.msg_namelen, addrbuflen,
s->sock_proto));
if (retval == NULL)
goto err_closefds;
@@ -5066,7 +5092,8 @@ socket_getaddrinfo(PyObject *self, PyObj
for (res = res0; res; res = res->ai_next) {
PyObject *single;
PyObject *addr =
- makesockaddr(-1, res->ai_addr, res->ai_addrlen, protocol);
+ makesockaddr(-1, res->ai_addr, res->ai_addrlen, res->ai_addrlen,
+ protocol);
if (addr == NULL)
goto err;
single = Py_BuildValue("iiisO", res->ai_family,
# HG changeset patch
# Parent 71ec881d834720532cb88af0c489e43f5f48bcf9
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
@@ -4585,6 +4585,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
@@ -5137,6 +5185,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
@@ -1247,27 +1247,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);
*len_ret = len + offsetof(struct sockaddr_un, sun_path);
retval = 1;
unix_out:
# HG changeset patch
# Parent b47c6fffbd04edcc55de5424d38e8bacba811f68
When parsing addresses returned by accept(), etc., do not assume
null termination of sun_path in AF_UNIX addresses: rely instead
on the returned address length. If this is longer then the
original buffer, ignore it and use the original length.
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -983,13 +983,22 @@ makebdaddr(bdaddr_t *bdaddr)
/*ARGSUSED*/
static PyObject *
-makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto)
+makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, socklen_t addrlen,
+ socklen_t buflen, int proto)
{
if (addrlen == 0) {
/* No address -- may be recvfrom() from known socket */
Py_INCREF(Py_None);
return Py_None;
}
+ /* buflen is the length of the buffer containing the address, and
+ addrlen is either the same, or is the length returned by the OS
+ after writing an address into the buffer. Some systems return
+ the length they would have written if there had been space
+ (e.g. when an oversized AF_UNIX address has its sun_path
+ truncated). */
+ if (addrlen > buflen)
+ addrlen = buflen;
switch (addr->sa_family) {
@@ -1009,18 +1018,28 @@ makesockaddr(SOCKET_T sockfd, struct soc
#if defined(AF_UNIX)
case AF_UNIX:
{
+ Py_ssize_t len, splen;
struct sockaddr_un *a = (struct sockaddr_un *) addr;
+
+ if (addrlen < offsetof(struct sockaddr_un, sun_path))
+ Py_RETURN_NONE;
+ else
+ splen = addrlen - offsetof(struct sockaddr_un, sun_path);
#ifdef linux
- if (a->sun_path[0] == 0) { /* Linux abstract namespace */
- addrlen -= offsetof(struct sockaddr_un, sun_path);
- return PyBytes_FromStringAndSize(a->sun_path, addrlen);
+ /* Backwards compatibility: return empty addresses as bytes */
+ if (splen == 0 || (splen > 0 && a->sun_path[0] == 0)) {
+ /* Linux abstract namespace */
+ return PyBytes_FromStringAndSize(a->sun_path, splen);
}
else
#endif /* linux */
{
- /* regular NULL-terminated string */
- return PyUnicode_DecodeFSDefault(a->sun_path);
+ /* Path text can occupy all of sun_path[], and therefore
+ lack null termination */
+ for (len = 0; len < splen && a->sun_path[len] != 0; len++)
+ ;
}
+ return PyUnicode_DecodeFSDefaultAndSize(a->sun_path, len);
}
#endif /* AF_UNIX */
@@ -1952,6 +1971,7 @@ sock_accept(PySocketSockObject *s)
sock_addr_t addrbuf;
SOCKET_T newfd = INVALID_SOCKET;
socklen_t addrlen;
+ socklen_t buflen;
PyObject *sock = NULL;
PyObject *addr = NULL;
PyObject *res = NULL;
@@ -1963,6 +1983,7 @@ sock_accept(PySocketSockObject *s)
if (!getsockaddrlen(s, &addrlen))
return NULL;
+ buflen = addrlen;
memset(&addrbuf, 0, addrlen);
if (!IS_SELECTABLE(s))
@@ -2025,7 +2046,7 @@ sock_accept(PySocketSockObject *s)
}
addr = makesockaddr(s->sock_fd, SAS2SA(&addrbuf),
- addrlen, s->sock_proto);
+ addrlen, buflen, s->sock_proto);
if (addr == NULL)
goto finally;
@@ -2469,16 +2490,18 @@ sock_getsockname(PySocketSockObject *s)
sock_addr_t addrbuf;
int res;
socklen_t addrlen;
+ socklen_t buflen;
if (!getsockaddrlen(s, &addrlen))
return NULL;
+ buflen = addrlen;
memset(&addrbuf, 0, addrlen);
Py_BEGIN_ALLOW_THREADS
res = getsockname(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
Py_END_ALLOW_THREADS
if (res < 0)
return s->errorhandler();
- return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen,
+ return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen, buflen,
s->sock_proto);
}
@@ -2498,16 +2521,18 @@ sock_getpeername(PySocketSockObject *s)
sock_addr_t addrbuf;
int res;
socklen_t addrlen;
+ socklen_t buflen;
if (!getsockaddrlen(s, &addrlen))
return NULL;
+ buflen = addrlen;
memset(&addrbuf, 0, addrlen);
Py_BEGIN_ALLOW_THREADS
res = getpeername(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
Py_END_ALLOW_THREADS
if (res < 0)
return s->errorhandler();
- return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen,
+ return makesockaddr(s->sock_fd, SAS2SA(&addrbuf), addrlen, buflen,
s->sock_proto);
}
@@ -2736,11 +2761,13 @@ sock_recvfrom_guts(PySocketSockObject *s
int timeout;
Py_ssize_t n = -1;
socklen_t addrlen;
+ socklen_t buflen;
*addr = NULL;
if (!getsockaddrlen(s, &addrlen))
return -1;
+ buflen = addrlen;
if (!IS_SELECTABLE(s)) {
select_error();
@@ -2775,7 +2802,7 @@ sock_recvfrom_guts(PySocketSockObject *s
}
if (!(*addr = makesockaddr(s->sock_fd, SAS2SA(&addrbuf),
- addrlen, s->sock_proto)))
+ addrlen, buflen, s->sock_proto)))
return -1;
return n;
@@ -3011,8 +3038,7 @@ sock_recvmsg_guts(PySocketSockObject *s,
cmsg_list,
(int)msg.msg_flags,
makesockaddr(s->sock_fd, SAS2SA(&addrbuf),
- ((msg.msg_namelen > addrbuflen) ?
- addrbuflen : msg.msg_namelen),
+ msg.msg_namelen, addrbuflen,
s->sock_proto));
if (retval == NULL)
goto err_closefds;
@@ -5264,7 +5290,8 @@ socket_getaddrinfo(PyObject *self, PyObj
for (res = res0; res; res = res->ai_next) {
PyObject *single;
PyObject *addr =
- makesockaddr(-1, res->ai_addr, res->ai_addrlen, protocol);
+ makesockaddr(-1, res->ai_addr, res->ai_addrlen, res->ai_addrlen,
+ protocol);
if (addr == NULL)
goto err;
single = Py_BuildValue("iiisO", res->ai_family,
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com