summaryrefslogtreecommitdiffstats
path: root/Modules/cPickle.c
diff options
context:
space:
mode:
authorTim Peters <tim.peters@gmail.com>2003-03-20 18:32:13 (GMT)
committerTim Peters <tim.peters@gmail.com>2003-03-20 18:32:13 (GMT)
commitd50ade68ec240ea8ea12604809d8c70985263dce (patch)
tree7dd938bc5a9dcf40707839e518420804bbb3cd94 /Modules/cPickle.c
parent62364ffb80a7dab0477fe7dc7aec5478ab6afec2 (diff)
downloadcpython-d50ade68ec240ea8ea12604809d8c70985263dce.zip
cpython-d50ade68ec240ea8ea12604809d8c70985263dce.tar.gz
cpython-d50ade68ec240ea8ea12604809d8c70985263dce.tar.bz2
SF bug 705836: struct.pack of floats in non-native endian order
pack_float, pack_double, save_float: All the routines for creating IEEE-format packed representations of floats and doubles simply ignored that rounding can (in rare cases) propagate out of a long string of 1 bits. At worst, the end-off carry can (by mistake) interfere with the exponent value, and then unpacking yields a result wrong by a factor of 2. In less severe cases, it can end up losing more low-order bits than intended, or fail to catch overflow *caused* by rounding. Bugfix candidate, but I already backported this to 2.2. In 2.3, this code remains in severe need of refactoring.
Diffstat (limited to 'Modules/cPickle.c')
-rw-r--r--Modules/cPickle.c30
1 files changed, 24 insertions, 6 deletions
diff --git a/Modules/cPickle.c b/Modules/cPickle.c
index 88f2fc1..964fc63 100644
--- a/Modules/cPickle.c
+++ b/Modules/cPickle.c
@@ -1156,12 +1156,8 @@ save_float(Picklerobject *self, PyObject *args)
return -1;
}
- if (e >= 1024) {
- /* XXX 1024 itself is reserved for Inf/NaN */
- PyErr_SetString(PyExc_OverflowError,
- "float too large to pack with d format");
- return -1;
- }
+ if (e >= 1024)
+ goto Overflow;
else if (e < -1022) {
/* Gradual underflow */
f = ldexp(f, 1022 + e);
@@ -1176,9 +1172,26 @@ save_float(Picklerobject *self, PyObject *args)
flo the low 24 bits (== 52 bits) */
f *= 268435456.0; /* 2**28 */
fhi = (long) floor(f); /* Truncate */
+ assert(fhi < 268435456);
+
f -= (double)fhi;
f *= 16777216.0; /* 2**24 */
flo = (long) floor(f + 0.5); /* Round */
+ assert(flo <= 16777216);
+ if (flo >> 24) {
+ /* The carry propagated out of a string of 24 1 bits. */
+ flo = 0;
+ ++fhi;
+ if (fhi >> 28) {
+ /* And it also progagated out of the next
+ * 28 bits.
+ */
+ fhi = 0;
+ ++e;
+ if (e >= 2047)
+ goto Overflow;
+ }
+ }
/* First byte */
*p = (s<<7) | (e>>4);
@@ -1224,6 +1237,11 @@ save_float(Picklerobject *self, PyObject *args)
}
return 0;
+
+ Overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "float too large to pack with d format");
+ return -1;
}