From 63ea5822ddec587608fab1a866484cbbf33f043f Mon Sep 17 00:00:00 2001 From: gamma-delta <29877714+gamma-delta@users.noreply.github.com> Date: Thu, 9 Jun 2022 13:54:30 -0500 Subject: [PATCH] hopefully prevent fork bombs, fix #106 --- .../petrak/hexcasting/api/spell/SpellDatum.kt | 65 ++++++++++++++----- .../petrak/hexcasting/api/spell/SpellList.kt | 8 +-- .../petrak/hexcasting/api/utils/HexUtils.kt | 27 ++++++-- 3 files changed, 73 insertions(+), 27 deletions(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/spell/SpellDatum.kt b/Common/src/main/java/at/petrak/hexcasting/api/spell/SpellDatum.kt index 1ea946e8..c7116ea8 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/spell/SpellDatum.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/spell/SpellDatum.kt @@ -5,6 +5,7 @@ import at.petrak.hexcasting.api.spell.math.HexPattern import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidSpellDatumType import at.petrak.hexcasting.api.utils.* import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.ListTag import net.minecraft.nbt.NbtUtils import net.minecraft.nbt.Tag import net.minecraft.network.chat.Component @@ -30,19 +31,47 @@ import java.util.* class SpellDatum private constructor(val payload: T) { val clazz: Class = payload.javaClass - fun serializeToNBT() = NBTBuilder { - when (val pl = payload) { - is Entity -> TAG_ENTITY %= compound { - TAG_ENTITY_UUID %= NbtUtils.createUUID(pl.uuid) - TAG_ENTITY_NAME_CHEATY %= Component.Serializer.toJson(pl.displayName) + fun serializeToNBT(): CompoundTag = this.serializeToNBTWithDepthCheck(0, 0)?.first + ?: NBTBuilder { TAG_WIDGET %= Widget.GARBAGE.name } + + + // The second int returned is the number of datums contained in this one. + fun serializeToNBTWithDepthCheck(depth: Int, total: Int): Pair? { + if (total > MAX_SERIALIZATION_TOTAL) + return null + + val tag = NBTBuilder { + when (val pl = payload) { + is SpellList -> { + // handle it specially! + if (depth + 1 > MAX_SERIALIZATION_DEPTH) { + return null + } + + val outList = ListTag() + var total1 = total + 1 // make mutable and include the list itself + for (elt in pl) { + val (t, subtotal) = elt.serializeToNBTWithDepthCheck(depth + 1, total1) ?: return null + total1 += subtotal + outList.add(t) + } + return Pair( + NBTBuilder { TAG_LIST %= outList }, + total1 + ) + } + is Entity -> TAG_ENTITY %= compound { + TAG_ENTITY_UUID %= NbtUtils.createUUID(pl.uuid) + TAG_ENTITY_NAME_CHEATY %= Component.Serializer.toJson(pl.displayName) + } + is Double -> TAG_DOUBLE %= pl + is Vec3 -> TAG_VEC3 %= pl.serializeToNBT() + is Widget -> TAG_WIDGET %= pl.name + is HexPattern -> TAG_PATTERN %= pl.serializeToNBT() + else -> throw RuntimeException("cannot serialize $pl because it is of type ${pl.javaClass.canonicalName} which is not serializable") } - is Double -> TAG_DOUBLE %= pl - is Vec3 -> TAG_VEC3 %= pl.serializeToNBT() - is SpellList -> TAG_LIST %= pl.serializeToNBT() - is Widget -> TAG_WIDGET %= pl.name - is HexPattern -> TAG_PATTERN %= pl.serializeToNBT() - else -> throw RuntimeException("cannot serialize $pl because it is of type ${pl.javaClass.canonicalName} which is not serializable") } + return Pair(tag, 1) } override fun toString(): String = @@ -77,6 +106,9 @@ class SpellDatum private constructor(val payload: T) { } companion object { + const val MAX_SERIALIZATION_DEPTH = 256 + const val MAX_SERIALIZATION_TOTAL = 1024 + @JvmStatic @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") fun make(payload: Any): SpellDatum<*> = @@ -162,10 +194,10 @@ class SpellDatum private constructor(val payload: T) { val vec = vecFromNBT(nbt.getLongArray(key)) // the focus color is really more red, but we don't want to show an error-y color out += String.format( - "(%.2f, %.2f, %.2f)", - vec.x, - vec.y, - vec.z + "(%.2f, %.2f, %.2f)", + vec.x, + vec.y, + vec.z ).lightPurple } TAG_LIST -> { @@ -200,7 +232,8 @@ class SpellDatum private constructor(val payload: T) { val subtag = nbt.getCompound(TAG_ENTITY) val json = subtag.getString(TAG_ENTITY_NAME_CHEATY) // handle pre-0.5.0 foci not having the tag - out += Component.Serializer.fromJson(json)?.aqua ?: "hexcasting.spelldata.entity.whoknows".asTranslatedComponent.white + out += Component.Serializer.fromJson(json)?.aqua + ?: "hexcasting.spelldata.entity.whoknows".asTranslatedComponent.white } else -> throw IllegalArgumentException("Unknown key $key: $nbt") } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/spell/SpellList.kt b/Common/src/main/java/at/petrak/hexcasting/api/spell/SpellList.kt index 74b6e3e6..d7399a06 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/spell/SpellList.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/spell/SpellList.kt @@ -9,17 +9,17 @@ import net.minecraft.server.level.ServerLevel * * ...Surely this won't have any performance implications. */ -sealed class SpellList: Iterable> { +sealed class SpellList : Iterable> { abstract val nonEmpty: Boolean abstract val car: SpellDatum<*> abstract val cdr: SpellList - class LPair(override val car: SpellDatum<*>, override val cdr: SpellList): SpellList() { + class LPair(override val car: SpellDatum<*>, override val cdr: SpellList) : SpellList() { override val nonEmpty = true } - class LList(val idx: Int, val list: List>): SpellList() { + class LList(val idx: Int, val list: List>) : SpellList() { override val nonEmpty: Boolean get() = idx < list.size override val car: SpellDatum<*> @@ -69,7 +69,7 @@ sealed class SpellList: Iterable> { override fun iterator() = SpellListIterator(this) - class SpellListIterator(var list: SpellList): Iterator> { + class SpellListIterator(var list: SpellList) : Iterator> { override fun hasNext() = list.nonEmpty override operator fun next(): SpellDatum<*> { val car = list.car diff --git a/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt b/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt index e3896b70..54342519 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/utils/HexUtils.kt @@ -1,12 +1,15 @@ @file:JvmName("HexUtils") + package at.petrak.hexcasting.api.utils import at.petrak.hexcasting.api.spell.SpellDatum +import at.petrak.hexcasting.api.spell.SpellList import at.petrak.hexcasting.api.spell.math.HexCoord import net.minecraft.ChatFormatting import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.ListTag import net.minecraft.nbt.LongArrayTag +import net.minecraft.nbt.Tag import net.minecraft.network.chat.* import net.minecraft.world.InteractionHand import net.minecraft.world.item.ItemStack @@ -179,14 +182,18 @@ interface WeakValue { private class WeakReferencedValue(var reference: WeakReference?) : WeakValue { override var value: T? get() = reference?.get() - set(value) { reference = value?.let { WeakReference(it) } } + set(value) { + reference = value?.let { WeakReference(it) } + } } private class WeakMappedValue(val keyGen: (T) -> K) : WeakValue { val reference = WeakHashMap() override var value: T? get() = reference.values.firstOrNull() - set(value) { if (value != null) reference[keyGen(value)] = value else reference.clear() } + set(value) { + if (value != null) reference[keyGen(value)] = value else reference.clear() + } } fun weakReference(value: T? = null): WeakValue = WeakReferencedValue(value?.let { WeakReference(it) }) @@ -197,13 +204,19 @@ fun weakMapped(keyGen: (T) -> K): WeakValue = WeakMappedValue(keyGen) inline operator fun WeakValue.getValue(thisRef: Any?, property: KProperty<*>): T? = value @Suppress("NOTHING_TO_INLINE") -inline operator fun WeakValue.setValue(thisRef: Any?, property: KProperty<*>, value: T?) { this.value = value } +inline operator fun WeakValue.setValue(thisRef: Any?, property: KProperty<*>, value: T?) { + this.value = value +} +/** + * Returns an empty list if it's too complicated. + */ fun Iterable>.serializeToNBT(): ListTag { - val tag = ListTag() - for (elt in this) - tag.add(elt.serializeToNBT()) - return tag + val out = SpellDatum.make(SpellList.LList(0, this.toList())).serializeToNBT() + return if (out.contains(SpellDatum.TAG_WIDGET)) + ListTag() + else + out.getList(SpellDatum.TAG_LIST, Tag.TAG_COMPOUND) } // Copy the impl from forge