### Eclipse Workspace Patch 1.0 #P jython-trunk Index: src/com/ziclix/python/sql/JDBC20DataHandler.java =================================================================== --- src/com/ziclix/python/sql/JDBC20DataHandler.java (revision 7173) +++ src/com/ziclix/python/sql/JDBC20DataHandler.java (working copy) @@ -24,6 +24,7 @@ import org.python.core.PyFile; import org.python.core.PyObject; import org.python.core.util.StringUtil; +import org.python.util.SafeDoubleParser; /** * Support for JDBC 2.x type mappings, including Arrays, CLOBs and BLOBs. @@ -126,7 +127,7 @@ // in JDBC 2.0, use of a scale is deprecated try { BigDecimal bd = set.getBigDecimal(col); - obj = (bd == null) ? Py.None : Py.newFloat(bd.doubleValue()); + obj = (bd == null) ? Py.None : Py.newFloat(SafeDoubleParser.doubleValue(bd)); } catch (SQLException e) { obj = super.getPyObject(set, col, type); } Index: src/org/python/core/adapter/ClassicPyObjectAdapter.java =================================================================== --- src/org/python/core/adapter/ClassicPyObjectAdapter.java (revision 7173) +++ src/org/python/core/adapter/ClassicPyObjectAdapter.java (working copy) @@ -10,6 +10,7 @@ import org.python.core.PyProxy; import org.python.core.PyType; import org.python.core.PyUnicode; +import org.python.util.SafeDoubleParser; /** * Implements the algorithm originally used in {@link Py#java2py} to adapt objects. @@ -142,7 +143,7 @@ } public PyObject adapt(Object o) { - return new PyFloat(((Number) o).doubleValue()); + return new PyFloat(SafeDoubleParser.doubleValue((Number) o)); } } Index: src/com/ziclix/python/sql/handler/OracleDataHandler.java =================================================================== --- src/com/ziclix/python/sql/handler/OracleDataHandler.java (revision 7173) +++ src/com/ziclix/python/sql/handler/OracleDataHandler.java (working copy) @@ -24,6 +24,7 @@ import org.python.core.Py; import org.python.core.PyInteger; import org.python.core.PyObject; +import org.python.util.SafeDoubleParser; import com.ziclix.python.sql.DataHandler; import com.ziclix.python.sql.FilterDataHandler; @@ -153,7 +154,7 @@ : Py.newDecimal(number); } else { // Floating point binary precision - obj = Py.newFloat(set.getBigDecimal(col).doubleValue()); + obj = Py.newFloat(SafeDoubleParser.doubleValue(set.getBigDecimal(col))); } } else { // Decimal precision. A plain integer when without a scale. Maybe a Index: src/org/python/util/SafeFloatParser.java =================================================================== --- src/org/python/util/SafeFloatParser.java (revision 0) +++ src/org/python/util/SafeFloatParser.java (revision 0) @@ -0,0 +1,60 @@ +package org.python.util; + +import java.math.BigDecimal; + +/** + * A safer way to parse float values + *
+ * Prevents brute force attacks using the famous Java bug.
+ */
+public final class SafeFloatParser extends SafeDecimalParser {
+
+ /**
+ * Safe way of parsing a Float value from a String
+ *
+ * @param s
+ * The input String
+ * @return the Float value
+ */
+ public static Float valueOf(String s) {
+ Float result = null;
+ Double decimalValue = decimalValueOf(s);
+ if (decimalValue != null) {
+ result = Float.valueOf(decimalValue.floatValue());
+ }
+ return result;
+ }
+
+ /**
+ * Safe way of parsing a Float value from a String
+ *
+ * @param s
+ * The input String
+ * @return the Float value
+ */
+ public static Float parseFloat(String s) {
+ return valueOf(s);
+ }
+
+ /**
+ * Safe way of getting the float value
+ * prevents BigDecimal from calling Float.parseFloat()
+ *
+ * @param number
+ * @return the float value
+ */
+ public static float floatValue(Number number) {
+ return Float.valueOf((float)decimalValue(number));
+ }
+
+ /**
+ * Safe way of getting the float value
+ * Prevents BigDecimal from calling Float.parseFloat()
+ *
+ * @param bigDecimal
+ * @return the float value
+ */
+ public static float floatValue(BigDecimal bigDecimal) {
+ return Float.valueOf((float)decimalValue(bigDecimal));
+ }
+}
Index: src/com/ziclix/python/sql/DataHandler.java
===================================================================
--- src/com/ziclix/python/sql/DataHandler.java (revision 7173)
+++ src/com/ziclix/python/sql/DataHandler.java (working copy)
@@ -33,6 +33,7 @@
import org.python.core.PyFile;
import org.python.core.PyList;
import org.python.core.PyObject;
+import org.python.util.SafeDoubleParser;
/**
* The DataHandler is responsible mapping the JDBC data type to
@@ -251,7 +252,7 @@
case Types.NUMERIC:
case Types.DECIMAL:
BigDecimal bd = set.getBigDecimal(col);
- obj = (bd == null) ? Py.None : Py.newFloat(bd.doubleValue());
+ obj = (bd == null) ? Py.None : Py.newFloat(SafeDoubleParser.doubleValue(bd));
break;
case Types.BIT:
@@ -370,7 +371,7 @@
case Types.NUMERIC:
case Types.DECIMAL:
BigDecimal bd = stmt.getBigDecimal(col);
- obj = (bd == null) ? Py.None : Py.newFloat(bd.doubleValue());
+ obj = (bd == null) ? Py.None : Py.newFloat(SafeDoubleParser.doubleValue(bd));
break;
case Types.BIT:
Index: src/org/python/antlr/GrammarActions.java
===================================================================
--- src/org/python/antlr/GrammarActions.java (revision 7173)
+++ src/org/python/antlr/GrammarActions.java (working copy)
@@ -10,6 +10,7 @@
import org.python.core.PyString;
import org.python.core.PyUnicode;
import org.python.core.codecs;
+import org.python.util.SafeDoubleParser;
import org.python.antlr.ast.alias;
import org.python.antlr.ast.arguments;
import org.python.antlr.ast.boolopType;
@@ -364,13 +365,13 @@
}
Object makeFloat(Token t) {
- return Py.newFloat(Double.valueOf(t.getText()));
+ return Py.newFloat(SafeDoubleParser.valueOf(t.getText()));
}
Object makeComplex(Token t) {
String s = t.getText();
s = s.substring(0, s.length() - 1);
- return Py.newImaginary(Double.valueOf(s));
+ return Py.newImaginary(SafeDoubleParser.valueOf(s));
}
Object makeInt(Token t) {
Index: src/com/ziclix/python/sql/Jython22DataHandler.java
===================================================================
--- src/com/ziclix/python/sql/Jython22DataHandler.java (revision 7173)
+++ src/com/ziclix/python/sql/Jython22DataHandler.java (working copy)
@@ -30,6 +30,7 @@
import org.python.core.PyLong;
import org.python.core.PyObject;
import org.python.core.util.StringUtil;
+import org.python.util.SafeDoubleParser;
/**
* A copy of the DataHandler class as it was before Jython 2.5. By that version,
@@ -263,7 +264,7 @@
bd = set.getBigDecimal(col, 10);
}
- obj = (bd == null) ? Py.None : Py.newFloat(bd.doubleValue());
+ obj = (bd == null) ? Py.None : Py.newFloat(SafeDoubleParser.doubleValue(bd));
break;
case Types.BIT:
@@ -351,7 +352,7 @@
case Types.DECIMAL:
BigDecimal bd = stmt.getBigDecimal(col, 10);
- obj = (bd == null) ? Py.None : Py.newFloat(bd.doubleValue());
+ obj = (bd == null) ? Py.None : Py.newFloat(SafeDoubleParser.doubleValue(bd));
break;
case Types.BIT:
Index: src/org/python/core/PyString.java
===================================================================
--- src/org/python/core/PyString.java (revision 7173)
+++ src/org/python/core/PyString.java (working copy)
@@ -9,6 +9,7 @@
import org.python.expose.ExposedNew;
import org.python.expose.ExposedType;
import org.python.expose.MethodType;
+import org.python.util.SafeDoubleParser;
/**
* A builtin python string.
@@ -874,7 +875,7 @@
break;
}
int end = endDouble(getString(),s);
- z = Double.valueOf(getString().substring(s, end)).doubleValue();
+ z = SafeDoubleParser.valueOf(getString().substring(s, end)).doubleValue();
if (z == Double.POSITIVE_INFINITY) {
throw Py.ValueError(String.format("float() out of range: %.150s", getString()));
}
@@ -1554,7 +1555,7 @@
if (lowSval.endsWith("d") || lowSval.endsWith("f")) {
throw new NumberFormatException("format specifiers not allowed");
}
- return Double.valueOf(sval).doubleValue();
+ return SafeDoubleParser.valueOf(sval).doubleValue();
}
catch (NumberFormatException exc) {
throw Py.ValueError("invalid literal for __float__: "+getString());
Index: src/org/python/expose/generate/MethodExposer.java
===================================================================
--- src/org/python/expose/generate/MethodExposer.java (revision 7173)
+++ src/org/python/expose/generate/MethodExposer.java (working copy)
@@ -1,6 +1,8 @@
package org.python.expose.generate;
import org.objectweb.asm.Type;
+import org.python.util.SafeDoubleParser;
+import org.python.util.SafeFloatParser;
public abstract class MethodExposer extends Exposer {
@@ -266,9 +268,9 @@
} else if(arg.equals(BOOLEAN)) {
mv.visitLdcInsn(Boolean.valueOf(def) ? 1 : 0);
} else if(arg.equals(Type.FLOAT_TYPE)) {
- mv.visitLdcInsn(new Float(def));
+ mv.visitLdcInsn(SafeFloatParser.valueOf(def));
} else if(arg.equals(Type.DOUBLE_TYPE)) {
- mv.visitLdcInsn(new Double(def));
+ mv.visitLdcInsn(SafeDoubleParser.valueOf(def));
}
}
Index: src/org/python/util/SafeDoubleParser.java
===================================================================
--- src/org/python/util/SafeDoubleParser.java (revision 0)
+++ src/org/python/util/SafeDoubleParser.java (revision 0)
@@ -0,0 +1,55 @@
+package org.python.util;
+
+import java.math.BigDecimal;
+
+/**
+ * A safer way to parse double values
+ *
+ * Prevents brute force attacks using the famous Java bug.
+ */
+final public class SafeDoubleParser extends SafeDecimalParser {
+
+ /**
+ * Safe way of parsing a Double value from a String
+ *
+ * @param s
+ * The input String
+ * @return the Double value
+ */
+ public static Double valueOf(String s) {
+ return decimalValueOf(s);
+ }
+
+ /**
+ * Safe way of parsing a Double value from a String
+ *
+ * @param s
+ * The input String
+ * @return the Double value
+ */
+ public static Double parseDouble(String s) {
+ return valueOf(s);
+ }
+
+ /**
+ * Safe way of getting the double value
+ * prevents BigDecimal from calling Double.parseDouble()
+ *
+ * @param number
+ * @return the double value
+ */
+ public static double doubleValue(Number number) {
+ return decimalValue(number);
+ }
+
+ /**
+ * Safe way of getting the double value
+ * Prevents BigDecimal from calling Double.parseDouble()
+ *
+ * @param bigDecimal
+ * @return the double value
+ */
+ public static double doubleValue(BigDecimal bigDecimal) {
+ return decimalValue(bigDecimal);
+ }
+}
Index: src/org/python/util/SafeDecimalParser.java
===================================================================
--- src/org/python/util/SafeDecimalParser.java (revision 0)
+++ src/org/python/util/SafeDecimalParser.java (revision 0)
@@ -0,0 +1,151 @@
+package org.python.util;
+
+import java.math.BigDecimal;
+
+class SafeDecimalParser {
+
+ /** Constant 2 */
+ protected static final BigDecimal TWO = new BigDecimal(2);
+
+ /** Lower allowed value */
+ protected static final BigDecimal LOWER = new BigDecimal("2.22507385850720113605e-308");
+
+ /** Upper allowed value */
+ protected static final BigDecimal UPPER = new BigDecimal("2.22507385850720125958e-308");
+
+ /** The middle of the bad interval - used for rounding bad values */
+ protected static final BigDecimal MIDDLE = LOWER.add(UPPER).divide(TWO);
+
+ /** Upper allowed value as Double */
+ private static final Double UPPER_DOUBLE = Double.valueOf(UPPER.doubleValue());
+
+ /** Lower allowed value as Double */
+ private static final Double LOWER_DOUBLE = Double.valueOf(LOWER.doubleValue());
+
+ /** Digit sequence to trigger the slow path */
+ private static final String SUSPICIOUS_DIGITS = "22250738585072";
+
+ /**
+ * Heuristic test if we should look closer at the value
+ *
+ * @param s
+ * The non-null input String
+ * @return true
if the value is suspicious, false
otherwise
+ */
+ final protected static boolean isSuspicious(String s) {
+ return digits(s).indexOf(SUSPICIOUS_DIGITS) >= 0;
+ }
+
+ /**
+ * Safe parsing of a String into a Double
+ *
+ * @param s
+ * The input String, can be null
+ * @return The Double value
+ */
+ final protected static Double decimalValueOf(String s) {
+ Double result = null;
+ if (s != null) {
+ if (isSuspicious(s)) {
+ // take the slow path
+ result = parseSafely(s);
+ } else {
+ result = Double.valueOf(s);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Safe way of getting the double value
+ * prevents BigDecimal from calling Double.parseDouble()
+ *
+ * @param number
+ * @return the double value
+ */
+ final protected static double decimalValue(Number number) {
+ double result = 0;
+ if (number != null) {
+ if (number instanceof BigDecimal) {
+ result = decimalValue((BigDecimal)number);
+ } else {
+ result = number.doubleValue();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Safe way of getting the double value
+ * Prevents BigDecimal from calling Double.parseDouble()
+ *
+ * @param bigDecimal
+ * @return the double value
+ */
+ final protected static double decimalValue(BigDecimal bigDecimal) {
+ double result = 0.0;
+ if (bigDecimal != null) {
+ if (isDangerous(bigDecimal)) {
+ result = decimalValueOf(bigDecimal.toString()).doubleValue();
+ } else {
+ result = bigDecimal.doubleValue();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Slow parsing of a suspicious value
+ *
+ * Rounding takes place if the value is inside the bad interval
+ *
+ * @param s
+ * The non-null input String
+ * @return the double value
+ */
+ final private static Double parseSafely(String s) {
+ Double result;
+ BigDecimal bd = new BigDecimal(s);
+ if (isDangerous(bd)) {
+ if (bd.compareTo(MIDDLE) >= 0) {
+ result = UPPER_DOUBLE;
+ } else {
+ result = LOWER_DOUBLE;
+ }
+ } else {
+ result = Double.valueOf(s);
+ }
+ return result;
+ }
+
+ /**
+ * Extract the digits from a numeric string
+ *
+ * @param s
+ * The non-null String value
+ * @return A String containing only the digits
+ */
+ final private static String digits(String s) {
+ char[] ca = s.toCharArray();
+ int len = ca.length;
+ StringBuilder b = new StringBuilder(len);
+ for (int i = 0; i < len; i++) {
+ char c = ca[i];
+ if (c >= '0' && c <= '9') {
+ b.append(c);
+ }
+ }
+ return b.toString();
+ }
+
+ /**
+ * Tests if the value is in the dangerous interval
+ *
+ * @param bd
+ * The big decimal value
+ * @return true
if the value is dangerous, false
otherwise
+ */
+ final private static boolean isDangerous(BigDecimal bd) {
+ return bd.compareTo(UPPER) < 0 && bd.compareTo(LOWER) > 0;
+ }
+}
Index: tests/java/org/python/util/SafeDecimalParserTest.java
===================================================================
--- tests/java/org/python/util/SafeDecimalParserTest.java (revision 0)
+++ tests/java/org/python/util/SafeDecimalParserTest.java (revision 0)
@@ -0,0 +1,161 @@
+package org.python.util;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import junit.framework.TestCase;
+
+public class SafeDecimalParserTest extends TestCase {
+
+ /**
+ * any number with the digit sequence 22250738585072 is suspicious
+ */
+ public void testIsSuspicious() {
+ assertFalse(SafeDecimalParser.isSuspicious("0"));
+ assertFalse(SafeDecimalParser.isSuspicious("1.23456"));
+ assertFalse(SafeDecimalParser.isSuspicious("1.23456e-308"));
+ assertFalse(SafeDecimalParser.isSuspicious("1.23456e308"));
+ //
+ assertTrue(SafeDecimalParser.isSuspicious("222507385850728298829948"));
+ assertTrue(SafeDecimalParser.isSuspicious("2.22507385850721302094943"));
+ assertTrue(SafeDecimalParser.isSuspicious("00002.225073858507277728288"));
+ assertTrue(SafeDecimalParser.isSuspicious("0.0002225073858507248829848"));
+ //
+ assertTrue(SafeDecimalParser.isSuspicious("2.22507385850728229400202e-308"));
+ assertTrue(SafeDecimalParser.isSuspicious("00002.22507385850728299492002e20"));
+ assertTrue(SafeDecimalParser.isSuspicious("0.00022250738585072100827272e-304"));
+ }
+
+ public void testDecimalValueOf_bad() {
+ String s;
+ s = "2.22507385850720113606e-308";
+ assertEqualsLower(SafeDecimalParser.decimalValueOf(s));
+ s = "2.22507385850720125957e-308";
+ assertEqualsUpper(SafeDecimalParser.decimalValueOf(s));
+ s = SafeDecimalParser.MIDDLE.toString();
+ assertEqualsUpper(SafeDecimalParser.decimalValueOf(s));
+ s = "2.2250738585072012e-308";
+ assertRoundedToIntervalBoundary(SafeDecimalParser.decimalValueOf(s));
+ s = "2.2250738585072012E-308";
+ assertRoundedToIntervalBoundary(SafeDecimalParser.decimalValueOf(s));
+ s = "0000002.2250738585072012E-308";
+ assertRoundedToIntervalBoundary(SafeDecimalParser.decimalValueOf(s));
+ s = "22.250738585072012E-309";
+ assertRoundedToIntervalBoundary(SafeDecimalParser.decimalValueOf(s));
+ }
+
+ public void testDecimalValueOf_good() {
+ assertGood("0");
+ assertGood("1.75");
+ assertGood("1.239402e9");
+ assertGood("22250738585072");
+ assertGood("2.250738585072012E-307");
+ assertGood(SafeDecimalParser.LOWER.toString());
+ assertGood(SafeDecimalParser.UPPER.toString());
+ }
+
+ public void testDecimalValue_Number() {
+ BigInteger bi = new BigInteger("22250738585072");
+ assertEquals(bi.doubleValue(), SafeDecimalParser.decimalValue(bi));
+ assertEquals(0.0, SafeDecimalParser.decimalValue((Number)null));
+ }
+
+ public void testDecimalValue_BigDecimal() {
+ BigDecimal bd = new BigDecimal("22250738585072.94289010");
+ assertEquals(bd.doubleValue(), SafeDecimalParser.decimalValue(bd));
+ assertEqualsUpper(SafeDecimalParser.decimalValue(SafeDecimalParser.MIDDLE));
+ assertEqualsUpper(SafeDecimalParser.decimalValue(SafeDecimalParser.UPPER));
+ assertEqualsLower(SafeDecimalParser.decimalValue(SafeDecimalParser.LOWER));
+ assertEquals(0.0, SafeDecimalParser.decimalValue((BigDecimal)null));
+ }
+
+ /**
+ * make sure lower, upper and middle values are consistent
+ */
+ public void testInterval() {
+ assertTrue(SafeDecimalParser.LOWER.compareTo(SafeDecimalParser.UPPER) < 0);
+ assertTrue(SafeDecimalParser.UPPER.compareTo(SafeDecimalParser.LOWER) > 0);
+ //
+ assertTrue(SafeDecimalParser.LOWER.compareTo(SafeDecimalParser.MIDDLE) < 0);
+ assertTrue(SafeDecimalParser.MIDDLE.compareTo(SafeDecimalParser.LOWER) > 0);
+ //
+ assertTrue(SafeDecimalParser.MIDDLE.compareTo(SafeDecimalParser.UPPER) < 0);
+ assertTrue(SafeDecimalParser.UPPER.compareTo(SafeDecimalParser.MIDDLE) > 0);
+ }
+
+ /**
+ * this test should not hang
+ */
+ public void testBigDecimalBoundaries() {
+ @SuppressWarnings("unused")
+ BigDecimal badd;
+ BigDecimal good;
+ //
+ good = new BigDecimal("2.22507385850720113605e-308");
+ good.doubleValue();
+ //
+ badd = new BigDecimal("2.22507385850720113606e-308");
+ badd = new BigDecimal("2.22507385850720125957e-308");
+ //
+ good = new BigDecimal("2.22507385850720125958e-308");
+ good.doubleValue();
+ }
+
+ /**
+ * make sure Double.valueOf(null) throws a NPE
+ */
+ public void testParseNull_original_Double() {
+ try {
+ Double d = Double.valueOf((String)null);
+ fail("oops - Double.valueOf(null) does not throw a NPE any more: " + d);
+ } catch (NullPointerException npe) {
+ // pass
+ }
+ try {
+ Double d = Double.parseDouble((String)null);
+ fail("oops - Double.parseDouble(null) does not throw a NPE any more: " + d);
+ } catch (NullPointerException npe) {
+ // pass
+ }
+ }
+
+ /**
+ * make sure we do not have to take formatting characters into account
+ */
+ public void testParseStrangeChars_original_Double() {
+ assertNumberFormatException("1,75");
+ assertNumberFormatException("1,234.75");
+ assertNumberFormatException("1'234.75");
+ }
+
+ private void assertNumberFormatException(String s) {
+ try {
+ Double.parseDouble(s);
+ fail("NumberFormatException expected");
+ } catch (NumberFormatException nfe) {
+ // pass
+ }
+ }
+
+ private void assertEqualsLower(double d) {
+ final double lowerDouble = SafeDecimalParser.LOWER.doubleValue();
+ assertEquals(lowerDouble, d);
+ }
+
+ private void assertEqualsUpper(double d) {
+ final double upperDouble = SafeDecimalParser.UPPER.doubleValue();
+ assertEquals(upperDouble, d);
+ }
+
+ private void assertRoundedToIntervalBoundary(Double d) {
+ final Double lowerDouble = Double.valueOf(SafeDecimalParser.LOWER.doubleValue());
+ final Double upperDouble = Double.valueOf(SafeDecimalParser.UPPER.doubleValue());
+ assertTrue(lowerDouble.equals(d) || upperDouble.equals(d));
+ }
+
+ private void assertGood(String s) {
+ Double d1 = SafeDecimalParser.decimalValueOf(s);
+ Double originalDouble = Double.valueOf(s);
+ assertEquals(originalDouble, d1);
+ }
+}
Index: tests/java/org/python/util/InterpreterTest.java
===================================================================
--- tests/java/org/python/util/InterpreterTest.java (revision 7173)
+++ tests/java/org/python/util/InterpreterTest.java (working copy)
@@ -23,6 +23,16 @@
PyObject pyo = interp.eval("{u'one': u'two'}");
assertEquals(test, pyo);
}
+
+ /**
+ * prevent endless loop due to the famous Java Double.parseDouble() bug
+ */
+ public void testParseDoubleBug() throws Exception {
+ PythonInterpreter.initialize(System.getProperties(), null, new String[] {});
+ PythonInterpreter interp = new PythonInterpreter();
+ PyObject result = interp.eval("2.2250738585072012e-308");
+ assertNotNull(result);
+ }
public void testMultipleThreads() {
final CountDownLatch doneSignal = new CountDownLatch(10);
Index: tests/java/org/python/util/SafeDoubleParserTest.java
===================================================================
--- tests/java/org/python/util/SafeDoubleParserTest.java (revision 0)
+++ tests/java/org/python/util/SafeDoubleParserTest.java (revision 0)
@@ -0,0 +1,123 @@
+package org.python.util;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import junit.framework.TestCase;
+
+public class SafeDoubleParserTest extends TestCase {
+
+ /**
+ * make sure to push bad values to the valid interval boundaries
+ */
+ public void testValueOf_bad() {
+ String s;
+ s = "2.22507385850720113606e-308";
+ assertEqualsLower(SafeDoubleParser.valueOf(s));
+ s = "2.22507385850720125957e-308";
+ assertEqualsUpper(SafeDoubleParser.valueOf(s));
+ s = SafeDecimalParser.MIDDLE.toString();
+ assertEqualsUpper(SafeDoubleParser.valueOf(s));
+ s = "2.2250738585072012e-308";
+ assertRoundedToIntervalBoundary(SafeDoubleParser.valueOf(s));
+ s = "2.2250738585072012E-308";
+ assertRoundedToIntervalBoundary(SafeDoubleParser.valueOf(s));
+ s = "0000002.2250738585072012E-308";
+ assertRoundedToIntervalBoundary(SafeDoubleParser.valueOf(s));
+ s = "22.250738585072012E-309";
+ assertRoundedToIntervalBoundary(SafeDoubleParser.valueOf(s));
+ }
+
+ /**
+ * make sure to push bad values to the valid interval boundaries
+ */
+ public void testParseDouble_bad() {
+ String s;
+ s = "2.22507385850720113606e-308";
+ assertEqualsLower(SafeDoubleParser.parseDouble(s));
+ s = "2.22507385850720125957e-308";
+ assertEqualsUpper(SafeDoubleParser.parseDouble(s));
+ s = "2.2250738585072012e-308";
+ assertRoundedToIntervalBoundary(SafeDoubleParser.parseDouble(s));
+ s = SafeDecimalParser.MIDDLE.toString();
+ assertEqualsUpper(SafeDoubleParser.parseDouble(s));
+ s = "2.2250738585072012E-308";
+ assertRoundedToIntervalBoundary(SafeDoubleParser.parseDouble(s));
+ s = "0000002.2250738585072012E-308";
+ assertRoundedToIntervalBoundary(SafeDoubleParser.parseDouble(s));
+ s = "22.250738585072012E-309";
+ assertRoundedToIntervalBoundary(SafeDoubleParser.parseDouble(s));
+ }
+
+ /**
+ * harmless values
+ */
+ public void testGood() {
+ assertGood("0");
+ assertGood("1.75");
+ assertGood("1.239402e9");
+ }
+
+ /**
+ * harmless but suspicious values
+ */
+ public void testGood_but_suspicious() {
+ assertGood("22250738585072");
+ assertGood("2.250738585072012E-307");
+ assertGood(SafeDecimalParser.LOWER.toString());
+ assertGood(SafeDecimalParser.UPPER.toString());
+ }
+
+ /**
+ * make sure there is no NPE
+ */
+ public void testValueOf_null() {
+ assertNull(SafeDoubleParser.valueOf(null));
+ }
+
+ /**
+ * make sure there is no NPE
+ */
+ public void testParseDouble_null() {
+ assertNull(SafeDoubleParser.parseDouble(null));
+ }
+
+ public void testDoubleValue_Number() {
+ BigInteger bi = new BigInteger("22250738585072");
+ assertEquals(bi.doubleValue(), SafeDoubleParser.doubleValue(bi));
+ assertEquals(0.0, SafeDoubleParser.doubleValue((Number)null));
+ }
+
+ public void testDoubleValue_BigDecimal() {
+ BigDecimal bd = new BigDecimal("22250738585072.94289010");
+ assertEquals(bd.doubleValue(), SafeDoubleParser.doubleValue(bd));
+ assertEqualsUpper(SafeDoubleParser.doubleValue(SafeDecimalParser.MIDDLE));
+ assertEqualsUpper(SafeDoubleParser.doubleValue(SafeDecimalParser.UPPER));
+ assertEqualsLower(SafeDoubleParser.doubleValue(SafeDecimalParser.LOWER));
+ assertEquals(0.0, SafeDoubleParser.doubleValue((BigDecimal)null));
+ }
+
+ private void assertRoundedToIntervalBoundary(Double d) {
+ final Double lowerDouble = Double.valueOf(SafeDecimalParser.LOWER.doubleValue());
+ final Double upperDouble = Double.valueOf(SafeDecimalParser.UPPER.doubleValue());
+ assertTrue(lowerDouble.equals(d) || upperDouble.equals(d));
+ }
+
+ private void assertEqualsLower(Double d) {
+ final Double lowerDouble = Double.valueOf(SafeDecimalParser.LOWER.doubleValue());
+ assertEquals(lowerDouble, d);
+ }
+
+ private void assertEqualsUpper(Double d) {
+ final Double upperDouble = Double.valueOf(SafeDecimalParser.UPPER.doubleValue());
+ assertEquals(upperDouble, d);
+ }
+
+ private void assertGood(String s) {
+ Double d1 = SafeDoubleParser.valueOf(s);
+ Double d2 = SafeDoubleParser.parseDouble(s);
+ Double originalDouble = Double.valueOf(s);
+ assertEquals(originalDouble, d1);
+ assertEquals(originalDouble, d2);
+ }
+}
Index: tests/java/org/python/util/SafeFloatParserTest.java
===================================================================
--- tests/java/org/python/util/SafeFloatParserTest.java (revision 0)
+++ tests/java/org/python/util/SafeFloatParserTest.java (revision 0)
@@ -0,0 +1,123 @@
+package org.python.util;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import junit.framework.TestCase;
+
+public class SafeFloatParserTest extends TestCase {
+
+ public void testFloatValue_Number() {
+ BigInteger bi = new BigInteger("22250738585072");
+ assertEquals(bi.floatValue(), SafeFloatParser.floatValue(bi));
+ assertEquals(0f, SafeFloatParser.floatValue((Number)null));
+ }
+
+ public void testFloatValue_BigDecimal() {
+ BigDecimal bd = new BigDecimal("22250738585072.94289010");
+ assertEquals(bd.floatValue(), SafeFloatParser.floatValue(bd));
+ assertEqualsUpper(SafeFloatParser.floatValue(SafeDecimalParser.MIDDLE));
+ assertEqualsUpper(SafeFloatParser.floatValue(SafeDecimalParser.UPPER));
+ assertEqualsLower(SafeFloatParser.floatValue(SafeDecimalParser.LOWER));
+ assertEquals(0f, SafeFloatParser.floatValue((BigDecimal)null));
+ }
+
+ /**
+ * make sure to push bad values to the valid interval boundaries
+ */
+ public void testValueOf_bad() {
+ String s;
+ s = "2.22507385850720113606e-308";
+ assertEqualsLower(SafeFloatParser.valueOf(s));
+ s = "2.22507385850720125957e-308";
+ assertEqualsUpper(SafeFloatParser.valueOf(s));
+ s = SafeDecimalParser.MIDDLE.toString();
+ assertEqualsUpper(SafeFloatParser.valueOf(s));
+ s = "2.2250738585072012e-308";
+ assertRoundedToIntervalBoundary(SafeFloatParser.valueOf(s));
+ s = "2.2250738585072012E-308";
+ assertRoundedToIntervalBoundary(SafeFloatParser.valueOf(s));
+ s = "0000002.2250738585072012E-308";
+ assertRoundedToIntervalBoundary(SafeFloatParser.valueOf(s));
+ s = "22.250738585072012E-309";
+ assertRoundedToIntervalBoundary(SafeFloatParser.valueOf(s));
+ }
+
+ /**
+ * make sure to push bad values to the valid interval boundaries
+ */
+ public void testParseFloat_bad() {
+ String s;
+ s = "2.22507385850720113606e-308";
+ assertEqualsLower(SafeFloatParser.parseFloat(s));
+ s = "2.22507385850720125957e-308";
+ assertEqualsUpper(SafeFloatParser.parseFloat(s));
+ s = "2.2250738585072012e-308";
+ assertRoundedToIntervalBoundary(SafeFloatParser.parseFloat(s));
+ s = SafeDecimalParser.MIDDLE.toString();
+ assertEqualsUpper(SafeFloatParser.parseFloat(s));
+ s = "2.2250738585072012E-308";
+ assertRoundedToIntervalBoundary(SafeFloatParser.parseFloat(s));
+ s = "0000002.2250738585072012E-308";
+ assertRoundedToIntervalBoundary(SafeFloatParser.parseFloat(s));
+ s = "22.250738585072012E-309";
+ assertRoundedToIntervalBoundary(SafeFloatParser.parseFloat(s));
+ }
+
+ /**
+ * harmless values
+ */
+ public void testGood() {
+ assertGood("0");
+ assertGood("1.75");
+ assertGood("1.239402e9");
+ }
+
+ /**
+ * harmless but suspicious values
+ */
+ public void testGood_but_suspicious() {
+ assertGood("22250738585072");
+ assertGood("2.250738585072012E-307");
+ assertGood(SafeDecimalParser.LOWER.toString());
+ assertGood(SafeDecimalParser.UPPER.toString());
+ }
+
+ /**
+ * make sure there is no NPE
+ */
+ public void testValueOf_null() {
+ assertNull(SafeFloatParser.valueOf(null));
+ }
+
+ /**
+ * make sure there is no NPE
+ */
+ public void testParseFloat_null() {
+ assertNull(SafeFloatParser.parseFloat(null));
+ }
+
+ private void assertRoundedToIntervalBoundary(Float f) {
+ final Float lowerFloat = Float.valueOf(SafeDecimalParser.LOWER.floatValue());
+ final Float upperFloat = Float.valueOf(SafeDecimalParser.UPPER.floatValue());
+ assertTrue(lowerFloat.equals(f) || upperFloat.equals(f));
+ }
+
+ private void assertEqualsLower(Float f) {
+ final Float lowerFloat = Float.valueOf(SafeDecimalParser.LOWER.floatValue());
+ assertEquals(lowerFloat, f);
+ }
+
+ private void assertEqualsUpper(Float f) {
+ final Float upperFloat = Float.valueOf(SafeDecimalParser.UPPER.floatValue());
+ assertEquals(upperFloat, f);
+ }
+
+ private void assertGood(String s) {
+ Float f1 = SafeFloatParser.valueOf(s);
+ Float f2 = SafeFloatParser.parseFloat(s);
+ Float originalFloat = Float.valueOf(s);
+ assertEquals(originalFloat, f1);
+ assertEquals(originalFloat, f2);
+ }
+}
Index: src/org/python/modules/cPickle.java
===================================================================
--- src/org/python/modules/cPickle.java (revision 7173)
+++ src/org/python/modules/cPickle.java (working copy)
@@ -44,6 +44,7 @@
import org.python.core.exceptions;
import org.python.core.imp;
import org.python.util.Generic;
+import org.python.util.SafeDoubleParser;
/**
*
@@ -1813,7 +1814,7 @@
final private void load_float() {
String line = file.readlineNoNl();
- push(new PyFloat(Double.valueOf(line).doubleValue()));
+ push(new PyFloat(SafeDoubleParser.valueOf(line).doubleValue()));
}
final private void load_binfloat() {