Created
February 7, 2011 21:35
-
-
Save anonymous/815270 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/org/jruby/RubyBigDecimal.java b/src/org/jruby/RubyBigDecimal.java | |
index e72fc30..bbd9f79 100644 | |
--- a/src/org/jruby/RubyBigDecimal.java | |
+++ b/src/org/jruby/RubyBigDecimal.java | |
@@ -46,6 +46,7 @@ import org.jruby.runtime.ObjectAllocator; | |
import org.jruby.runtime.ThreadContext; | |
import org.jruby.runtime.Visibility; | |
import org.jruby.runtime.builtin.IRubyObject; | |
+import org.jruby.util.ConvertDouble; | |
/** | |
* @author <a href="mailto:ola.bini@ki.se">Ola Bini</a> | |
@@ -339,8 +340,6 @@ public class RubyBigDecimal extends RubyNumeric { | |
} | |
private final static Pattern INFINITY_PATTERN = Pattern.compile("^([+-])?Infinity$"); | |
- private final static Pattern NUMBER_PATTERN | |
- = Pattern.compile("^([+-]?\\d*\\.?\\d*([eE][+-]?)?\\d*).*"); | |
@JRubyMethod(name = "new", required = 1, optional = 1, meta = true) | |
public static RubyBigDecimal newInstance(IRubyObject recv, IRubyObject[] args) { | |
@@ -364,15 +363,7 @@ public class RubyBigDecimal extends RubyNumeric { | |
} | |
return newInfinity(runtime, sign); | |
} | |
- | |
- // Clean-up string representation so that it could be understood | |
- // by Java's BigDecimal. Not terribly efficient for now. | |
- // 1. MRI allows d and D as exponent separators | |
- strValue = strValue.replaceFirst("[dD]", "E"); | |
- // 2. MRI allows underscores anywhere | |
- strValue = strValue.replaceAll("_", ""); | |
- // 3. MRI ignores the trailing junk | |
- strValue = NUMBER_PATTERN.matcher(strValue).replaceFirst("$1"); | |
+ strValue = ConvertDouble.normalizeDoubleString(strValue); | |
try { | |
decimal = new BigDecimal(strValue); | |
diff --git a/src/org/jruby/lexer/yacc/RubyYaccLexer.java b/src/org/jruby/lexer/yacc/RubyYaccLexer.java | |
index f3b12cd..093af2b 100755 | |
--- a/src/org/jruby/lexer/yacc/RubyYaccLexer.java | |
+++ b/src/org/jruby/lexer/yacc/RubyYaccLexer.java | |
@@ -59,6 +59,7 @@ import org.jruby.lexer.yacc.SyntaxException.PID; | |
import org.jruby.parser.ParserSupport; | |
import org.jruby.parser.Tokens; | |
import org.jruby.util.ByteList; | |
+import org.jruby.util.ConvertDouble; | |
import org.jruby.util.StringSupport; | |
@@ -129,7 +130,7 @@ public class RubyYaccLexer { | |
private int getFloatToken(String number) { | |
double d; | |
try { | |
- d = Double.parseDouble(number); | |
+ d = ConvertDouble.parseDouble(number); | |
} catch (NumberFormatException e) { | |
warnings.warn(ID.FLOAT_OUT_OF_RANGE, getPosition(), "Float " + number + " out of range.", number); | |
diff --git a/src/org/jruby/util/ConvertDouble.java b/src/org/jruby/util/ConvertDouble.java | |
index 67aead3..0cd4954 100644 | |
--- a/src/org/jruby/util/ConvertDouble.java | |
+++ b/src/org/jruby/util/ConvertDouble.java | |
@@ -27,7 +27,17 @@ | |
***** END LICENSE BLOCK *****/ | |
package org.jruby.util; | |
+import java.math.BigDecimal; | |
+import java.math.BigInteger; | |
+import java.math.MathContext; | |
+import java.util.Arrays; | |
+import java.util.regex.Pattern; | |
+import org.jruby.RubyBigDecimal; | |
+ | |
public class ConvertDouble { | |
+ private final static Pattern NUMBER_PATTERN | |
+ = Pattern.compile("^([+-]?\\d*\\.?\\d*([eE][+-]?)?\\d*).*"); | |
+ | |
/** | |
* Converts supplied ByteList into a double. strict-mode will not like | |
* extra text non-numeric text or multiple sequention underscores. | |
@@ -45,6 +55,64 @@ public class ConvertDouble { | |
return new DoubleConverter().parse(bytes, strict, true); | |
} | |
+ private static final BigDecimal MIN_NORMAL_DOUBLE = new BigDecimal(Double.longBitsToDouble(0x0010000000000000L)); | |
+ | |
+ /** | |
+ * Parse the given String into a double value. | |
+ * | |
+ * Note that this includes workarounds for the JDK DOS exploint involving numbers | |
+ * near 2.2250738585072012e-308 that cause default Double.parseDouble to enter | |
+ * an infinite loop. | |
+ * | |
+ * @param value The string to parse | |
+ * @return the parsed double value | |
+ */ | |
+ public static double parseDouble(String value) { | |
+ // normalize to a clean form | |
+ String normalString = normalizeDoubleString(value); | |
+ | |
+ // split mantissa and exponent into separate pieces | |
+ int offset = normalString.indexOf('E'); | |
+ BigDecimal base; | |
+ int exponent; | |
+ if (offset == -1) { | |
+ base = new BigDecimal(normalString); | |
+ exponent = 0; | |
+ } else { | |
+ base = new BigDecimal(normalString.substring(0, offset)); | |
+ exponent = Integer.parseInt(normalString.charAt(offset + 1) == '+' ? | |
+ normalString.substring(offset + 2) : | |
+ normalString.substring(offset + 1)); | |
+ } | |
+ | |
+ // scale final value inside BigDecimal using mantissa and exponent | |
+ BigDecimal finalValue = base.scaleByPowerOfTen(exponent); | |
+ | |
+ // if final value is > 0 but < MIN_NORMAL, just return MIN_NORMAL | |
+ if (finalValue.abs().equals(finalValue) && finalValue.compareTo(MIN_NORMAL_DOUBLE) <= 0 && finalValue.compareTo(BigDecimal.ZERO) > 0) { | |
+ // We special case values in this range, since it is now a widely-known exploit. | |
+ // Defaulting to Java's minimum "normal" double value. | |
+ // See JRUBY-5441. | |
+ return Double.MIN_NORMAL; | |
+ } | |
+ | |
+ // if not in our magic range, just parse normally | |
+ return Double.parseDouble(value); | |
+ } | |
+ | |
+ public static String normalizeDoubleString(String strValue) { | |
+ // Clean-up string representation so that it could be understood | |
+ // by Java's BigDecimal. Not terribly efficient for now. | |
+ // 1. MRI allows d and D as exponent separators | |
+ strValue = strValue.replaceFirst("[edD]", "E"); | |
+ // 2. MRI allows underscores anywhere and + before digits | |
+ strValue = strValue.replaceAll("_", ""); | |
+ // 3. MRI ignores the trailing junk | |
+ strValue = NUMBER_PATTERN.matcher(strValue).replaceFirst("$1"); | |
+ // 4. trim any leading or trailing whitespace | |
+ return strValue.trim(); | |
+ } | |
+ | |
public static class DoubleConverter { | |
private byte[] bytes; | |
private int index; | |
@@ -127,7 +195,7 @@ public class ConvertDouble { | |
strictError(); // We know it is not whitespace at this point | |
} | |
- return Double.parseDouble(new String(chars)); | |
+ return parseDouble(new String(chars)); | |
} | |
private void strictError() { |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment