Fix malformed batch queue growth vulnerability in Unbatcher#4113
Conversation
…tack - Add Unbatcher.Clear() to drain queued batches and return writers to pool - Make GetNextMessage() throw when message size prefix exceeds remaining bytes, clearing all batches before throwing - Wrap batch processing loops in NetworkServer and NetworkClient with try-catch to handle malformed batch exceptions and disconnect - Disconnect (not just log) when batches remain after processing loop - Add tests for malformed batch detection and Clear()
|
@copilot fix build errors |
…sion Unity compatibility
I investigated the CI failure — the only failure is a pre-existing flaky test ( I did push a fix in commit |
Codecov Report❌ Patch coverage is
❌ Your patch check has failed because the patch coverage (72.88%) is below the target coverage (90.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## master #4113 +/- ##
==========================================
- Coverage 42.92% 42.73% -0.19%
==========================================
Files 156 156
Lines 14859 14887 +28
==========================================
- Hits 6378 6362 -16
- Misses 8481 8525 +44
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
You should've wrapped the implicit conversion in |
… Unbatcher.Clear()
Done — |
A connected client can send batches with a valid 8-byte timestamp but a varint message-length prefix exceeding the remaining bytes.
GetNextMessage()returnsfalsewithout retiring the batch, leaving it permanently queued. Repeated packets growconnection.unbatchermemory indefinitely (~16 KB per frame at default transport limits) without ever triggering a disconnect—even withexceptionsDisconnect = true. Truncated varint prefixes (e.g. a lone0xFF) similarly throw uncaught fromDecompressVarUInt, bypassing all disconnect paths.Unbatcher
Clear()to drain all queued batches back to pool and reset the readerGetNextMessage()now throwsInvalidOperationException(after clearing) whenreader.Remaining < size, instead of silently returningfalseNetworkServer / NetworkClient
OnTransportDatatry-catch—catches both the newInvalidOperationExceptionand pre-existing uncaughtEndOfStreamExceptionfrom truncated varints, then disconnectsClear()+Disconnect()instead of only loggingTests
GetNextMessage_MalformedBatch_SizeLargerThanRemaining— verifies throw + clear on oversized length prefixGetNextMessage_MalformedBatch_NoQueueGrowth— verifies repeated malformed batches cannot accumulateClear— verifies drain-and-reuse lifecycle