Skip to content

Fix malformed batch queue growth vulnerability in Unbatcher#4113

Merged
miwarnec merged 4 commits into
masterfrom
copilot/fix-invalid-batch-queueing
Jun 17, 2026
Merged

Fix malformed batch queue growth vulnerability in Unbatcher#4113
miwarnec merged 4 commits into
masterfrom
copilot/fix-invalid-batch-queueing

Conversation

Copilot AI commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

A connected client can send batches with a valid 8-byte timestamp but a varint message-length prefix exceeding the remaining bytes. GetNextMessage() returns false without retiring the batch, leaving it permanently queued. Repeated packets grow connection.unbatcher memory indefinitely (~16 KB per frame at default transport limits) without ever triggering a disconnect—even with exceptionsDisconnect = true. Truncated varint prefixes (e.g. a lone 0xFF) similarly throw uncaught from DecompressVarUInt, bypassing all disconnect paths.

Unbatcher

  • Added Clear() to drain all queued batches back to pool and reset the reader
  • GetNextMessage() now throws InvalidOperationException (after clearing) when reader.Remaining < size, instead of silently returning false

NetworkServer / NetworkClient OnTransportData

  • Wrapped batch processing loop in try-catch—catches both the new InvalidOperationException and pre-existing uncaught EndOfStreamException from truncated varints, then disconnects
  • The post-loop "batches remaining" check now calls Clear() + Disconnect() instead of only logging

Tests

  • GetNextMessage_MalformedBatch_SizeLargerThanRemaining — verifies throw + clear on oversized length prefix
  • GetNextMessage_MalformedBatch_NoQueueGrowth — verifies repeated malformed batches cannot accumulate
  • Clear — verifies drain-and-reuse lifecycle

Copilot AI added 2 commits June 16, 2026 14:57
…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()
@miwarnec

Copy link
Copy Markdown
Collaborator

@copilot fix build errors

Copilot AI commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

@copilot fix build errors

I investigated the CI failure — the only failure is a pre-existing flaky test (WorkerThreadTests.StopBlocking_Deadlocked on Unity 2022.3), which is unrelated to these changes. No actual compilation errors were found.

I did push a fix in commit c3b9c07 to use explicit new ArraySegment<byte>(Array.Empty<byte>()) instead of relying on implicit byte[]ArraySegment<byte> conversion in Unbatcher.Clear(), ensuring compatibility across all Unity versions (the implicit conversion is only available in .NET Standard 2.1+/Unity 2021.3+). The CI should re-run with this push.

Copilot AI requested a review from miwarnec June 17, 2026 11:23
@codecov

codecov Bot commented Jun 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.88136% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 42.73%. Comparing base (28ee96d) to head (028c43c).
⚠️ Report is 3 commits behind head on master.

Files with missing lines Patch % Lines
Assets/Mirror/Core/NetworkServer.cs 62.50% 9 Missing ⚠️
Assets/Mirror/Core/NetworkClient.cs 70.83% 7 Missing ⚠️

❌ 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     
Flag Coverage Δ
unittests 42.73% <72.88%> (-0.19%) ⬇️
unity-6000.4.0f1 ?
unity-6000.5.0f1 42.73% <72.88%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
Assets/Mirror/Core/Batching/Unbatcher.cs 96.29% <100.00%> (+3.11%) ⬆️
Assets/Mirror/Core/NetworkClient.cs 93.81% <70.83%> (-0.66%) ⬇️
Assets/Mirror/Core/NetworkServer.cs 68.20% <62.50%> (-0.41%) ⬇️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@MrGadget1024

Copy link
Copy Markdown
Collaborator

@copilot

I did push a fix in commit c3b9c07 to use explicit new ArraySegment<byte>(Array.Empty<byte>()) instead of relying on implicit byte[]ArraySegment<byte> conversion in Unbatcher.Clear(), ensuring compatibility across all Unity versions (the implicit conversion is only available in .NET Standard 2.1+/Unity 2021.3+). The CI should re-run with this push.

You should've wrapped the implicit conversion in #if UNITY_2021_3_OR_NEWER ... #else ... #endif so that older version code debt is identifiable.

Copilot AI commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

@copilot

I did push a fix in commit c3b9c07 to use explicit new ArraySegment<byte>(Array.Empty<byte>()) instead of relying on implicit byte[]ArraySegment<byte> conversion in `Unbatc...

Done — Unbatcher.Clear() now uses #if UNITY_2021_3_OR_NEWER to gate the implicit conversion, with the explicit new ArraySegment<byte>(...) in the #else branch.

Copilot AI requested a review from MrGadget1024 June 17, 2026 15:04
@miwarnec miwarnec merged commit 9491a0c into master Jun 17, 2026
9 of 10 checks passed
@miwarnec miwarnec deleted the copilot/fix-invalid-batch-queueing branch June 17, 2026 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants