Harden the mod against hostile servers and players

- PacketReader for all network data: lengths are checked against the real data
  (Mono allocated the claimed size first - a fake 2 GB length crashed the game)
- FsmSync: remote players can no longer switch arbitrary FSMs - never PLAYER /
  Systems / MSCO objects, CLICK only on FSMs that wait for a click, bolt/assembly
  events only along a transition the FSM really has for that event
- Time/weather (msco.world) and '* ' system-style chat only from the host in P2P
  (the dedicated server filters them itself); other msco.* ids are reserved

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 18:44:03 +02:00
co-authored by Claude Opus 5.5
parent 9c933ed486
commit a499577b7c
7 changed files with 88 additions and 9 deletions
+37
View File
@@ -0,0 +1,37 @@
using System.IO;
namespace MSCO
{
/// <summary>
/// BinaryReader für Daten aus dem Netz (Server, Mitspieler). Mono legt bei ReadBytes(n) und ReadString() vorab ein
/// Array bzw. einen StringBuilder in der angegebenen Länge an - eine gefälschte Länge (z.B. 2 Milliarden) ließ das
/// Spiel mit "Out of Memory" abstürzen. Hier wird jede Länge gegen die tatsächlich vorhandenen Bytes geprüft.
/// </summary>
public sealed class PacketReader : BinaryReader
{
public PacketReader(byte[] data) : base(new MemoryStream(data ?? new byte[0], false)) { }
long Remaining { get { return BaseStream.Length - BaseStream.Position; } }
public override byte[] ReadBytes(int count)
{
if (count < 0 || count > Remaining) throw new EndOfStreamException("Invalid length " + count);
return base.ReadBytes(count);
}
public override char[] ReadChars(int count)
{
if (count < 0 || count > Remaining) throw new EndOfStreamException("Invalid length " + count);
return base.ReadChars(count);
}
public override string ReadString()
{
long start = BaseStream.Position;
int length = Read7BitEncodedInt();
if (length < 0 || length > Remaining) throw new EndOfStreamException("Invalid string length " + length);
BaseStream.Position = start;
return base.ReadString();
}
}
}