Skip to content

Commit a8c5a62

Browse files
committed
Merge branch '2.x' into 3.1
2 parents ae17737 + 5994ac5 commit a8c5a62

5 files changed

Lines changed: 116 additions & 3 deletions

File tree

cbor/src/main/java/tools/jackson/dataformat/cbor/CBORParser.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2583,7 +2583,8 @@ private final String _finishLongText(int len) throws JacksonException
25832583
}
25842584

25852585
/**
2586-
* Consumes as many ascii chars as possible in a tight loop. Returns the amount of bytes remaining.
2586+
* Consumes as many ascii chars as possible in a tight loop.
2587+
* Returns the amount of bytes remaining.
25872588
*/
25882589
private final int _finishLongTextAscii(int len) throws JacksonException
25892590
{
@@ -2609,7 +2610,10 @@ private final int _finishLongTextAscii(int len) throws JacksonException
26092610
--outPtr;
26102611
_inputPtr = inPtr - 1;
26112612
_textBuffer.setCurrentLength(outPtr);
2612-
return len - outPtr;
2613+
// `len` was already decremented for all previous iterations; subtract only
2614+
// the bytes consumed in THIS iteration (= _inputPtr), since
2615+
// _tryToLoadToHaveAtLeast always resets _inputPtr to 0 before the inner loop.
2616+
return len - _inputPtr;
26132617
}
26142618
_inputPtr = inPtr;
26152619
if (outPtr >= outBuf.length) {
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package tools.jackson.dataformat.cbor.parse;
2+
3+
import java.io.ByteArrayInputStream;
4+
import java.io.ByteArrayOutputStream;
5+
import java.util.Arrays;
6+
7+
import org.junit.jupiter.api.Test;
8+
9+
import tools.jackson.dataformat.cbor.CBORMapper;
10+
11+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
12+
13+
// For [dataformats-binary#686]
14+
public class CBORLongAsciiRead686Test
15+
{
16+
// TextBuffer segment sizes grow 1.5× per flip, starting at 200 chars (MIN=500):
17+
// S0=200, S1=500, S2=750, S3=1125, S4=1687, S5=2530, S6=3795, S7=5692, S8=8538
18+
// After 8 flips the segment is 8538 chars — the first one larger than the 8000-byte I/O buffer.
19+
// Total chars consumed reaching S8: 200+500+750+1125+1687+2530+3795+5692 = 16279.
20+
private static final int CHARS_TO_REACH_S8 = 16279;
21+
private static final int IO_BUFFER_SIZE = 8000;
22+
23+
/**
24+
* Parses a definite-length CBOR map {a: <16279 'a' bytes>, b: <8000 'a' bytes + 0xB7>, n: "x"}
25+
* via InputStream and expects all tokens to be read without error.
26+
* <p>
27+
* Actual result on affected versions: JsonParseException "Unsupported major type (5)"
28+
*/
29+
@Test
30+
public void testFinishLongTextAsciiDoesNotLeaveNonAsciiByte()
31+
{
32+
byte[] strA = new byte[CHARS_TO_REACH_S8];
33+
Arrays.fill(strA, (byte) 'a');
34+
35+
// 0xC2 0xB7 = U+00B7 "·" (middle dot): a valid 2-byte UTF-8 sequence.
36+
// 0xC2 is non-ASCII so _finishLongTextAscii exits, but it must leave len non-negative
37+
// so that _finishLongText's while loop can still decode the sequence.
38+
byte[] strB = new byte[IO_BUFFER_SIZE + 2];
39+
Arrays.fill(strB, (byte) 'a');
40+
strB[IO_BUFFER_SIZE] = (byte) 0xC2;
41+
strB[IO_BUFFER_SIZE + 1] = (byte) 0xB7;
42+
43+
assertDoesNotThrow(() ->
44+
new CBORMapper()
45+
.readValue(new ByteArrayInputStream(buildMap(strA, strB)), Object.class));
46+
}
47+
48+
/**
49+
* definite-length map(3): {a: strA, b: strB, n: "x"}
50+
*/
51+
private static byte[] buildMap(byte[] strA, byte[] strB) throws Exception
52+
{
53+
ByteArrayOutputStream bos = new ByteArrayOutputStream();
54+
bos.write(0xa3);
55+
writeText(bos, new byte[]{'a'});
56+
writeText(bos, strA);
57+
writeText(bos, new byte[]{'b'});
58+
writeText(bos, strB);
59+
writeText(bos, new byte[]{'n'});
60+
writeText(bos, new byte[]{'x'});
61+
return bos.toByteArray();
62+
}
63+
64+
private static void writeText(ByteArrayOutputStream bos, byte[] bytes) throws Exception
65+
{
66+
int n = bytes.length;
67+
if (n <= 23) {
68+
bos.write(0x60 | n);
69+
} else if (n <= 0xFF) {
70+
bos.write(0x78);
71+
bos.write(n);
72+
} else if (n <= 0xFFFF) {
73+
bos.write(0x79);
74+
bos.write((n >> 8) & 0xFF);
75+
bos.write(n & 0xFF);
76+
}
77+
bos.write(bytes);
78+
}
79+
}

release-notes/CREDITS-2.x

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,5 +429,10 @@ Vincent Eigenberger (@beseder1)
429429
(2.20.1)
430430

431431
Yohei Kishimoto (@morokosi)
432-
* Reported #599: (cbor) Unable to deserialize stringref-enabled CBOR with ignored properties
432+
* Reported #599: (cbor) Unable to deserialize stringref-enabled CBOR with ignored propertie
433433
(2.21.0)
434+
435+
Halil İbrahim Şener (@hisener)
436+
* Fixed #686: `CBORParser._finishLongTextAscii` returns negative length when `TextBuffer`
437+
segment > I/O buffer, leaving non-ASCII byte unconsumed
438+
(2.21.3)

release-notes/VERSION

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ implementations)
1818

1919
No changes since 3.1
2020

21+
3.1.2 (not yet released)
22+
23+
#686: `CBORParser._finishLongTextAscii` returns negative length when `TextBuffer`
24+
segment > I/O buffer, leaving non-ASCII byte unconsumed
25+
(fixed by Halil İbrahim Ş)
26+
2127
3.1.1 (27-Mar-2026)
2228

2329
#387: Avro generator should enforce `StreamWriteConstraints.maxNestingDepth`

release-notes/VERSION-2.x

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ Active maintainers:
1818

1919
No changes since 2.21
2020

21+
2.21.3 (not yet released)
22+
23+
#686: `CBORParser._finishLongTextAscii` returns negative length when `TextBuffer`
24+
segment > I/O buffer, leaving non-ASCII byte unconsumed
25+
(fixed by Halil İbrahim Ş)
26+
27+
2.21.2 (20-Mar-2026)
28+
29+
No changes since 2.21.1.
30+
2131
2.21.1 (22-Feb-2026)
2232

2333
#645: (avro) Remove use of Avro `Schema.Parser().setValidate()` to allow
@@ -72,6 +82,15 @@ No changes since 2.21
7282
- Fixes wrt [core#1438] (ParserBase.close() not resetting current token)
7383
- Generate SBOMs [JSTEP-14]
7484

85+
2.19.5 (not yet released)
86+
87+
#645: (avro) Remove use of Avro `Schema.Parser().setValidate()` to allow
88+
use of Avro core 1.12.1 (2.x)
89+
#649: (cbor, smile) `StreamReadConstraints.maxDocumentLength` not checked
90+
when creating parser with fixed buffer
91+
#651: (smile) Ensure Smile backend supports `StreamReadConstraints.maxTokenCount`
92+
#652: (cbor) Ensure CBOR backend supports `StreamReadConstraints.maxTokenCount`
93+
7594
2.19.4 (29-Oct-2025)
7695
2.19.3 (29-Oct-2025)
7796

0 commit comments

Comments
 (0)