@@ -661,6 +661,10 @@ public static class Vanilla
661
661
{
662
662
private static final long serialVersionUID = 1L ;
663
663
664
+ // Arbitrarily chosen.
665
+ // Introduced to resolve CVE-2020-36518 and as a temporary hotfix for #2816
666
+ private static final int MAX_DEPTH = 1000 ;
667
+
664
668
public final static Vanilla std = new Vanilla ();
665
669
666
670
// @since 2.9
@@ -693,64 +697,76 @@ public Boolean supportsUpdate(DeserializationConfig config) {
693
697
}
694
698
695
699
@ Override
696
- public Object deserialize (JsonParser p , DeserializationContext ctxt ) throws IOException
700
+ public Object deserialize (JsonParser p , DeserializationContext ctxt ) throws IOException {
701
+ return deserialize (p , ctxt , 0 );
702
+ }
703
+
704
+ private Object deserialize (JsonParser p , DeserializationContext ctxt , int depth ) throws IOException
697
705
{
698
706
switch (p .currentTokenId ()) {
699
- case JsonTokenId .ID_START_OBJECT :
700
- {
707
+ case JsonTokenId .ID_START_OBJECT : {
701
708
JsonToken t = p .nextToken ();
702
709
if (t == JsonToken .END_OBJECT ) {
703
- return new LinkedHashMap <String ,Object >(2 );
710
+ return new LinkedHashMap <String , Object >(2 );
704
711
}
705
712
}
706
- case JsonTokenId .ID_FIELD_NAME :
707
- return mapObject (p , ctxt );
708
- case JsonTokenId .ID_START_ARRAY :
709
- {
713
+ case JsonTokenId .ID_FIELD_NAME :
714
+ if (depth > MAX_DEPTH ) {
715
+ throw new JsonParseException (p , "JSON is too deeply nested." );
716
+ }
717
+
718
+ return mapObject (p , ctxt , depth );
719
+ case JsonTokenId .ID_START_ARRAY : {
710
720
JsonToken t = p .nextToken ();
711
721
if (t == JsonToken .END_ARRAY ) { // and empty one too
712
- if (ctxt .isEnabled (DeserializationFeature .USE_JAVA_ARRAY_FOR_JSON_ARRAY )) {
722
+ if (ctxt .isEnabled (
723
+ DeserializationFeature .USE_JAVA_ARRAY_FOR_JSON_ARRAY )) {
713
724
return NO_OBJECTS ;
714
725
}
715
726
return new ArrayList <Object >(2 );
716
727
}
717
728
}
718
- if (ctxt .isEnabled (DeserializationFeature .USE_JAVA_ARRAY_FOR_JSON_ARRAY )) {
719
- return mapArrayToArray (p , ctxt );
720
- }
721
- return mapArray (p , ctxt );
722
- case JsonTokenId .ID_EMBEDDED_OBJECT :
723
- return p .getEmbeddedObject ();
724
- case JsonTokenId .ID_STRING :
725
- return p .getText ();
726
729
727
- case JsonTokenId .ID_NUMBER_INT :
728
- if (ctxt .hasSomeOfFeatures (F_MASK_INT_COERCIONS )) {
729
- return _coerceIntegral (p , ctxt );
730
+ if (depth > MAX_DEPTH ) {
731
+ throw new JsonParseException (p , "JSON is too deeply nested." );
730
732
}
731
- return p .getNumberValue (); // should be optimal, whatever it is
732
733
733
- case JsonTokenId .ID_NUMBER_FLOAT :
734
- if (ctxt .isEnabled (DeserializationFeature .USE_BIG_DECIMAL_FOR_FLOATS )) {
735
- return p .getDecimalValue ();
734
+ if (ctxt .isEnabled (DeserializationFeature .USE_JAVA_ARRAY_FOR_JSON_ARRAY )) {
735
+ return mapArrayToArray (p , ctxt , depth );
736
736
}
737
- return p .getNumberValue ();
737
+ return mapArray (p , ctxt , depth );
738
+ case JsonTokenId .ID_EMBEDDED_OBJECT :
739
+ return p .getEmbeddedObject ();
740
+ case JsonTokenId .ID_STRING :
741
+ return p .getText ();
742
+
743
+ case JsonTokenId .ID_NUMBER_INT :
744
+ if (ctxt .hasSomeOfFeatures (F_MASK_INT_COERCIONS )) {
745
+ return _coerceIntegral (p , ctxt );
746
+ }
747
+ return p .getNumberValue (); // should be optimal, whatever it is
738
748
739
- case JsonTokenId .ID_TRUE :
740
- return Boolean .TRUE ;
741
- case JsonTokenId .ID_FALSE :
742
- return Boolean .FALSE ;
749
+ case JsonTokenId .ID_NUMBER_FLOAT :
750
+ if (ctxt .isEnabled (DeserializationFeature .USE_BIG_DECIMAL_FOR_FLOATS )) {
751
+ return p .getDecimalValue ();
752
+ }
753
+ return p .getNumberValue ();
743
754
744
- case JsonTokenId .ID_END_OBJECT :
745
- // 28-Oct-2015, tatu: [databind#989] We may also be given END_OBJECT (similar to FIELD_NAME),
746
- // if caller has advanced to the first token of Object, but for empty Object
747
- return new LinkedHashMap < String , Object >( 2 ) ;
755
+ case JsonTokenId .ID_TRUE :
756
+ return Boolean . TRUE ;
757
+ case JsonTokenId . ID_FALSE :
758
+ return Boolean . FALSE ;
748
759
749
- case JsonTokenId .ID_NULL : // 08-Nov-2016, tatu: yes, occurs
750
- return null ;
760
+ case JsonTokenId .ID_END_OBJECT :
761
+ // 28-Oct-2015, tatu: [databind#989] We may also be given END_OBJECT (similar to FIELD_NAME),
762
+ // if caller has advanced to the first token of Object, but for empty Object
763
+ return new LinkedHashMap <String , Object >(2 );
751
764
752
- //case JsonTokenId.ID_END_ARRAY: // invalid
753
- default :
765
+ case JsonTokenId .ID_NULL : // 08-Nov-2016, tatu: yes, occurs
766
+ return null ;
767
+
768
+ //case JsonTokenId.ID_END_ARRAY: // invalid
769
+ default :
754
770
}
755
771
return ctxt .handleUnexpectedToken (Object .class , p );
756
772
}
@@ -858,15 +874,15 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt, Object into
858
874
return deserialize (p , ctxt );
859
875
}
860
876
861
- protected Object mapArray (JsonParser p , DeserializationContext ctxt ) throws IOException
877
+ protected Object mapArray (JsonParser p , DeserializationContext ctxt , int depth ) throws IOException
862
878
{
863
- Object value = deserialize (p , ctxt );
879
+ Object value = deserialize (p , ctxt , depth + 1 );
864
880
if (p .nextToken () == JsonToken .END_ARRAY ) {
865
881
ArrayList <Object > l = new ArrayList <Object >(2 );
866
882
l .add (value );
867
883
return l ;
868
884
}
869
- Object value2 = deserialize (p , ctxt );
885
+ Object value2 = deserialize (p , ctxt , depth + 1 );
870
886
if (p .nextToken () == JsonToken .END_ARRAY ) {
871
887
ArrayList <Object > l = new ArrayList <Object >(2 );
872
888
l .add (value );
@@ -880,7 +896,7 @@ protected Object mapArray(JsonParser p, DeserializationContext ctxt) throws IOEx
880
896
values [ptr ++] = value2 ;
881
897
int totalSize = ptr ;
882
898
do {
883
- value = deserialize (p , ctxt );
899
+ value = deserialize (p , ctxt , depth + 1 );
884
900
++totalSize ;
885
901
if (ptr >= values .length ) {
886
902
values = buffer .appendCompletedChunk (values );
@@ -897,12 +913,12 @@ protected Object mapArray(JsonParser p, DeserializationContext ctxt) throws IOEx
897
913
/**
898
914
* Method called to map a JSON Array into a Java Object array (Object[]).
899
915
*/
900
- protected Object [] mapArrayToArray (JsonParser p , DeserializationContext ctxt ) throws IOException {
916
+ protected Object [] mapArrayToArray (JsonParser p , DeserializationContext ctxt , int depth ) throws IOException {
901
917
ObjectBuffer buffer = ctxt .leaseObjectBuffer ();
902
918
Object [] values = buffer .resetAndStart ();
903
919
int ptr = 0 ;
904
920
do {
905
- Object value = deserialize (p , ctxt );
921
+ Object value = deserialize (p , ctxt , depth + 1 );
906
922
if (ptr >= values .length ) {
907
923
values = buffer .appendCompletedChunk (values );
908
924
ptr = 0 ;
@@ -915,13 +931,13 @@ protected Object[] mapArrayToArray(JsonParser p, DeserializationContext ctxt) th
915
931
/**
916
932
* Method called to map a JSON Object into a Java value.
917
933
*/
918
- protected Object mapObject (JsonParser p , DeserializationContext ctxt ) throws IOException
934
+ protected Object mapObject (JsonParser p , DeserializationContext ctxt , int depth ) throws IOException
919
935
{
920
936
// will point to FIELD_NAME at this point, guaranteed
921
937
// 19-Jul-2021, tatu: Was incorrectly using "getText()" before 2.13, fixed for 2.13.0
922
938
String key1 = p .currentName ();
923
939
p .nextToken ();
924
- Object value1 = deserialize (p , ctxt );
940
+ Object value1 = deserialize (p , ctxt , depth + 1 );
925
941
926
942
String key2 = p .nextFieldName ();
927
943
if (key2 == null ) { // single entry; but we want modifiable
@@ -930,7 +946,7 @@ protected Object mapObject(JsonParser p, DeserializationContext ctxt) throws IOE
930
946
return result ;
931
947
}
932
948
p .nextToken ();
933
- Object value2 = deserialize (p , ctxt );
949
+ Object value2 = deserialize (p , ctxt , depth + 1 );
934
950
935
951
String key = p .nextFieldName ();
936
952
if (key == null ) {
@@ -952,7 +968,7 @@ protected Object mapObject(JsonParser p, DeserializationContext ctxt) throws IOE
952
968
953
969
do {
954
970
p .nextToken ();
955
- final Object newValue = deserialize (p , ctxt );
971
+ final Object newValue = deserialize (p , ctxt , depth + 1 );
956
972
final Object oldValue = result .put (key , newValue );
957
973
if (oldValue != null ) {
958
974
return _mapObjectWithDups (p , ctxt , result , key , oldValue , newValue ,
0 commit comments