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

File tree

17 files changed

+143
-22
lines changed

17 files changed

+143
-22
lines changed

Include/intobject.h

Lines changed: 1 addition & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 9 additions & 0 deletions
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

Lines changed: 15 additions & 0 deletions
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

Lines changed: 21 additions & 0 deletions
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

Lines changed: 4 additions & 0 deletions
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

Lines changed: 10 additions & 0 deletions
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

Lines changed: 23 additions & 4 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 4 additions & 3 deletions
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;

0 commit comments

Comments
 (0)