Skip to content

JsonGenerator#close() is not idempotent and may cause data corruption. #20

@ppcedric

Description

@ppcedric

If we close a JsonGenerator twice, the internal buffer is returned twice to the shared pool.
Next, the same buffer is wrongly shared between two distinct generators.
This result into hard to predict errors including data-corruption.

Test-case:

		StringWriter sw = new StringWriter();
		JsonGeneratorFactory factory = Json.createGeneratorFactory(Collections.emptyMap());
		try (JsonGenerator generator = factory.createGenerator(sw)) {
			generator.writeStartObject();
			generator.writeEnd();
			generator.close();  // Culprit
			System.out.println(sw.toString()); // Correct: {}
		} // Try/resource/close
		

		StringWriter sw1 = new StringWriter();
		StringWriter sw2 = new StringWriter();
		try (JsonGenerator generator1 = factory.createGenerator(sw1);
JsonGenerator generator2 = factory.createGenerator(sw2)) {
			generator1.writeStartObject();
			generator1.write("key", "value");
			
			generator2.writeStartArray();
			generator2.write("item");
			generator2.write("item2");
			
			generator1.write("key2", "value2");
			
			generator2.writeEnd();
			
			generator1.writeEnd();
		}
		System.out.println(sw1); // This output: ["item","item2,]key2":"value2"}
		System.out.println(sw2); // This output: ["item","item2,]


Explanation:

As you can see, both generated strings are completely broken.

The culprit in this test-case is the pre-emptive "generator.close()" line.
This line cause the JsonGeneratorImpl to recycle buffer to the pool.
Next, the try/resource close this generator again, which cause the same internal buffer to be recycled a second time.
Few line laters, when creating two new distinct generators, they internally receives the same buffer from the pool which cause their output to be mixed...

Well, the quick workarround is to make sure that a generator is not closed more than necessary in the program..
But, because JsonGenerator implements Closeable, the close() method is supposed to be idempotent as specified in the Closeabled javadoc:

  • If the stream is already closed then invoking this method has no effect.

This is recalled in the javadoc of the super interface AutoCloseable that a Closeable is required to be idempotent:

  • Note that unlike the {@link java.io.Closeable#close close}

  • method of {@link java.io.Closeable}, this {@code close} method
  • is not required to be idempotent.

So, closing the JsonGenerator more than once should be tolerated and should not corrupt the internal buffer pool.
( It seems that the same issue occurs in other places such as JsonParser#close() )

Thanks,
Kind Regards,
Cédric.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions