Skip to content

Commit 4a28258

Browse files
committed
Prevent StackOverflowError when serializing Path using JsonValueWriter
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. Signed-off-by: Dmytro Nosan <[email protected]>
1 parent d496170 commit 4a28258

File tree

4 files changed

+37
-3
lines changed

4 files changed

+37
-3
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/JsonValueWriter.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818

1919
import java.io.IOException;
2020
import java.io.UncheckedIOException;
21+
import java.nio.file.Path;
2122
import java.util.ArrayDeque;
2223
import java.util.Arrays;
2324
import java.util.Deque;
@@ -114,6 +115,10 @@ else if (value instanceof WritableJson writableJson) {
114115
throw new UncheckedIOException(ex);
115116
}
116117
}
118+
// https://github.com/spring-projects/spring-boot/issues/44502
119+
else if (value instanceof Path p) {
120+
writeString(p.toString());
121+
}
117122
else if (value instanceof Iterable<?> iterable) {
118123
writeArray(iterable::forEach);
119124
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/JsonValueWriterTests.java

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.boot.json;
1818

19+
import java.nio.file.Path;
1920
import java.util.LinkedHashMap;
2021
import java.util.LinkedHashSet;
2122
import java.util.List;
@@ -240,6 +241,18 @@ void endWhenNotStartedThrowsException() {
240241
.isThrownBy(() -> valueWriter.end(Series.ARRAY)));
241242
}
242243

244+
// https://github.com/spring-projects/spring-boot/issues/44502
245+
@Test
246+
void writeJavaNioPathWhenSingleElementShouldNotCauseStackOverflowError() {
247+
assertThat(doWrite((valueWriter) -> valueWriter.write(Path.of("overflow")))).isEqualTo(quoted("overflow"));
248+
}
249+
250+
@Test
251+
void writeJavaNioPathShouldNotCauseStackOverflowError() {
252+
assertThat(doWrite((valueWriter) -> valueWriter.write(Path.of("stack/overflow/error"))))
253+
.isEqualTo(quoted("stack\\/overflow\\/error"));
254+
}
255+
243256
private <V> String write(V value) {
244257
return doWrite((valueWriter) -> valueWriter.write(value));
245258
}

spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-structured-logging/src/test/java/smoketest/structuredlogging/SampleStructuredLoggingApplicationTests.java

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,9 +16,12 @@
1616

1717
package smoketest.structuredlogging;
1818

19+
import java.nio.file.Path;
20+
1921
import org.junit.jupiter.api.AfterEach;
2022
import org.junit.jupiter.api.Test;
2123
import org.junit.jupiter.api.extension.ExtendWith;
24+
import org.slf4j.LoggerFactory;
2225

2326
import org.springframework.boot.logging.LoggingSystem;
2427
import org.springframework.boot.logging.LoggingSystemProperty;
@@ -43,6 +46,18 @@ void reset() {
4346
}
4447
}
4548

49+
// https://github.com/spring-projects/spring-boot/issues/44502
50+
@Test
51+
void javaNioPathShouldNotCauseStackOverflowError(CapturedOutput output) {
52+
SampleStructuredLoggingApplication.main(new String[0]);
53+
LoggerFactory.getLogger(SampleStructuredLoggingApplication.class)
54+
.atInfo()
55+
.addKeyValue("directory", Path.of("stack/overflow/error"))
56+
.log("java.nio.file.Path works as expected");
57+
assertThat(output).contains("java.nio.file.Path works as expected").contains("""
58+
"directory":"stack\\/overflow\\/error""");
59+
}
60+
4661
@Test
4762
void shouldNotLogBanner(CapturedOutput output) {
4863
SampleStructuredLoggingApplication.main(new String[0]);

src/checkstyle/checkstyle-suppressions.xml

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<suppress files="LogbackLoggingSystemTests\.java" checks="IllegalImport" />
1313
<suppress files="LogbackLoggingSystemParallelInitializationTests\.java" checks="IllegalImport" />
1414
<suppress files="LogbackConfigurationAotContributionTests\.java" checks="IllegalImport" />
15+
<suppress files="SampleStructuredLoggingApplicationTests\.java" checks="IllegalImport" message="LoggerFactory" />
1516
<suppress files="MetricsAutoConfigurationMeterRegistryPostProcessorIntegrationTests\.java" checks="IllegalImport" message="LoggerFactory"/>
1617
<suppress files="SpringApplicationTests\.java" checks="FinalClass" />
1718
<suppress files=".+Configuration\.java" checks="HideUtilityClassConstructor" />

0 commit comments

Comments
 (0)