Skip to content

Commit e48e09c

Browse files
naweissmiss-islington
authored andcommitted
gh-107545: Fix misleading setsockopt() error messages (GH-107546)
(cherry picked from commit a50822f) Co-authored-by: naweiss <naweiss@users.noreply.github.com>
1 parent f487008 commit e48e09c

File tree

3 files changed

+98
-31
lines changed

3 files changed

+98
-31
lines changed

Lib/test/test_socket.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1538,6 +1538,40 @@ def testSetSockOpt(self):
15381538
reuse = sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)
15391539
self.assertFalse(reuse == 0, "failed to set reuse mode")
15401540

1541+
def test_setsockopt_errors(self):
1542+
# See issue #107546.
1543+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1544+
self.addCleanup(sock.close)
1545+
1546+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # No error expected.
1547+
1548+
with self.assertRaises(OverflowError):
1549+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 2 ** 100)
1550+
1551+
with self.assertRaises(OverflowError):
1552+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, - 2 ** 100)
1553+
1554+
with self.assertRaises(OverflowError):
1555+
sock.setsockopt(socket.SOL_SOCKET, 2 ** 100, 1)
1556+
1557+
with self.assertRaises(OverflowError):
1558+
sock.setsockopt(2 ** 100, socket.SO_REUSEADDR, 1)
1559+
1560+
with self.assertRaisesRegex(TypeError, "socket option should be int, bytes-like object or None"):
1561+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, dict())
1562+
1563+
with self.assertRaisesRegex(TypeError, "requires 4 arguments when the third argument is None"):
1564+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, None)
1565+
1566+
with self.assertRaisesRegex(TypeError, "only takes 4 arguments when the third argument is None"):
1567+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1, 2)
1568+
1569+
with self.assertRaisesRegex(TypeError, "takes at least 3 arguments"):
1570+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)
1571+
1572+
with self.assertRaisesRegex(TypeError, "takes at most 4 arguments"):
1573+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1, 2, 3)
1574+
15411575
def testSendAfterClose(self):
15421576
# testing send() after close() with timeout
15431577
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improve the error messages that may be raised by
2+
:meth:`~socket.socket.setsockopt`.

Modules/socketmodule.c

Lines changed: 62 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3338,32 +3338,59 @@ sock_setsockopt(PyObject *self, PyObject *args)
33383338
{
33393339
PySocketSockObject *s = _PySocketSockObject_CAST(self);
33403340

3341+
Py_ssize_t arglen;
33413342
int level;
33423343
int optname;
33433344
int res;
3344-
Py_buffer optval;
3345+
Py_buffer buffer;
33453346
int flag;
33463347
unsigned int optlen;
3347-
PyObject *none;
3348+
PyObject *optval;
3349+
3350+
if (!PyArg_ParseTuple(args, "iiO|I:setsockopt",
3351+
&level, &optname, &optval, &optlen))
3352+
{
3353+
return NULL;
3354+
}
3355+
3356+
arglen = PyTuple_Size(args);
3357+
if (arglen == 3 && optval == Py_None) {
3358+
PyErr_Format(PyExc_TypeError,
3359+
"setsockopt() requires 4 arguments when the third argument is None",
3360+
arglen);
3361+
return NULL;
3362+
}
3363+
if (arglen == 4 && optval != Py_None) {
3364+
PyErr_Format(PyExc_TypeError,
3365+
"setsockopt() only takes 4 arguments when the third argument is None (got %T)",
3366+
optval);
3367+
return NULL;
3368+
}
33483369

33493370
#ifdef AF_VSOCK
33503371
if (s->sock_family == AF_VSOCK) {
3372+
if (!PyIndex_Check(optval)) {
3373+
PyErr_Format(PyExc_TypeError,
3374+
"setsockopt() argument 3 for AF_VSOCK must be an int (got %T)",
3375+
optval);
3376+
}
33513377
uint64_t vflag; // Must be set width of 64 bits
33523378
/* setsockopt(level, opt, flag) */
3353-
if (PyArg_ParseTuple(args, "iiK:setsockopt",
3354-
&level, &optname, &vflag)) {
3355-
// level should always be set to AF_VSOCK
3356-
res = setsockopt(get_sock_fd(s), level, optname,
3357-
(void*)&vflag, sizeof vflag);
3358-
goto done;
3379+
if (!PyArg_Parse(optval, "K", &vflag)) {
3380+
return NULL;
33593381
}
3360-
return NULL;
3382+
// level should always be set to AF_VSOCK
3383+
res = setsockopt(get_sock_fd(s), level, optname,
3384+
(void*)&vflag, sizeof vflag);
3385+
goto done;
33613386
}
33623387
#endif
33633388

33643389
/* setsockopt(level, opt, flag) */
3365-
if (PyArg_ParseTuple(args, "iii:setsockopt",
3366-
&level, &optname, &flag)) {
3390+
if (PyIndex_Check(optval)) {
3391+
if (!PyArg_Parse(optval, "i", &flag)) {
3392+
return NULL;
3393+
}
33673394
#ifdef MS_WINDOWS
33683395
if (optname == SIO_TCP_SET_ACK_FREQUENCY) {
33693396
int dummy;
@@ -3380,36 +3407,40 @@ sock_setsockopt(PyObject *self, PyObject *args)
33803407
goto done;
33813408
}
33823409

3383-
PyErr_Clear();
3384-
/* setsockopt(level, opt, None, flag) */
3385-
if (PyArg_ParseTuple(args, "iiO!I:setsockopt",
3386-
&level, &optname, Py_TYPE(Py_None), &none, &optlen)) {
3410+
/* setsockopt(level, opt, None, optlen) */
3411+
if (optval == Py_None) {
33873412
assert(sizeof(socklen_t) >= sizeof(unsigned int));
33883413
res = setsockopt(get_sock_fd(s), level, optname,
33893414
NULL, (socklen_t)optlen);
33903415
goto done;
33913416
}
33923417

3393-
PyErr_Clear();
33943418
/* setsockopt(level, opt, buffer) */
3395-
if (!PyArg_ParseTuple(args, "iiy*:setsockopt",
3396-
&level, &optname, &optval))
3397-
return NULL;
3398-
3419+
if (PyObject_CheckBuffer(optval)) {
3420+
if (!PyArg_Parse(optval, "y*", &buffer)) {
3421+
return NULL;
3422+
}
33993423
#ifdef MS_WINDOWS
3400-
if (optval.len > INT_MAX) {
3401-
PyBuffer_Release(&optval);
3402-
PyErr_Format(PyExc_OverflowError,
3403-
"socket option is larger than %i bytes",
3404-
INT_MAX);
3405-
return NULL;
3406-
}
3407-
res = setsockopt(get_sock_fd(s), level, optname,
3408-
optval.buf, (int)optval.len);
3424+
if (buffer.len > INT_MAX) {
3425+
PyBuffer_Release(&buffer);
3426+
PyErr_Format(PyExc_OverflowError,
3427+
"socket option is larger than %i bytes",
3428+
INT_MAX);
3429+
return NULL;
3430+
}
3431+
res = setsockopt(get_sock_fd(s), level, optname,
3432+
buffer.buf, (int)buffer.len);
34093433
#else
3410-
res = setsockopt(get_sock_fd(s), level, optname, optval.buf, optval.len);
3434+
res = setsockopt(get_sock_fd(s), level, optname, buffer.buf, buffer.len);
34113435
#endif
3412-
PyBuffer_Release(&optval);
3436+
PyBuffer_Release(&buffer);
3437+
goto done;
3438+
}
3439+
3440+
PyErr_Format(PyExc_TypeError,
3441+
"socket option should be int, bytes-like object or None (got %T)",
3442+
optval);
3443+
return NULL;
34133444

34143445
done:
34153446
if (res < 0) {

0 commit comments

Comments
 (0)