Skip to content

gh-107545: Fix misleading setsockopt error message #107546

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Cleanup setsockopt implementation
  • Loading branch information
naweiss committed Jul 7, 2025
commit e3d4e7469626f3b0ac1d1ae23024b5205c8be977
11 changes: 4 additions & 7 deletions Lib/test/test_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -1538,27 +1538,24 @@ def testSetSockOpt(self):
reuse = sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)
self.assertFalse(reuse == 0, "failed to set reuse mode")

@unittest.skipIf(_testcapi is None, "requires _testcapi")
def test_setsockopt_errors(self):
# See issue #107546.
from _testcapi import INT_MAX, INT_MIN

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.addCleanup(sock.close)

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # No error expected.

with self.assertRaises(OverflowError):
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, INT_MAX + 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 2 ** 100)

with self.assertRaises(OverflowError):
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, INT_MIN - 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, - 2 ** 100)

with self.assertRaises(OverflowError):
sock.setsockopt(socket.SOL_SOCKET, INT_MAX + 1, 1)
sock.setsockopt(socket.SOL_SOCKET, 2 ** 100, 1)

with self.assertRaises(OverflowError):
sock.setsockopt(INT_MAX + 1, socket.SO_REUSEADDR, 1)
sock.setsockopt(2 ** 100, socket.SO_REUSEADDR, 1)

with self.assertRaises(TypeError):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can test the error message as well.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good Idea, I also created some more type checks and added their exact message to the tests code.

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, dict())
Expand Down
73 changes: 38 additions & 35 deletions Modules/socketmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3338,26 +3338,32 @@ sock_setsockopt(PyObject *self, PyObject *args)
Py_buffer optval;
int flag;
unsigned int optlen;
PyObject *none;
PyObject *type;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semantically, this is not type, but value.

Copy link
Author

@naweiss naweiss Jul 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I choose the name optval for all cases which access the value of the option.


if (!PyArg_ParseTuple(args, "iiO|I:setsockopt",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All this should be after the code for AF_VSOCK. Otherwise we will get wrong error messages for AF_VSOCK.

Copy link
Author

@naweiss naweiss Jul 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs for setsockopt state there are 3 different overloads and does not mention anything different for AF_VSOCK.
So, I think that the old error messages where misleading:

>>> s.setsockopt(socket.AF_VSOCK, socket.SO_REUSEADDR, 1, 2, 3)
TypeError: setsockopt() takes exactly 3 arguments (5 given)
>>> s.setsockopt(socket.AF_VSOCK, socket.SO_REUSEADDR, None, 1)
TypeError: setsockopt() takes exactly 3 arguments (4 given)
>>> s.setsockopt(socket.AF_VSOCK, socket.SO_REUSEADDR, b"aaa")
TypeError: setsockopt() argument 3 must be int, not bytes

It weird that suddenly the error message tell you that setsockopt only supports 3 arguments although there is overload with 4. I personally would expect that AF_VSOCK would still allow buffer/None argument (like in my implementation) but would raise an error telling the user that this are unsupported types specifically for AF_VSOCK. Something like this:

>>> s.setsockopt(socket.AF_VSOCK, socket.SO_REUSEADDR, 1, 2, 3)
TypeError: setsockopt() takes at most 4 arguments (5 given)
>>> s.setsockopt(socket.AF_VSOCK, socket.SO_REUSEADDR, None, 1)
TypeError: setsockopt() argument 3 for AF_VSOCK must only be an int (got None)
>>> s.setsockopt(socket.AF_VSOCK, socket.SO_REUSEADDR, b"aaa")
TypeError: setsockopt() argument 3 for AF_VSOCK must only be an int (got bytes)

&level, &optname, &type, &optlen)) {
return NULL;
}

#ifdef AF_VSOCK
if (s->sock_family == AF_VSOCK) {
uint64_t vflag; // Must be set width of 64 bits
/* setsockopt(level, opt, flag) */
if (PyArg_ParseTuple(args, "iiK:setsockopt",
&level, &optname, &vflag)) {
// level should always be set to AF_VSOCK
res = setsockopt(get_sock_fd(s), level, optname,
(void*)&vflag, sizeof vflag);
goto done;
if (!PyArg_Parse(type, "K", &vflag)) {
return NULL;
}
return NULL;
// level should always be set to AF_VSOCK
res = setsockopt(get_sock_fd(s), level, optname,
(void*)&vflag, sizeof vflag);
goto done;
}
#endif

/* setsockopt(level, opt, flag) */
if (PyArg_ParseTuple(args, "iii:setsockopt",
&level, &optname, &flag)) {
if (PyIndex_Check(type)) {
if (!PyArg_Parse(type, "i", &flag)) {
return NULL;
}
#ifdef MS_WINDOWS
if (optname == SIO_TCP_SET_ACK_FREQUENCY) {
DWORD dummy;
Expand All @@ -3373,43 +3379,40 @@ sock_setsockopt(PyObject *self, PyObject *args)
(char*)&flag, sizeof flag);
goto done;
}
if (!PyErr_ExceptionMatches(PyExc_TypeError)) {
return NULL;
}

PyErr_Clear();
/* setsockopt(level, opt, None, flag) */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/* setsockopt(level, opt, None, flag) */
/* setsockopt(level, opt, None, optlen) */

if (PyArg_ParseTuple(args, "iiO!I:setsockopt",
&level, &optname, Py_TYPE(Py_None), &none, &optlen)) {
if (type == Py_None) {
assert(sizeof(socklen_t) >= sizeof(unsigned int));
res = setsockopt(get_sock_fd(s), level, optname,
NULL, (socklen_t)optlen);
goto done;
}
if (!PyErr_ExceptionMatches(PyExc_TypeError)) {
return NULL;
}

PyErr_Clear();
/* setsockopt(level, opt, buffer) */
if (!PyArg_ParseTuple(args, "iiy*:setsockopt",
&level, &optname, &optval))
return NULL;

if (PyObject_CheckBuffer(type)) {
if (!PyArg_Parse(type, "y*", &optval)) {
return NULL;
}
#ifdef MS_WINDOWS
if (optval.len > INT_MAX) {
PyBuffer_Release(&optval);
PyErr_Format(PyExc_OverflowError,
"socket option is larger than %i bytes",
INT_MAX);
return NULL;
}
res = setsockopt(get_sock_fd(s), level, optname,
optval.buf, (int)optval.len);
if (optval.len > INT_MAX) {
PyBuffer_Release(&optval);
PyErr_Format(PyExc_OverflowError,
"socket option is larger than %i bytes",
INT_MAX);
return NULL;
}
res = setsockopt(get_sock_fd(s), level, optname,
optval.buf, (int)optval.len);
#else
res = setsockopt(get_sock_fd(s), level, optname, optval.buf, optval.len);
res = setsockopt(get_sock_fd(s), level, optname, optval.buf, optval.len);
#endif
PyBuffer_Release(&optval);
PyBuffer_Release(&optval);
goto done;
}

PyErr_Format(PyExc_TypeError,
"socket option should be integer, bytes-like object or None");
return NULL;

done:
if (res < 0) {
Expand Down
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy