mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-09-15 23:02:34 +02:00
Apparently cheat clients have figured out that none of SS14's code does validation against NaN inputs. Uh oh. IRobustSerializer can now be configured to remove NaN values when reading. This is intended to be set on the server to completely block the issue. Added "Unsafe" float types that can be used to bypass the new configurable behavior, in case somebody *really* needs NaNs. An alternative option was to make a "SafeFloat" type, and only apply the sanitization to that. The problem is that would require updating hundreds if not thousands of messages in SS14, and probably significantly confuse contributors on "when use what." Blocking NaNs by default is likely to cause little issues while ensuring the entire exploit is guaranteed impossible.
204 lines
5.9 KiB
C#
204 lines
5.9 KiB
C#
using JetBrains.Annotations;
|
|
using NUnit.Framework;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.Maths;
|
|
using Robust.Shared.Serialization;
|
|
using Robust.UnitTesting.Shared;
|
|
|
|
namespace Robust.Shared.IntegrationTests.Serialization;
|
|
|
|
[Serializable, NetSerializable]
|
|
[UsedImplicitly(Reason = "Needed so RobustSerializer is guaranteed to pick up on the unsafe types.")]
|
|
internal sealed class MakeTheseSerializable
|
|
{
|
|
public UnsafeFloat Single;
|
|
public UnsafeDouble Double;
|
|
public UnsafeHalf Half;
|
|
public Half SafeHalf;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests the serialization behavior of float types when <see cref="IRobustSerializer"/> is *not* set to do anything special.
|
|
/// Tests both primitives and Robust's "Unsafe" variants.
|
|
/// </summary>
|
|
[TestFixture, TestOf(typeof(RobustSerializer)), TestOf(typeof(NetUnsafeFloatSerializer))]
|
|
internal sealed class NetSerializerDefaultFloatTest : OurRobustUnitTest
|
|
{
|
|
private IRobustSerializer _serializer = null!;
|
|
|
|
[OneTimeSetUp]
|
|
public void Setup()
|
|
{
|
|
_serializer = IoCManager.Resolve<IRobustSerializer>();
|
|
_serializer.Initialize();
|
|
}
|
|
|
|
internal static readonly TestCaseData[] PassThroughFloatTests =
|
|
[
|
|
new TestCaseData(0.0).Returns(0.0),
|
|
new TestCaseData(1.0).Returns(1.0),
|
|
new TestCaseData(double.NaN).Returns(double.NaN),
|
|
new TestCaseData(double.PositiveInfinity).Returns(double.PositiveInfinity),
|
|
];
|
|
|
|
[TestCaseSource(nameof(PassThroughFloatTests))]
|
|
public double TestSingle(double input)
|
|
{
|
|
var ms = new MemoryStream();
|
|
_serializer.Serialize(ms, (float)input);
|
|
|
|
ms.Position = 0;
|
|
|
|
return _serializer.Deserialize<float>(ms);
|
|
}
|
|
|
|
[TestCaseSource(nameof(PassThroughFloatTests))]
|
|
public double TestUnsafeSingle(double input)
|
|
{
|
|
var ms = new MemoryStream();
|
|
_serializer.Serialize(ms, (UnsafeFloat)input);
|
|
|
|
ms.Position = 0;
|
|
|
|
return _serializer.Deserialize<UnsafeFloat>(ms);
|
|
}
|
|
|
|
[TestCaseSource(nameof(PassThroughFloatTests))]
|
|
public double TestDouble(double input)
|
|
{
|
|
var ms = new MemoryStream();
|
|
_serializer.Serialize(ms, input);
|
|
|
|
ms.Position = 0;
|
|
|
|
return _serializer.Deserialize<double>(ms);
|
|
}
|
|
|
|
[TestCaseSource(nameof(PassThroughFloatTests))]
|
|
public double TestUnsafeDouble(double input)
|
|
{
|
|
var ms = new MemoryStream();
|
|
_serializer.Serialize(ms, (UnsafeDouble)input);
|
|
|
|
ms.Position = 0;
|
|
|
|
return _serializer.Deserialize<UnsafeDouble>(ms);
|
|
}
|
|
|
|
[TestCaseSource(nameof(PassThroughFloatTests))]
|
|
public double TestHalf(double input)
|
|
{
|
|
var ms = new MemoryStream();
|
|
_serializer.Serialize(ms, (Half)input);
|
|
|
|
ms.Position = 0;
|
|
|
|
return (double)_serializer.Deserialize<Half>(ms);
|
|
}
|
|
|
|
[TestCaseSource(nameof(PassThroughFloatTests))]
|
|
public double TestUnsafeHalf(double input)
|
|
{
|
|
var ms = new MemoryStream();
|
|
_serializer.Serialize(ms, (UnsafeHalf)(Half)input);
|
|
|
|
ms.Position = 0;
|
|
|
|
return (double)(Half)_serializer.Deserialize<UnsafeHalf>(ms);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests the serialization behavior of float types when <see cref="IRobustSerializer"/> is set to remove NaNs on read.
|
|
/// Tests both primitives and Robust's "Unsafe" variants.
|
|
/// </summary>
|
|
[TestFixture]
|
|
[TestOf(typeof(RobustSerializer)), TestOf(typeof(NetUnsafeFloatSerializer)), TestOf(typeof(NetSafeFloatSerializer))]
|
|
internal sealed class NetSerializerSafeFloatTest : OurRobustUnitTest
|
|
{
|
|
private IRobustSerializer _serializer = default!;
|
|
|
|
[OneTimeSetUp]
|
|
public void Setup()
|
|
{
|
|
_serializer = IoCManager.Resolve<IRobustSerializer>();
|
|
_serializer.FloatFlags = SerializerFloatFlags.RemoveReadNan;
|
|
_serializer.Initialize();
|
|
}
|
|
|
|
internal static readonly TestCaseData[] SafeFloatTests =
|
|
[
|
|
new TestCaseData(0.0).Returns(0.0),
|
|
new TestCaseData(1.0).Returns(1.0),
|
|
new TestCaseData(double.NaN).Returns(0.0),
|
|
new TestCaseData(double.PositiveInfinity).Returns(double.PositiveInfinity),
|
|
];
|
|
|
|
[TestCaseSource(nameof(SafeFloatTests))]
|
|
public double TestSingle(double input)
|
|
{
|
|
var ms = new MemoryStream();
|
|
_serializer.Serialize(ms, (float)input);
|
|
|
|
ms.Position = 0;
|
|
|
|
return _serializer.Deserialize<float>(ms);
|
|
}
|
|
|
|
[TestCaseSource(typeof(NetSerializerDefaultFloatTest), nameof(NetSerializerDefaultFloatTest.PassThroughFloatTests))]
|
|
public double TestUnsafeSingle(double input)
|
|
{
|
|
var ms = new MemoryStream();
|
|
_serializer.Serialize(ms, (UnsafeFloat)input);
|
|
|
|
ms.Position = 0;
|
|
|
|
return _serializer.Deserialize<UnsafeFloat>(ms);
|
|
}
|
|
|
|
[TestCaseSource(nameof(SafeFloatTests))]
|
|
public double TestDouble(double input)
|
|
{
|
|
var ms = new MemoryStream();
|
|
_serializer.Serialize(ms, input);
|
|
|
|
ms.Position = 0;
|
|
|
|
return _serializer.Deserialize<double>(ms);
|
|
}
|
|
|
|
[TestCaseSource(typeof(NetSerializerDefaultFloatTest), nameof(NetSerializerDefaultFloatTest.PassThroughFloatTests))]
|
|
public double TestUnsafeDouble(double input)
|
|
{
|
|
var ms = new MemoryStream();
|
|
_serializer.Serialize(ms, (UnsafeDouble)input);
|
|
|
|
ms.Position = 0;
|
|
|
|
return _serializer.Deserialize<UnsafeDouble>(ms);
|
|
}
|
|
|
|
|
|
[TestCaseSource(nameof(SafeFloatTests))]
|
|
public double TestHalf(double input)
|
|
{
|
|
var ms = new MemoryStream();
|
|
_serializer.Serialize(ms, (Half)input);
|
|
|
|
ms.Position = 0;
|
|
|
|
return (double)_serializer.Deserialize<Half>(ms);
|
|
}
|
|
|
|
[TestCaseSource(typeof(NetSerializerDefaultFloatTest), nameof(NetSerializerDefaultFloatTest.PassThroughFloatTests))]
|
|
public double TestUnsafeHalf(double input)
|
|
{
|
|
var ms = new MemoryStream();
|
|
_serializer.Serialize(ms, (UnsafeHalf)(Half)input);
|
|
|
|
ms.Position = 0;
|
|
|
|
return (double)(Half)_serializer.Deserialize<UnsafeHalf>(ms);
|
|
}
|
|
}
|