From a69991b261b5f7e9d4d94874a2e258301aa43e9d Mon Sep 17 00:00:00 2001 From: Dmytro Nosan Date: Mon, 3 Mar 2025 14:03:16 +0200 Subject: [PATCH 1/2] Prevent stack overflow when writing Path Prior to this commit, serializing `java.nio.file.Path` caused a StackOverflowError because `Path.iterator()` always returns itself as the first element of the iterator, which results in a StackOverflowError. This commit serializes `java.nio.file.Path` as JSON String. See gh-44507 Signed-off-by: Dmytro Nosan --- .../boot/json/JsonValueWriter.java | 7 ++++++- .../boot/json/JsonValueWriterTests.java | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java index dbd8237aeb..c0270e2173 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.springframework.boot.json; import java.io.IOException; import java.io.UncheckedIOException; +import java.nio.file.Path; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Deque; @@ -114,6 +115,10 @@ class JsonValueWriter { throw new UncheckedIOException(ex); } } + // https://github.com/spring-projects/spring-boot/issues/44502 + else if (value instanceof Path p) { + writeString(p.toString()); + } else if (value instanceof Iterable iterable) { writeArray(iterable::forEach); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java index 94939db8da..4c4d4314de 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.boot.json; +import java.nio.file.Path; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -240,6 +241,18 @@ class JsonValueWriterTests { .isThrownBy(() -> valueWriter.end(Series.ARRAY))); } + // https://github.com/spring-projects/spring-boot/issues/44502 + @Test + void writeJavaNioPathWhenSingleElementShouldBeSerializedAsString() { + assertThat(doWrite((valueWriter) -> valueWriter.write(Path.of("overflow")))).isEqualTo(quoted("overflow")); + } + + @Test + void writeJavaNioPathShouldShouldBeSerializedAsString() { + assertThat(doWrite((valueWriter) -> valueWriter.write(Path.of("stack/overflow/error")))) + .isEqualTo(quoted("stack\\/overflow\\/error")); + } + private String write(V value) { return doWrite((valueWriter) -> valueWriter.write(value)); } From 175c9d30959d5a68ca1e72605a3c99dd02836faf Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 16 Apr 2025 11:24:45 +0100 Subject: [PATCH 2/2] Polish "Prevent stack overflow when writing Path" See gh-44507 --- .../springframework/boot/json/JsonValueWriter.java | 1 - .../boot/json/JsonValueWriterTests.java | 12 +++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java index c0270e2173..1d8046a0f6 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java @@ -115,7 +115,6 @@ class JsonValueWriter { throw new UncheckedIOException(ex); } } - // https://github.com/spring-projects/spring-boot/issues/44502 else if (value instanceof Path p) { writeString(p.toString()); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java index 4c4d4314de..f2966b3e7a 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java @@ -241,16 +241,14 @@ class JsonValueWriterTests { .isThrownBy(() -> valueWriter.end(Series.ARRAY))); } - // https://github.com/spring-projects/spring-boot/issues/44502 - @Test + @Test // gh-44502 void writeJavaNioPathWhenSingleElementShouldBeSerializedAsString() { - assertThat(doWrite((valueWriter) -> valueWriter.write(Path.of("overflow")))).isEqualTo(quoted("overflow")); + assertThat(doWrite((valueWriter) -> valueWriter.write(Path.of("a")))).isEqualTo(quoted("a")); } - @Test - void writeJavaNioPathShouldShouldBeSerializedAsString() { - assertThat(doWrite((valueWriter) -> valueWriter.write(Path.of("stack/overflow/error")))) - .isEqualTo(quoted("stack\\/overflow\\/error")); + @Test // gh-44502 + void writeJavaNioPathShouldBeSerializedAsString() { + assertThat(doWrite((valueWriter) -> valueWriter.write(Path.of("a/b/c")))).isEqualTo(quoted("a\\/b\\/c")); } private String write(V value) {