Commit e27337b5 authored by Bob Ippolito's avatar Bob Ippolito

fix #1229380 No struct.pack exception for some out of range integers

parent 669fa188
...@@ -10,6 +10,8 @@ del sys ...@@ -10,6 +10,8 @@ del sys
verify((struct.pack('=i', 1)[0] == chr(0)) == ISBIGENDIAN, verify((struct.pack('=i', 1)[0] == chr(0)) == ISBIGENDIAN,
"bigendian determination appears wrong") "bigendian determination appears wrong")
PY_STRUCT_RANGE_CHECKING = 1
def string_reverse(s): def string_reverse(s):
chars = list(s) chars = list(s)
chars.reverse() chars.reverse()
...@@ -266,7 +268,7 @@ class IntTester: ...@@ -266,7 +268,7 @@ class IntTester:
else: else:
# x is out of range -- verify pack realizes that. # x is out of range -- verify pack realizes that.
if code in self.BUGGY_RANGE_CHECK: if not PY_STRUCT_RANGE_CHECKING and code in self.BUGGY_RANGE_CHECK:
if verbose: if verbose:
print "Skipping buggy range check for code", code print "Skipping buggy range check for code", code
else: else:
...@@ -442,6 +444,7 @@ def test_705836(): ...@@ -442,6 +444,7 @@ def test_705836():
test_705836() test_705836()
def test_1229380(): def test_1229380():
import sys
for endian in ('', '>', '<'): for endian in ('', '>', '<'):
for cls in (int, long): for cls in (int, long):
for fmt in ('B', 'H', 'I', 'L'): for fmt in ('B', 'H', 'I', 'L'):
...@@ -453,8 +456,7 @@ def test_1229380(): ...@@ -453,8 +456,7 @@ def test_1229380():
any_err(struct.pack, endian + 'I', sys.maxint * 4L) any_err(struct.pack, endian + 'I', sys.maxint * 4L)
any_err(struct.pack, endian + 'L', sys.maxint * 4L) any_err(struct.pack, endian + 'L', sys.maxint * 4L)
if 0: if PY_STRUCT_RANGE_CHECKING:
# TODO: bug #1229380
test_1229380() test_1229380()
class PackBufferTestCase(unittest.TestCase): class PackBufferTestCase(unittest.TestCase):
......
...@@ -23,6 +23,10 @@ typedef int Py_ssize_t; ...@@ -23,6 +23,10 @@ typedef int Py_ssize_t;
#define PY_USE_INT_WHEN_POSSIBLE 1 #define PY_USE_INT_WHEN_POSSIBLE 1
*/ */
/* PY_STRUCT_RANGE_CHECKING performs range checking on all arguments
to be packed. This will break some incorrect code that happened
to accidentally do the right thing anyway (such as binhex). */
#define PY_STRUCT_RANGE_CHECKING 1
/* The translation function for each format character is table driven */ /* The translation function for each format character is table driven */
typedef struct _formatdef { typedef struct _formatdef {
...@@ -150,9 +154,14 @@ get_ulong(PyObject *v, unsigned long *p) ...@@ -150,9 +154,14 @@ get_ulong(PyObject *v, unsigned long *p)
*p = x; *p = x;
return 0; return 0;
} }
else { if (get_long(v, (long *)p) < 0)
return get_long(v, (long *)p); return -1;
if (((long)*p) < 0) {
PyErr_SetString(StructError,
"unsigned argument is < 0");
return -1;
} }
return 0;
} }
#ifdef HAVE_LONG_LONG #ifdef HAVE_LONG_LONG
...@@ -223,6 +232,38 @@ unpack_double(const char *p, /* start of 8-byte string */ ...@@ -223,6 +232,38 @@ unpack_double(const char *p, /* start of 8-byte string */
return PyFloat_FromDouble(x); return PyFloat_FromDouble(x);
} }
#ifdef PY_STRUCT_RANGE_CHECKING
/* Helper to format the range error exceptions */
static int
_range_error(char format, int size, int is_unsigned)
{
if (is_unsigned == 0) {
long smallest = 0, largest = 0;
int i = size * 8;
while (--i > 0) {
smallest = (smallest * 2) - 1;
largest = (largest * 2) + 1;
}
PyErr_Format(StructError,
"'%c' format requires %ld <= number <= %ld",
format,
smallest,
largest);
} else {
unsigned long largest = 0;
int i = size * 8;
while (--i >= 0)
largest = (largest * 2) + 1;
PyErr_Format(StructError,
"'%c' format requires 0 <= number <= %lu",
format,
largest);
}
return -1;
}
#endif
/* A large number of small routines follow, with names of the form /* A large number of small routines follow, with names of the form
...@@ -380,7 +421,7 @@ np_byte(char *p, PyObject *v, const formatdef *f) ...@@ -380,7 +421,7 @@ np_byte(char *p, PyObject *v, const formatdef *f)
return -1; return -1;
if (x < -128 || x > 127){ if (x < -128 || x > 127){
PyErr_SetString(StructError, PyErr_SetString(StructError,
"byte format requires -128<=number<=127"); "byte format requires -128 <= number <= 127");
return -1; return -1;
} }
*p = (char)x; *p = (char)x;
...@@ -395,7 +436,7 @@ np_ubyte(char *p, PyObject *v, const formatdef *f) ...@@ -395,7 +436,7 @@ np_ubyte(char *p, PyObject *v, const formatdef *f)
return -1; return -1;
if (x < 0 || x > 255){ if (x < 0 || x > 255){
PyErr_SetString(StructError, PyErr_SetString(StructError,
"ubyte format requires 0<=number<=255"); "ubyte format requires 0 <= number <= 255");
return -1; return -1;
} }
*p = (char)x; *p = (char)x;
...@@ -424,7 +465,7 @@ np_short(char *p, PyObject *v, const formatdef *f) ...@@ -424,7 +465,7 @@ np_short(char *p, PyObject *v, const formatdef *f)
if (x < SHRT_MIN || x > SHRT_MAX){ if (x < SHRT_MIN || x > SHRT_MAX){
PyErr_SetString(StructError, PyErr_SetString(StructError,
"short format requires " STRINGIFY(SHRT_MIN) "short format requires " STRINGIFY(SHRT_MIN)
"<=number<=" STRINGIFY(SHRT_MAX)); " <= number <= " STRINGIFY(SHRT_MAX));
return -1; return -1;
} }
y = (short)x; y = (short)x;
...@@ -441,7 +482,7 @@ np_ushort(char *p, PyObject *v, const formatdef *f) ...@@ -441,7 +482,7 @@ np_ushort(char *p, PyObject *v, const formatdef *f)
return -1; return -1;
if (x < 0 || x > USHRT_MAX){ if (x < 0 || x > USHRT_MAX){
PyErr_SetString(StructError, PyErr_SetString(StructError,
"short format requires 0<=number<=" STRINGIFY(USHRT_MAX)); "short format requires 0 <= number <= " STRINGIFY(USHRT_MAX));
return -1; return -1;
} }
y = (unsigned short)x; y = (unsigned short)x;
...@@ -456,6 +497,10 @@ np_int(char *p, PyObject *v, const formatdef *f) ...@@ -456,6 +497,10 @@ np_int(char *p, PyObject *v, const formatdef *f)
int y; int y;
if (get_long(v, &x) < 0) if (get_long(v, &x) < 0)
return -1; return -1;
#if defined(PY_STRUCT_RANGE_CHECKING) && (SIZEOF_LONG > SIZEOF_INT)
if (x < INT_MIN || x > INT_MAX)
return _range_error(f->format, sizeof(y), 0);
#endif
y = (int)x; y = (int)x;
memcpy(p, (char *)&y, sizeof y); memcpy(p, (char *)&y, sizeof y);
return 0; return 0;
...@@ -467,8 +512,16 @@ np_uint(char *p, PyObject *v, const formatdef *f) ...@@ -467,8 +512,16 @@ np_uint(char *p, PyObject *v, const formatdef *f)
unsigned long x; unsigned long x;
unsigned int y; unsigned int y;
if (get_ulong(v, &x) < 0) if (get_ulong(v, &x) < 0)
#ifdef PY_STRUCT_RANGE_CHECKING
return _range_error(f->format, sizeof(y), 1);
#else
return -1; return -1;
#endif
y = (unsigned int)x; y = (unsigned int)x;
#if defined(PY_STRUCT_RANGE_CHECKING) && (SIZEOF_LONG > SIZEOF_INT)
if (x < UINT_MIN || x > UINT_MAX)
return _range_error(f->format, sizeof(y), 1);
#endif
memcpy(p, (char *)&y, sizeof y); memcpy(p, (char *)&y, sizeof y);
return 0; return 0;
} }
...@@ -488,7 +541,11 @@ np_ulong(char *p, PyObject *v, const formatdef *f) ...@@ -488,7 +541,11 @@ np_ulong(char *p, PyObject *v, const formatdef *f)
{ {
unsigned long x; unsigned long x;
if (get_ulong(v, &x) < 0) if (get_ulong(v, &x) < 0)
#ifdef PY_STRUCT_RANGE_CHECKING
return _range_error(f->format, sizeof(x), 1);
#else
return -1; return -1;
#endif
memcpy(p, (char *)&x, sizeof x); memcpy(p, (char *)&x, sizeof x);
return 0; return 0;
} }
...@@ -683,6 +740,15 @@ bp_int(char *p, PyObject *v, const formatdef *f) ...@@ -683,6 +740,15 @@ bp_int(char *p, PyObject *v, const formatdef *f)
if (get_long(v, &x) < 0) if (get_long(v, &x) < 0)
return -1; return -1;
i = f->size; i = f->size;
#ifdef PY_STRUCT_RANGE_CHECKING
if (i != SIZEOF_LONG && (
(i == 2 && (x < -32768 || x > 32767))
#if SIZEOF_LONG != 4
|| (i == 4) && (x < -2147483648L || x > -2147483647L)
#endif
))
return _range_error(f->format, i, 0);
#endif
do { do {
p[--i] = (char)x; p[--i] = (char)x;
x >>= 8; x >>= 8;
...@@ -698,6 +764,10 @@ bp_uint(char *p, PyObject *v, const formatdef *f) ...@@ -698,6 +764,10 @@ bp_uint(char *p, PyObject *v, const formatdef *f)
if (get_ulong(v, &x) < 0) if (get_ulong(v, &x) < 0)
return -1; return -1;
i = f->size; i = f->size;
#ifdef PY_STRUCT_RANGE_CHECKING
if (i != SIZEOF_LONG && x >= (1 << (i * 8)))
return _range_error(f->format, f->size, 1);
#endif
do { do {
p[--i] = (char)x; p[--i] = (char)x;
x >>= 8; x >>= 8;
...@@ -763,8 +833,8 @@ bp_double(char *p, PyObject *v, const formatdef *f) ...@@ -763,8 +833,8 @@ bp_double(char *p, PyObject *v, const formatdef *f)
static formatdef bigendian_table[] = { static formatdef bigendian_table[] = {
{'x', 1, 0, NULL}, {'x', 1, 0, NULL},
{'b', 1, 0, bu_int, bp_int}, {'b', 1, 0, nu_byte, np_byte},
{'B', 1, 0, bu_uint, bp_int}, {'B', 1, 0, nu_ubyte, np_ubyte},
{'c', 1, 0, nu_char, np_char}, {'c', 1, 0, nu_char, np_char},
{'s', 1, 0, NULL}, {'s', 1, 0, NULL},
{'p', 1, 0, NULL}, {'p', 1, 0, NULL},
...@@ -882,6 +952,15 @@ lp_int(char *p, PyObject *v, const formatdef *f) ...@@ -882,6 +952,15 @@ lp_int(char *p, PyObject *v, const formatdef *f)
if (get_long(v, &x) < 0) if (get_long(v, &x) < 0)
return -1; return -1;
i = f->size; i = f->size;
#ifdef PY_STRUCT_RANGE_CHECKING
if (i != SIZEOF_LONG && (
(i == 2 && (x < -32768 || x > 32767))
#if SIZEOF_LONG != 4
|| (i == 4) && (x < -2147483648L || x > -2147483647L)
#endif
))
return _range_error(f->format, i, 0);
#endif
do { do {
*p++ = (char)x; *p++ = (char)x;
x >>= 8; x >>= 8;
...@@ -897,6 +976,10 @@ lp_uint(char *p, PyObject *v, const formatdef *f) ...@@ -897,6 +976,10 @@ lp_uint(char *p, PyObject *v, const formatdef *f)
if (get_ulong(v, &x) < 0) if (get_ulong(v, &x) < 0)
return -1; return -1;
i = f->size; i = f->size;
#ifdef PY_STRUCT_RANGE_CHECKING
if (i != SIZEOF_LONG && x >= (1 << (i * 8)))
return _range_error(f->format, f->size, 1);
#endif
do { do {
*p++ = (char)x; *p++ = (char)x;
x >>= 8; x >>= 8;
...@@ -962,8 +1045,8 @@ lp_double(char *p, PyObject *v, const formatdef *f) ...@@ -962,8 +1045,8 @@ lp_double(char *p, PyObject *v, const formatdef *f)
static formatdef lilendian_table[] = { static formatdef lilendian_table[] = {
{'x', 1, 0, NULL}, {'x', 1, 0, NULL},
{'b', 1, 0, lu_int, lp_int}, {'b', 1, 0, nu_byte, np_byte},
{'B', 1, 0, lu_uint, lp_int}, {'B', 1, 0, nu_ubyte, np_ubyte},
{'c', 1, 0, nu_char, np_char}, {'c', 1, 0, nu_char, np_char},
{'s', 1, 0, NULL}, {'s', 1, 0, NULL},
{'p', 1, 0, NULL}, {'p', 1, 0, NULL},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment