Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Got NegativeArraySizeException when calling writeValueAsString() #1012

Closed
klettier opened this issue May 3, 2023 · 7 comments
Closed

Got NegativeArraySizeException when calling writeValueAsString() #1012

klettier opened this issue May 3, 2023 · 7 comments
Milestone

Comments

@klettier
Copy link

klettier commented May 3, 2023

Hi,

I'm serializing an object containing 15M values

  • Jackson version: 2.14.2

Stack trace

java.lang.NegativeArraySizeException: -1649955036
	at java.base/java.lang.AbstractStringBuilder.<init>(AbstractStringBuilder.java:86)
	at java.base/java.lang.StringBuilder.<init>(StringBuilder.java:116)
	at com.fasterxml.jackson.core.util.TextBuffer.contentsAsString(TextBuffer.java:455)
	at com.fasterxml.jackson.core.io.SegmentedStringWriter.getAndClear(SegmentedStringWriter.java:85)
	at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3875)

Looks like an int overflow somewhere in TextBuffer 👀

Minimal test case that reproduce the issue

  data class MyData(
      @JsonProperty("foo")
      val foo: Collection<Item> = emptyList(),

      @JsonProperty("bar")
      val bar: Collection<Item> = emptyList(),
  )

  data class Item(
      @JsonProperty("id")
      val id: String,

      @JsonProperty("value")
      val value: String,
  )

  @Test
  fun overflow() {
      val report = MyData(
          foo = (1..5_000_000).map { Item(UUID.randomUUID().toString(), UUID.randomUUID().toString()) },
          bar = (1..10_000_000).map {
              Item(
                  "foo=${UUID.randomUUID()}bar=${UUID.randomUUID()},barbar=${UUID.randomUUID()}",
                  UUID.randomUUID().toString(),
              )
          },
      )

      jacksonObjectMapper().writeValueAsString(report)
  }

Result

image

@klettier klettier changed the title Got NegativeArraySizeException calling writeValueAsString Got NegativeArraySizeException when calling writeValueAsString May 3, 2023
@pjfanning
Copy link
Member

Can you provide a test case? - it would speed this along

@klettier
Copy link
Author

klettier commented May 3, 2023

Yes I'm working on it

@klettier
Copy link
Author

klettier commented May 3, 2023

Minimal Test case that reproduce the issue added

@pjfanning
Copy link
Member

I debugged the test case and the issue is that the TextBuffer gets too big. StringBuilder will only support Integer.MAX_VALUE chars (approx 2Gb in utf-8 or ascii - 1 byte per char).

@cowtowncoder the _segmentSize in TextBuffer can turn negative when the int overflows. We could throw exceptions after any increments to _segmentSize if the value goes negative.

There is little benefit to making _segmentSize a long because in the end contentsAsString and contentsAsArray can only support cases where the size is <= Integer.MAX_SIZE.

@pjfanning
Copy link
Member

This is basically the same issue as #351

@pjfanning
Copy link
Member

I modified my test to write to a writeValue(OutputStream, value) instead (I used a FileOutputStream) and that successfully handled the large JSON. Writing to a String is basically limited to 2 billion chars.

@cowtowncoder cowtowncoder changed the title Got NegativeArraySizeException when calling writeValueAsString Got NegativeArraySizeException when calling writeValueAsString() May 3, 2023
@cowtowncoder
Copy link
Member

Ok, will merge the fix. Thank you @pjfanning for the fix, @klettier for reporting it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants