Skip to content

Commit 25c7e6b

Browse files
refactor: Ensure lengths are always written as uints (#2946)
* refactor: Ensure lengths are always written as uints * Update CHANGELOG.md --------- Co-authored-by: Noel Stephens <noel.stephens@unity3d.com>
1 parent 98d4bbc commit 25c7e6b

3 files changed

Lines changed: 111 additions & 54 deletions

File tree

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,17 @@ Additional documentation and release notes are available at [Multiplayer Documen
88

99
## [Unreleased]
1010

11+
### Added
12+
13+
1114
### Fixed
1215

1316
- Fixed issue where SessionOwner message was being treated as a new entry for the new message indexing when it should have been ordinally sorted with the legacy message indices. (#2942)
1417

18+
### Changed
19+
- Changed `FastBufferReader` and `FastBufferWriter` so that they always ensure the length of items serialized is always serialized as an `uint` and added a check before casting for safe reading and writing.(#2946)
20+
21+
1522
## [2.0.0-exp.4] - 2024-05-31
1623

1724
### Added

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

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,8 @@ public void ReadNetworkSerializableInPlace<T>(ref T value) where T : INetworkSer
523523
/// <param name="oneByteChars">Whether or not to use one byte per character. This will only allow ASCII</param>
524524
public unsafe void ReadValue(out string s, bool oneByteChars = false)
525525
{
526-
ReadValue(out uint length);
527-
s = "".PadRight((int)length);
526+
ReadLength(out int length);
527+
s = "".PadRight(length);
528528
int target = s.Length;
529529
fixed (char* native = s)
530530
{
@@ -562,18 +562,18 @@ public unsafe void ReadValueSafe(out string s, bool oneByteChars = false)
562562
}
563563
#endif
564564

565-
if (!TryBeginReadInternal(sizeof(uint)))
565+
if (!TryBeginReadInternal(SizeOfLengthField()))
566566
{
567567
throw new OverflowException("Reading past the end of the buffer");
568568
}
569569

570-
ReadValue(out uint length);
570+
ReadLength(out int length);
571571

572-
if (!TryBeginReadInternal((int)length * (oneByteChars ? 1 : sizeof(char))))
572+
if (!TryBeginReadInternal(length * (oneByteChars ? 1 : sizeof(char))))
573573
{
574574
throw new OverflowException("Reading past the end of the buffer");
575575
}
576-
s = "".PadRight((int)length);
576+
s = "".PadRight(length);
577577
int target = s.Length;
578578
fixed (char* native = s)
579579
{
@@ -592,6 +592,33 @@ public unsafe void ReadValueSafe(out string s, bool oneByteChars = false)
592592
}
593593
}
594594

595+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
596+
private static int SizeOfLengthField() => sizeof(uint);
597+
598+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
599+
private void ReadLengthSafe(out uint length) => ReadUnmanagedSafe(out length);
600+
601+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
602+
private void ReadLength(out uint length) => ReadUnmanaged(out length);
603+
604+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
605+
private void ReadLengthSafe(out int length)
606+
{
607+
ReadLengthSafe(out uint temp);
608+
if (temp > int.MaxValue)
609+
{
610+
throw new InvalidCastException("length value outside of int32 range");
611+
}
612+
length = (int)temp;
613+
}
614+
615+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
616+
private void ReadLength(out int length)
617+
{
618+
ReadLength(out uint temp);
619+
length = (int)temp;
620+
}
621+
595622
/// <summary>
596623
/// Read a partial value. The value is zero-initialized and then the specified number of bytes is read into it.
597624
/// </summary>
@@ -777,7 +804,7 @@ internal unsafe void ReadUnmanagedSafe<T>(out T value) where T : unmanaged
777804
[MethodImpl(MethodImplOptions.AggressiveInlining)]
778805
internal unsafe void ReadUnmanaged<T>(out T[] value) where T : unmanaged
779806
{
780-
ReadUnmanaged(out int sizeInTs);
807+
ReadLength(out int sizeInTs);
781808
int sizeInBytes = sizeInTs * sizeof(T);
782809
value = new T[sizeInTs];
783810
fixed (T* ptr = value)
@@ -789,7 +816,7 @@ internal unsafe void ReadUnmanaged<T>(out T[] value) where T : unmanaged
789816
[MethodImpl(MethodImplOptions.AggressiveInlining)]
790817
internal unsafe void ReadUnmanagedSafe<T>(out T[] value) where T : unmanaged
791818
{
792-
ReadUnmanagedSafe(out int sizeInTs);
819+
ReadLengthSafe(out int sizeInTs);
793820
int sizeInBytes = sizeInTs * sizeof(T);
794821
value = new T[sizeInTs];
795822
fixed (T* ptr = value)
@@ -801,7 +828,7 @@ internal unsafe void ReadUnmanagedSafe<T>(out T[] value) where T : unmanaged
801828
[MethodImpl(MethodImplOptions.AggressiveInlining)]
802829
internal unsafe void ReadUnmanaged<T>(out NativeArray<T> value, Allocator allocator) where T : unmanaged
803830
{
804-
ReadUnmanaged(out int sizeInTs);
831+
ReadLength(out int sizeInTs);
805832
int sizeInBytes = sizeInTs * sizeof(T);
806833
value = new NativeArray<T>(sizeInTs, allocator);
807834
byte* bytes = (byte*)value.GetUnsafePtr();
@@ -810,7 +837,7 @@ internal unsafe void ReadUnmanaged<T>(out NativeArray<T> value, Allocator alloca
810837
[MethodImpl(MethodImplOptions.AggressiveInlining)]
811838
internal unsafe void ReadUnmanagedSafe<T>(out NativeArray<T> value, Allocator allocator) where T : unmanaged
812839
{
813-
ReadUnmanagedSafe(out int sizeInTs);
840+
ReadLengthSafe(out int sizeInTs);
814841
int sizeInBytes = sizeInTs * sizeof(T);
815842
value = new NativeArray<T>(sizeInTs, allocator);
816843
byte* bytes = (byte*)value.GetUnsafePtr();
@@ -820,7 +847,7 @@ internal unsafe void ReadUnmanagedSafe<T>(out NativeArray<T> value, Allocator al
820847
[MethodImpl(MethodImplOptions.AggressiveInlining)]
821848
internal unsafe void ReadUnmanagedInPlace<T>(ref NativeList<T> value) where T : unmanaged
822849
{
823-
ReadUnmanaged(out int sizeInTs);
850+
ReadLength(out int sizeInTs);
824851
int sizeInBytes = sizeInTs * sizeof(T);
825852
value.Resize(sizeInTs, NativeArrayOptions.UninitializedMemory);
826853
byte* bytes = (byte*)value.GetUnsafePtr();
@@ -829,7 +856,7 @@ internal unsafe void ReadUnmanagedInPlace<T>(ref NativeList<T> value) where T :
829856
[MethodImpl(MethodImplOptions.AggressiveInlining)]
830857
internal unsafe void ReadUnmanagedSafeInPlace<T>(ref NativeList<T> value) where T : unmanaged
831858
{
832-
ReadUnmanagedSafe(out int sizeInTs);
859+
ReadLengthSafe(out int sizeInTs);
833860
int sizeInBytes = sizeInTs * sizeof(T);
834861
value.Resize(sizeInTs, NativeArrayOptions.UninitializedMemory);
835862
byte* bytes = (byte*)value.GetUnsafePtr();
@@ -1078,7 +1105,7 @@ public void ReadValueSafeInPlace<T>(ref NativeList<T> value, FastBufferWriter.Fo
10781105
[MethodImpl(MethodImplOptions.AggressiveInlining)]
10791106
internal void ReadValueSafeInPlace<T>(ref NativeHashSet<T> value) where T : unmanaged, IEquatable<T>
10801107
{
1081-
ReadUnmanagedSafe(out int length);
1108+
ReadLengthSafe(out int length);
10821109
value.Clear();
10831110
for (var i = 0; i < length; ++i)
10841111
{
@@ -1093,7 +1120,7 @@ internal void ReadValueSafeInPlace<TKey, TVal>(ref NativeHashMap<TKey, TVal> val
10931120
where TKey : unmanaged, IEquatable<TKey>
10941121
where TVal : unmanaged
10951122
{
1096-
ReadUnmanagedSafe(out int length);
1123+
ReadLengthSafe(out int length);
10971124
value.Clear();
10981125
for (var i = 0; i < length; ++i)
10991126
{
@@ -1553,7 +1580,7 @@ internal void ReadValueSafeInPlace<TKey, TVal>(ref NativeHashMap<TKey, TVal> val
15531580
/// This method is a little difficult to use, since you have to know the size of the string before
15541581
/// reading it, but is useful when the string is a known, fixed size. Note that the size of the
15551582
/// string is also encoded, so the size to call TryBeginRead on is actually the fixed size (in bytes)
1556-
/// plus sizeof(int)
1583+
/// plus sizeof(uint)
15571584
/// </summary>
15581585
/// <param name="value">the value to read</param>
15591586
/// <param name="unused">An unused parameter used for enabling overload resolution based on generic constraints</param>
@@ -1562,7 +1589,7 @@ internal void ReadValueSafeInPlace<TKey, TVal>(ref NativeHashMap<TKey, TVal> val
15621589
public unsafe void ReadValue<T>(out T value, FastBufferWriter.ForFixedStrings unused = default)
15631590
where T : unmanaged, INativeList<byte>, IUTF8Bytes
15641591
{
1565-
ReadUnmanaged(out int length);
1592+
ReadLength(out int length);
15661593
value = new T
15671594
{
15681595
Length = length
@@ -1584,7 +1611,7 @@ public unsafe void ReadValue<T>(out T value, FastBufferWriter.ForFixedStrings un
15841611
public unsafe void ReadValueSafe<T>(out T value, FastBufferWriter.ForFixedStrings unused = default)
15851612
where T : unmanaged, INativeList<byte>, IUTF8Bytes
15861613
{
1587-
ReadUnmanagedSafe(out int length);
1614+
ReadLengthSafe(out int length);
15881615
value = new T
15891616
{
15901617
Length = length
@@ -1606,7 +1633,7 @@ public unsafe void ReadValueSafe<T>(out T value, FastBufferWriter.ForFixedString
16061633
public unsafe void ReadValueSafeInPlace<T>(ref T value, FastBufferWriter.ForFixedStrings unused = default)
16071634
where T : unmanaged, INativeList<byte>, IUTF8Bytes
16081635
{
1609-
ReadUnmanagedSafe(out int length);
1636+
ReadLengthSafe(out int length);
16101637
value.Length = length;
16111638
ReadBytesSafe(value.GetUnsafePtr(), length);
16121639
}
@@ -1625,7 +1652,7 @@ public unsafe void ReadValueSafeInPlace<T>(ref T value, FastBufferWriter.ForFixe
16251652
public unsafe void ReadValueSafe<T>(out NativeArray<T> value, Allocator allocator)
16261653
where T : unmanaged, INativeList<byte>, IUTF8Bytes
16271654
{
1628-
ReadUnmanagedSafe(out int length);
1655+
ReadLengthSafe(out int length);
16291656
value = new NativeArray<T>(length, allocator);
16301657
var ptr = (T*)value.GetUnsafePtr();
16311658
for (var i = 0; i < length; ++i)
@@ -1647,7 +1674,7 @@ public unsafe void ReadValueSafe<T>(out NativeArray<T> value, Allocator allocato
16471674
public unsafe void ReadValueSafeTemp<T>(out NativeArray<T> value)
16481675
where T : unmanaged, INativeList<byte>, IUTF8Bytes
16491676
{
1650-
ReadUnmanagedSafe(out int length);
1677+
ReadLengthSafe(out int length);
16511678
value = new NativeArray<T>(length, Allocator.Temp);
16521679
var ptr = (T*)value.GetUnsafePtr();
16531680
for (var i = 0; i < length; ++i)
@@ -1669,7 +1696,7 @@ public unsafe void ReadValueSafeTemp<T>(out NativeArray<T> value)
16691696
public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForFixedStrings unused = default)
16701697
where T : unmanaged, INativeList<byte>, IUTF8Bytes
16711698
{
1672-
ReadUnmanagedSafe(out int length);
1699+
ReadLengthSafe(out int length);
16731700
value = new T[length];
16741701
for (var i = 0; i < length; ++i)
16751702
{
@@ -1691,7 +1718,7 @@ public void ReadValueSafe<T>(out T[] value, FastBufferWriter.ForFixedStrings unu
16911718
public void ReadValueSafeInPlace<T>(ref NativeList<T> value)
16921719
where T : unmanaged, INativeList<byte>, IUTF8Bytes
16931720
{
1694-
ReadUnmanagedSafe(out int length);
1721+
ReadLengthSafe(out int length);
16951722
value.Resize(length, NativeArrayOptions.UninitializedMemory);
16961723
for (var i = 0; i < length; ++i)
16971724
{

0 commit comments

Comments
 (0)