From cff0b9f34d21fe748e495b3aa8704e92d7e61b64 Mon Sep 17 00:00:00 2001 From: thegrb93 Date: Mon, 14 Apr 2025 11:26:57 -0400 Subject: [PATCH 1/6] Improve network error handling --- lua/pac3/editor/client/wear.lua | 27 +++++++++++++-------------- lua/pac3/editor/server/wear.lua | 23 ++++++++++------------- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/lua/pac3/editor/client/wear.lua b/lua/pac3/editor/client/wear.lua index 038b8123e..36b4d0a81 100644 --- a/lua/pac3/editor/client/wear.lua +++ b/lua/pac3/editor/client/wear.lua @@ -63,17 +63,10 @@ end do -- to server local function net_write_table(tbl) - local buffer = pac.StringStream() buffer:writeTable(tbl) - local data = buffer:getString() - local ok, err = pcall(net.WriteStream, data) - - if not ok then - return ok, err - end - + net.WriteStream(data) return #data end @@ -104,10 +97,11 @@ do -- to server net.Start("pac_submit") - local bytes, err = net_write_table(data) + local ok, bytes = pcall(net_write_table, data) - if not bytes then - pace.Notify(false, "unable to transfer data to server: " .. tostring(err or "too big"), pace.pac_show_uniqueid:GetBool() and string.format("%s (%s)", part:GetName(), part:GetPrintUniqueID()) or part:GetName()) + if not ok then + net.Abort() + pace.Notify(false, "unable to transfer data to server: " .. tostring(bytes or "too big"), pace.pac_show_uniqueid:GetBool() and string.format("%s (%s)", part:GetName(), part:GetPrintUniqueID()) or part:GetName()) return false end @@ -127,8 +121,9 @@ do -- to server end net.Start("pac_submit") - local ret, err = net_write_table(data) - if ret == nil then + local ok, err = pcall(net_write_table, data) + if not ok then + net.Abort() pace.Notify(false, "unable to transfer data to server: " .. tostring(err or "too big"), name) return false end @@ -243,7 +238,11 @@ net.Receive("pac_submit", function() end local buffer = pac.StringStream(data) - local data = buffer:readTable() + local ok, data = pcall(buffer.readTable, buffer) + if not ok then + pac.Message("received invalid message from server!?") + return + end if type(data.owner) ~= "Player" or not data.owner:IsValid() then pac.Message("received message from server but owner is not valid!? typeof " .. type(data.owner) .. " || ", data.owner) diff --git a/lua/pac3/editor/server/wear.lua b/lua/pac3/editor/server/wear.lua index a003a5ab4..8632693eb 100644 --- a/lua/pac3/editor/server/wear.lua +++ b/lua/pac3/editor/server/wear.lua @@ -46,17 +46,10 @@ local function make_copy(tbl, input) end local function net_write_table(tbl) - local buffer = pac.StringStream() buffer:writeTable(tbl) - local data = buffer:getString() - local ok, err = pcall(net.WriteStream, data) - - if not ok then - return ok, err - end - + net.WriteStream(data) return #data end @@ -272,17 +265,18 @@ function pace.SubmitPartNow(data, filter) local ret = pac.CallHook("SendData", players, data) if ret == nil then net.Start("pac_submit") - local bytes, err = net_write_table(data) + local ok, err = pcall(net_write_table, data) - if not bytes then + if ok then + net.Send(players) + else + net.Abort() local errStr = tostring(err) ErrorNoHalt("[PAC3] Outfit broadcast failed for " .. tostring(owner) .. ": " .. errStr .. "\n") if owner and owner:IsValid() then owner:ChatPrint("[PAC3] ERROR: Could not broadcast your outfit: " .. errStr) end - else - net.Send(players) end end @@ -402,7 +396,10 @@ pace.PCallNetReceive(net.Receive, "pac_submit", function(len, ply) return end local buffer = pac.StringStream(data) - pace.HandleReceivedData(ply, buffer:readTable()) + local ok,tbl = pcall(buffer.readTable, buffer) + if ok then + pace.HandleReceivedData(ply, tbl) + end end) end) From 641689262ca70f6934af6acae3c681d618df1aed Mon Sep 17 00:00:00 2001 From: thegrb93 Date: Tue, 15 Apr 2025 01:11:14 -0400 Subject: [PATCH 2/6] Add owner --- lua/pac3/editor/client/wear.lua | 7 +++++++ lua/pac3/editor/server/wear.lua | 1 + 2 files changed, 8 insertions(+) diff --git a/lua/pac3/editor/client/wear.lua b/lua/pac3/editor/client/wear.lua index 36b4d0a81..8a03a5d73 100644 --- a/lua/pac3/editor/client/wear.lua +++ b/lua/pac3/editor/client/wear.lua @@ -231,6 +231,13 @@ end net.Receive("pac_submit", function() if not pac.IsEnabled() then return end + local owner = net.ReadEntity() + if owner:IsValid() and owner:IsPlayer() then + pac.Message("Receiving outfit from ", owner) + else + return + end + net.ReadStream(ply, function(data) if not data then pac.Message("message from server timed out") diff --git a/lua/pac3/editor/server/wear.lua b/lua/pac3/editor/server/wear.lua index 8632693eb..e78a46935 100644 --- a/lua/pac3/editor/server/wear.lua +++ b/lua/pac3/editor/server/wear.lua @@ -265,6 +265,7 @@ function pace.SubmitPartNow(data, filter) local ret = pac.CallHook("SendData", players, data) if ret == nil then net.Start("pac_submit") + net.WriteEntity(owner) local ok, err = pcall(net_write_table, data) if ok then From 9870fc2b01c03d114ed6090c62353d86e926d41e Mon Sep 17 00:00:00 2001 From: thegrb93 Date: Tue, 15 Apr 2025 14:40:47 -0400 Subject: [PATCH 3/6] Fix SubmitPart vulnerabilities --- lua/pac3/editor/server/wear.lua | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/lua/pac3/editor/server/wear.lua b/lua/pac3/editor/server/wear.lua index e78a46935..0e43f7d69 100644 --- a/lua/pac3/editor/server/wear.lua +++ b/lua/pac3/editor/server/wear.lua @@ -7,6 +7,7 @@ local isfunction = isfunction local ProtectedCall = ProtectedCall pace.StreamQueue = pace.StreamQueue or {} +pace.MaxStreamQueue = 32 -- Max queued outfits per player timer.Create("pac_check_stream_queue", 0.1, 0, function() local item = table.remove(pace.StreamQueue) @@ -294,18 +295,22 @@ end -- Inserts the given part into the StreamQueue function pace.SubmitPart(data, filter, callback) - if istable(data.part) then - pac.dprint("queuing part %q from %s", data.part.self.Name, tostring(data.owner)) - table.insert(pace.StreamQueue, { - data = data, - filter = filter, - callback = callback - }) - - return "queue" + if not (istable(data.part) and IsValid(data.owner)) then return end + local owner = data.owner + local count = 0 + for _, v in ipairs(pace.StreamQueue) do + if v.data.owner == owner then + if count == pace.MaxStreamQueue then return end + count = count + 1 + end end - return pace.SubmitPartNow(data, filter) + pac.dprint("queuing part %q from %s", data.part.self.Name, tostring(data.owner)) + table.insert(pace.StreamQueue, { + data = data, + filter = filter, + callback = callback + }) end -- Inserts the given part into the StreamQueue, and notifies when it completes @@ -407,7 +412,7 @@ end) function pace.ClearOutfit(ply) local uid = pac.Hash(ply) - pace.SubmitPart({part = "__ALL__", uid = pac.Hash(ply), owner = ply}) + pace.RemovePart({part = "__ALL__", uid = pac.Hash(ply), owner = ply}) pace.CallHook("RemoveOutfit", ply) end From 62572bbfa5b44ceba84bb483819bf5ecac073b67 Mon Sep 17 00:00:00 2001 From: thegrb93 Date: Tue, 15 Apr 2025 14:43:57 -0400 Subject: [PATCH 4/6] cleanup --- lua/pac3/editor/server/wear.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/lua/pac3/editor/server/wear.lua b/lua/pac3/editor/server/wear.lua index 0e43f7d69..4a5cc825c 100644 --- a/lua/pac3/editor/server/wear.lua +++ b/lua/pac3/editor/server/wear.lua @@ -410,10 +410,7 @@ pace.PCallNetReceive(net.Receive, "pac_submit", function(len, ply) end) function pace.ClearOutfit(ply) - local uid = pac.Hash(ply) - pace.RemovePart({part = "__ALL__", uid = pac.Hash(ply), owner = ply}) - pace.CallHook("RemoveOutfit", ply) end function pace.RequestOutfits(ply) From a50f268a15859d6c9c14cf34830cfa37c66825e5 Mon Sep 17 00:00:00 2001 From: thegrb93 Date: Tue, 15 Apr 2025 18:39:54 -0400 Subject: [PATCH 5/6] or isstring --- lua/pac3/editor/server/wear.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/pac3/editor/server/wear.lua b/lua/pac3/editor/server/wear.lua index 4a5cc825c..155f45cc2 100644 --- a/lua/pac3/editor/server/wear.lua +++ b/lua/pac3/editor/server/wear.lua @@ -295,7 +295,7 @@ end -- Inserts the given part into the StreamQueue function pace.SubmitPart(data, filter, callback) - if not (istable(data.part) and IsValid(data.owner)) then return end + if not ((istable(data.part) or isstring(data.part)) and IsValid(data.owner)) then return end local owner = data.owner local count = 0 for _, v in ipairs(pace.StreamQueue) do From c71e99e40dcdab87d5453252d344b6b46b00acf8 Mon Sep 17 00:00:00 2001 From: thegrb93 Date: Tue, 15 Apr 2025 21:46:42 -0400 Subject: [PATCH 6/6] Fix --- lua/pac3/editor/server/wear.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/pac3/editor/server/wear.lua b/lua/pac3/editor/server/wear.lua index 155f45cc2..6a35acfe7 100644 --- a/lua/pac3/editor/server/wear.lua +++ b/lua/pac3/editor/server/wear.lua @@ -305,7 +305,7 @@ function pace.SubmitPart(data, filter, callback) end end - pac.dprint("queuing part %q from %s", data.part.self.Name, tostring(data.owner)) + if data.part.self then pac.dprint("queuing part %q from %s", data.part.self.Name, tostring(data.owner)) end table.insert(pace.StreamQueue, { data = data, filter = filter,