mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-24 20:36:19 +02:00
- With events.tricks a plugin hears the local skater's tricks: trick_started (where), trick_landed (points, multiplier, where it started and landed, how long it took) and line_broken (a slam or a wipeout); reskate.score.current() is the score HUD's line, multiplier and whether a trick is going on. Without the permission the events cannot be registered. - The score HUD is read into the trick watch (Extension/Skater/score_hud.h, trick_watch.h) on the client tick only while a running plugin has events.tricks; a new level starts a new line. plugins::start takes the image base for it. - reskate-test has a tricks module (skipped until a trick lands) and ptest trick, which waits 30 s for one and prints what its events said; reskate-hostile has trick-loop. Not yet checked against real tricks in game.
129 lines
5.8 KiB
Lua
129 lines
5.8 KiB
Lua
-- reskate-hostile: a plugin written to break the sandbox, one attack per command. Every attack
|
|
-- must end with this plugin stopped or refused and the game running as before. `hostile list`
|
|
-- names them; after one that stops it, `plugin on reskate-hostile` starts it again.
|
|
local attacks, order = {}, {}
|
|
local function attack(name, expect, fn)
|
|
attacks[name] = {expect = expect, fn = fn}
|
|
table.insert(order, name)
|
|
end
|
|
|
|
attack("loop", "stopped: longer than 50 ms", function()
|
|
while true do end
|
|
end)
|
|
attack("caught-loop", "stopped: longer than 50 ms, though it catches the error", function()
|
|
while true do pcall(function() while true do end end) end
|
|
end)
|
|
attack("tick-loop", "stopped at the next tick", function()
|
|
reskate.on("tick", function() while true do end end)
|
|
end)
|
|
attack("memory", "stopped: more than 64 MB", function()
|
|
local t = {}
|
|
while true do t[#t + 1] = string.rep("x", 8000000) end
|
|
end)
|
|
attack("caught-memory", "stopped: more than 64 MB, though it catches the error", function()
|
|
pcall(function()
|
|
local t = {}
|
|
while true do t[#t + 1] = string.rep("y", 8000000) end
|
|
end)
|
|
reskate.console.print("ESCAPED: carried on after a memory bomb")
|
|
end)
|
|
attack("huge-string", "stopped: more than 64 MB", function()
|
|
local s = string.rep("x", 100000000)
|
|
return #s
|
|
end)
|
|
attack("buffer", "stopped: more than 64 MB", function()
|
|
local b = buffer.create(1e9)
|
|
return buffer.len(b)
|
|
end)
|
|
attack("coroutines", "stopped: its time or its memory", function()
|
|
while true do coroutine.wrap(function() end)() end
|
|
end)
|
|
attack("errors", "stopped after 5 errors in 10 s", function()
|
|
reskate.on("tick", function() error("hostile: an error every tick") end)
|
|
end)
|
|
attack("slow", "stopped after 3 s over 2 ms a tick", function()
|
|
reskate.on("tick", function()
|
|
local start = os.clock()
|
|
while os.clock() - start < 0.004 do end
|
|
end)
|
|
end)
|
|
attack("log-flood", "keeps running: 50 of the 1000 lines are logged", function()
|
|
for i = 1, 1000 do reskate.log("hostile flood " .. i) end
|
|
end)
|
|
attack("timers", "keeps running: refused past 1024 timers", function()
|
|
for i = 1, 2000 do reskate.time.after(600000, function() end) end
|
|
end)
|
|
attack("recursion", "keeps running: an error, not a crash", function()
|
|
local function deep(n) return deep(n + 1) + 1 end
|
|
return deep(1)
|
|
end)
|
|
attack("slow-unload", "an unload handler that never ends: cut off at 20 ms when it is switched off", function()
|
|
reskate.on("unload", function() while true do end end)
|
|
reskate.console.print("hostile: now switch it off: plugin off reskate-hostile")
|
|
end)
|
|
|
|
attack("teleport-spam", "keeps running: two teleports a second, the rest refused", function()
|
|
local sent = 0
|
|
for i = 1, 1000 do
|
|
if reskate.skater.teleport(vector.create(0, 0, 0)) then sent += 1 end
|
|
end
|
|
reskate.console.print("hostile teleport-spam: " .. sent .. " of 1000 sent")
|
|
end)
|
|
attack("raycast-flood", "keeps running: refused past 64 raycasts a tick", function()
|
|
for i = 1, 100000 do reskate.world.raycast(vector.create(0, 100, 0), vector.create(0, -100, 0)) end
|
|
end)
|
|
|
|
attack("trick-loop", "stopped at the next trick landed", function()
|
|
reskate.on("trick_landed", function() while true do end end)
|
|
reskate.console.print("hostile: now land a trick")
|
|
end)
|
|
|
|
-- Each returns true when that way out worked.
|
|
local escapes = {
|
|
{"io", function() return io ~= nil end},
|
|
{"loadstring or load", function() return loadstring ~= nil or load ~= nil end},
|
|
{"dofile or loadfile", function() return dofile ~= nil or loadfile ~= nil end},
|
|
{"package", function() return package ~= nil end},
|
|
{"os beyond the clock", function() return os.execute ~= nil or os.getenv ~= nil or os.remove ~= nil or os.exit ~= nil end},
|
|
{"changing reskate", function() return pcall(function() reskate.log = nil end) end},
|
|
{"rawset on reskate", function() return pcall(rawset, reskate, "x", 1) end},
|
|
{"a metatable on reskate", function() return pcall(setmetatable, reskate, {}) end},
|
|
{"changing a library", function() return pcall(function() string.rep = nil end) end},
|
|
{"the string metatable", function() return pcall(function() getmetatable("").__index = {} end) end},
|
|
{"reading outside the package", function() return pcall(reskate.package.read, "../../../Windows/win.ini") end},
|
|
{"a junction-style path", function() return pcall(reskate.package.read, "..\\..\\Windows\\win.ini") end},
|
|
{"require outside", function() return pcall(require, "C:/Windows/win.ini") end},
|
|
{"require up", function() return pcall(require, "../manifest") end},
|
|
{"taking a plugin command", function() return pcall(reskate.console.command, "plugin off", "x", function() end) end},
|
|
{"taking a ReSkate command", function() return pcall(reskate.console.command, "load", "x", function() end) end},
|
|
{"storage without the permission", function() return pcall(reskate.storage.get, "x") end},
|
|
}
|
|
attack("escapes", "keeps running: every way out refused", function()
|
|
local escaped = 0
|
|
for _, try in escapes do
|
|
local ok, worked = pcall(try[2])
|
|
if ok and worked then
|
|
escaped += 1
|
|
reskate.console.print("ESCAPED: " .. try[1])
|
|
end
|
|
end
|
|
reskate.console.print("hostile escapes: " .. (#escapes - escaped) .. " of " .. #escapes .. " refused")
|
|
end)
|
|
|
|
reskate.console.command("hostile", "Attacks the plugin sandbox: hostile list, hostile <attack>", function(args)
|
|
local name = args[1]
|
|
if not name or name == "list" then
|
|
for _, n in order do reskate.console.print(n .. ": " .. attacks[n].expect) end
|
|
return
|
|
end
|
|
local found = attacks[name]
|
|
if not found then
|
|
reskate.console.print("hostile: there is no attack " .. name .. " (hostile list names them)")
|
|
return
|
|
end
|
|
reskate.console.print("hostile: " .. name .. ", expected: " .. found.expect)
|
|
found.fn()
|
|
end)
|
|
|
|
reskate.log("reskate-hostile loaded: hostile list names its attacks")
|