Skip to content

Commit 5852ada

Browse files
authored
Merge pull request #25 from Open-SB/kill-misbehaving-client-scripts
Kill misbehaving client scripts via an interrupt hook
2 parents a1f9cc8 + 83ff661 commit 5852ada

3 files changed

Lines changed: 38 additions & 3 deletions

File tree

modules/client/wm/sandbox/init.luau

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
local game = game
22
local setmetatable = setmetatable
3+
local os = os
34

45
local Log = require("@shared/log")
56
local Environment = require("@self/environment")
67
local VM = require("@self/vm")
78
local Rules = require("@self/rules")
9+
local Errors = require("@shared/errors")
810

911
local Players = game:GetService("Players")
12+
local RunService = game:GetService("RunService")
13+
14+
local lastSteppedStart = 0
15+
local SCRIPT_TIMEOUT = 0.1
16+
17+
RunService.Stepped:Connect(function()
18+
lastSteppedStart = os.clock()
19+
end)
1020

1121
local localPlayer = Players.LocalPlayer
1222

@@ -90,7 +100,16 @@ function Module.new(owner: Player, bytecode: buffer, script: BaseScript?): sandb
90100
scriptLookup[script] = sandbox
91101
end
92102

93-
local main, luau_close = VM.luau_load(bytecode, sandbox)
103+
local main, luau_close = VM.luau_load(bytecode, sandbox, @native function()
104+
if os.clock() - lastSteppedStart >= SCRIPT_TIMEOUT then
105+
-- calls sandbox.Terminator (luau_close), then cleans up sandbox
106+
Module.terminate(sandbox, true)
107+
error(Errors.scriptTimeout(SCRIPT_TIMEOUT), 2)
108+
end
109+
110+
return nil
111+
end)
112+
94113
sandbox.Terminator = luau_close
95114

96115
return sandbox, main

modules/client/wm/sandbox/vm.luau

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ local function luau_deserialize(bytecode)
541541
end
542542

543543
local vmStart = debug_info(function() end, "l")
544-
local function luau_load(module, sandbox)
544+
local function luau_load(module, sandbox, interruptHook: () -> ())
545545
if type(module) ~= "table" then
546546
module = luau_deserialize(module)
547547
end
@@ -561,7 +561,7 @@ local function luau_load(module, sandbox)
561561
local function luau_wrapclosure(module, proto, upvals, _env)
562562
local info = { proto = proto, sandbox = sandbox }
563563

564-
-- TODO: @native
564+
@native
565565
local function luau_execute(...)
566566
local runningInfo = { thread = coroutine_running(), pc = 0, closureInfo = info }
567567
info.running = runningInfo
@@ -735,6 +735,8 @@ local function luau_load(module, sandbox)
735735

736736
stack[A] = sb[inst.K]
737737
elseif op == 21 then --[[ CALL ]]
738+
interruptHook()
739+
738740
local A, B, C = inst.A, inst.B, inst.C
739741

740742
local func = stack[A]
@@ -752,13 +754,17 @@ local function luau_load(module, sandbox)
752754

753755
table_move(ret_list, 1, ret_num, A, stack)
754756
elseif op == 22 then --[[ RETURN ]]
757+
interruptHook()
758+
755759
local A = inst.A
756760
local B = inst.B
757761

758762
return table_unpack(stack, A, A + if B - 1 == LUA_MULTRET then top - A else (B - 2))
759763
elseif op == 23 then --[[ JUMP ]]
760764
pc += inst.D
761765
elseif op == 24 then --[[ JUMPBACK ]]
766+
interruptHook()
767+
762768
-- selene: allow(if_same_then_else)
763769
pc += inst.D
764770
elseif op == 25 then --[[ JUMPIF ]]
@@ -919,6 +925,8 @@ local function luau_load(module, sandbox)
919925
end
920926
end
921927
elseif op == 57 then --[[ FORNLOOP ]]
928+
interruptHook()
929+
922930
local A = inst.A
923931
local step = stack[A + 1]
924932
local index = stack[A + 2] + step
@@ -935,6 +943,8 @@ local function luau_load(module, sandbox)
935943
end
936944
end
937945
elseif op == 58 then --[[ FORGLOOP ]]
946+
interruptHook()
947+
938948
local A = inst.A
939949

940950
top = A + 6
@@ -1027,6 +1037,8 @@ local function luau_load(module, sandbox)
10271037

10281038
pc += 1 --// adjust for aux
10291039
elseif op == 67 then --[[ JUMPX ]]
1040+
interruptHook()
1041+
10301042
pc += inst.E
10311043
elseif op == 68 then --[[ FASTCALL ]]
10321044
--[[ Skipped ]]

modules/shared/errors.luau

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,8 @@ function Errors.connectNonFunction(): string
4848
return `Attempt to connect failed: Passed value is not a function`
4949
end
5050

51+
function Errors.scriptTimeout(allowedExecutionTime: number): string
52+
return `Script timeout: exhausted allowed execution time ({allowedExecutionTime})`
53+
end
54+
5155
return table.freeze(Errors)

0 commit comments

Comments
 (0)