Skip to content
This repository was archived by the owner on Dec 10, 2025. It is now read-only.

Commit 7136deb

Browse files
authored
Merge pull request #122 from SLNE-Development/copilot/sub-pr-113
Add ArrayDeque rationale and nesting depth limit to deep copy
2 parents 6b809c9 + ee7f6cc commit 7136deb

File tree

3 files changed

+36
-0
lines changed

3 files changed

+36
-0
lines changed

surf-cloud-api/surf-cloud-api-common/src/main/kotlin/dev/slne/surf/cloud/api/common/player/ppdc/PersistentPlayerDataContainerView.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,23 @@ interface PersistentPlayerDataContainerView {
8787
fun writeToBuf(buf: SurfByteBuf)
8888

8989
fun snapshot(): PersistentPlayerDataContainerView
90+
91+
companion object {
92+
/**
93+
* Maximum nesting depth for compound tags.
94+
* This limit prevents memory exhaustion from extremely large nested structures.
95+
* Set to a reasonable limit that should handle most legitimate use cases while
96+
* protecting against pathological inputs.
97+
*/
98+
const val MAX_NESTING_DEPTH = 512
99+
100+
inline fun ensureValidNestingDepth(
101+
depth: Int,
102+
exceptionFactory: (message: String) -> Throwable = ::IllegalStateException
103+
) {
104+
if (depth > MAX_NESTING_DEPTH) {
105+
throw exceptionFactory("Exceeded maximum allowed nesting depth of $MAX_NESTING_DEPTH. This likely indicates a corrupted or maliciously crafted data structure.")
106+
}
107+
}
108+
}
90109
}

surf-cloud-core/surf-cloud-core-common/src/main/kotlin/dev/slne/surf/cloud/core/common/player/ppdc/PersistentPlayerDataContainerViewImpl.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,14 @@ abstract class PersistentPlayerDataContainerViewImpl : PersistentPlayerDataConta
143143
* to build a complete copy. It avoids stack overflow issues that can occur with deeply nested structures
144144
* when using a recursive approach.
145145
*
146+
* Uses `ArrayDeque` instead of `Stack` for better performance characteristics:
147+
* - `ArrayDeque` is not synchronized, making it faster for single-threaded use
148+
* - `Stack` extends `Vector`, which has legacy synchronization overhead
149+
* - `ArrayDeque` is the recommended implementation for stack operations in modern Java/Kotlin
150+
*
146151
* @param root The root `CompoundBinaryTag` to be deep copied.
147152
* @return A deep copy of the specified `CompoundBinaryTag`.
153+
* @throws IllegalStateException if the structure is too deeply nested (exceeds [MAX_NESTING_DEPTH])
148154
*/
149155
private fun deepCopy(root: CompoundBinaryTag): CompoundBinaryTag {
150156
data class Frame(
@@ -181,6 +187,8 @@ abstract class PersistentPlayerDataContainerViewImpl : PersistentPlayerDataConta
181187
val (key, value) = top.entries[top.idx++]
182188

183189
if (value is CompoundBinaryTag) {
190+
PersistentPlayerDataContainerView.ensureValidNestingDepth(stack.size)
191+
184192
stack.addLast(top)
185193
stack.addLast(
186194
Frame(

surf-cloud-core/surf-cloud-core-common/src/main/kotlin/dev/slne/surf/cloud/core/common/player/ppdc/network/PdcOp.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import dev.slne.surf.cloud.api.common.netty.protocol.buffer.readList
66
import dev.slne.surf.cloud.api.common.netty.protocol.buffer.readUtf
77
import dev.slne.surf.cloud.api.common.netty.protocol.buffer.writeCollection
88
import dev.slne.surf.cloud.api.common.netty.protocol.buffer.writeUtf
9+
import dev.slne.surf.cloud.api.common.player.ppdc.PersistentPlayerDataContainerView
910
import dev.slne.surf.cloud.api.common.util.ByIdMap
1011
import io.netty.buffer.ByteBuf
1112
import net.kyori.adventure.nbt.BinaryTag
@@ -19,12 +20,20 @@ sealed interface PdcOp {
1920
val value: BinaryTag
2021
) : PdcOp {
2122
override val type = Type.PUT
23+
24+
init {
25+
PersistentPlayerDataContainerView.ensureValidNestingDepth(path.size)
26+
}
2227
}
2328

2429
data class Remove(
2530
override val path: List<String>
2631
) : PdcOp {
2732
override val type = Type.REMOVE
33+
34+
init {
35+
PersistentPlayerDataContainerView.ensureValidNestingDepth(path.size)
36+
}
2837
}
2938

3039
companion object {

0 commit comments

Comments
 (0)