Projects
Eulaceura:Mainline:GA
jackson-databind
_service:obs_scm:CVE-2020-36518.patch
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:obs_scm:CVE-2020-36518.patch of Package jackson-databind
From: Markus Koschany <apo@debian.org> Date: Tue, 15 Nov 2022 12:51:57 +0100 Subject: CVE-2020-36518 Bug-Debian: https://bugs.debian.org/1007109 Origin: https://github.com/FasterXML/jackson-databind/commit/83b928dab9ba6ef81cf48987fcd12071e1ddb0c9 Origin: https://github.com/FasterXML/jackson-databind/commit/fcfc4998ec23f0b1f7f8a9521c2b317b6c25892b --- .../deser/std/UntypedObjectDeserializer.java | 140 +++++++++++---------- .../deser/DeepNestingUntypedDeserTest.java | 70 +++++++++++ 2 files changed, 147 insertions(+), 63 deletions(-) create mode 100644 src/test/java/com/fasterxml/jackson/databind/deser/DeepNestingUntypedDeserTest.java diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java index 67be238..41f6dd9 100644 --- a/src/main/java/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java @@ -220,10 +220,9 @@ public class UntypedObjectDeserializer */ @Override public boolean isCachable() { - /* 26-Mar-2015, tatu: With respect to [databind#735], there are concerns over - * cachability. It seems like we SHOULD be safe here; but just in case there - * are problems with false sharing, this may need to be revisited. - */ + // 26-Mar-2015, tatu: With respect to [databind#735], there are concerns over + // cachability. It seems like we SHOULD be safe here; but just in case there + // are problems with false sharing, this may need to be revisited. return true; } @@ -266,9 +265,8 @@ public class UntypedObjectDeserializer if (_numberDeserializer != null) { return _numberDeserializer.deserialize(p, ctxt); } - /* Caller may want to get all integral values returned as {@link java.math.BigInteger}, - * or {@link java.lang.Long} for consistency - */ + // Caller may want to get all integral values returned as {@link java.math.BigInteger}, + // or {@link java.lang.Long} for consistency if (ctxt.hasSomeOfFeatures(F_MASK_INT_COERCIONS)) { return _coerceIntegral(p, ctxt); } @@ -599,10 +597,9 @@ public class UntypedObjectDeserializer } /* - /********************************************************** - /* Separate "vanilla" implementation for common case of - /* no custom deserializer overrides - /********************************************************** + /********************************************************************** + /* Separate "vanilla" implementation for common case of no deser overrides + /********************************************************************** */ @JacksonStdImpl @@ -611,11 +608,13 @@ public class UntypedObjectDeserializer { private static final long serialVersionUID = 1L; + // Arbitrarily chosen. + // Introduced to resolve CVE-2020-36518 and as a temporary hotfix for #2816 + private static final int MAX_DEPTH = 1000; + public final static Vanilla std = new Vanilla(); - /** - * @since 2.9 - */ + // @since 2.9 protected final boolean _nonMerging; public Vanilla() { this(false); } @@ -639,65 +638,77 @@ public class UntypedObjectDeserializer return _nonMerging ? Boolean.FALSE : null; } - @Override - public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + return deserialize(p, ctxt, 0); + } + + private Object deserialize(JsonParser p, DeserializationContext ctxt, int depth) throws IOException { - switch (p.getCurrentTokenId()) { - case JsonTokenId.ID_START_OBJECT: - { + switch (p.currentTokenId()) { + case JsonTokenId.ID_START_OBJECT: { JsonToken t = p.nextToken(); if (t == JsonToken.END_OBJECT) { - return new LinkedHashMap<String,Object>(2); + return new LinkedHashMap<String, Object>(2); } } - case JsonTokenId.ID_FIELD_NAME: - return mapObject(p, ctxt); - case JsonTokenId.ID_START_ARRAY: - { + case JsonTokenId.ID_FIELD_NAME: + if (depth > MAX_DEPTH) { + throw new JsonParseException(p, "JSON is too deeply nested."); + } + + return mapObject(p, ctxt, depth); + case JsonTokenId.ID_START_ARRAY: { JsonToken t = p.nextToken(); if (t == JsonToken.END_ARRAY) { // and empty one too - if (ctxt.isEnabled(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)) { + if (ctxt.isEnabled( + DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)) { return NO_OBJECTS; } return new ArrayList<Object>(2); } } - if (ctxt.isEnabled(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)) { - return mapArrayToArray(p, ctxt); - } - return mapArray(p, ctxt); - case JsonTokenId.ID_EMBEDDED_OBJECT: - return p.getEmbeddedObject(); - case JsonTokenId.ID_STRING: - return p.getText(); - case JsonTokenId.ID_NUMBER_INT: - if (ctxt.hasSomeOfFeatures(F_MASK_INT_COERCIONS)) { - return _coerceIntegral(p, ctxt); + if (depth > MAX_DEPTH) { + throw new JsonParseException(p, "JSON is too deeply nested."); } - return p.getNumberValue(); // should be optimal, whatever it is - case JsonTokenId.ID_NUMBER_FLOAT: - if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { - return p.getDecimalValue(); + if (ctxt.isEnabled(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)) { + return mapArrayToArray(p, ctxt, depth); } - return p.getNumberValue(); + return mapArray(p, ctxt, depth); + case JsonTokenId.ID_EMBEDDED_OBJECT: + return p.getEmbeddedObject(); + case JsonTokenId.ID_STRING: + return p.getText(); + + case JsonTokenId.ID_NUMBER_INT: + if (ctxt.hasSomeOfFeatures(F_MASK_INT_COERCIONS)) { + return _coerceIntegral(p, ctxt); + } + return p.getNumberValue(); // should be optimal, whatever it is - case JsonTokenId.ID_TRUE: - return Boolean.TRUE; - case JsonTokenId.ID_FALSE: - return Boolean.FALSE; + case JsonTokenId.ID_NUMBER_FLOAT: + if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) { + return p.getDecimalValue(); + } + return p.getNumberValue(); - case JsonTokenId.ID_END_OBJECT: - // 28-Oct-2015, tatu: [databind#989] We may also be given END_OBJECT (similar to FIELD_NAME), - // if caller has advanced to the first token of Object, but for empty Object - return new LinkedHashMap<String,Object>(2); + case JsonTokenId.ID_TRUE: + return Boolean.TRUE; + case JsonTokenId.ID_FALSE: + return Boolean.FALSE; - case JsonTokenId.ID_NULL: // 08-Nov-2016, tatu: yes, occurs - return null; + case JsonTokenId.ID_END_OBJECT: + // 28-Oct-2015, tatu: [databind#989] We may also be given END_OBJECT (similar to FIELD_NAME), + // if caller has advanced to the first token of Object, but for empty Object + return new LinkedHashMap<String, Object>(2); - //case JsonTokenId.ID_END_ARRAY: // invalid - default: + case JsonTokenId.ID_NULL: // 08-Nov-2016, tatu: yes, occurs + return null; + + //case JsonTokenId.ID_END_ARRAY: // invalid + default: } return ctxt.handleUnexpectedToken(Object.class, p); } @@ -806,15 +817,16 @@ public class UntypedObjectDeserializer return deserialize(p, ctxt); } - protected Object mapArray(JsonParser p, DeserializationContext ctxt) throws IOException + protected Object mapArray(JsonParser p, DeserializationContext ctxt, int depth) throws IOException { - Object value = deserialize(p, ctxt); + ++depth; + Object value = deserialize(p, ctxt, depth); if (p.nextToken() == JsonToken.END_ARRAY) { ArrayList<Object> l = new ArrayList<Object>(2); l.add(value); return l; } - Object value2 = deserialize(p, ctxt); + Object value2 = deserialize(p, ctxt, depth); if (p.nextToken() == JsonToken.END_ARRAY) { ArrayList<Object> l = new ArrayList<Object>(2); l.add(value); @@ -828,7 +840,7 @@ public class UntypedObjectDeserializer values[ptr++] = value2; int totalSize = ptr; do { - value = deserialize(p, ctxt); + value = deserialize(p, ctxt, depth); ++totalSize; if (ptr >= values.length) { values = buffer.appendCompletedChunk(values); @@ -845,12 +857,13 @@ public class UntypedObjectDeserializer /** * Method called to map a JSON Array into a Java Object array (Object[]). */ - protected Object[] mapArrayToArray(JsonParser p, DeserializationContext ctxt) throws IOException { + protected Object[] mapArrayToArray(JsonParser p, DeserializationContext ctxt, int depth) throws IOException { + ++depth; ObjectBuffer buffer = ctxt.leaseObjectBuffer(); Object[] values = buffer.resetAndStart(); int ptr = 0; do { - Object value = deserialize(p, ctxt); + Object value = deserialize(p, ctxt, depth); if (ptr >= values.length) { values = buffer.appendCompletedChunk(values); ptr = 0; @@ -863,12 +876,13 @@ public class UntypedObjectDeserializer /** * Method called to map a JSON Object into a Java value. */ - protected Object mapObject(JsonParser p, DeserializationContext ctxt) throws IOException + protected Object mapObject(JsonParser p, DeserializationContext ctxt, int depth) throws IOException { + ++depth; // will point to FIELD_NAME at this point, guaranteed String key1 = p.getText(); p.nextToken(); - Object value1 = deserialize(p, ctxt); + Object value1 = deserialize(p, ctxt, depth); String key2 = p.nextFieldName(); if (key2 == null) { // single entry; but we want modifiable @@ -877,7 +891,7 @@ public class UntypedObjectDeserializer return result; } p.nextToken(); - Object value2 = deserialize(p, ctxt); + Object value2 = deserialize(p, ctxt, depth); String key = p.nextFieldName(); if (key == null) { @@ -892,7 +906,7 @@ public class UntypedObjectDeserializer result.put(key2, value2); do { p.nextToken(); - result.put(key, deserialize(p, ctxt)); + result.put(key, deserialize(p, ctxt, depth)); } while ((key = p.nextFieldName()) != null); return result; } diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/DeepNestingUntypedDeserTest.java b/src/test/java/com/fasterxml/jackson/databind/deser/DeepNestingUntypedDeserTest.java new file mode 100644 index 0000000..ad0194d --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/deser/DeepNestingUntypedDeserTest.java @@ -0,0 +1,70 @@ +package com.fasterxml.jackson.databind.deser; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.BaseMapTest; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.List; +import java.util.Map; + +public class DeepNestingUntypedDeserTest extends BaseMapTest +{ + // 28-Mar-2021, tatu: Currently 3000 fails for untyped/Object, + // 4000 for untyped/Array + private final static int TOO_DEEP_NESTING = 4000; + private final static int NOT_TOO_DEEP = 1000; + + private final ObjectMapper MAPPER = new ObjectMapper(); + + public void testTooDeepUntypedWithArray() throws Exception + { + final String doc = _nestedDoc(TOO_DEEP_NESTING, "[ ", "] "); + try { + MAPPER.readValue(doc, Object.class); + fail("Should have thrown an exception."); + } catch (JsonParseException jpe) { + assertTrue(jpe.getMessage().startsWith("JSON is too deeply nested.")); + } + } + + public void testUntypedWithArray() throws Exception + { + final String doc = _nestedDoc(NOT_TOO_DEEP, "[ ", "] "); + Object ob = MAPPER.readValue(doc, Object.class); + assertTrue(ob instanceof List<?>); + } + + public void testTooDeepUntypedWithObject() throws Exception + { + final String doc = "{"+_nestedDoc(TOO_DEEP_NESTING, "\"x\":{", "} ") + "}"; + try { + MAPPER.readValue(doc, Object.class); + fail("Should have thrown an exception."); + } catch (JsonParseException jpe) { + assertTrue(jpe.getMessage().startsWith("JSON is too deeply nested.")); + } + } + + public void testUntypedWithObject() throws Exception + { + final String doc = "{"+_nestedDoc(NOT_TOO_DEEP, "\"x\":{", "} ") + "}"; + Object ob = MAPPER.readValue(doc, Object.class); + assertTrue(ob instanceof Map<?, ?>); + } + + private String _nestedDoc(int nesting, String open, String close) { + StringBuilder sb = new StringBuilder(nesting * (open.length() + close.length())); + for (int i = 0; i < nesting; ++i) { + sb.append(open); + if ((i & 31) == 0) { + sb.append("\n"); + } + } + for (int i = 0; i < nesting; ++i) { + sb.append(close); + if ((i & 31) == 0) { + sb.append("\n"); + } + } + return sb.toString(); + } +}
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2