Skip to content

Commit

Permalink
Issue #15989: Fix several occurrences of integer overflow
Browse files Browse the repository at this point in the history
when result of PyInt_AsLong() or PyLong_AsLong() narrowed
to int without checks.

This is a backport of changesets 13e2e44db99d and 525407d89277.
  • Loading branch information
serhiy-storchaka committed Jan 19, 2013
1 parent ac7b49f commit 74f49ab
Show file tree
Hide file tree
Showing 17 changed files with 143 additions and 22 deletions.
1 change: 1 addition & 0 deletions Include/intobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ PyAPI_FUNC(PyObject *) PyInt_FromSize_t(size_t);
PyAPI_FUNC(PyObject *) PyInt_FromSsize_t(Py_ssize_t);
PyAPI_FUNC(long) PyInt_AsLong(PyObject *);
PyAPI_FUNC(Py_ssize_t) PyInt_AsSsize_t(PyObject *);
PyAPI_FUNC(int) _PyInt_AsInt(PyObject *);
PyAPI_FUNC(unsigned long) PyInt_AsUnsignedLongMask(PyObject *);
#ifdef HAVE_LONG_LONG
PyAPI_FUNC(unsigned PY_LONG_LONG) PyInt_AsUnsignedLongLongMask(PyObject *);
Expand Down
1 change: 1 addition & 0 deletions Include/longobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ PyAPI_FUNC(long) PyLong_AsLongAndOverflow(PyObject *, int *);
PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(PyObject *);
PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject *);
PyAPI_FUNC(Py_ssize_t) PyLong_AsSsize_t(PyObject *);
PyAPI_FUNC(int) _PyLong_AsInt(PyObject *);
PyAPI_FUNC(PyObject *) PyLong_GetInfo(void);

/* For use by intobject.c only */
Expand Down
9 changes: 9 additions & 0 deletions Lib/ctypes/test/test_structures.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import unittest
from ctypes import *
from struct import calcsize
import _testcapi

class SubclassesTest(unittest.TestCase):
def test_subclass(self):
Expand Down Expand Up @@ -199,6 +200,14 @@ class X(Structure):
"_pack_": -1}
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):
class Person(Structure):
_fields_ = [("name", c_char*6),
Expand Down
15 changes: 15 additions & 0 deletions Lib/test/string_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import unittest, string, sys, struct
from test import test_support
from UserList import UserList
import _testcapi

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

if _testcapi.PY_SSIZE_T_MAX < sys.maxint:
self.checkraises(OverflowError, '%*s', '__mod__',
(_testcapi.PY_SSIZE_T_MAX + 1, ''))
if _testcapi.INT_MAX < sys.maxint:
self.checkraises(OverflowError, '%.*f', '__mod__',
(_testcapi.INT_MAX + 1, 1. / 7))
# Issue 15989
if 1 << (_testcapi.PY_SSIZE_T_MAX.bit_length() + 1) <= sys.maxint:
self.checkraises(OverflowError, '%*s', '__mod__',
(1 << (_testcapi.PY_SSIZE_T_MAX.bit_length() + 1), ''))
if _testcapi.UINT_MAX < sys.maxint:
self.checkraises(OverflowError, '%.*f', '__mod__',
(_testcapi.UINT_MAX + 1, 1. / 7))

class X(object): pass
self.checkraises(TypeError, 'abc', '__mod__', X())

Expand Down
21 changes: 21 additions & 0 deletions Lib/test/test_fcntl.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import os
import struct
import sys
import _testcapi
import unittest
from test.test_support import (verbose, TESTFN, unlink, run_unittest,
import_module)
Expand Down Expand Up @@ -81,6 +82,26 @@ def test_fcntl_file_descriptor(self):
rv = fcntl.fcntl(self.f, fcntl.F_SETLKW, lockdata)
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(ValueError, fcntl.fcntl, _testcapi.INT_MAX + 1,
fcntl.F_SETFL, os.O_NONBLOCK)
self.assertRaises(ValueError, fcntl.fcntl, F(_testcapi.INT_MAX + 1),
fcntl.F_SETFL, os.O_NONBLOCK)
self.assertRaises(ValueError, fcntl.fcntl, _testcapi.INT_MIN - 1,
fcntl.F_SETFL, os.O_NONBLOCK)
self.assertRaises(ValueError, fcntl.fcntl, F(_testcapi.INT_MIN - 1),
fcntl.F_SETFL, os.O_NONBLOCK)

def test_fcntl_64_bit(self):
# Issue #1309352: fcntl shouldn't fail when the third arg fits in a
# C 'long' but not in a C 'int'.
Expand Down
4 changes: 4 additions & 0 deletions Lib/test/test_fileio.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from weakref import proxy
from functools import wraps
from UserList import UserList
import _testcapi

from test.test_support import TESTFN, check_warnings, run_unittest, make_bad_fd
from test.test_support import py3k_bytes as bytes
Expand Down Expand Up @@ -343,6 +344,9 @@ def testInvalidFd(self):
if sys.platform == 'win32':
import msvcrt
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):
# verify that we get a sensible error message for bad mode argument
Expand Down
10 changes: 10 additions & 0 deletions Lib/test/test_poll.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Test case for the os.poll() function

import os, select, random, unittest
import _testcapi
from test.test_support import TESTFN, run_unittest

try:
Expand Down Expand Up @@ -150,6 +151,15 @@ def test_poll3(self):
if x != 5:
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():
run_unittest(PollTests)

Expand Down
27 changes: 23 additions & 4 deletions Lib/test/test_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import errno
import socket
import select
import _testcapi
import time
import traceback
import Queue
Expand Down Expand Up @@ -700,11 +701,17 @@ def test_sendall_interrupted(self):
def test_sendall_interrupted_with_timeout(self):
self.check_sendall_interrupted(True)

def testListenBacklog0(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.bind((HOST, 0))
# backlog = 0
srv.listen(0)
self.assertRaises(OverflowError, srv.listen, _testcapi.INT_MAX + 1)
srv.close()

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

def _testShutdown(self):
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)

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

def testSetBlocking(self):
# 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()
try:
self.serv.accept()
except socket.error:
pass
end = time.time()
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):
pass
Expand Down
2 changes: 1 addition & 1 deletion Modules/_ctypes/stgdict.c
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct

isPacked = PyObject_GetAttrString(type, "_pack_");
if (isPacked) {
pack = PyInt_AsLong(isPacked);
pack = _PyInt_AsInt(isPacked);
if (pack < 0 || PyErr_Occurred()) {
Py_XDECREF(isPacked);
PyErr_SetString(PyExc_ValueError,
Expand Down
7 changes: 4 additions & 3 deletions Modules/_io/_iomodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,8 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
int text = 0, binary = 0, universal = 0;

char rawmode[5], *m;
int line_buffering, isatty;
int line_buffering;
long isatty;

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

Expand Down Expand Up @@ -443,12 +444,12 @@ io_open(PyObject *self, PyObject *args, PyObject *kwds)
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
{
struct stat st;
long fileno;
int fileno;
PyObject *res = PyObject_CallMethod(raw, "fileno", NULL);
if (res == NULL)
goto error;

fileno = PyInt_AsLong(res);
fileno = _PyInt_AsInt(res);
Py_DECREF(res);
if (fileno == -1 && PyErr_Occurred())
goto error;
Expand Down
2 changes: 1 addition & 1 deletion Modules/_io/fileio.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
return -1;
}

fd = PyLong_AsLong(nameobj);
fd = _PyLong_AsInt(nameobj);
if (fd < 0) {
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_ValueError,
Expand Down
12 changes: 8 additions & 4 deletions Modules/selectmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -343,10 +343,13 @@ update_ufd_array(pollObject *self)

i = pos = 0;
while (PyDict_Next(self->dict, &pos, &key, &value)) {
self->ufds[i].fd = PyInt_AsLong(key);
assert(i < self->ufd_len);
/* Never overflow */
self->ufds[i].fd = (int)PyInt_AsLong(key);
self->ufds[i].events = (short)PyInt_AsLong(value);
i++;
}
assert(i == self->ufd_len);
self->ufd_uptodate = 1;
return 1;
}
Expand All @@ -362,10 +365,11 @@ static PyObject *
poll_register(pollObject *self, PyObject *args)
{
PyObject *o, *key, *value;
int fd, events = POLLIN | POLLPRI | POLLOUT;
int fd;
short events = POLLIN | POLLPRI | POLLOUT;
int err;

if (!PyArg_ParseTuple(args, "O|i:register", &o, &events)) {
if (!PyArg_ParseTuple(args, "O|h:register", &o, &events)) {
return NULL;
}

Expand Down Expand Up @@ -503,7 +507,7 @@ poll_poll(pollObject *self, PyObject *args)
tout = PyNumber_Int(tout);
if (!tout)
return NULL;
timeout = PyInt_AsLong(tout);
timeout = _PyInt_AsInt(tout);
Py_DECREF(tout);
if (timeout == -1 && PyErr_Occurred())
return NULL;
Expand Down
6 changes: 3 additions & 3 deletions Modules/socketmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1713,7 +1713,7 @@ info is a pair (hostaddr, port).");
static PyObject *
sock_setblocking(PySocketSockObject *s, PyObject *arg)
{
int block;
long block;

block = PyInt_AsLong(arg);
if (block == -1 && PyErr_Occurred())
Expand Down Expand Up @@ -2243,7 +2243,7 @@ sock_listen(PySocketSockObject *s, PyObject *arg)
int backlog;
int res;

backlog = PyInt_AsLong(arg);
backlog = _PyInt_AsInt(arg);
if (backlog == -1 && PyErr_Occurred())
return NULL;
Py_BEGIN_ALLOW_THREADS
Expand Down Expand Up @@ -2894,7 +2894,7 @@ sock_shutdown(PySocketSockObject *s, PyObject *arg)
int how;
int res;

how = PyInt_AsLong(arg);
how = _PyInt_AsInt(arg);
if (how == -1 && PyErr_Occurred())
return NULL;
Py_BEGIN_ALLOW_THREADS
Expand Down
8 changes: 4 additions & 4 deletions Objects/fileobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2659,10 +2659,10 @@ int PyObject_AsFileDescriptor(PyObject *o)
PyObject *meth;

if (PyInt_Check(o)) {
fd = PyInt_AsLong(o);
fd = _PyInt_AsInt(o);
}
else if (PyLong_Check(o)) {
fd = PyLong_AsLong(o);
fd = _PyLong_AsInt(o);
}
else if ((meth = PyObject_GetAttrString(o, "fileno")) != NULL)
{
Expand All @@ -2672,11 +2672,11 @@ int PyObject_AsFileDescriptor(PyObject *o)
return -1;

if (PyInt_Check(fno)) {
fd = PyInt_AsLong(fno);
fd = _PyInt_AsInt(fno);
Py_DECREF(fno);
}
else if (PyLong_Check(fno)) {
fd = PyLong_AsLong(fno);
fd = _PyLong_AsInt(fno);
Py_DECREF(fno);
}
else {
Expand Down
14 changes: 14 additions & 0 deletions Objects/intobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,20 @@ PyInt_AsLong(register PyObject *op)
return val;
}

int
_PyInt_AsInt(PyObject *obj)
{
long result = PyInt_AsLong(obj);
if (result == -1 && PyErr_Occurred())
return -1;
if (result > INT_MAX || result < INT_MIN) {
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert to C int");
return -1;
}
return (int)result;
}

Py_ssize_t
PyInt_AsSsize_t(register PyObject *op)
{
Expand Down
18 changes: 18 additions & 0 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,24 @@ PyLong_AsLong(PyObject *obj)
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.
Returns -1 and sets an error condition if overflow occurs. */

Expand Down
Loading

0 comments on commit 74f49ab

Please sign in to comment.