Skip to content

Commit 74f49ab

Browse files
Issue #15989: Fix several occurrences of integer overflow
when result of PyInt_AsLong() or PyLong_AsLong() narrowed to int without checks. This is a backport of changesets 13e2e44db99d and 525407d89277.
1 parent ac7b49f commit 74f49ab

17 files changed

+143
-22
lines changed

Include/intobject.h

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ PyAPI_FUNC(PyObject *) PyInt_FromSize_t(size_t);
4040
PyAPI_FUNC(PyObject *) PyInt_FromSsize_t(Py_ssize_t);
4141
PyAPI_FUNC(long) PyInt_AsLong(PyObject *);
4242
PyAPI_FUNC(Py_ssize_t) PyInt_AsSsize_t(PyObject *);
43+
PyAPI_FUNC(int) _PyInt_AsInt(PyObject *);
4344
PyAPI_FUNC(unsigned long) PyInt_AsUnsignedLongMask(PyObject *);
4445
#ifdef HAVE_LONG_LONG
4546
PyAPI_FUNC(unsigned PY_LONG_LONG) PyInt_AsUnsignedLongLongMask(PyObject *);

Include/longobject.h

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ PyAPI_FUNC(long) PyLong_AsLongAndOverflow(PyObject *, int *);
2525
PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(PyObject *);
2626
PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject *);
2727
PyAPI_FUNC(Py_ssize_t) PyLong_AsSsize_t(PyObject *);
28+
PyAPI_FUNC(int) _PyLong_AsInt(PyObject *);
2829
PyAPI_FUNC(PyObject *) PyLong_GetInfo(void);
2930

3031
/* For use by intobject.c only */

Lib/ctypes/test/test_structures.py

+9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import unittest
22
from ctypes import *
33
from struct import calcsize
4+
import _testcapi
45

56
class SubclassesTest(unittest.TestCase):
67
def test_subclass(self):
@@ -199,6 +200,14 @@ class X(Structure):
199200
"_pack_": -1}
200201
self.assertRaises(ValueError, type(Structure), "X", (Structure,), d)
201202

203+
# Issue 15989
204+
d = {"_fields_": [("a", c_byte)],
205+
"_pack_": _testcapi.INT_MAX + 1}
206+
self.assertRaises(ValueError, type(Structure), "X", (Structure,), d)
207+
d = {"_fields_": [("a", c_byte)],
208+
"_pack_": _testcapi.UINT_MAX + 2}
209+
self.assertRaises(ValueError, type(Structure), "X", (Structure,), d)
210+
202211
def test_initializers(self):
203212
class Person(Structure):
204213
_fields_ = [("name", c_char*6),

Lib/test/string_tests.py

+15
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import unittest, string, sys, struct
66
from test import test_support
77
from UserList import UserList
8+
import _testcapi
89

910
class Sequence:
1011
def __init__(self, seq='wxyz'): self.seq = seq
@@ -1113,6 +1114,20 @@ def test_formatting(self):
11131114
self.checkraises(TypeError, '%10.*f', '__mod__', ('foo', 42.))
11141115
self.checkraises(ValueError, '%10', '__mod__', (42,))
11151116

1117+
if _testcapi.PY_SSIZE_T_MAX < sys.maxint:
1118+
self.checkraises(OverflowError, '%*s', '__mod__',
1119+
(_testcapi.PY_SSIZE_T_MAX + 1, ''))
1120+
if _testcapi.INT_MAX < sys.maxint:
1121+
self.checkraises(OverflowError, '%.*f', '__mod__',
1122+
(_testcapi.INT_MAX + 1, 1. / 7))
1123+
# Issue 15989
1124+
if 1 << (_testcapi.PY_SSIZE_T_MAX.bit_length() + 1) <= sys.maxint:
1125+
self.checkraises(OverflowError, '%*s', '__mod__',
1126+
(1 << (_testcapi.PY_SSIZE_T_MAX.bit_length() + 1), ''))
1127+
if _testcapi.UINT_MAX < sys.maxint:
1128+
self.checkraises(OverflowError, '%.*f', '__mod__',
1129+
(_testcapi.UINT_MAX + 1, 1. / 7))
1130+
11161131
class X(object): pass
11171132
self.checkraises(TypeError, 'abc', '__mod__', X())
11181133

Lib/test/test_fcntl.py

+21
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import os
77
import struct
88
import sys
9+
import _testcapi
910
import unittest
1011
from test.test_support import (verbose, TESTFN, unlink, run_unittest,
1112
import_module)
@@ -81,6 +82,26 @@ def test_fcntl_file_descriptor(self):
8182
rv = fcntl.fcntl(self.f, fcntl.F_SETLKW, lockdata)
8283
self.f.close()
8384

85+
def test_fcntl_bad_file(self):
86+
class F:
87+
def __init__(self, fn):
88+
self.fn = fn
89+
def fileno(self):
90+
return self.fn
91+
self.assertRaises(ValueError, fcntl.fcntl, -1, fcntl.F_SETFL, os.O_NONBLOCK)
92+
self.assertRaises(ValueError, fcntl.fcntl, F(-1), fcntl.F_SETFL, os.O_NONBLOCK)
93+
self.assertRaises(TypeError, fcntl.fcntl, 'spam', fcntl.F_SETFL, os.O_NONBLOCK)
94+
self.assertRaises(TypeError, fcntl.fcntl, F('spam'), fcntl.F_SETFL, os.O_NONBLOCK)
95+
# Issue 15989
96+
self.assertRaises(ValueError, fcntl.fcntl, _testcapi.INT_MAX + 1,
97+
fcntl.F_SETFL, os.O_NONBLOCK)
98+
self.assertRaises(ValueError, fcntl.fcntl, F(_testcapi.INT_MAX + 1),
99+
fcntl.F_SETFL, os.O_NONBLOCK)
100+
self.assertRaises(ValueError, fcntl.fcntl, _testcapi.INT_MIN - 1,
101+
fcntl.F_SETFL, os.O_NONBLOCK)
102+
self.assertRaises(ValueError, fcntl.fcntl, F(_testcapi.INT_MIN - 1),
103+
fcntl.F_SETFL, os.O_NONBLOCK)
104+
84105
def test_fcntl_64_bit(self):
85106
# Issue #1309352: fcntl shouldn't fail when the third arg fits in a
86107
# C 'long' but not in a C 'int'.

Lib/test/test_fileio.py

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from weakref import proxy
1111
from functools import wraps
1212
from UserList import UserList
13+
import _testcapi
1314

1415
from test.test_support import TESTFN, check_warnings, run_unittest, make_bad_fd
1516
from test.test_support import py3k_bytes as bytes
@@ -343,6 +344,9 @@ def testInvalidFd(self):
343344
if sys.platform == 'win32':
344345
import msvcrt
345346
self.assertRaises(IOError, msvcrt.get_osfhandle, make_bad_fd())
347+
# Issue 15989
348+
self.assertRaises(TypeError, _FileIO, _testcapi.INT_MAX + 1)
349+
self.assertRaises(TypeError, _FileIO, _testcapi.INT_MIN - 1)
346350

347351
def testBadModeArgument(self):
348352
# verify that we get a sensible error message for bad mode argument

Lib/test/test_poll.py

+10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Test case for the os.poll() function
22

33
import os, select, random, unittest
4+
import _testcapi
45
from test.test_support import TESTFN, run_unittest
56

67
try:
@@ -150,6 +151,15 @@ def test_poll3(self):
150151
if x != 5:
151152
self.fail('Overflow must have occurred')
152153

154+
pollster = select.poll()
155+
# Issue 15989
156+
self.assertRaises(OverflowError, pollster.register, 0,
157+
_testcapi.SHRT_MAX + 1)
158+
self.assertRaises(OverflowError, pollster.register, 0,
159+
_testcapi.USHRT_MAX + 1)
160+
self.assertRaises(OverflowError, pollster.poll, _testcapi.INT_MAX + 1)
161+
self.assertRaises(OverflowError, pollster.poll, _testcapi.UINT_MAX + 1)
162+
153163
def test_main():
154164
run_unittest(PollTests)
155165

Lib/test/test_socket.py

+23-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import errno
77
import socket
88
import select
9+
import _testcapi
910
import time
1011
import traceback
1112
import Queue
@@ -700,11 +701,17 @@ def test_sendall_interrupted(self):
700701
def test_sendall_interrupted_with_timeout(self):
701702
self.check_sendall_interrupted(True)
702703

703-
def testListenBacklog0(self):
704+
def test_listen_backlog(self):
705+
for backlog in 0, -1:
706+
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
707+
srv.bind((HOST, 0))
708+
srv.listen(backlog)
709+
srv.close()
710+
711+
# Issue 15989
704712
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
705713
srv.bind((HOST, 0))
706-
# backlog = 0
707-
srv.listen(0)
714+
self.assertRaises(OverflowError, srv.listen, _testcapi.INT_MAX + 1)
708715
srv.close()
709716

710717
@unittest.skipUnless(SUPPORTS_IPV6, 'IPv6 required for this test.')
@@ -808,6 +815,11 @@ def testShutdown(self):
808815

809816
def _testShutdown(self):
810817
self.serv_conn.send(MSG)
818+
# Issue 15989
819+
self.assertRaises(OverflowError, self.serv_conn.shutdown,
820+
_testcapi.INT_MAX + 1)
821+
self.assertRaises(OverflowError, self.serv_conn.shutdown,
822+
2 + (_testcapi.UINT_MAX + 1))
811823
self.serv_conn.shutdown(2)
812824

813825
@unittest.skipUnless(thread, 'Threading required for this test.')
@@ -883,14 +895,21 @@ def __init__(self, methodName='runTest'):
883895

884896
def testSetBlocking(self):
885897
# Testing whether set blocking works
886-
self.serv.setblocking(0)
898+
self.serv.setblocking(True)
899+
self.assertIsNone(self.serv.gettimeout())
900+
self.serv.setblocking(False)
901+
self.assertEqual(self.serv.gettimeout(), 0.0)
887902
start = time.time()
888903
try:
889904
self.serv.accept()
890905
except socket.error:
891906
pass
892907
end = time.time()
893908
self.assertTrue((end - start) < 1.0, "Error setting non-blocking mode.")
909+
# Issue 15989
910+
if _testcapi.UINT_MAX < _testcapi.ULONG_MAX:
911+
self.serv.setblocking(_testcapi.UINT_MAX + 1)
912+
self.assertIsNone(self.serv.gettimeout())
894913

895914
def _testSetBlocking(self):
896915
pass

Modules/_ctypes/stgdict.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
343343

344344
isPacked = PyObject_GetAttrString(type, "_pack_");
345345
if (isPacked) {
346-
pack = PyInt_AsLong(isPacked);
346+
pack = _PyInt_AsInt(isPacked);
347347
if (pack < 0 || PyErr_Occurred()) {
348348
Py_XDECREF(isPacked);
349349
PyErr_SetString(PyExc_ValueError,

Modules/_io/_iomodule.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,8 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
300300
int text = 0, binary = 0, universal = 0;
301301

302302
char rawmode[5], *m;
303-
int line_buffering, isatty;
303+
int line_buffering;
304+
long isatty;
304305

305306
PyObject *raw, *modeobj = NULL, *buffer = NULL, *wrapper = NULL;
306307

@@ -443,12 +444,12 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
443444
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
444445
{
445446
struct stat st;
446-
long fileno;
447+
int fileno;
447448
PyObject *res = PyObject_CallMethod(raw, "fileno", NULL);
448449
if (res == NULL)
449450
goto error;
450451

451-
fileno = PyInt_AsLong(res);
452+
fileno = _PyInt_AsInt(res);
452453
Py_DECREF(res);
453454
if (fileno == -1 && PyErr_Occurred())
454455
goto error;

Modules/_io/fileio.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
211211
return -1;
212212
}
213213

214-
fd = PyLong_AsLong(nameobj);
214+
fd = _PyLong_AsInt(nameobj);
215215
if (fd < 0) {
216216
if (!PyErr_Occurred()) {
217217
PyErr_SetString(PyExc_ValueError,

Modules/selectmodule.c

+8-4
Original file line numberDiff line numberDiff line change
@@ -343,10 +343,13 @@ update_ufd_array(pollObject *self)
343343

344344
i = pos = 0;
345345
while (PyDict_Next(self->dict, &pos, &key, &value)) {
346-
self->ufds[i].fd = PyInt_AsLong(key);
346+
assert(i < self->ufd_len);
347+
/* Never overflow */
348+
self->ufds[i].fd = (int)PyInt_AsLong(key);
347349
self->ufds[i].events = (short)PyInt_AsLong(value);
348350
i++;
349351
}
352+
assert(i == self->ufd_len);
350353
self->ufd_uptodate = 1;
351354
return 1;
352355
}
@@ -362,10 +365,11 @@ static PyObject *
362365
poll_register(pollObject *self, PyObject *args)
363366
{
364367
PyObject *o, *key, *value;
365-
int fd, events = POLLIN | POLLPRI | POLLOUT;
368+
int fd;
369+
short events = POLLIN | POLLPRI | POLLOUT;
366370
int err;
367371

368-
if (!PyArg_ParseTuple(args, "O|i:register", &o, &events)) {
372+
if (!PyArg_ParseTuple(args, "O|h:register", &o, &events)) {
369373
return NULL;
370374
}
371375

@@ -503,7 +507,7 @@ poll_poll(pollObject *self, PyObject *args)
503507
tout = PyNumber_Int(tout);
504508
if (!tout)
505509
return NULL;
506-
timeout = PyInt_AsLong(tout);
510+
timeout = _PyInt_AsInt(tout);
507511
Py_DECREF(tout);
508512
if (timeout == -1 && PyErr_Occurred())
509513
return NULL;

Modules/socketmodule.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -1713,7 +1713,7 @@ info is a pair (hostaddr, port).");
17131713
static PyObject *
17141714
sock_setblocking(PySocketSockObject *s, PyObject *arg)
17151715
{
1716-
int block;
1716+
long block;
17171717

17181718
block = PyInt_AsLong(arg);
17191719
if (block == -1 && PyErr_Occurred())
@@ -2243,7 +2243,7 @@ sock_listen(PySocketSockObject *s, PyObject *arg)
22432243
int backlog;
22442244
int res;
22452245

2246-
backlog = PyInt_AsLong(arg);
2246+
backlog = _PyInt_AsInt(arg);
22472247
if (backlog == -1 && PyErr_Occurred())
22482248
return NULL;
22492249
Py_BEGIN_ALLOW_THREADS
@@ -2894,7 +2894,7 @@ sock_shutdown(PySocketSockObject *s, PyObject *arg)
28942894
int how;
28952895
int res;
28962896

2897-
how = PyInt_AsLong(arg);
2897+
how = _PyInt_AsInt(arg);
28982898
if (how == -1 && PyErr_Occurred())
28992899
return NULL;
29002900
Py_BEGIN_ALLOW_THREADS

Objects/fileobject.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -2659,10 +2659,10 @@ int PyObject_AsFileDescriptor(PyObject *o)
26592659
PyObject *meth;
26602660

26612661
if (PyInt_Check(o)) {
2662-
fd = PyInt_AsLong(o);
2662+
fd = _PyInt_AsInt(o);
26632663
}
26642664
else if (PyLong_Check(o)) {
2665-
fd = PyLong_AsLong(o);
2665+
fd = _PyLong_AsInt(o);
26662666
}
26672667
else if ((meth = PyObject_GetAttrString(o, "fileno")) != NULL)
26682668
{
@@ -2672,11 +2672,11 @@ int PyObject_AsFileDescriptor(PyObject *o)
26722672
return -1;
26732673

26742674
if (PyInt_Check(fno)) {
2675-
fd = PyInt_AsLong(fno);
2675+
fd = _PyInt_AsInt(fno);
26762676
Py_DECREF(fno);
26772677
}
26782678
else if (PyLong_Check(fno)) {
2679-
fd = PyLong_AsLong(fno);
2679+
fd = _PyLong_AsInt(fno);
26802680
Py_DECREF(fno);
26812681
}
26822682
else {

Objects/intobject.c

+14
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,20 @@ PyInt_AsLong(register PyObject *op)
189189
return val;
190190
}
191191

192+
int
193+
_PyInt_AsInt(PyObject *obj)
194+
{
195+
long result = PyInt_AsLong(obj);
196+
if (result == -1 && PyErr_Occurred())
197+
return -1;
198+
if (result > INT_MAX || result < INT_MIN) {
199+
PyErr_SetString(PyExc_OverflowError,
200+
"Python int too large to convert to C int");
201+
return -1;
202+
}
203+
return (int)result;
204+
}
205+
192206
Py_ssize_t
193207
PyInt_AsSsize_t(register PyObject *op)
194208
{

Objects/longobject.c

+18
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,24 @@ PyLong_AsLong(PyObject *obj)
339339
return result;
340340
}
341341

342+
/* Get a C int from a long int object or any object that has an __int__
343+
method. Return -1 and set an error if overflow occurs. */
344+
345+
int
346+
_PyLong_AsInt(PyObject *obj)
347+
{
348+
int overflow;
349+
long result = PyLong_AsLongAndOverflow(obj, &overflow);
350+
if (overflow || result > INT_MAX || result < INT_MIN) {
351+
/* XXX: could be cute and give a different
352+
message for overflow == -1 */
353+
PyErr_SetString(PyExc_OverflowError,
354+
"Python int too large to convert to C int");
355+
return -1;
356+
}
357+
return (int)result;
358+
}
359+
342360
/* Get a Py_ssize_t from a long int object.
343361
Returns -1 and sets an error condition if overflow occurs. */
344362

0 commit comments

Comments
 (0)