Permalink
Browse files

Fix JRUBY-5441: Attempt to create decimal 2.2250738585072012e-308 sen…

…ds JVM into tight infinite loop

There were only two places we called Double.parseDouble. I modified both places to use my own logic that splits up the mantissa and exponent and uses BigDecimal math to produce a result.
  • Loading branch information...
headius committed Feb 2, 2011
1 parent ae4847e commit 4c712963885c0117b95066d927520a6a738c2a65
@@ -46,6 +46,7 @@
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 static IRubyObject induced_from(IRubyObject recv, IRubyObject arg) {
}
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 static RubyBigDecimal newInstance(IRubyObject recv, IRubyObject[] args) {
}
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);
@@ -59,6 +59,7 @@
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 Encoding getEncoding() {
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);
@@ -27,7 +27,16 @@
***** END LICENSE BLOCK *****/
package org.jruby.util;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
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 +54,35 @@ public static final double byteListToDouble19(ByteList bytes, boolean strict) {
return new DoubleConverter().parse(bytes, strict, true);
}
public static double parseDouble(String value) {
String normalString = normalizeDoubleString(value);
int offset = normalString.indexOf('E');
BigDecimal base;
int exponent;
if (offset == -1) {
base = new BigDecimal(value);
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));
}
return base.scaleByPowerOfTen(exponent).doubleValue();
}
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
strValue = strValue.replaceAll("_", "");
// 3. MRI ignores the trailing junk
strValue = NUMBER_PATTERN.matcher(strValue).replaceFirst("$1");
return strValue;
}
public static class DoubleConverter {
private byte[] bytes;
private int index;
@@ -127,7 +165,7 @@ private double completeCalculation() {
strictError(); // We know it is not whitespace at this point
}
return Double.parseDouble(new String(chars));
return parseDouble(new String(chars));
}
private void strictError() {

0 comments on commit 4c71296

Please sign in to comment.