From 48d721f60287f5c374eeef44795726faa28b2ef7 Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 7 Mar 2026 21:23:00 -0700 Subject: [PATCH 1/2] Harden malformed vehicle packet parsing --- src/TNetwork.cpp | 8 +++++++- src/TServer.cpp | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index b6bb27f8..02fb55b4 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -670,7 +670,13 @@ void TNetwork::TCPClient(const std::weak_ptr& c) { Client->Disconnect("TCPRcv failed"); break; } - mServer.GlobalParser(c, std::move(res), mPPSMonitor, *this, false); + try { + mServer.GlobalParser(c, std::move(res), mPPSMonitor, *this, false); + } catch (const std::exception& e) { + beammp_warnf("Failed to receive/parse packet via TCP from client {}: {}", Client->GetID(), e.what()); + Client->Disconnect("Failed to parse packet"); + break; + } } if (QueueSync.joinable()) diff --git a/src/TServer.cpp b/src/TServer.cpp index 26188402..e38fb049 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -54,6 +55,15 @@ static std::optional> GetPidVid(const std::string& str) { return std::nullopt; } +static std::optional ExtractStructuredPayload(const std::string& PacketData, char BeginMarker) { + auto FoundPos = PacketData.find(BeginMarker); + if (FoundPos == std::string::npos) { + return std::nullopt; + } + + return PacketData.substr(FoundPos); +} + TEST_CASE("GetPidVid") { SUBCASE("Valid singledigit") { const auto MaybePidVid = GetPidVid("0-1"); @@ -121,6 +131,13 @@ TEST_CASE("GetPidVid") { } } +TEST_CASE("ExtractStructuredPayload") { + CHECK_EQ(ExtractStructuredPayload("1-2:{\"reset\":true}", '{').value(), "{\"reset\":true}"); + CHECK_EQ(ExtractStructuredPayload("1-2:[0,1,2,3]", '[').value(), "[0,1,2,3]"); + CHECK_FALSE(ExtractStructuredPayload("1-2:null", '{').has_value()); + CHECK_FALSE(ExtractStructuredPayload("1-2:null", '[').has_value()); +} + TServer::TServer(const std::vector& Arguments) { beammp_info("BeamMP Server v" + Application::ServerVersionString()); Application::SetSubsystemStatus("Server", Application::Status::Starting); @@ -472,7 +489,13 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ } if (PID != -1 && VID != -1 && PID == c.GetID()) { - Data = Data.substr(Data.find('{')); + auto ResetData = ExtractStructuredPayload(Data, '{'); + if (!ResetData.has_value()) { + beammp_warnf("Malformed 'Or' packet from client {}: missing '{{' in '{}'", c.GetID(), Packet); + return; + } + + Data = std::move(ResetData.value()); LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehicleReset", "", c.GetID(), VID, Data)); Network.SendToAll(&c, StringToVector(Packet), false, true); } @@ -501,7 +524,13 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ } if (PID != -1 && VID != -1 && PID == c.GetID()) { - Data = Data.substr(Data.find('[')); + auto PaintData = ExtractStructuredPayload(Data, '['); + if (!PaintData.has_value()) { + beammp_warnf("Malformed 'Op' packet from client {}: missing '[' in '{}'", c.GetID(), Packet); + return; + } + + Data = std::move(PaintData.value()); LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehiclePaintChanged", "", c.GetID(), VID, Data)); Network.SendToAll(&c, StringToVector(Packet), false, true); From 8a613e0b162dae9b974ac53489b6c4ec4a5d8b5a Mon Sep 17 00:00:00 2001 From: Tixx <83774803+WiserTixx@users.noreply.github.com> Date: Sun, 8 Mar 2026 17:24:30 +0100 Subject: [PATCH 2/2] Prevent log spam and simplify code --- src/TServer.cpp | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/src/TServer.cpp b/src/TServer.cpp index e38fb049..587cef67 100644 --- a/src/TServer.cpp +++ b/src/TServer.cpp @@ -54,16 +54,6 @@ static std::optional> GetPidVid(const std::string& str) { } return std::nullopt; } - -static std::optional ExtractStructuredPayload(const std::string& PacketData, char BeginMarker) { - auto FoundPos = PacketData.find(BeginMarker); - if (FoundPos == std::string::npos) { - return std::nullopt; - } - - return PacketData.substr(FoundPos); -} - TEST_CASE("GetPidVid") { SUBCASE("Valid singledigit") { const auto MaybePidVid = GetPidVid("0-1"); @@ -130,14 +120,6 @@ TEST_CASE("GetPidVid") { CHECK(!MaybePidVid); } } - -TEST_CASE("ExtractStructuredPayload") { - CHECK_EQ(ExtractStructuredPayload("1-2:{\"reset\":true}", '{').value(), "{\"reset\":true}"); - CHECK_EQ(ExtractStructuredPayload("1-2:[0,1,2,3]", '[').value(), "[0,1,2,3]"); - CHECK_FALSE(ExtractStructuredPayload("1-2:null", '{').has_value()); - CHECK_FALSE(ExtractStructuredPayload("1-2:null", '[').has_value()); -} - TServer::TServer(const std::vector& Arguments) { beammp_info("BeamMP Server v" + Application::ServerVersionString()); Application::SetSubsystemStatus("Server", Application::Status::Starting); @@ -489,13 +471,13 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ } if (PID != -1 && VID != -1 && PID == c.GetID()) { - auto ResetData = ExtractStructuredPayload(Data, '{'); - if (!ResetData.has_value()) { - beammp_warnf("Malformed 'Or' packet from client {}: missing '{{' in '{}'", c.GetID(), Packet); + auto BracketPos = Data.find('{'); + if (BracketPos == std::string::npos) { + beammp_debugf("Invalid 'Or' packet body from client {}", c.GetID()); return; } - Data = std::move(ResetData.value()); + Data = Data.substr(BracketPos); LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehicleReset", "", c.GetID(), VID, Data)); Network.SendToAll(&c, StringToVector(Packet), false, true); } @@ -524,13 +506,14 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ } if (PID != -1 && VID != -1 && PID == c.GetID()) { - auto PaintData = ExtractStructuredPayload(Data, '['); - if (!PaintData.has_value()) { - beammp_warnf("Malformed 'Op' packet from client {}: missing '[' in '{}'", c.GetID(), Packet); + auto BracketPos = Data.find('['); + if (BracketPos == std::string::npos) { + beammp_debugf("Invalid 'Op' packet body from client {}", c.GetID()); return; } - Data = std::move(PaintData.value()); + Data = Data.substr(BracketPos); + LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehiclePaintChanged", "", c.GetID(), VID, Data)); Network.SendToAll(&c, StringToVector(Packet), false, true);