Skip to content

Fix WriterBasedJsonGenerator AIOOBE for zero-length custom escape - #1669

Closed
kalayciburak wants to merge 2 commits into
FasterXML:3.xfrom
kalayciburak:fix/1668-zero-length-custom-escape-buffer-boundary
Closed

Fix WriterBasedJsonGenerator AIOOBE for zero-length custom escape#1669
kalayciburak wants to merge 2 commits into
FasterXML:3.xfrom
kalayciburak:fix/1668-zero-length-custom-escape-buffer-boundary

Conversation

@kalayciburak

@kalayciburak kalayciburak commented Aug 18, 2026

Copy link
Copy Markdown

Summary

Fixes #1668.

WriterBasedJsonGenerator.writeString(String) throws
ArrayIndexOutOfBoundsException when a CharacterEscapes implementation
returns a zero-length sequence and that escaped character is the last
character of the string, aligned with the output buffer end.

_writeStringCustom plus _prependOrWriteCharacterEscape left
_outputHead == _outputTail at the buffer end. The closing quote then
wrote at index 4000 of a 4000-char buffer.

Changes

  • Empty custom escape now appends nothing (skip the write) instead of
    changing _flushBuffer.
  • After the custom-escape scan, recycle the buffer if it is empty at
    the end so the next write does not need a no-op flush.
  • Add ZeroLengthCustomEscape1668Test covering Writer and UTF-8
    generators, plus writeString(String) and writeString(char[]).

Testing

./mvnw -B -ff -ntp test -Dtest=ZeroLengthCustomEscape1668Test,TestCustomEscaping -Dsurefire.useModulePath=false

Result: 9/9 GREEN.

./mvnw -B -ff -ntp test -Dsurefire.useModulePath=false

Result: 1836 tests, 0 failures, 2 skipped.

The same empty-escape case exists on 2.21 if a backport is wanted.

Comment thread release-notes/CREDITS
@hdimitrieski
* Reported #1668: WriterBasedJsonGenerator AIOOBE when a zero-length custom
escape lands at the output buffer boundary
(3.3.0)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is fine for the moment but is likely to be added to an earlier release since this is a bug fix - we can give you the details when we decide on which release to target. It may end up needing to be a 2.18 fix and we will forward merge it

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be ok with 2.21 backport (2.18 only for secvuln at this point, will be closed by end of 2026)

@pjfanning

Copy link
Copy Markdown
Member

@kalayciburak If you haven't submitted a CLA, we will need one from you.

https://github.com/FasterXML/jackson/blob/main/CLA-jackson-2026.pdf

which is usually done by printing, filling/signing, scan/photo, email to cla at fasterxml dot com.

throw _wrapIOFailure(e);
}
} else if (_outputTail >= _outputEnd) {
// 18-Aug-2026, [core#1668]: a zero-length custom escape can leave

@cowtowncoder cowtowncoder Aug 18, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This really should not be necessary.... Seems like wrong place for fix (side-effect)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair point, flushBuffer was the wrong place. empty escape now just skips the write so we dont leave tail at the buffer end. pushed

@cowtowncoder

cowtowncoder commented Aug 18, 2026

Copy link
Copy Markdown
Member

Thank you for reporthing the issue and contributing this @kalayciburak. I am bit confused by fix -- 0-length should append nothing, so why change "flushBuffer()" and not prevent call.

…sterXML#1668)

A CharacterEscapes sequence of length 0 at the last character of a
string can leave _outputHead == _outputTail at the buffer end. The
closing quote then wrote past the buffer.

Skip the empty escape write instead of changing _flushBuffer. After
the custom-escape scan, recycle the buffer if it is empty at the end
so the next write does not need a no-op flush.
@kalayciburak
kalayciburak force-pushed the fix/1668-zero-length-custom-escape-buffer-boundary branch from 274b5cf to f68bff1 Compare August 18, 2026 18:00
@cowtowncoder cowtowncoder added the cla-needed PR looks good (although may also require code review), but CLA needed from submitter label Aug 20, 2026
@cowtowncoder cowtowncoder added cla-received PR already covered by CLA (optional label) and removed cla-needed PR looks good (although may also require code review), but CLA needed from submitter labels Aug 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📈 Overall Code Coverage

Metric Coverage Change
Instructions coverage 83.91% 📈 +0.090%
Branches branches 76.89% 📈 +0.130%

Overall project coverage from JaCoCo test results. Change values compare against the latest base branch build.

@cowtowncoder

Copy link
Copy Markdown
Member

Ok; good news, got the CLA!

I also realized that maybe change in _flushBuffer() makes sense, so will re-create 2.21 targeted PR (#1672), merge via that, crediting you @kalayciburak .

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-received PR already covered by CLA (optional label)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WriterBasedJsonGenerator throws ArrayIndexOutOfBoundsException when a zero-length custom escape ends exactly at the output buffer boundary

3 participants