From 06af4077300f78b903ea5b4c949aa363f20624bd Mon Sep 17 00:00:00 2001 From: Jorge Bescos Gascon Date: Thu, 5 May 2022 11:27:35 +0200 Subject: [PATCH] JsonGenerator#close() is not idempotent and may cause data corruption. Signed-off-by: Jorge Bescos Gascon --- .../eclipse/parsson/JsonGeneratorImpl.java | 24 ++++++++------ .../parsson/tests/JsonGeneratorTest.java | 33 ++++++++++++++++++- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/impl/src/main/java/org/eclipse/parsson/JsonGeneratorImpl.java b/impl/src/main/java/org/eclipse/parsson/JsonGeneratorImpl.java index 63fab88e..129184e0 100644 --- a/impl/src/main/java/org/eclipse/parsson/JsonGeneratorImpl.java +++ b/impl/src/main/java/org/eclipse/parsson/JsonGeneratorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -90,6 +90,7 @@ private static enum Scope { // flush the underlying output source private final char buf[]; // capacity >= INT_MIN_VALUE_CHARS.length private int len = 0; + private boolean closed = false; JsonGeneratorImpl(Writer writer, BufferPool bufferPool) { this.writer = writer; @@ -515,16 +516,19 @@ private static class Context { @Override public void close() { - if (currentContext.scope != Scope.IN_NONE || currentContext.first) { - throw new JsonGenerationException(JsonMessages.GENERATOR_INCOMPLETE_JSON()); - } - flushBuffer(); - try { - writer.close(); - } catch (IOException ioe) { - throw new JsonException(JsonMessages.GENERATOR_CLOSE_IO_ERR(), ioe); + if (!closed) { + if (currentContext.scope != Scope.IN_NONE || currentContext.first) { + throw new JsonGenerationException(JsonMessages.GENERATOR_INCOMPLETE_JSON()); + } + flushBuffer(); + try { + writer.close(); + } catch (IOException ioe) { + throw new JsonException(JsonMessages.GENERATOR_CLOSE_IO_ERR(), ioe); + } + bufferPool.recycle(buf); + closed = true; } - bufferPool.recycle(buf); } // begin, end-1 indexes represent characters that need not diff --git a/impl/src/test/java/org/eclipse/parsson/tests/JsonGeneratorTest.java b/impl/src/test/java/org/eclipse/parsson/tests/JsonGeneratorTest.java index bb34a786..58e515ba 100644 --- a/impl/src/test/java/org/eclipse/parsson/tests/JsonGeneratorTest.java +++ b/impl/src/test/java/org/eclipse/parsson/tests/JsonGeneratorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -22,6 +22,7 @@ import jakarta.json.*; import jakarta.json.stream.*; import java.io.*; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Random; @@ -542,4 +543,34 @@ public void testFlush() throws Exception { assertEquals("{}", baos.toString("UTF-8")); } + public void testClose() { + StringWriter sw = new StringWriter(); + JsonGeneratorFactory factory = Json.createGeneratorFactory(Collections.emptyMap()); + try (JsonGenerator generator = factory.createGenerator(sw)) { + generator.writeStartObject(); + generator.writeEnd(); + // Unnecessary close() + generator.close(); + assertEquals("{}", sw.toString()); + } + 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(); + } + assertEquals("{\"key\":\"value\",\"key2\":\"value2\"}", sw1.toString()); + assertEquals("[\"item\",\"item2\"]", sw2.toString()); + } }