Merge branch '2.7'
diff --git a/release-notes/VERSION b/release-notes/VERSION
index 7db8ee3..a964afa 100644
--- a/release-notes/VERSION
+++ b/release-notes/VERSION
@@ -54,6 +54,8 @@
#307: JsonGenerationException: Split surrogate on writeRaw() input thrown for
input of a certain size
(reported by Mike N)
+#315: `OutOfMemoryError` when writing BigDecimal
+ (reported by gmethwin@github)
2.7.6 (23-Jul-2016)
diff --git a/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java b/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java
index 19bcfd6..d2d05f2 100644
--- a/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java
+++ b/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java
@@ -1,6 +1,7 @@
package com.fasterxml.jackson.core.base;
import java.io.*;
+import java.math.BigDecimal;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.json.DupDetector;
@@ -41,6 +42,16 @@
protected final static String WRITE_RAW = "write a raw (unencoded) value";
protected final static String WRITE_STRING = "write a string";
+ /**
+ * This value is the limit of scale allowed for serializing {@link BigDecimal}
+ * in "plain" (non-engineering) notation; intent is to prevent asymmetric
+ * attack whereupon simple eng-notation with big scale is used to generate
+ * huge "plain" serialization. See [core#315] for details.
+ *
+ * @since 2.7.7
+ */
+ protected final static int MAX_BIG_DECIMAL_SCALE = 9999;
+
/*
/**********************************************************
/* Configuration
@@ -426,6 +437,26 @@
return new DefaultPrettyPrinter();
}
+ /**
+ * Helper method used to serialize a {@link java.math.BigDecimal} as a String,
+ * for serialization, taking into account configuration settings
+ *
+ * @since 2.7.7
+ */
+ protected String _asString(BigDecimal value) throws IOException {
+ if (Feature.WRITE_BIGDECIMAL_AS_PLAIN.enabledIn(_features)) {
+ // 24-Aug-2016, tatu: [core#315] prevent possible DoS vector
+ int scale = value.scale();
+ if ((scale < -MAX_BIG_DECIMAL_SCALE) || (scale > MAX_BIG_DECIMAL_SCALE)) {
+ _reportError(String.format(
+"Attempt to write plain `java.math.BigDecimal` (see JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN) with illegal scale (%d): needs to be between [-%d, %d]",
+scale, MAX_BIG_DECIMAL_SCALE, MAX_BIG_DECIMAL_SCALE));
+ }
+ return value.toPlainString();
+ }
+ return value.toString();
+ }
+
/*
/**********************************************************
/* UTF-8 related helper method(s)
diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java
index 15ea15f..80559d4 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java
@@ -938,14 +938,10 @@
_verifyValueWrite(WRITE_NUMBER);
if (value == null) {
_writeNull();
- } else if (_cfgNumbersAsStrings) {
- String raw = Feature.WRITE_BIGDECIMAL_AS_PLAIN.enabledIn(_features)
- ? value.toPlainString() : value.toString();
- _writeQuotedRaw(raw);
- } else if (Feature.WRITE_BIGDECIMAL_AS_PLAIN.enabledIn(_features)) {
- writeRaw(value.toPlainString());
+ } else if (_cfgNumbersAsStrings) {
+ _writeQuotedRaw(_asString(value));
} else {
- writeRaw(value.toString());
+ writeRaw(_asString(value));
}
}
diff --git a/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java
index 74b47c5..180497a 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java
@@ -719,13 +719,10 @@
_verifyValueWrite(WRITE_NUMBER);
if (value == null) {
_writeNull();
- } else if (_cfgNumbersAsStrings) {
- String raw = isEnabled(Feature.WRITE_BIGDECIMAL_AS_PLAIN) ? value.toPlainString() : value.toString();
- _writeQuotedRaw(raw);
- } else if (isEnabled(Feature.WRITE_BIGDECIMAL_AS_PLAIN)) {
- writeRaw(value.toPlainString());
+ } else if (_cfgNumbersAsStrings) {
+ _writeQuotedRaw(_asString(value));
} else {
- writeRaw(value.toString());
+ writeRaw(_asString(value));
}
}
@@ -734,7 +731,7 @@
{
_verifyValueWrite(WRITE_NUMBER);
if (_cfgNumbersAsStrings) {
- _writeQuotedRaw(encodedValue);
+ _writeQuotedRaw(encodedValue);
} else {
writeRaw(encodedValue);
}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/GeneratorFeaturesTest.java b/src/test/java/com/fasterxml/jackson/core/json/GeneratorFeaturesTest.java
index a04e5e6..20bdff4 100644
--- a/src/test/java/com/fasterxml/jackson/core/json/GeneratorFeaturesTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/json/GeneratorFeaturesTest.java
@@ -131,7 +131,62 @@
g.close();
assertEquals(quote("100"), bos.toString("UTF-8"));
}
-
+
+ // [core#315]
+ public void testTooBigBigDecimal() throws Exception
+ {
+ JsonFactory f = new JsonFactory();
+ f.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
+
+ // 24-Aug-2016, tatu: Initial check limits scale to [-9999,+9999]
+ BigDecimal BIG = new BigDecimal("1E+9999");
+ BigDecimal TOO_BIG = new BigDecimal("1E+10000");
+ BigDecimal SMALL = new BigDecimal("1E-9999");
+ BigDecimal TOO_SMALL = new BigDecimal("1E-10000");
+
+ for (boolean useBytes : new boolean[] { false, true } ) {
+ for (boolean asString : new boolean[] { false, true } ) {
+ JsonGenerator g;
+
+ if (useBytes) {
+ g = f.createGenerator(new ByteArrayOutputStream());
+ } else {
+ g = f.createGenerator(new StringWriter());
+ }
+ if (asString) {
+ g.enable(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS);
+ }
+
+ // first, ok cases:
+ g.writeStartArray();
+ g.writeNumber(BIG);
+ g.writeNumber(SMALL);
+ g.writeEndArray();
+ g.close();
+
+ // then invalid
+ for (BigDecimal input : new BigDecimal[] { TOO_BIG, TOO_SMALL }) {
+ if (useBytes) {
+ g = f.createGenerator(new ByteArrayOutputStream());
+ } else {
+ g = f.createGenerator(new StringWriter());
+ }
+ if (asString) {
+ g.enable(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS);
+ }
+ try {
+ g.writeNumber(input);
+ fail("Should not have written without exception: "+input);
+ } catch (JsonGenerationException e) {
+ verifyException(e, "Attempt to write plain `java.math.BigDecimal`");
+ verifyException(e, "illegal scale");
+ }
+ g.close();
+ }
+ }
+ }
+ }
+
private String _writeNumbers(JsonFactory f) throws IOException
{
StringWriter sw = new StringWriter();