From 1cf8449e22af1c3bb63e4e15c85cace31459b1bf Mon Sep 17 00:00:00 2001 From: jack Date: Sat, 18 Oct 2025 14:47:43 +0200 Subject: [PATCH] Fix decompression size limit to support max-sized file transfers Root cause: BinaryProtocol.decode() was rejecting decompressed payloads larger than maxPayloadBytes (1 MB), but TLV-encoded file transfers are slightly larger due to metadata overhead. Fixes: - Changed decompression limit from maxPayloadBytes to maxFramedFileBytes - This accounts for TLV overhead (~50 bytes) + binary protocol headers - Now allows ~1.12 MB decompressed payloads (1 MB + overhead budget) The failing test was: - Creating 1 MB file content - TLV encoding adds ~50 bytes (1,048,627 total) - Compression reduces to ~1,084 bytes (highly repetitive data) - During decode, decompression was rejecting the 1,048,627 byte output - Now correctly allows it since 1,048,627 < 1,179,760 (maxFramedFileBytes) All 154 tests now pass including 'Max-sized file transfer survives reassembly' --- bitchat/Protocols/BinaryProtocol.swift | 3 ++- bitchat/Services/BLEService.swift | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/bitchat/Protocols/BinaryProtocol.swift b/bitchat/Protocols/BinaryProtocol.swift index fa87b848..cde49fd4 100644 --- a/bitchat/Protocols/BinaryProtocol.swift +++ b/bitchat/Protocols/BinaryProtocol.swift @@ -333,7 +333,8 @@ struct BinaryProtocol { originalSize = Int(rawSize) } // Guard to keep decompression bounded to sane BLE payload limits - guard originalSize >= 0 && originalSize <= FileTransferLimits.maxPayloadBytes else { return nil } + // Use maxFramedFileBytes to account for TLV overhead in file transfer payloads + guard originalSize >= 0 && originalSize <= FileTransferLimits.maxFramedFileBytes else { return nil } let compressedSize = payloadLength - lengthFieldBytes guard compressedSize >= 0, let compressed = readData(compressedSize) else { return nil } diff --git a/bitchat/Services/BLEService.swift b/bitchat/Services/BLEService.swift index a2c291e3..8303c6ca 100644 --- a/bitchat/Services/BLEService.swift +++ b/bitchat/Services/BLEService.swift @@ -3395,7 +3395,7 @@ extension BLEService { if peerID == myPeerID { return } - + // Minimum header: 8 bytes ID + 2 index + 2 total + 1 type guard packet.payload.count >= 13 else { return } @@ -3441,10 +3441,11 @@ extension BLEService { } return FileTransferLimits.maxPayloadBytes }() - guard currentSize + fragmentData.count <= assemblyLimit else { + let projectedSize = currentSize + fragmentData.count + guard projectedSize <= assemblyLimit else { // Exceeds size limit - evict this assembly SecureLogger.warning( - "🚫 Fragment assembly exceeds size limit (\(currentSize + fragmentData.count) bytes > \(assemblyLimit)), evicting", + "🚫 Fragment assembly exceeds size limit (\(projectedSize) bytes > \(assemblyLimit)), evicting. Type=\(originalType) Index=\(index)/\(total)", category: .security ) incomingFragments.removeValue(forKey: key) @@ -3464,14 +3465,14 @@ extension BLEService { reassembled.append(fragment) } } - + // Decode the original packet bytes we reassembled, so flags/compression are preserved if let originalPacket = BinaryProtocol.decode(reassembled) { handleReceivedPacket(originalPacket, from: peerID) } else { SecureLogger.error("❌ Failed to decode reassembled packet (type=\(originalType), total=\(total))", category: .session) } - + // Cleanup incomingFragments.removeValue(forKey: key) fragmentMetadata.removeValue(forKey: key)