Skip to content

Commit

Permalink
bpo-35415: validate fileno argument to socket.socket (pythonGH-10917)
Browse files Browse the repository at this point in the history
  • Loading branch information
dimaqq authored and miss-islington committed Dec 17, 2018
1 parent 05c1b38 commit e991270
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 25 deletions.
49 changes: 44 additions & 5 deletions Lib/test/test_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -1700,18 +1700,15 @@ def test_socket_consistent_sock_type(self):
s.setblocking(False)
self.assertEqual(s.type, socket.SOCK_STREAM)

@unittest.skipIf(os.name == 'nt', 'Will not work on Windows')
def test_unknown_socket_family_repr(self):
# Test that when created with a family that's not one of the known
# AF_*/SOCK_* constants, socket.family just returns the number.
#
# To do this we fool socket.socket into believing it already has an
# open fd because on this path it doesn't actually verify the family and
# type and populates the socket object.
#
# On Windows this trick won't work, so the test is skipped.
fd, path = tempfile.mkstemp()
self.addCleanup(os.unlink, path)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
fd = sock.detach()
unknown_family = max(socket.AddressFamily.__members__.values()) + 1

unknown_type = max(
Expand Down Expand Up @@ -1785,6 +1782,48 @@ def test_socket_fileno(self):
s.bind(os.path.join(tmpdir, 'socket'))
self._test_socket_fileno(s, socket.AF_UNIX, socket.SOCK_STREAM)

def test_socket_fileno_rejects_float(self):
with self.assertRaisesRegex(TypeError, "integer argument expected"):
socket.socket(socket.AF_INET, socket.SOCK_STREAM, fileno=42.5)

def test_socket_fileno_rejects_other_types(self):
with self.assertRaisesRegex(TypeError, "integer is required"):
socket.socket(socket.AF_INET, socket.SOCK_STREAM, fileno="foo")

def test_socket_fileno_rejects_invalid_socket(self):
with self.assertRaisesRegex(ValueError, "negative file descriptor"):
socket.socket(socket.AF_INET, socket.SOCK_STREAM, fileno=-1)

@unittest.skipIf(os.name == "nt", "Windows disallows -1 only")
def test_socket_fileno_rejects_negative(self):
with self.assertRaisesRegex(ValueError, "negative file descriptor"):
socket.socket(socket.AF_INET, socket.SOCK_STREAM, fileno=-42)

def test_socket_fileno_requires_valid_fd(self):
WSAENOTSOCK = 10038
with self.assertRaises(OSError) as cm:
socket.socket(fileno=support.make_bad_fd())
self.assertIn(cm.exception.errno, (errno.EBADF, WSAENOTSOCK))

with self.assertRaises(OSError) as cm:
socket.socket(
socket.AF_INET,
socket.SOCK_STREAM,
fileno=support.make_bad_fd())
self.assertIn(cm.exception.errno, (errno.EBADF, WSAENOTSOCK))

def test_socket_fileno_requires_socket_fd(self):
with tempfile.NamedTemporaryFile() as afile:
with self.assertRaises(OSError):
socket.socket(fileno=afile.fileno())

with self.assertRaises(OSError) as cm:
socket.socket(
socket.AF_INET,
socket.SOCK_STREAM,
fileno=afile.fileno())
self.assertEqual(cm.exception.errno, errno.ENOTSOCK)


@unittest.skipUnless(HAVE_SOCKET_CAN, 'SocketCan required for this test.')
class BasicCANTest(unittest.TestCase):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Validate fileno= argument to socket.socket().
49 changes: 29 additions & 20 deletions Modules/socketmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -5018,28 +5018,45 @@ sock_initobj(PyObject *self, PyObject *args, PyObject *kwds)
else
#endif
{

if (PyFloat_Check(fdobj)) {
PyErr_SetString(PyExc_TypeError,
"integer argument expected, got float");
return -1;
}

fd = PyLong_AsSocket_t(fdobj);
if (fd == (SOCKET_T)(-1) && PyErr_Occurred())
return -1;
#ifdef MS_WINDOWS
if (fd == INVALID_SOCKET) {
PyErr_SetString(PyExc_ValueError,
"can't use invalid socket value");
#else
if (fd < 0) {
#endif
PyErr_SetString(PyExc_ValueError, "negative file descriptor");
return -1;
}

if (family == -1) {
sock_addr_t addrbuf;
socklen_t addrlen = sizeof(sock_addr_t);
/* validate that passed file descriptor is valid and a socket. */
sock_addr_t addrbuf;
socklen_t addrlen = sizeof(sock_addr_t);

memset(&addrbuf, 0, addrlen);
if (getsockname(fd, SAS2SA(&addrbuf), &addrlen) == 0) {
memset(&addrbuf, 0, addrlen);
if (getsockname(fd, SAS2SA(&addrbuf), &addrlen) == 0) {
if (family == -1) {
family = SAS2SA(&addrbuf)->sa_family;
} else {
}
} else {
#ifdef MS_WINDOWS
PyErr_SetFromWindowsErrWithFilename(0, "family");
/* getsockname() on an unbound socket is an error on Windows.
Invalid descriptor and not a socket is same error code.
Error out if family must be resolved, or bad descriptor. */
if (family == -1 || CHECK_ERRNO(ENOTSOCK)) {
#else
PyErr_SetFromErrnoWithFilename(PyExc_OSError, "family");
/* getsockname() is not supported for SOL_ALG on Linux. */
if (family == -1 || CHECK_ERRNO(EBADF) || CHECK_ERRNO(ENOTSOCK)) {
#endif
set_error();
return -1;
}
}
Expand All @@ -5052,11 +5069,7 @@ sock_initobj(PyObject *self, PyObject *args, PyObject *kwds)
{
type = tmp;
} else {
#ifdef MS_WINDOWS
PyErr_SetFromWindowsErrWithFilename(0, "type");
#else
PyErr_SetFromErrnoWithFilename(PyExc_OSError, "type");
#endif
set_error();
return -1;
}
}
Expand All @@ -5072,11 +5085,7 @@ sock_initobj(PyObject *self, PyObject *args, PyObject *kwds)
{
proto = tmp;
} else {
#ifdef MS_WINDOWS
PyErr_SetFromWindowsErrWithFilename(0, "protocol");
#else
PyErr_SetFromErrnoWithFilename(PyExc_OSError, "protocol");
#endif
set_error();
return -1;
}
}
Expand Down

0 comments on commit e991270

Please sign in to comment.