Issue #15989: Fix several occurrences of integer overflow

when result of PyLong_AsLong() narrowed to int without checks.

This is a backport of changesets 13e2e44db99d and 525407d89277.
This commit is contained in:
Serhiy Storchaka 2013-01-19 12:41:45 +02:00
commit 9101e23ff6
19 changed files with 151 additions and 25 deletions

View File

@ -26,6 +26,9 @@ PyAPI_FUNC(Py_ssize_t) PyLong_AsSsize_t(PyObject *);
PyAPI_FUNC(size_t) PyLong_AsSize_t(PyObject *); PyAPI_FUNC(size_t) PyLong_AsSize_t(PyObject *);
PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(PyObject *); PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(PyObject *);
PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject *); PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject *);
#ifndef Py_LIMITED_API
PyAPI_FUNC(int) _PyLong_AsInt(PyObject *);
#endif
PyAPI_FUNC(PyObject *) PyLong_GetInfo(void); PyAPI_FUNC(PyObject *) PyLong_GetInfo(void);
/* It may be useful in the future. I've added it in the PyInt -> PyLong /* It may be useful in the future. I've added it in the PyInt -> PyLong

View File

@ -1,6 +1,7 @@
import unittest import unittest
from ctypes import * from ctypes import *
from struct import calcsize from struct import calcsize
import _testcapi
class SubclassesTest(unittest.TestCase): class SubclassesTest(unittest.TestCase):
def test_subclass(self): def test_subclass(self):
@ -199,6 +200,14 @@ class StructureTestCase(unittest.TestCase):
"_pack_": -1} "_pack_": -1}
self.assertRaises(ValueError, type(Structure), "X", (Structure,), d) self.assertRaises(ValueError, type(Structure), "X", (Structure,), d)
# Issue 15989
d = {"_fields_": [("a", c_byte)],
"_pack_": _testcapi.INT_MAX + 1}
self.assertRaises(ValueError, type(Structure), "X", (Structure,), d)
d = {"_fields_": [("a", c_byte)],
"_pack_": _testcapi.UINT_MAX + 2}
self.assertRaises(ValueError, type(Structure), "X", (Structure,), d)
def test_initializers(self): def test_initializers(self):
class Person(Structure): class Person(Structure):
_fields_ = [("name", c_char*6), _fields_ = [("name", c_char*6),

View File

@ -5,6 +5,7 @@ Common tests shared by test_str, test_unicode, test_userstring and test_string.
import unittest, string, sys, struct import unittest, string, sys, struct
from test import support from test import support
from collections import UserList from collections import UserList
import _testcapi
class Sequence: class Sequence:
def __init__(self, seq='wxyz'): self.seq = seq def __init__(self, seq='wxyz'): self.seq = seq
@ -1206,6 +1207,16 @@ class MixinStrUnicodeUserStringTest:
self.checkraises(ValueError, '%%%df' % (2**64), '__mod__', (3.2)) self.checkraises(ValueError, '%%%df' % (2**64), '__mod__', (3.2))
self.checkraises(ValueError, '%%.%df' % (2**64), '__mod__', (3.2)) self.checkraises(ValueError, '%%.%df' % (2**64), '__mod__', (3.2))
self.checkraises(OverflowError, '%*s', '__mod__',
(_testcapi.PY_SSIZE_T_MAX + 1, ''))
self.checkraises(OverflowError, '%.*f', '__mod__',
(_testcapi.INT_MAX + 1, 1. / 7))
# Issue 15989
self.checkraises(OverflowError, '%*s', '__mod__',
(1 << (_testcapi.PY_SSIZE_T_MAX.bit_length() + 1), ''))
self.checkraises(OverflowError, '%.*f', '__mod__',
(_testcapi.UINT_MAX + 1, 1. / 7))
class X(object): pass class X(object): pass
self.checkraises(TypeError, 'abc', '__mod__', X()) self.checkraises(TypeError, 'abc', '__mod__', X())

View File

@ -6,6 +6,7 @@ OS/2+EMX doesn't support the file locking operations.
import os import os
import struct import struct
import sys import sys
import _testcapi
import unittest import unittest
from test.support import verbose, TESTFN, unlink, run_unittest, import_module from test.support import verbose, TESTFN, unlink, run_unittest, import_module
@ -76,6 +77,26 @@ class TestFcntl(unittest.TestCase):
rv = fcntl.fcntl(self.f, fcntl.F_SETLKW, lockdata) rv = fcntl.fcntl(self.f, fcntl.F_SETLKW, lockdata)
self.f.close() self.f.close()
def test_fcntl_bad_file(self):
class F:
def __init__(self, fn):
self.fn = fn
def fileno(self):
return self.fn
self.assertRaises(ValueError, fcntl.fcntl, -1, fcntl.F_SETFL, os.O_NONBLOCK)
self.assertRaises(ValueError, fcntl.fcntl, F(-1), fcntl.F_SETFL, os.O_NONBLOCK)
self.assertRaises(TypeError, fcntl.fcntl, 'spam', fcntl.F_SETFL, os.O_NONBLOCK)
self.assertRaises(TypeError, fcntl.fcntl, F('spam'), fcntl.F_SETFL, os.O_NONBLOCK)
# Issue 15989
self.assertRaises(OverflowError, fcntl.fcntl, _testcapi.INT_MAX + 1,
fcntl.F_SETFL, os.O_NONBLOCK)
self.assertRaises(OverflowError, fcntl.fcntl, F(_testcapi.INT_MAX + 1),
fcntl.F_SETFL, os.O_NONBLOCK)
self.assertRaises(OverflowError, fcntl.fcntl, _testcapi.INT_MIN - 1,
fcntl.F_SETFL, os.O_NONBLOCK)
self.assertRaises(OverflowError, fcntl.fcntl, F(_testcapi.INT_MIN - 1),
fcntl.F_SETFL, os.O_NONBLOCK)
def test_fcntl_64_bit(self): def test_fcntl_64_bit(self):
# Issue #1309352: fcntl shouldn't fail when the third arg fits in a # Issue #1309352: fcntl shouldn't fail when the third arg fits in a
# C 'long' but not in a C 'int'. # C 'long' but not in a C 'int'.

View File

@ -8,6 +8,7 @@ import unittest
from array import array from array import array
from weakref import proxy from weakref import proxy
from functools import wraps from functools import wraps
import _testcapi
from test.support import TESTFN, check_warnings, run_unittest, make_bad_fd from test.support import TESTFN, check_warnings, run_unittest, make_bad_fd
from collections import UserList from collections import UserList
@ -347,6 +348,9 @@ class OtherFileTests(unittest.TestCase):
if sys.platform == 'win32': if sys.platform == 'win32':
import msvcrt import msvcrt
self.assertRaises(IOError, msvcrt.get_osfhandle, make_bad_fd()) self.assertRaises(IOError, msvcrt.get_osfhandle, make_bad_fd())
# Issue 15989
self.assertRaises(TypeError, _FileIO, _testcapi.INT_MAX + 1)
self.assertRaises(TypeError, _FileIO, _testcapi.INT_MIN - 1)
def testBadModeArgument(self): def testBadModeArgument(self):
# verify that we get a sensible error message for bad mode argument # verify that we get a sensible error message for bad mode argument

View File

@ -32,6 +32,7 @@ import time
import unittest import unittest
import warnings import warnings
import weakref import weakref
import _testcapi
from collections import deque, UserList from collections import deque, UserList
from itertools import cycle, count from itertools import cycle, count
from test import support from test import support
@ -1962,6 +1963,14 @@ class TextIOWrapperTest(unittest.TestCase):
os.environ.clear() os.environ.clear()
os.environ.update(old_environ) os.environ.update(old_environ)
# Issue 15989
def test_device_encoding(self):
b = self.BytesIO()
b.fileno = lambda: _testcapi.INT_MAX + 1
self.assertRaises(OverflowError, self.TextIOWrapper, b)
b.fileno = lambda: _testcapi.UINT_MAX + 1
self.assertRaises(OverflowError, self.TextIOWrapper, b)
def test_encoding(self): def test_encoding(self):
# Check the encoding attribute is always set, and valid # Check the encoding attribute is always set, and valid
b = self.BytesIO() b = self.BytesIO()

View File

@ -1,6 +1,7 @@
# Test case for the os.poll() function # Test case for the os.poll() function
import os, select, random, unittest import os, select, random, unittest
import _testcapi
from test.support import TESTFN, run_unittest from test.support import TESTFN, run_unittest
try: try:
@ -150,6 +151,15 @@ class PollTests(unittest.TestCase):
if x != 5: if x != 5:
self.fail('Overflow must have occurred') self.fail('Overflow must have occurred')
pollster = select.poll()
# Issue 15989
self.assertRaises(OverflowError, pollster.register, 0,
_testcapi.SHRT_MAX + 1)
self.assertRaises(OverflowError, pollster.register, 0,
_testcapi.USHRT_MAX + 1)
self.assertRaises(OverflowError, pollster.poll, _testcapi.INT_MAX + 1)
self.assertRaises(OverflowError, pollster.poll, _testcapi.UINT_MAX + 1)
def test_main(): def test_main():
run_unittest(PollTests) run_unittest(PollTests)

View File

@ -17,6 +17,7 @@ import stat
import tempfile import tempfile
import unittest import unittest
import warnings import warnings
import _testcapi
_DUMMY_SYMLINK = os.path.join(tempfile.gettempdir(), _DUMMY_SYMLINK = os.path.join(tempfile.gettempdir(),
support.TESTFN + '-dummy-symlink') support.TESTFN + '-dummy-symlink')
@ -537,6 +538,10 @@ class PosixTester(unittest.TestCase):
except OSError: except OSError:
pass pass
# Issue 15989
self.assertRaises(OverflowError, os.pipe2, _testcapi.INT_MAX + 1)
self.assertRaises(OverflowError, os.pipe2, _testcapi.UINT_MAX + 1)
def test_utime(self): def test_utime(self):
if hasattr(posix, 'utime'): if hasattr(posix, 'utime'):
now = time.time() now = time.time()

View File

@ -1262,11 +1262,17 @@ class GeneralModuleTests(unittest.TestCase):
for protocol in range(pickle.HIGHEST_PROTOCOL + 1): for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
self.assertRaises(TypeError, pickle.dumps, sock, protocol) self.assertRaises(TypeError, pickle.dumps, sock, protocol)
def test_listen_backlog0(self): def test_listen_backlog(self):
for backlog in 0, -1:
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.bind((HOST, 0))
srv.listen(backlog)
srv.close()
# Issue 15989
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.bind((HOST, 0)) srv.bind((HOST, 0))
# backlog = 0 self.assertRaises(OverflowError, srv.listen, _testcapi.INT_MAX + 1)
srv.listen(0)
srv.close() srv.close()
@unittest.skipUnless(support.IPV6_ENABLED, 'IPv6 required for this test.') @unittest.skipUnless(support.IPV6_ENABLED, 'IPv6 required for this test.')
@ -1582,6 +1588,11 @@ class BasicTCPTest(SocketConnectedTest):
def _testShutdown(self): def _testShutdown(self):
self.serv_conn.send(MSG) self.serv_conn.send(MSG)
# Issue 15989
self.assertRaises(OverflowError, self.serv_conn.shutdown,
_testcapi.INT_MAX + 1)
self.assertRaises(OverflowError, self.serv_conn.shutdown,
2 + (_testcapi.UINT_MAX + 1))
self.serv_conn.shutdown(2) self.serv_conn.shutdown(2)
def testDetach(self): def testDetach(self):
@ -3555,7 +3566,10 @@ class NonBlockingTCPTests(ThreadedTCPSocketTest):
def testSetBlocking(self): def testSetBlocking(self):
# Testing whether set blocking works # Testing whether set blocking works
self.serv.setblocking(0) self.serv.setblocking(True)
self.assertIsNone(self.serv.gettimeout())
self.serv.setblocking(False)
self.assertEqual(self.serv.gettimeout(), 0.0)
start = time.time() start = time.time()
try: try:
self.serv.accept() self.serv.accept()
@ -3563,6 +3577,10 @@ class NonBlockingTCPTests(ThreadedTCPSocketTest):
pass pass
end = time.time() end = time.time()
self.assertTrue((end - start) < 1.0, "Error setting non-blocking mode.") self.assertTrue((end - start) < 1.0, "Error setting non-blocking mode.")
# Issue 15989
if _testcapi.UINT_MAX < _testcapi.ULONG_MAX:
self.serv.setblocking(_testcapi.UINT_MAX + 1)
self.assertIsNone(self.serv.gettimeout())
def _testSetBlocking(self): def _testSetBlocking(self):
pass pass

View File

@ -335,7 +335,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
isPacked = PyObject_GetAttrString(type, "_pack_"); isPacked = PyObject_GetAttrString(type, "_pack_");
if (isPacked) { if (isPacked) {
pack = PyLong_AsLong(isPacked); pack = _PyLong_AsInt(isPacked);
if (pack < 0 || PyErr_Occurred()) { if (pack < 0 || PyErr_Occurred()) {
Py_XDECREF(isPacked); Py_XDECREF(isPacked);
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,

View File

@ -244,7 +244,7 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
return -1; return -1;
} }
fd = PyLong_AsLong(nameobj); fd = _PyLong_AsInt(nameobj);
if (fd < 0) { if (fd < 0) {
if (!PyErr_Occurred()) { if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
@ -382,7 +382,7 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
goto error; goto error;
} }
self->fd = PyLong_AsLong(fdobj); self->fd = _PyLong_AsInt(fdobj);
Py_DECREF(fdobj); Py_DECREF(fdobj);
if (self->fd == -1) { if (self->fd == -1) {
goto error; goto error;

View File

@ -881,7 +881,7 @@ textiowrapper_init(textio *self, PyObject *args, PyObject *kwds)
} }
} }
else { else {
int fd = (int) PyLong_AsLong(fileno); int fd = _PyLong_AsInt(fileno);
Py_DECREF(fileno); Py_DECREF(fileno);
if (fd == -1 && PyErr_Occurred()) { if (fd == -1 && PyErr_Occurred()) {
goto error; goto error;

View File

@ -725,7 +725,7 @@ build_node_children(PyObject *tuple, node *root, int *line_num)
/* elem must always be a sequence, however simple */ /* elem must always be a sequence, however simple */
PyObject* elem = PySequence_GetItem(tuple, i); PyObject* elem = PySequence_GetItem(tuple, i);
int ok = elem != NULL; int ok = elem != NULL;
long type = 0; int type = 0;
char *strn = 0; char *strn = 0;
if (ok) if (ok)
@ -736,8 +736,14 @@ build_node_children(PyObject *tuple, node *root, int *line_num)
ok = 0; ok = 0;
else { else {
ok = PyLong_Check(temp); ok = PyLong_Check(temp);
if (ok) if (ok) {
type = PyLong_AS_LONG(temp); type = _PyLong_AsInt(temp);
if (type == -1 && PyErr_Occurred()) {
Py_DECREF(temp);
Py_DECREF(elem);
return 0;
}
}
Py_DECREF(temp); Py_DECREF(temp);
} }
} }
@ -773,8 +779,16 @@ build_node_children(PyObject *tuple, node *root, int *line_num)
if (len == 3) { if (len == 3) {
PyObject *o = PySequence_GetItem(elem, 2); PyObject *o = PySequence_GetItem(elem, 2);
if (o != NULL) { if (o != NULL) {
if (PyLong_Check(o)) if (PyLong_Check(o)) {
*line_num = PyLong_AS_LONG(o); int num = _PyLong_AsInt(o);
if (num == -1 && PyErr_Occurred()) {
Py_DECREF(o);
Py_DECREF(temp);
Py_DECREF(elem);
return 0;
}
*line_num = num;
}
else { else {
PyErr_Format(parser_error, PyErr_Format(parser_error,
"third item in terminal node must be an" "third item in terminal node must be an"

View File

@ -8174,7 +8174,7 @@ posix_pipe2(PyObject *self, PyObject *arg)
int fds[2]; int fds[2];
int res; int res;
flags = PyLong_AsLong(arg); flags = _PyLong_AsInt(arg);
if (flags == -1 && PyErr_Occurred()) if (flags == -1 && PyErr_Occurred())
return NULL; return NULL;

View File

@ -357,10 +357,13 @@ update_ufd_array(pollObject *self)
i = pos = 0; i = pos = 0;
while (PyDict_Next(self->dict, &pos, &key, &value)) { while (PyDict_Next(self->dict, &pos, &key, &value)) {
self->ufds[i].fd = PyLong_AsLong(key); assert(i < self->ufd_len);
/* Never overflow */
self->ufds[i].fd = (int)PyLong_AsLong(key);
self->ufds[i].events = (short)PyLong_AsLong(value); self->ufds[i].events = (short)PyLong_AsLong(value);
i++; i++;
} }
assert(i == self->ufd_len);
self->ufd_uptodate = 1; self->ufd_uptodate = 1;
return 1; return 1;
} }
@ -376,10 +379,11 @@ static PyObject *
poll_register(pollObject *self, PyObject *args) poll_register(pollObject *self, PyObject *args)
{ {
PyObject *o, *key, *value; PyObject *o, *key, *value;
int fd, events = POLLIN | POLLPRI | POLLOUT; int fd;
short events = POLLIN | POLLPRI | POLLOUT;
int err; int err;
if (!PyArg_ParseTuple(args, "O|i:register", &o, &events)) { if (!PyArg_ParseTuple(args, "O|h:register", &o, &events)) {
return NULL; return NULL;
} }
@ -518,7 +522,7 @@ poll_poll(pollObject *self, PyObject *args)
tout = PyNumber_Long(tout); tout = PyNumber_Long(tout);
if (!tout) if (!tout)
return NULL; return NULL;
timeout = PyLong_AsLong(tout); timeout = _PyLong_AsInt(tout);
Py_DECREF(tout); Py_DECREF(tout);
if (timeout == -1 && PyErr_Occurred()) if (timeout == -1 && PyErr_Occurred())
return NULL; return NULL;

View File

@ -2073,7 +2073,7 @@ For IP sockets, the address info is a pair (hostaddr, port).");
static PyObject * static PyObject *
sock_setblocking(PySocketSockObject *s, PyObject *arg) sock_setblocking(PySocketSockObject *s, PyObject *arg)
{ {
int block; long block;
block = PyLong_AsLong(arg); block = PyLong_AsLong(arg);
if (block == -1 && PyErr_Occurred()) if (block == -1 && PyErr_Occurred())
@ -2555,7 +2555,7 @@ sock_listen(PySocketSockObject *s, PyObject *arg)
int backlog; int backlog;
int res; int res;
backlog = PyLong_AsLong(arg); backlog = _PyLong_AsInt(arg);
if (backlog == -1 && PyErr_Occurred()) if (backlog == -1 && PyErr_Occurred())
return NULL; return NULL;
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
@ -3712,7 +3712,7 @@ sock_shutdown(PySocketSockObject *s, PyObject *arg)
int how; int how;
int res; int res;
how = PyLong_AsLong(arg); how = _PyLong_AsInt(arg);
if (how == -1 && PyErr_Occurred()) if (how == -1 && PyErr_Occurred())
return NULL; return NULL;
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS

View File

@ -200,7 +200,7 @@ PyObject_AsFileDescriptor(PyObject *o)
_Py_IDENTIFIER(fileno); _Py_IDENTIFIER(fileno);
if (PyLong_Check(o)) { if (PyLong_Check(o)) {
fd = PyLong_AsLong(o); fd = _PyLong_AsInt(o);
} }
else if ((meth = _PyObject_GetAttrId(o, &PyId_fileno)) != NULL) else if ((meth = _PyObject_GetAttrId(o, &PyId_fileno)) != NULL)
{ {
@ -210,7 +210,7 @@ PyObject_AsFileDescriptor(PyObject *o)
return -1; return -1;
if (PyLong_Check(fno)) { if (PyLong_Check(fno)) {
fd = PyLong_AsLong(fno); fd = _PyLong_AsInt(fno);
Py_DECREF(fno); Py_DECREF(fno);
} }
else { else {

View File

@ -434,6 +434,24 @@ PyLong_AsLong(PyObject *obj)
return result; return result;
} }
/* Get a C int from a long int object or any object that has an __int__
method. Return -1 and set an error if overflow occurs. */
int
_PyLong_AsInt(PyObject *obj)
{
int overflow;
long result = PyLong_AsLongAndOverflow(obj, &overflow);
if (overflow || result > INT_MAX || result < INT_MIN) {
/* XXX: could be cute and give a different
message for overflow == -1 */
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert to C int");
return -1;
}
return (int)result;
}
/* Get a Py_ssize_t from a long int object. /* Get a Py_ssize_t from a long int object.
Returns -1 and sets an error condition if overflow occurs. */ Returns -1 and sets an error condition if overflow occurs. */

View File

@ -13569,7 +13569,7 @@ PyUnicode_Format(PyObject *format, PyObject *args)
"* wants int"); "* wants int");
goto onError; goto onError;
} }
width = PyLong_AsLong(v); width = PyLong_AsSsize_t(v);
if (width == -1 && PyErr_Occurred()) if (width == -1 && PyErr_Occurred())
goto onError; goto onError;
if (width < 0) { if (width < 0) {
@ -13609,7 +13609,7 @@ PyUnicode_Format(PyObject *format, PyObject *args)
"* wants int"); "* wants int");
goto onError; goto onError;
} }
prec = PyLong_AsLong(v); prec = _PyLong_AsInt(v);
if (prec == -1 && PyErr_Occurred()) if (prec == -1 && PyErr_Occurred())
goto onError; goto onError;
if (prec < 0) if (prec < 0)