Skip to content

Commit

Permalink
Fix #315
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Aug 25, 2016
1 parent 956e0ce commit 9664297
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 20 deletions.
2 changes: 2 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ JSON library.
#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)

Expand Down
31 changes: 31 additions & 0 deletions src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -41,6 +42,16 @@ public abstract class GeneratorBase extends JsonGenerator
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
Expand Down Expand Up @@ -414,6 +425,26 @@ protected PrettyPrinter _constructDefaultPrettyPrinter() {
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -906,14 +906,10 @@ public void writeNumber(BigDecimal value) throws IOException
_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));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -683,13 +683,10 @@ public void writeNumber(BigDecimal value) throws IOException
_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));
}
}

Expand All @@ -698,7 +695,7 @@ public void writeNumber(String encodedValue) throws IOException
{
_verifyValueWrite(WRITE_NUMBER);
if (_cfgNumbersAsStrings) {
_writeQuotedRaw(encodedValue);
_writeQuotedRaw(encodedValue);
} else {
writeRaw(encodedValue);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ public class TestJsonGeneratorFeatures

public void testConfigDefaults() throws IOException
{
JsonGenerator jg = JSON_F.createGenerator(new StringWriter());
assertFalse(jg.isEnabled(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS));
assertFalse(jg.isEnabled(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN));
jg.close();
JsonGenerator g = JSON_F.createGenerator(new StringWriter());
assertFalse(g.isEnabled(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS));
assertFalse(g.isEnabled(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN));
g.close();
}

public void testFieldNameQuoting() throws IOException
Expand Down Expand Up @@ -65,7 +65,7 @@ public void testNumbersAsJSONStrings() throws IOException
_writeNumbers(jf));
}

// [Issue#85]
// [core#85]
public void testBigDecimalAsPlain() throws IOException
{
JsonFactory jf = new JsonFactory();
Expand All @@ -85,7 +85,7 @@ public void testBigDecimalAsPlain() throws IOException
assertEquals("100", sw.toString());
}

// [issue#184]
// [core#184]
public void testBigDecimalAsPlainString() throws Exception
{
JsonFactory jf = new JsonFactory();
Expand All @@ -106,6 +106,61 @@ public void testBigDecimalAsPlainString() throws Exception
jg.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 jf) throws IOException
{
Expand Down

0 comments on commit 9664297

Please sign in to comment.