]> granicus.if.org Git - python/commitdiff
Issue #15989: Fix several occurrences of integer overflow
authorSerhiy Storchaka <storchaka@gmail.com>
Mon, 14 Jan 2013 23:12:17 +0000 (01:12 +0200)
committerSerhiy Storchaka <storchaka@gmail.com>
Mon, 14 Jan 2013 23:12:17 +0000 (01:12 +0200)
when result of PyLong_AsLong() narrowed to int without checks.

19 files changed:
Include/longobject.h
Lib/ctypes/test/test_structures.py
Lib/test/string_tests.py
Lib/test/test_fcntl.py
Lib/test/test_fileio.py
Lib/test/test_io.py
Lib/test/test_poll.py
Lib/test/test_posix.py
Lib/test/test_socket.py
Modules/_ctypes/stgdict.c
Modules/_io/fileio.c
Modules/_io/textio.c
Modules/parsermodule.c
Modules/posixmodule.c
Modules/selectmodule.c
Modules/socketmodule.c
Objects/fileobject.c
Objects/longobject.c
Objects/unicodeobject.c

index d741f1b4c0127a1f72db66e1a70768e40b6b3c60..cd0cf3081482376d8cd67ddd3965fe722257456c 100644 (file)
@@ -26,6 +26,9 @@ PyAPI_FUNC(Py_ssize_t) PyLong_AsSsize_t(PyObject *);
 PyAPI_FUNC(size_t) PyLong_AsSize_t(PyObject *);
 PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(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);
 
 /* It may be useful in the future. I've added it in the PyInt -> PyLong
index b6d8b012ff04d6f8ead703d1af3b60b62005dabc..d764ce2ed94b98bc05b61dcc5ce9f6545833ef1b 100644 (file)
@@ -1,6 +1,7 @@
 import unittest
 from ctypes import *
 from struct import calcsize
+import _testcapi
 
 class SubclassesTest(unittest.TestCase):
     def test_subclass(self):
@@ -199,6 +200,14 @@ class StructureTestCase(unittest.TestCase):
              "_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),
index 27e4662c9229c281bf4e341bb9308cb7fa8d0702..385b03992dba8d28f0c59bd76d274309a784df65 100644 (file)
@@ -5,6 +5,7 @@ Common tests shared by test_str, test_unicode, test_userstring and test_string.
 import unittest, string, sys, struct
 from test import support
 from collections import UserList
+import _testcapi
 
 class Sequence:
     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(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
         self.checkraises(TypeError, 'abc', '__mod__', X())
 
index 6d9ee0bf5aa3331f469d5293c0a199191384ed07..f977187fec1b24cb8bc9b0ca25d7e6bc87cfb567 100644 (file)
@@ -3,6 +3,7 @@
 import os
 import struct
 import sys
+import _testcapi
 import unittest
 from test.support import verbose, TESTFN, unlink, run_unittest, import_module
 
@@ -69,6 +70,26 @@ class TestFcntl(unittest.TestCase):
         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(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):
         # Issue #1309352: fcntl shouldn't fail when the third arg fits in a
         # C 'long' but not in a C 'int'.
index 0badf51f8066095d102f31eae8dfde6d719f0e47..0ccbda275ccd09ec28242b43ebc9c6547d9ba275 100644 (file)
@@ -8,6 +8,7 @@ import unittest
 from array import array
 from weakref import proxy
 from functools import wraps
+import _testcapi
 
 from test.support import TESTFN, check_warnings, run_unittest, make_bad_fd
 from collections import UserList
@@ -347,6 +348,9 @@ class OtherFileTests(unittest.TestCase):
         if sys.platform == 'win32':
             import msvcrt
             self.assertRaises(OSError, 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
index 7906060e7bef340e0ad6d8a612a8fbd234ee6d3e..8727ddeaf184a0a7aa4e515445e5a1e2f78c131c 100644 (file)
@@ -32,6 +32,7 @@ import time
 import unittest
 import warnings
 import weakref
+import _testcapi
 from collections import deque, UserList
 from itertools import cycle, count
 from test import support
@@ -1970,6 +1971,14 @@ class TextIOWrapperTest(unittest.TestCase):
             os.environ.clear()
             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):
         # Check the encoding attribute is always set, and valid
         b = self.BytesIO()
index 3b7926d5b22cf432e5019b0a042690e64103413a..a2fec3da242e3b1db3bc3e9ce0b2673fd60d9e96 100644 (file)
@@ -1,6 +1,7 @@
 # Test case for the os.poll() function
 
 import os, select, random, unittest, subprocess
+import _testcapi
 from test.support import TESTFN, run_unittest
 
 try:
@@ -151,6 +152,15 @@ class PollTests(unittest.TestCase):
         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)
 
index 13529432488d68bc15e188a79761be97ed94157a..978983048cce4afd683d6ba663b0e36ba5b4f930 100644 (file)
@@ -17,6 +17,7 @@ import stat
 import tempfile
 import unittest
 import warnings
+import _testcapi
 
 _DUMMY_SYMLINK = os.path.join(tempfile.gettempdir(),
                               support.TESTFN + '-dummy-symlink')
@@ -537,6 +538,10 @@ class PosixTester(unittest.TestCase):
         except OSError:
             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):
         if hasattr(posix, 'utime'):
             now = time.time()
index be9206e99bd9a2f5340711fa7915f99be0d7a7ec..282596f08e5acd4dbfe88bede005354db137a9bd 100644 (file)
@@ -1262,11 +1262,17 @@ class GeneralModuleTests(unittest.TestCase):
             for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
                 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.bind((HOST, 0))
-        # backlog = 0
-        srv.listen(0)
+        self.assertRaises(OverflowError, srv.listen, _testcapi.INT_MAX + 1)
         srv.close()
 
     @unittest.skipUnless(support.IPV6_ENABLED, 'IPv6 required for this test.')
@@ -1582,6 +1588,11 @@ class BasicTCPTest(SocketConnectedTest):
 
     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)
 
     def testDetach(self):
@@ -3563,6 +3574,11 @@ class NonBlockingTCPTests(ThreadedTCPSocketTest):
             pass
         end = time.time()
         self.assertTrue((end - start) < 1.0, "Error setting non-blocking mode.")
+        # Issue 15989
+        self.assertRaises(OverflowError, self.serv.setblocking,
+                          _testcapi.INT_MAX + 1)
+        self.assertRaises(OverflowError, self.serv.setblocking,
+                          _testcapi.UINT_MAX + 1)
 
     def _testSetBlocking(self):
         pass
index e5b0e4c13984919f4d2fb0be2660cf24f41654c3..25d999626a1cab044c31a9c39c22593da91db5b9 100644 (file)
@@ -335,7 +335,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
 
     isPacked = PyObject_GetAttrString(type, "_pack_");
     if (isPacked) {
-        pack = PyLong_AsLong(isPacked);
+        pack = _PyLong_AsInt(isPacked);
         if (pack < 0 || PyErr_Occurred()) {
             Py_XDECREF(isPacked);
             PyErr_SetString(PyExc_ValueError,
index 34425b7ae0d70681713d1042849ca9f33c23c9f0..fd7c1fc8ac8a46d2de7333ff421cf2ef0aecbb6e 100644 (file)
@@ -244,7 +244,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,
@@ -382,7 +382,7 @@ fileio_init(PyObject *oself, PyObject *args, PyObject *kwds)
                 goto error;
             }
 
-            self->fd = PyLong_AsLong(fdobj);
+            self->fd = _PyLong_AsInt(fdobj);
             Py_DECREF(fdobj);
             if (self->fd == -1) {
                 goto error;
index 4fc0caa84e28c8c7f57d82353d507e6472043427..afd8d41ad80295d801f122f64165d492ebe4990e 100644 (file)
@@ -881,7 +881,7 @@ textiowrapper_init(textio *self, PyObject *args, PyObject *kwds)
             }
         }
         else {
-            int fd = (int) PyLong_AsLong(fileno);
+            int fd = _PyLong_AsInt(fileno);
             Py_DECREF(fileno);
             if (fd == -1 && PyErr_Occurred()) {
                 goto error;
index fea603e20000f4a735364274644b0b4357985f02..e86fe4d2eb58076a9cf944c3d72d8531852be83a 100644 (file)
@@ -725,7 +725,7 @@ build_node_children(PyObject *tuple, node *root, int *line_num)
         /* elem must always be a sequence, however simple */
         PyObject* elem = PySequence_GetItem(tuple, i);
         int ok = elem != NULL;
-        long  type = 0;
+        int type = 0;
         char *strn = 0;
 
         if (ok)
@@ -736,8 +736,14 @@ build_node_children(PyObject *tuple, node *root, int *line_num)
                 ok = 0;
             else {
                 ok = PyLong_Check(temp);
-                if (ok)
-                    type = PyLong_AS_LONG(temp);
+                if (ok) {
+                    type = _PyLong_AsInt(temp);
+                    if (type == -1 && PyErr_Occurred()) {
+                        Py_DECREF(temp);
+                        Py_DECREF(elem);
+                        return 0;
+                    }
+                }
                 Py_DECREF(temp);
             }
         }
@@ -773,8 +779,16 @@ build_node_children(PyObject *tuple, node *root, int *line_num)
             if (len == 3) {
                 PyObject *o = PySequence_GetItem(elem, 2);
                 if (o != NULL) {
-                    if (PyLong_Check(o))
-                        *line_num = PyLong_AS_LONG(o);
+                    if (PyLong_Check(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 {
                         PyErr_Format(parser_error,
                                      "third item in terminal node must be an"
index 24585c595aa10e8c97c11258741ca90cd8a9b232..fb58507bf635268d3e0e38cccd3e190e83b784da 100644 (file)
@@ -7647,7 +7647,7 @@ posix_pipe2(PyObject *self, PyObject *arg)
     int fds[2];
     int res;
 
-    flags = PyLong_AsLong(arg);
+    flags = _PyLong_AsInt(arg);
     if (flags == -1 && PyErr_Occurred())
         return NULL;
 
index 52be4d8eea09cda25d0f025ead0d1557517a34e1..e79bea30a72e684e31b302d0161175aee89ad934 100644 (file)
@@ -352,10 +352,13 @@ update_ufd_array(pollObject *self)
 
     i = pos = 0;
     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);
         i++;
     }
+    assert(i == self->ufd_len);
     self->ufd_uptodate = 1;
     return 1;
 }
@@ -371,10 +374,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;
     }
 
@@ -513,7 +517,7 @@ poll_poll(pollObject *self, PyObject *args)
         tout = PyNumber_Long(tout);
         if (!tout)
             return NULL;
-        timeout = PyLong_AsLong(tout);
+        timeout = _PyLong_AsInt(tout);
         Py_DECREF(tout);
         if (timeout == -1 && PyErr_Occurred())
             return NULL;
index 4cd69035cd143e88caa9c48360e12621b530d1b3..94793c951bc0a06555dd4133970179fbd7eb7804 100644 (file)
@@ -2013,7 +2013,7 @@ For IP sockets, the address info is a pair (hostaddr, port).");
 static PyObject *
 sock_setblocking(PySocketSockObject *s, PyObject *arg)
 {
-    int block;
+    long block;
 
     block = PyLong_AsLong(arg);
     if (block == -1 && PyErr_Occurred())
@@ -2495,7 +2495,7 @@ sock_listen(PySocketSockObject *s, PyObject *arg)
     int backlog;
     int res;
 
-    backlog = PyLong_AsLong(arg);
+    backlog = _PyLong_AsInt(arg);
     if (backlog == -1 && PyErr_Occurred())
         return NULL;
     Py_BEGIN_ALLOW_THREADS
@@ -3647,7 +3647,7 @@ sock_shutdown(PySocketSockObject *s, PyObject *arg)
     int how;
     int res;
 
-    how = PyLong_AsLong(arg);
+    how = _PyLong_AsInt(arg);
     if (how == -1 && PyErr_Occurred())
         return NULL;
     Py_BEGIN_ALLOW_THREADS
index e1c47ce37262b87136214f9517d05b090a10f34d..3a31314086fef3d508e3d0fb471c29904cecf6a9 100644 (file)
@@ -200,7 +200,7 @@ PyObject_AsFileDescriptor(PyObject *o)
     _Py_IDENTIFIER(fileno);
 
     if (PyLong_Check(o)) {
-        fd = PyLong_AsLong(o);
+        fd = _PyLong_AsInt(o);
     }
     else if ((meth = _PyObject_GetAttrId(o, &PyId_fileno)) != NULL)
     {
@@ -210,7 +210,7 @@ PyObject_AsFileDescriptor(PyObject *o)
             return -1;
 
         if (PyLong_Check(fno)) {
-            fd = PyLong_AsLong(fno);
+            fd = _PyLong_AsInt(fno);
             Py_DECREF(fno);
         }
         else {
index 5a50f24330f03b0194f14ac96349639ee088b266..1a82b1c67ad44540dc4fcea244dbb751165bfa98 100644 (file)
@@ -434,6 +434,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. */
 
index 6a12d71f87638f2f111b3182100384ab31a819f6..65393d2efadda0794dd442670d5b2664171fab35 100644 (file)
@@ -13521,7 +13521,7 @@ unicode_format_arg_parse(struct unicode_formatter_t *ctx,
                             "* wants int");
             return -1;
         }
-        arg->width = PyLong_AsLong(v);
+        arg->width = PyLong_AsSsize_t(v);
         if (arg->width == -1 && PyErr_Occurred())
             return -1;
         if (arg->width < 0) {
@@ -13568,7 +13568,7 @@ unicode_format_arg_parse(struct unicode_formatter_t *ctx,
                                 "* wants int");
                 return -1;
             }
-            arg->prec = PyLong_AsLong(v);
+            arg->prec = _PyLong_AsInt(v);
             if (arg->prec == -1 && PyErr_Occurred())
                 return -1;
             if (arg->prec < 0)