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
Add clearer error messages to setsockopt
  • Loading branch information
naweiss committed Jul 8, 2025
commit ca87487d4b05077dc3d025922a6249a725a5401c
15 changes: 14 additions & 1 deletion Lib/test/test_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -1557,9 +1557,22 @@ def test_setsockopt_errors(self):
with self.assertRaises(OverflowError):
sock.setsockopt(2 ** 100, socket.SO_REUSEADDR, 1)

with self.assertRaises(TypeError):
msg = "socket option should be should be integer, bytes-like object or None"
with self.assertRaises(TypeError, msg=msg):
Copy link
Member

Choose a reason for hiding this comment

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

It should be assertRaisesRegex. And the expected error message is a regular expression, so you need to escape parentheses.

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, dict())

msg = "setsockopt() takes 3 or 4 arguments (2 given)"
with self.assertRaises(TypeError, msg=msg):
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)

msg = "setsockopt() take 4 arguments when socket option is None (3 given)"
with self.assertRaises(TypeError, msg=msg):
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, None)

msg = "setsockopt() argument 3 must be NoneType, not int"
with self.assertRaises(TypeError, msg=msg):
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1, 2)

def testSendAfterClose(self):
# testing send() after close() with timeout
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
Expand Down
55 changes: 39 additions & 16 deletions Modules/socketmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3332,24 +3332,41 @@ sock_setsockopt(PyObject *self, PyObject *args)
{
PySocketSockObject *s = _PySocketSockObject_CAST(self);

Py_ssize_t arglen;
int level;
int optname;
int res;
Py_buffer optval;
Py_buffer buffer;
int flag;
unsigned int optlen;
PyObject *type;
PyObject *optval;

if (!PyArg_ParseTuple(args, "iiO|I:setsockopt",
&level, &optname, &type, &optlen)) {
return NULL;
arglen = PyTuple_Size(args);
switch (arglen) {
case 3:
if (!PyArg_ParseTuple(args, "iiO:setsockopt",
&level, &optname, &optval)) {
return NULL;
}
Copy link
Member

Choose a reason for hiding this comment

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

Can you please move this test for None and 4 arguments here?

if (optval == Py_None) { PyErr_Format(...) }

break;
case 4:
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.

Suggested change
if (!PyArg_ParseTuple(args, "iiO!I:setsockopt",
if (!PyArg_ParseTuple(args, "iiOI:setsockopt",

Copy link
Author

Choose a reason for hiding this comment

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

This ensures that the 3rd argument would be of the specified type.
In my case Py_TYPE(Py_None) that way I ensure that if we have 4 arguments the 3rd must be None.

&level, &optname, Py_TYPE(Py_None), &optval, &optlen)) {
return NULL;
}
break;
Copy link
Member

Choose a reason for hiding this comment

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

We need if (optval != Py_None) {error...} here.

Copy link
Author

@naweiss naweiss Jul 11, 2025

Choose a reason for hiding this comment

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

Look at https://github.com/python/cpython/pull/107546/files#r2192102447.
The "O!" format along with Py_TYPE(Py_None) ensures that the type of the 3rd argument would be None.

default:
PyErr_Format(PyExc_TypeError,
"setsockopt() takes 3 or 4 arguments (%zd given)",
arglen);
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_Parse(type, "K", &vflag)) {
if (!PyArg_Parse(optval, "K", &vflag)) {
return NULL;
}
// level should always be set to AF_VSOCK
Expand All @@ -3360,8 +3377,8 @@ sock_setsockopt(PyObject *self, PyObject *args)
#endif

/* setsockopt(level, opt, flag) */
if (PyIndex_Check(type)) {
if (!PyArg_Parse(type, "i", &flag)) {
if (PyIndex_Check(optval)) {
if (!PyArg_Parse(optval, "i", &flag)) {
return NULL;
}
#ifdef MS_WINDOWS
Expand All @@ -3381,32 +3398,38 @@ sock_setsockopt(PyObject *self, PyObject *args)
}

/* 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 (type == Py_None) {
if (optval == Py_None) {
if (arglen != 4) {
PyErr_Format(PyExc_TypeError,
"setsockopt() take 4 arguments when socket option is None (%zd given)",
arglen);
return NULL;
}
assert(sizeof(socklen_t) >= sizeof(unsigned int));
res = setsockopt(get_sock_fd(s), level, optname,
NULL, (socklen_t)optlen);
goto done;
}

/* setsockopt(level, opt, buffer) */
if (PyObject_CheckBuffer(type)) {
if (!PyArg_Parse(type, "y*", &optval)) {
if (PyObject_CheckBuffer(optval)) {
if (!PyArg_Parse(optval, "y*", &buffer)) {
return NULL;
}
#ifdef MS_WINDOWS
if (optval.len > INT_MAX) {
PyBuffer_Release(&optval);
if (buffer.len > INT_MAX) {
PyBuffer_Release(&buffer);
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);
buffer.buf, (int)buffer.len);
#else
res = setsockopt(get_sock_fd(s), level, optname, optval.buf, optval.len);
res = setsockopt(get_sock_fd(s), level, optname, buffer.buf, buffer.len);
#endif
PyBuffer_Release(&optval);
PyBuffer_Release(&buffer);
goto done;
}

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