Skip to content

Commit 8e62d9e

Browse files
fix: Add size validation to named and unnamed message sending [Up Port] (#3049)
* fix Adding the fixes from #3043 * test Adding the tests from #3043 * update Adding changelog entry * fix Check for CustomMessageManager existing before invoking.
1 parent c0cd658 commit 8e62d9e

File tree

7 files changed

+122
-4
lines changed

7 files changed

+122
-4
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
1010

1111
### Added
1212

13+
- Added message size validation to named and unnamed message sending functions for better error messages. (#3049)
1314
- Added "Check for NetworkObject Component" property to the Multiplayer->Netcode for GameObjects project settings. When disabled, this will bypass the in-editor `NetworkObject` check on `NetworkBehaviour` components. (#3031)
1415
- Added `NetworkTransform.SwitchTransformSpaceWhenParented` property that, when enabled, will handle the world to local, local to world, and local to local transform space transitions when interpolation is enabled. (#3013)
1516
- Added `NetworkTransform.TickSyncChildren` that, when enabled, will tick synchronize nested and/or child `NetworkTransform` components to eliminate any potential visual jittering that could occur if the `NetworkTransform` instances get into a state where their state updates are landing on different network ticks. (#3013)

com.unity.netcode.gameobjects/Runtime/Messaging/CustomMessageManager.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ public void SendUnnamedMessage(IReadOnlyList<ulong> clientIds, FastBufferWriter
9595
return;
9696
}
9797

98+
ValidateMessageSize(messageBuffer, networkDelivery, isNamed: false);
9899

99100
if (m_NetworkManager.IsHost)
100101
{
@@ -131,6 +132,8 @@ public void SendUnnamedMessage(IReadOnlyList<ulong> clientIds, FastBufferWriter
131132
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
132133
public void SendUnnamedMessage(ulong clientId, FastBufferWriter messageBuffer, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)
133134
{
135+
ValidateMessageSize(messageBuffer, networkDelivery, isNamed: false);
136+
134137
if (m_NetworkManager.IsHost)
135138
{
136139
if (clientId == m_NetworkManager.LocalClientId)
@@ -286,6 +289,8 @@ public void SendNamedMessageToAll(string messageName, FastBufferWriter messageSt
286289
/// <param name="networkDelivery">The delivery type (QoS) to send data with</param>
287290
public void SendNamedMessage(string messageName, ulong clientId, FastBufferWriter messageStream, NetworkDelivery networkDelivery = NetworkDelivery.ReliableSequenced)
288291
{
292+
ValidateMessageSize(messageStream, networkDelivery, isNamed: true);
293+
289294
ulong hash = 0;
290295
switch (m_NetworkManager.NetworkConfig.RpcHashSize)
291296
{
@@ -367,6 +372,8 @@ public void SendNamedMessage(string messageName, IReadOnlyList<ulong> clientIds,
367372
return;
368373
}
369374

375+
ValidateMessageSize(messageStream, networkDelivery, isNamed: true);
376+
370377
ulong hash = 0;
371378
switch (m_NetworkManager.NetworkConfig.RpcHashSize)
372379
{
@@ -405,5 +412,32 @@ public void SendNamedMessage(string messageName, IReadOnlyList<ulong> clientIds,
405412
m_NetworkManager.NetworkMetrics.TrackNamedMessageSent(clientIds, messageName, size);
406413
}
407414
}
415+
416+
/// <summary>
417+
/// Validate the size of the message. If it's a non-fragmented delivery type the message must fit within the
418+
/// max allowed size with headers also subtracted. Named messages also include the hash
419+
/// of the name string. Only validates in editor and development builds.
420+
/// </summary>
421+
/// <param name="messageStream">The named message payload</param>
422+
/// <param name="networkDelivery">Delivery method</param>
423+
/// <param name="isNamed">Is the message named (or unnamed)</param>
424+
/// <exception cref="OverflowException">Exception thrown in case validation fails</exception>
425+
private unsafe void ValidateMessageSize(FastBufferWriter messageStream, NetworkDelivery networkDelivery, bool isNamed)
426+
{
427+
#if DEVELOPMENT_BUILD || UNITY_EDITOR
428+
var maxNonFragmentedSize = m_NetworkManager.MessageManager.NonFragmentedMessageMaxSize - FastBufferWriter.GetWriteSize<NetworkMessageHeader>() - sizeof(NetworkBatchHeader);
429+
if (isNamed)
430+
{
431+
maxNonFragmentedSize -= sizeof(ulong); // MessageName hash
432+
}
433+
if (networkDelivery != NetworkDelivery.ReliableFragmentedSequenced
434+
&& messageStream.Length > maxNonFragmentedSize)
435+
{
436+
throw new OverflowException($"Given message size ({messageStream.Length} bytes) is greater than " +
437+
$"the maximum allowed for the selected delivery method ({maxNonFragmentedSize} bytes). Try using " +
438+
$"ReliableFragmentedSequenced delivery method instead.");
439+
}
440+
#endif
441+
}
408442
}
409443
}

com.unity.netcode.gameobjects/Runtime/Messaging/Messages/UnnamedMessage.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int
2020

2121
public void Handle(ref NetworkContext context)
2222
{
23-
((NetworkManager)context.SystemOwner).CustomMessagingManager.InvokeUnnamedMessage(context.SenderId, m_ReceivedData, context.SerializedHeaderSize);
23+
((NetworkManager)context.SystemOwner).CustomMessagingManager?.InvokeUnnamedMessage(context.SenderId, m_ReceivedData, context.SerializedHeaderSize);
2424
}
2525
}
2626
}

com.unity.netcode.gameobjects/Runtime/Messaging/NetworkMessageManager.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,11 @@ internal unsafe int SendPreSerializedMessage<TMessageType>(in FastBufferWriter t
733733
}
734734

735735
ref var writeQueueItem = ref sendQueueItem.ElementAt(sendQueueItem.Length - 1);
736-
writeQueueItem.Writer.TryBeginWrite(tmpSerializer.Length + headerSerializer.Length);
736+
if (!writeQueueItem.Writer.TryBeginWrite(tmpSerializer.Length + headerSerializer.Length))
737+
{
738+
Debug.LogError($"Not enough space to write message, size={tmpSerializer.Length + headerSerializer.Length} space used={writeQueueItem.Writer.Position} total size={writeQueueItem.Writer.Capacity}");
739+
continue;
740+
}
737741

738742
writeQueueItem.Writer.WriteBytes(headerSerializer.GetUnsafePtr(), headerSerializer.Length);
739743
writeQueueItem.Writer.WriteBytes(tmpSerializer.GetUnsafePtr(), tmpSerializer.Length);

com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@ public unsafe void WriteBytes(byte* value, int size, int offset = 0)
700700
}
701701
if (Handle->Position + size > Handle->AllowedWriteMark)
702702
{
703-
throw new OverflowException($"Attempted to write without first calling {nameof(TryBeginWrite)}()");
703+
throw new OverflowException($"Attempted to write without first calling {nameof(TryBeginWrite)}(), Position+Size={Handle->Position + size} > AllowedWriteMark={Handle->AllowedWriteMark}");
704704
}
705705
#endif
706706
UnsafeUtility.MemCpy((Handle->BufferPointer + Handle->Position), value + offset, size);
@@ -729,7 +729,7 @@ public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0)
729729

730730
if (!TryBeginWriteInternal(size))
731731
{
732-
throw new OverflowException("Writing past the end of the buffer");
732+
throw new OverflowException($"Writing past the end of the buffer, size is {size} bytes but remaining capacity is {Handle->Capacity - Handle->Position} bytes");
733733
}
734734
UnsafeUtility.MemCpy((Handle->BufferPointer + Handle->Position), value + offset, size);
735735
Handle->Position += size;

com.unity.netcode.gameobjects/Tests/Runtime/Messaging/NamedMessageTests.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,5 +239,45 @@ public void WhenSendingNamedMessageToNullClientList_ArgumentNullExceptionIsThrow
239239
});
240240
}
241241
}
242+
243+
[Test]
244+
public unsafe void ErrorMessageIsPrintedWhenAttemptingToSendNamedMessageWithTooBigBuffer()
245+
{
246+
// First try a valid send with the maximum allowed size (this is atm 1264)
247+
var msgSize = m_ServerNetworkManager.MessageManager.NonFragmentedMessageMaxSize - FastBufferWriter.GetWriteSize<NetworkMessageHeader>() - sizeof(ulong)/*MessageName hash*/ - sizeof(NetworkBatchHeader);
248+
var bufferSize = m_ServerNetworkManager.MessageManager.NonFragmentedMessageMaxSize;
249+
var messageName = Guid.NewGuid().ToString();
250+
var messageContent = new byte[msgSize];
251+
var writer = new FastBufferWriter(bufferSize, Allocator.Temp, bufferSize * 2);
252+
using (writer)
253+
{
254+
writer.TryBeginWrite(msgSize);
255+
writer.WriteBytes(messageContent, msgSize, 0);
256+
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage(messageName, new List<ulong> { FirstClient.LocalClientId }, writer);
257+
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage(messageName, FirstClient.LocalClientId, writer);
258+
}
259+
260+
msgSize++;
261+
messageContent = new byte[msgSize];
262+
writer = new FastBufferWriter(bufferSize, Allocator.Temp, bufferSize * 2);
263+
using (writer)
264+
{
265+
writer.TryBeginWrite(msgSize);
266+
writer.WriteBytes(messageContent, msgSize, 0);
267+
var message = Assert.Throws<OverflowException>(
268+
() =>
269+
{
270+
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage(messageName, new List<ulong> { FirstClient.LocalClientId }, writer);
271+
}).Message;
272+
Assert.IsTrue(message.Contains($"Given message size ({msgSize} bytes) is greater than the maximum"), $"Unexpected exception: {message}");
273+
274+
message = Assert.Throws<OverflowException>(
275+
() =>
276+
{
277+
m_ServerNetworkManager.CustomMessagingManager.SendNamedMessage(messageName, FirstClient.LocalClientId, writer);
278+
}).Message;
279+
Assert.IsTrue(message.Contains($"Given message size ({msgSize} bytes) is greater than the maximum"), $"Unexpected exception: {message}");
280+
}
281+
}
242282
}
243283
}

com.unity.netcode.gameobjects/Tests/Runtime/Messaging/UnnamedMessageTests.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,5 +194,44 @@ public void WhenSendingNamedMessageToNullClientList_ArgumentNullExceptionIsThrow
194194
});
195195
}
196196
}
197+
198+
[Test]
199+
public unsafe void ErrorMessageIsPrintedWhenAttemptingToSendUnnamedMessageWithTooBigBuffer()
200+
{
201+
// First try a valid send with the maximum allowed size (this is atm 1272)
202+
var msgSize = m_ServerNetworkManager.MessageManager.NonFragmentedMessageMaxSize - FastBufferWriter.GetWriteSize<NetworkMessageHeader>() - sizeof(NetworkBatchHeader);
203+
var bufferSize = m_ServerNetworkManager.MessageManager.NonFragmentedMessageMaxSize;
204+
var messageContent = new byte[msgSize];
205+
var writer = new FastBufferWriter(bufferSize, Allocator.Temp, bufferSize * 2);
206+
using (writer)
207+
{
208+
writer.TryBeginWrite(msgSize);
209+
writer.WriteBytes(messageContent, msgSize, 0);
210+
m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessage(new List<ulong> { FirstClient.LocalClientId }, writer);
211+
m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessage(FirstClient.LocalClientId, writer);
212+
}
213+
214+
msgSize++;
215+
messageContent = new byte[msgSize];
216+
writer = new FastBufferWriter(bufferSize, Allocator.Temp, bufferSize * 2);
217+
using (writer)
218+
{
219+
writer.TryBeginWrite(msgSize);
220+
writer.WriteBytes(messageContent, msgSize, 0);
221+
var message = Assert.Throws<OverflowException>(
222+
() =>
223+
{
224+
m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessage(new List<ulong> { FirstClient.LocalClientId }, writer);
225+
}).Message;
226+
Assert.IsTrue(message.Contains($"Given message size ({msgSize} bytes) is greater than the maximum"), $"Unexpected exception: {message}");
227+
228+
message = Assert.Throws<OverflowException>(
229+
() =>
230+
{
231+
m_ServerNetworkManager.CustomMessagingManager.SendUnnamedMessage(FirstClient.LocalClientId, writer);
232+
}).Message;
233+
Assert.IsTrue(message.Contains($"Given message size ({msgSize} bytes) is greater than the maximum"), $"Unexpected exception: {message}");
234+
}
235+
}
197236
}
198237
}

0 commit comments

Comments
 (0)