diff --git a/app/src/main/java/com/bitchat/android/sync/GCSFilter.kt b/app/src/main/java/com/bitchat/android/sync/GCSFilter.kt index 212def6a..6411360f 100644 --- a/app/src/main/java/com/bitchat/android/sync/GCSFilter.kt +++ b/app/src/main/java/com/bitchat/android/sync/GCSFilter.kt @@ -18,6 +18,14 @@ import kotlin.math.ln * - Bitstream is packed MSB-first in each byte. */ object GCSFilter { + /** + * Highest Golomb-Rice parameter accepted from the wire. P maps to an FPR of + * ~1/2^P; beyond 32 the remainder width exceeds any practical filter, and + * the shifts in decode silently wrap (Kotlin shifts use the low 6 bits of + * the count) into garbage values. + */ + const val MAX_P = 32 + data class Params( val p: Int, // Golomb-Rice parameter (>= 1) val m: Long, // Range M = N * 2^P @@ -70,6 +78,11 @@ object GCSFilter { } fun decodeToSortedSet(p: Int, m: Long, data: ByteArray): LongArray { + // p and m arrive off the wire. Reject out-of-range parameters rather + // than decoding garbage: callers read the result as "peer has nothing" + // and fall back to sending the data, which is the safe direction. + // Matches the iOS guard in GCSFilter.decodeToSortedSet. + if (p < 1 || p > MAX_P || m <= 1L) return LongArray(0) val values = ArrayList() val reader = BitReader(data) var acc = 0L