Skip to content

Commit 14c23e2

Browse files
authored
fix for issue found by OSS Fuzz (#810)
1 parent e95fee5 commit 14c23e2

File tree

7 files changed

+141
-64
lines changed

7 files changed

+141
-64
lines changed

src/main/java/com/fasterxml/jackson/core/io/doubleparser/AbstractFloatingPointBitsFromCharArray.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ private long parseHexFloatLiteral(
338338
// ------------------------
339339
index = skipWhitespace(str, index, endIndex);
340340
if (illegal || index < endIndex
341-
|| digitCount == 0 && str[virtualIndexOfPoint] != '.'
341+
|| digitCount == 0
342342
|| !hasExponent) {
343343
return PARSE_ERROR;
344344
}

src/main/java/com/fasterxml/jackson/core/io/doubleparser/AbstractFloatingPointBitsFromCharSequence.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ private long parseHexFloatLiteral(
328328
// ------------------------
329329
index = skipWhitespace(str, index, endIndex);
330330
if (illegal || index < endIndex
331-
|| digitCount == 0 && str.charAt(virtualIndexOfPoint) != '.'
331+
|| digitCount == 0
332332
|| !hasExponent) {
333333
return PARSE_ERROR;
334334
}

src/main/java/com/fasterxml/jackson/core/io/doubleparser/FastDoubleSwar.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,9 @@ public static int tryToParseEightDigitsUtf8(long chunk) {
132132
}
133133

134134
// The last 2 multiplications are independent of each other.
135-
val = (val * (1 + (10 << 8))) >>> 8;
136-
val = (((val & 0xff_000000ffL) * (100 + (100_0000L << 32)))
137-
+ (((val >>> 16) & 0xff_000000ffL) * (1 + (1_0000L << 32)))) >>> 32;
135+
val = val * (1 + (10 << 8)) >>> 8;
136+
val = (val & 0xff_000000ffL) * (100 + (100_0000L << 32))
137+
+ (val >>> 16 & 0xff_000000ffL) * (1 + (1_0000L << 32)) >>> 32;
138138
return (int) val;
139139
}
140140

@@ -326,4 +326,4 @@ public static long readLongFromByteArrayBigEndian(byte[] a, int offset) {
326326
| ((a[offset + 6] & 0xffL) << 8)
327327
| (a[offset + 7] & 0xffL);
328328
}
329-
}
329+
}

src/main/java/com/fasterxml/jackson/core/io/doubleparser/package-info.java

-8
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,6 @@
4242
* </dl>
4343
*
4444
* <dl>
45-
* <dt><i>HexSignificand:</i>
46-
* <dd><i>HexNumeral</i>
47-
* <dd><i>HexNumeral</i> {@code .}
48-
* <dd>{@code 0x} <i>[HexDigits]</i> {@code .} <i>HexDigits</i>
49-
* <dd>{@code 0X} <i>[HexDigits]</i> {@code .} <i>HexDigits</i>
50-
* </dl>
51-
*
52-
* <dl>
5345
* <dt><i>BinaryExponent:</i>
5446
* <dd><i>BinaryExponentIndicator SignedInteger</i>
5547
* </dl>

src/test/java/com/fasterxml/jackson/core/io/doubleparser/AbstractDoubleHandPickedTest.java

+4
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ List<DynamicNode> dynamicTestsIllegalInputs() {
8989
dynamicTest("0x123.456789abcde", () -> testIllegalInput("0x123.456789abcde")),
9090
dynamicTest(".", () -> testIllegalInput(".")),
9191
dynamicTest("0x.", () -> testIllegalInput("0x.")),
92+
dynamicTest("0x", () -> testIllegalInput("0x")),
93+
dynamicTest("0x1", () -> testIllegalInput("0x1")),
94+
dynamicTest("0xp1", () -> testIllegalInput("0xp1")),
95+
dynamicTest("0x1.", () -> testIllegalInput("0x1.")),
9296
dynamicTest(".e2", () -> testIllegalInput(".e2"))
9397
);
9498
}

src/test/java/com/fasterxml/jackson/core/io/doubleparser/AbstractLexicallyGeneratedTest.java

+68-19
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
import org.junit.jupiter.api.DynamicTest;
1515
import org.junit.jupiter.api.TestFactory;
1616

17-
import java.util.ArrayList;
18-
import java.util.List;
1917
import java.util.Random;
2018
import java.util.stream.IntStream;
2119
import java.util.stream.Stream;
@@ -46,15 +44,15 @@ abstract class AbstractLexicallyGeneratedTest {
4644
* (Make sure to take a note of the seed value if
4745
* tests failed.)
4846
*/
49-
public static final long SEED = 0;//System.nanoTime();
47+
public static final long SEED = System.nanoTime();
5048

5149
@TestFactory
5250
@Disabled
53-
Stream<DynamicTest> dynamicTestsRandomStringFrom10SyntaxRuleWithoutWhitespace() {
51+
Stream<DynamicTest> dynamicTestsRandomStringFrom1SyntaxRuleWithoutWhitespace() {
5452
Random rng = new Random(SEED);
5553
LexicalGenerator gen = new LexicalGenerator(false, true);
56-
return IntStream.range(1, 10_000).mapToObj(i -> {
57-
String str = gen.produceRandomInputStringFromLexicalRuleWithoutWhitespace(10, rng);
54+
return IntStream.range(0, 100).mapToObj(i -> {
55+
String str = gen.produceRandomInputStringFromLexicalRuleWithoutWhitespace(1, rng);
5856
return dynamicTest(i + ": " + str,
5957
() -> testAgainstJdk(str));
6058
}
@@ -63,11 +61,24 @@ Stream<DynamicTest> dynamicTestsRandomStringFrom10SyntaxRuleWithoutWhitespace()
6361

6462
@TestFactory
6563
@Disabled
66-
Stream<DynamicTest> dynamicTestsRandomStringFrom1SyntaxRuleWithoutWhitespace() {
64+
Stream<DynamicTest> dynamicTestsRandomStringFrom2SyntaxRuleWithoutWhitespace() {
6765
Random rng = new Random(SEED);
6866
LexicalGenerator gen = new LexicalGenerator(false, true);
69-
return IntStream.range(1, 10_000).mapToObj(i -> {
70-
String str = gen.produceRandomInputStringFromLexicalRuleWithoutWhitespace(1, rng);
67+
return IntStream.range(0, 10_000).mapToObj(i -> {
68+
String str = gen.produceRandomInputStringFromLexicalRuleWithoutWhitespace(2, rng);
69+
return dynamicTest(i + ": " + str,
70+
() -> testAgainstJdk(str));
71+
}
72+
);
73+
}
74+
75+
@TestFactory
76+
@Disabled
77+
Stream<DynamicTest> dynamicTestsRandomStringFrom3SyntaxRuleWithoutWhitespace() {
78+
Random rng = new Random(SEED);
79+
LexicalGenerator gen = new LexicalGenerator(false, true);
80+
return IntStream.range(0, 10_000).mapToObj(i -> {
81+
String str = gen.produceRandomInputStringFromLexicalRuleWithoutWhitespace(3, rng);
7182
return dynamicTest(i + ": " + str,
7283
() -> testAgainstJdk(str));
7384
}
@@ -76,11 +87,24 @@ Stream<DynamicTest> dynamicTestsRandomStringFrom1SyntaxRuleWithoutWhitespace() {
7687

7788
@TestFactory
7889
@Disabled
79-
Stream<DynamicTest> dynamicTestsRandomStringFrom40SyntaxRuleWithoutWhitespace() {
90+
Stream<DynamicTest> dynamicTestsRandomStringFrom4SyntaxRuleWithoutWhitespace() {
8091
Random rng = new Random(SEED);
8192
LexicalGenerator gen = new LexicalGenerator(false, true);
82-
return IntStream.range(1, 10_000).mapToObj(i -> {
83-
String str = gen.produceRandomInputStringFromLexicalRuleWithoutWhitespace(40, rng);
93+
return IntStream.range(0, 10_000).mapToObj(i -> {
94+
String str = gen.produceRandomInputStringFromLexicalRuleWithoutWhitespace(4, rng);
95+
return dynamicTest(i + ": " + str,
96+
() -> testAgainstJdk(str));
97+
}
98+
);
99+
}
100+
101+
@TestFactory
102+
@Disabled
103+
Stream<DynamicTest> dynamicTestsRandomStringFrom10SyntaxRuleWithoutWhitespace() {
104+
Random rng = new Random(SEED);
105+
LexicalGenerator gen = new LexicalGenerator(false, true);
106+
return IntStream.range(0, 10_000).mapToObj(i -> {
107+
String str = gen.produceRandomInputStringFromLexicalRuleWithoutWhitespace(10, rng);
84108
return dynamicTest(i + ": " + str,
85109
() -> testAgainstJdk(str));
86110
}
@@ -102,13 +126,38 @@ Stream<DynamicTest> dynamicTestsRandomStringsOfIncreasingLengthWithWhitespace()
102126

103127
@TestFactory
104128
@Disabled
105-
List<DynamicNode> dynamicTestsAllSingleCharacterInputs() {
106-
ArrayList<DynamicNode> list = new ArrayList<>();
107-
for (int codePoint = 0; codePoint <= Character.MAX_VALUE; codePoint++) {
108-
String str = "" + (char) codePoint;
109-
list.add(dynamicTest("0x" + Integer.toHexString(codePoint), () -> testAgainstJdk(str)));
110-
}
111-
return list;
129+
Stream<DynamicNode> dynamicTestsAsciiCharacterInputsUpTo4Characters() {
130+
int maxLength = 4;
131+
Random rng = new Random();
132+
return IntStream.range(0, 10_000).mapToObj(i -> {
133+
char[] ch = new char[4];
134+
int n = rng.nextInt(maxLength) + 1;
135+
for (int j = 0; j < n; j++) {
136+
ch[j] = nextAsciiChar(rng);
137+
}
138+
StringBuilder str = new StringBuilder();
139+
StringBuilder title = new StringBuilder(Integer.toString(n));
140+
title.append(':');
141+
for (int j = 0; j < 4; j++) {
142+
char c = ch[j];
143+
if (c >= ' ') {
144+
if (Character.isISOControl(c) || Character.isWhitespace(c)) {
145+
title.append("&#x").append(Integer.toHexString(c)).append(';');
146+
str.append(c);
147+
} else {
148+
title.append((char) c);
149+
str.append(c);
150+
}
151+
}
152+
}
153+
return dynamicTest(title.toString(), () -> testAgainstJdk(str.toString()));
154+
});
155+
}
156+
157+
private static char nextAsciiChar(Random rng) {
158+
//U+0020 SPACE
159+
//U+007F DELETE
160+
return (char) (rng.nextInt(0x7f - 0x20) + 0x20);
112161
}
113162

114163
/**

src/test/java/com/fasterxml/jackson/core/io/doubleparser/LexicalGenerator.java

+63-31
Original file line numberDiff line numberDiff line change
@@ -132,40 +132,46 @@ private int produceRandomDecimalFloatingPointLiteral(int remaining, Random rng,
132132
remaining = produceRandomDigits(remaining, rng, buf);
133133
buf.append('.');
134134
remaining--;
135-
if (rng.nextBoolean()) {
135+
if (remaining > 0 && rng.nextBoolean()) {
136136
remaining = produceRandomDigits(remaining, rng, buf);
137137
}
138-
if (rng.nextBoolean()) {
138+
if (remaining > 0 && rng.nextBoolean()) {
139139
remaining = produceRandomExponentPart(remaining, rng, buf);
140140
}
141-
if (rng.nextBoolean()) {
141+
if (remaining > 0 && rng.nextBoolean()) {
142142
remaining = produceRandomFloatTypeSuffix(remaining, rng, buf);
143143
}
144144
break;
145145
case 1:
146146
buf.append('.');
147147
remaining--;
148-
remaining = produceRandomDigits(remaining, rng, buf);
149-
if (rng.nextBoolean()) {
148+
if (remaining > 0) {
149+
remaining = produceRandomDigits(remaining, rng, buf);
150+
}
151+
if (remaining > 0 && rng.nextBoolean()) {
150152
remaining = produceRandomExponentPart(remaining, rng, buf);
151153
}
152-
if (rng.nextBoolean()) {
154+
if (remaining > 0 && rng.nextBoolean()) {
153155
remaining = produceRandomFloatTypeSuffix(remaining, rng, buf);
154156
}
155157
break;
156158
case 2:
157159
remaining = produceRandomDigits(remaining, rng, buf);
158-
remaining = produceRandomExponentPart(remaining, rng, buf);
159-
if (rng.nextBoolean()) {
160+
if (remaining > 0) {
161+
remaining = produceRandomExponentPart(remaining, rng, buf);
162+
}
163+
if (remaining > 0 && rng.nextBoolean()) {
160164
remaining = produceRandomFloatTypeSuffix(remaining, rng, buf);
161165
}
162166
break;
163167
case 3:
164168
remaining = produceRandomDigits(remaining, rng, buf);
165-
if (rng.nextBoolean()) {
169+
if (remaining > 0 && rng.nextBoolean()) {
166170
remaining = produceRandomExponentPart(remaining, rng, buf);
167171
}
168-
remaining = produceRandomFloatTypeSuffix(remaining, rng, buf);
172+
if (remaining > 0) {
173+
remaining = produceRandomFloatTypeSuffix(remaining, rng, buf);
174+
}
169175
break;
170176
}
171177
return remaining;
@@ -205,10 +211,12 @@ private int produceRandomDigits(int remaining, Random rng, StringBuilder buf) {
205211
break;
206212
case 1:
207213
remaining = produceRandomDigit(remaining, rng, buf);
208-
if (rng.nextBoolean()) {
214+
if (remaining > 0 && rng.nextBoolean()) {
209215
remaining = produceRandomDigitsAndUnderscores(remaining, rng, buf);
210216
}
211-
remaining = produceRandomDigit(remaining, rng, buf);
217+
if (remaining > 0) {
218+
remaining = produceRandomDigit(remaining, rng, buf);
219+
}
212220
break;
213221
}
214222

@@ -226,17 +234,21 @@ private int produceRandomDigitsAndUnderscores(int remaining, Random rng, StringB
226234
switch (rng.nextInt(2)) {
227235
case 0:
228236
remaining = produceRandomDigitOrUnderscore(remaining, rng, buf);
229-
int todo = rng.nextInt(Math.max(remaining, 1));
230-
for (int i = 0; i < todo; i++) {
231-
remaining = produceRandomDigitOrUnderscore(remaining, rng, buf);
237+
if (remaining > 0) {
238+
int todo = rng.nextInt(Math.max(remaining, 1));
239+
for (int i = 0; i < todo; i++) {
240+
remaining = produceRandomDigitOrUnderscore(remaining, rng, buf);
241+
}
232242
}
233243
break;
234244
case 1:
235245
remaining = produceRandomDigit(remaining, rng, buf);
236-
if (rng.nextBoolean()) {
246+
if (remaining > 0 && rng.nextBoolean()) {
237247
remaining = produceRandomDigitsAndUnderscores(remaining, rng, buf);
238248
}
239-
remaining = produceRandomDigit(remaining, rng, buf);
249+
if (remaining > 0) {
250+
remaining = produceRandomDigit(remaining, rng, buf);
251+
}
240252
break;
241253
}
242254
return remaining;
@@ -289,22 +301,28 @@ private int produceRandomFloatTypeSuffix(int remaining, Random rng, StringBuilde
289301
private int produceRandomFloatValue(int remaining, Random rng, StringBuilder buf) {
290302
switch (rng.nextInt(4)) {
291303
case 0:
292-
if (rng.nextBoolean()) {
304+
if (remaining > 0 && rng.nextBoolean()) {
293305
remaining = produceRandomSign(remaining, rng, buf);
294306
}
295-
remaining = produceRandomNaNOrInfinity(remaining, rng, buf);
307+
if (remaining > 0 && remaining > 0) {
308+
remaining = produceRandomNaNOrInfinity(remaining, rng, buf);
309+
}
296310
break;
297311
case 1:
298312
if (rng.nextBoolean()) {
299313
remaining = produceRandomSign(remaining, rng, buf);
300314
}
301-
remaining = produceRandomDecimalFloatingPointLiteral(remaining, rng, buf);
315+
if (remaining > 0) {
316+
remaining = produceRandomDecimalFloatingPointLiteral(remaining, rng, buf);
317+
}
302318
break;
303319
case 2:
304320
if (rng.nextBoolean()) {
305321
remaining = produceRandomSign(remaining, rng, buf);
306322
}
307-
remaining = produceRandomHexFloatingPointLiteral(remaining, rng, buf);
323+
if (remaining > 0) {
324+
remaining = produceRandomHexFloatingPointLiteral(remaining, rng, buf);
325+
}
308326
break;
309327
case 3:
310328
remaining = produceRandomSignedInteger(remaining, rng, buf);
@@ -348,15 +366,21 @@ private int produceRandomHexDigitsAndUnderscores(int remaining, Random rng, Stri
348366

349367
private int produceRandomHexFloatingPointLiteral(int remaining, Random rng, StringBuilder buf) {
350368
remaining = produceRandomHexSignificand(remaining, rng, buf);
351-
remaining = produceRandomBinaryExponent(remaining, rng, buf);
352-
remaining = produceRandomFloatTypeSuffix(remaining, rng, buf);
369+
if (remaining > 0) {
370+
remaining = produceRandomBinaryExponent(remaining, rng, buf);
371+
}
372+
if (remaining > 0) {
373+
remaining = produceRandomFloatTypeSuffix(remaining, rng, buf);
374+
}
353375
return remaining;
354376
}
355377

356378
private int produceRandomHexNumeral(int remaining, Random rng, StringBuilder buf) {
357379
buf.append(rng.nextBoolean() ? "0x" : "0X");
358380
remaining--;
359-
remaining = produceRandomHexDigits(remaining, rng, buf);
381+
if (remaining > 0) {
382+
remaining = produceRandomHexDigits(remaining, rng, buf);
383+
}
360384
return remaining;
361385
}
362386

@@ -382,12 +406,14 @@ private int produceRandomHexSignificand(int remaining, Random rng, StringBuilder
382406
case 2:
383407
buf.append(rng.nextBoolean() ? "0x" : "0X");
384408
remaining--;
385-
if (rng.nextBoolean()) {
409+
if (remaining > 0 && rng.nextBoolean()) {
410+
remaining = produceRandomHexDigits(remaining, rng, buf);
411+
}
412+
if (remaining > 0 && rng.nextBoolean()) {
413+
buf.append('.');
414+
remaining--;
386415
remaining = produceRandomHexDigits(remaining, rng, buf);
387416
}
388-
buf.append('.');
389-
remaining--;
390-
remaining = produceRandomHexDigits(remaining, rng, buf);
391417
break;
392418
}
393419
return remaining;
@@ -403,8 +429,12 @@ private int produceRandomHexSignificand(int remaining, Random rng, StringBuilder
403429
public String produceRandomInputStringFromLexicalRuleWithWhitespace(int remaining, Random rng) {
404430
StringBuilder buf = new StringBuilder();
405431
remaining = produceRandomWhitespaces(remaining, rng, buf);
406-
remaining = produceRandomFloatValue(remaining, rng, buf);
407-
remaining = produceRandomWhitespaces(remaining, rng, buf);
432+
if (remaining > 0) {
433+
remaining = produceRandomFloatValue(remaining, rng, buf);
434+
}
435+
if (remaining > 0) {
436+
remaining = produceRandomWhitespaces(remaining, rng, buf);
437+
}
408438
return buf.toString();
409439
}
410440

@@ -452,7 +482,9 @@ private int produceRandomSignedInteger(int remaining, Random rng, StringBuilder
452482
if (rng.nextBoolean()) {
453483
remaining = produceRandomSign(remaining, rng, buf);
454484
}
455-
remaining = produceRandomDigits(remaining, rng, buf);
485+
if (remaining > 0) {
486+
remaining = produceRandomDigits(remaining, rng, buf);
487+
}
456488
return remaining;
457489
}
458490

0 commit comments

Comments
 (0)