Skip to content

Commit d8f82c7

Browse files
committed
Fix #30
1 parent 9b00435 commit d8f82c7

File tree

3 files changed

+188
-33
lines changed

3 files changed

+188
-33
lines changed

cbor/src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParser.java

Lines changed: 113 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
public final class CBORParser extends ParserMinimalBase
2121
{
2222
private final static byte[] NO_BYTES = new byte[0];
23-
23+
2424
/**
2525
* Enumeration that defines all togglable features for CBOR generators.
2626
*/
@@ -66,13 +66,13 @@ private Feature(boolean defaultState) {
6666
// Constants for handling of 16-bit "mini-floats"
6767
private final static double MATH_POW_2_10 = Math.pow(2, 10);
6868
private final static double MATH_POW_2_NEG14 = Math.pow(2, -14);
69-
69+
7070
/*
7171
/**********************************************************
7272
/* Configuration
7373
/**********************************************************
7474
*/
75-
75+
7676
/**
7777
* Codec used for data binding when (if) requested.
7878
*/
@@ -167,7 +167,7 @@ private Feature(boolean defaultState) {
167167
* in the end it'll be converted to 1-based)
168168
*/
169169
protected int _tokenInputCol = 0;
170-
170+
171171
/*
172172
/**********************************************************
173173
/* Parsing state
@@ -179,6 +179,7 @@ private Feature(boolean defaultState) {
179179
* the next token is to be parsed (root, array, object).
180180
*/
181181
protected CBORReadContext _parsingContext;
182+
182183
/**
183184
* Buffer that contains contents of String values, including
184185
* field names if necessary (name split across boundary,
@@ -199,7 +200,7 @@ private Feature(boolean defaultState) {
199200
* representation being available via read context)
200201
*/
201202
protected boolean _nameCopied = false;
202-
203+
203204
/**
204205
* ByteArrayBuilder is needed if 'getBinaryValue' is called. If so,
205206
* we better reuse it for remainder of content.
@@ -247,7 +248,7 @@ private Feature(boolean defaultState) {
247248
* buffer.
248249
*/
249250
protected boolean _bufferRecyclable;
250-
251+
251252
/*
252253
/**********************************************************
253254
/* Additional parsing state
@@ -372,7 +373,7 @@ private Feature(boolean defaultState) {
372373
/* Life-cycle
373374
/**********************************************************
374375
*/
375-
376+
376377
public CBORParser(IOContext ctxt, int parserFeatures, int cborFeatures,
377378
ObjectCodec codec, ByteQuadsCanonicalizer sym,
378379
InputStream in, byte[] inputBuffer, int start, int end,
@@ -423,7 +424,7 @@ public Version version() {
423424
/* Configuration
424425
/**********************************************************
425426
*/
426-
427+
427428
// public JsonParser overrideStdFeatures(int values, int mask)
428429

429430
@Override
@@ -675,11 +676,30 @@ public JsonToken nextToken() throws IOException
675676
_numberInt = _decode16Bits();
676677
break;
677678
case 2:
678-
_numberInt = _decode32Bits();
679+
// 15-Oct-2016, as per [dataformats-binary#30], we got an edge case here
680+
{
681+
int v = _decode32Bits();
682+
if (v >= 0) {
683+
_numberInt = v;
684+
} else {
685+
long l = (long) v;
686+
_numberLong = l & 0xFFFFFFFFL;
687+
_numTypesValid = NR_LONG;
688+
}
689+
}
679690
break;
680691
case 3:
681-
_numberLong = _decode64Bits();
682-
_numTypesValid = NR_LONG;
692+
// 15-Oct-2016, as per [dataformats-binary#30], we got an edge case here
693+
{
694+
long l = _decode64Bits();
695+
if (l >= 0L) {
696+
_numberLong = l;
697+
_numTypesValid = NR_LONG;
698+
} else {
699+
_numberBigInt = _bigPositive(l);
700+
_numTypesValid = NR_BIGINT;
701+
}
702+
}
683703
break;
684704
default:
685705
_invalidToken(ch);
@@ -699,11 +719,29 @@ public JsonToken nextToken() throws IOException
699719
_numberInt = -_decode16Bits() - 1;
700720
break;
701721
case 2:
702-
_numberInt = -_decode32Bits() - 1;
722+
// 15-Oct-2016, as per [dataformats-binary#30], we got an edge case here
723+
{
724+
int v = _decode32Bits();
725+
if (v < 0) {
726+
_numberLong = ((long) v) + -1L;
727+
_numTypesValid = NR_LONG;
728+
} else {
729+
_numberInt = -v - 1;
730+
}
731+
}
703732
break;
704733
case 3:
705-
_numberLong = -_decode64Bits() - 1L;
706-
_numTypesValid = NR_LONG;
734+
// 15-Oct-2016, as per [dataformats-binary#30], we got an edge case here
735+
{
736+
long l = _decode64Bits();
737+
if (l >= 0L) {
738+
_numberLong = -l - 1L;
739+
_numTypesValid = NR_LONG;
740+
} else {
741+
_numberBigInt = _bigNegative(l);
742+
_numTypesValid = NR_BIGINT;
743+
}
744+
}
707745
break;
708746
default:
709747
_invalidToken(ch);
@@ -784,7 +822,7 @@ public JsonToken nextToken() throws IOException
784822
}
785823
return null;
786824
}
787-
825+
788826
protected String _numberToName(int ch, boolean neg) throws IOException
789827
{
790828
final int lowBits = ch & 0x1F;
@@ -1109,11 +1147,30 @@ public String nextTextValue() throws IOException
11091147
_numberInt = _decode16Bits();
11101148
break;
11111149
case 2:
1112-
_numberInt = _decode32Bits();
1150+
// 15-Oct-2016, as per [dataformats-binary#30], we got an edge case here
1151+
{
1152+
int v = _decode32Bits();
1153+
if (v < 0) {
1154+
long l = (long) v;
1155+
_numberLong = l & 0xFFFFFFFFL;
1156+
_numTypesValid = NR_LONG;
1157+
} else{
1158+
_numberInt = v;
1159+
}
1160+
}
11131161
break;
11141162
case 3:
1115-
_numberLong = _decode64Bits();
1116-
_numTypesValid = NR_LONG;
1163+
// 15-Oct-2016, as per [dataformats-binary#30], we got an edge case here
1164+
{
1165+
long l = _decode64Bits();
1166+
if (l >= 0L) {
1167+
_numberLong = l;
1168+
_numTypesValid = NR_LONG;
1169+
} else {
1170+
_numberBigInt = _bigPositive(l);
1171+
_numTypesValid = NR_BIGINT;
1172+
}
1173+
}
11171174
break;
11181175
default:
11191176
_invalidToken(ch);
@@ -1134,11 +1191,29 @@ public String nextTextValue() throws IOException
11341191
_numberInt = -_decode16Bits() - 1;
11351192
break;
11361193
case 2:
1137-
_numberInt = -_decode32Bits() - 1;
1194+
// 15-Oct-2016, as per [dataformats-binary#30], we got an edge case here
1195+
{
1196+
int v = _decode32Bits();
1197+
if (v < 0) {
1198+
_numberLong = ((long) v) + -1L;
1199+
_numTypesValid = NR_LONG;
1200+
} else {
1201+
_numberInt = -v - 1;
1202+
}
1203+
}
11381204
break;
11391205
case 3:
1140-
_numberLong = -_decode64Bits() - 1L;
1141-
_numTypesValid = NR_LONG;
1206+
// 15-Oct-2016, as per [dataformats-binary#30], we got an edge case here
1207+
{
1208+
long l = _decode64Bits();
1209+
if (l >= 0L) {
1210+
_numberLong = l;
1211+
_numTypesValid = NR_LONG;
1212+
} else {
1213+
_numberBigInt = _bigNegative(l);
1214+
_numTypesValid = NR_BIGINT;
1215+
}
1216+
}
11421217
break;
11431218
default:
11441219
_invalidToken(ch);
@@ -3081,5 +3156,21 @@ protected void _reportInvalidOther(int mask, int ptr) throws JsonParseException
30813156
_inputPtr = ptr;
30823157
_reportInvalidOther(mask);
30833158
}
3159+
3160+
/*
3161+
/**********************************************************
3162+
/* Internal methods, other
3163+
/**********************************************************
3164+
*/
3165+
3166+
private final static BigInteger BIT_63 = BigInteger.ONE.shiftLeft(63);
3167+
3168+
private final BigInteger _bigPositive(long l) {
3169+
BigInteger biggie = BigInteger.valueOf((l << 1) >>> 1);
3170+
return biggie.or(BIT_63);
3171+
}
3172+
3173+
private final BigInteger _bigNegative(long l) {
3174+
return BigInteger.valueOf(l).subtract(BigInteger.ONE);
3175+
}
30843176
}
3085-

cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/parse/ParserNumbersTest.java

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
@SuppressWarnings("resource")
2121
public class ParserNumbersTest extends CBORTestBase
2222
{
23+
private final CBORFactory CBOR_F = cborFactory();
24+
2325
public void testIntValues() throws Exception
2426
{
2527
// first, single-byte
@@ -71,13 +73,42 @@ private void _verifyInt(CBORFactory f, int value) throws Exception
7173
p.close();
7274
}
7375

76+
// Special tests for "gray area" for uint32 values that do not fit
77+
// in Java int; from [dataformats-binary#30]
78+
public void testInt32Overflow() throws Exception
79+
{
80+
// feed in max uint32, which is 2x+1 as big as Integer.MAX_VALUE
81+
byte[] input = new byte[] {
82+
(byte) CBORConstants.PREFIX_TYPE_INT_POS + 26, // uint32, that is, 4 more bytes
83+
-1, -1, -1, -1
84+
};
85+
CBORParser p = CBOR_F.createParser(input);
86+
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
87+
// should be exposed as `long` because these uint32 values do not fit in Java `int`
88+
assertEquals(0xFFFFFFFFL, p.getLongValue());
89+
assertEquals(NumberType.LONG, p.getNumberType());
90+
p.close();
91+
92+
// and then the reverse; something that ought to be negative
93+
input = new byte[] {
94+
(byte) CBORConstants.PREFIX_TYPE_INT_NEG + 26, // int32, that is, 4 more bytes
95+
(byte) 0x80, 0, 0, 0
96+
};
97+
p = CBOR_F.createParser(input);
98+
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
99+
// should be exposed as `long` because this value won't fit in `int` either
100+
long exp = -1L + Integer.MIN_VALUE;
101+
assertEquals(exp, p.getLongValue());
102+
assertEquals(NumberType.LONG, p.getNumberType());
103+
p.close();
104+
}
105+
74106
public void testLongValues() throws Exception
75107
{
76-
CBORFactory f = cborFactory();
77-
_verifyLong(f, 1L + Integer.MAX_VALUE);
78-
_verifyLong(f, Long.MIN_VALUE);
79-
_verifyLong(f, Long.MAX_VALUE);
80-
_verifyLong(f, -1L + Integer.MIN_VALUE);
108+
_verifyLong(CBOR_F, 1L + Integer.MAX_VALUE);
109+
_verifyLong(CBOR_F, Long.MIN_VALUE);
110+
_verifyLong(CBOR_F, Long.MAX_VALUE);
111+
_verifyLong(CBOR_F, -1L + Integer.MIN_VALUE);
81112
}
82113

83114
private void _verifyLong(CBORFactory f, long value) throws Exception
@@ -107,14 +138,45 @@ private void _verifyLong(CBORFactory f, long value) throws Exception
107138
p.close();
108139
}
109140

141+
142+
// Special tests for "gray area" for uint64 values that do not fit
143+
// in Java long; from [dataformats-binary#30]
144+
public void testInt64Overflow() throws Exception
145+
{
146+
// feed in max uint64, which is 2x+1 as big as Long.MAX_VALUE
147+
byte[] input = new byte[] {
148+
(byte) CBORConstants.PREFIX_TYPE_INT_POS + 27, // uint64, that is, 8 more bytes
149+
-1, -1, -1, -1, -1, -1, -1, -1
150+
};
151+
CBORParser p = CBOR_F.createParser(input);
152+
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
153+
// should be exposed as BigInteger
154+
assertEquals(NumberType.BIG_INTEGER, p.getNumberType());
155+
BigInteger exp = BigInteger.valueOf(Long.MAX_VALUE).shiftLeft(1)
156+
.add(BigInteger.ONE);
157+
assertEquals(exp, p.getBigIntegerValue());
158+
p.close();
159+
160+
// and then the reverse; something that ought to be negative
161+
input = new byte[] {
162+
(byte) CBORConstants.PREFIX_TYPE_INT_NEG + 27,
163+
(byte) 0x80, 0, 0, 0,
164+
0, 0, 0, 0
165+
};
166+
p = CBOR_F.createParser(input);
167+
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
168+
// should be exposed as `long` because this value won't fit in `int` either
169+
exp = BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.ONE);
170+
assertEquals(exp, p.getBigIntegerValue());
171+
assertEquals(NumberType.BIG_INTEGER, p.getNumberType());
172+
p.close();
173+
}
174+
110175
public void testDoubleValues() throws Exception
111176
{
112-
// first, single-byte
113-
CBORFactory f = cborFactory();
114-
// single byte
115-
_verifyDouble(f, 0.25);
116-
_verifyDouble(f, 20.5);
117-
_verifyDouble(f, -5000.25);
177+
_verifyDouble(CBOR_F, 0.25);
178+
_verifyDouble(CBOR_F, 20.5);
179+
_verifyDouble(CBOR_F, -5000.25);
118180
}
119181

120182
private void _verifyDouble(CBORFactory f, double value) throws Exception

release-notes/VERSION

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Modules:
1313

1414
2.8.5 (not yet released)
1515

16+
#30 (cbor): Overflow when decoding uint32 for Major type 0
17+
(reported by TianlinZhou@github)
1618
#31 (cbor): Exception serializing double[][]
1719

1820
2.8.4 (14-Oct-2016)

0 commit comments

Comments
 (0)