Skip to content

Commit

Permalink
Made pasting more safe
Browse files Browse the repository at this point in the history
Pasting malicious code can no longer freeze the program.
Using BogoMIPS calculator from OpenComputers to estimate IPS.
  • Loading branch information
Vexatos committed Jun 16, 2021
1 parent 6fa7872 commit 736b14e
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 3 deletions.
103 changes: 103 additions & 0 deletions src/sandbox_utils.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
-- Based on microbenchmark by asiekierka in OpenComputers

local jit = require("jit")

local utils = {}

local hookInterval
local ipsCount

local function calcIpsCount(hookInterval)
-- Disable JIT optimizations on the current function.
-- Required because LuaJIT would otherwise optimize
-- the "infinite" loop and make it actually infinite.
jit.off(true)

local bogomipsDivider = 0.05
local bogomipsDeadline = love.timer.getTime() + bogomipsDivider
local ipsCount = 0
local bogomipsBusy = true

local function calcBogoMips()
ipsCount = ipsCount + hookInterval
if love.timer.getTime() > bogomipsDeadline then
bogomipsBusy = false
end
end

-- The following is a bit of nonsensical-seeming code attempting
-- to cover Lua's VM sufficiently for the IPS calculation.
local bogomipsTmpA = {{["b"]=3, ["d"]=9}}
local function c(k)
if k <= 2 then
bogomipsTmpA[1].d = k / 2.0
end
end

debug.sethook(calcBogoMips, "", hookInterval)
while bogomipsBusy do
local st = ""
for k=2,4 do
st = st .. "a" .. k
c(k)
if k >= 3 then
bogomipsTmpA[1].b = bogomipsTmpA[1].b * (k ^ k)
end
end
end

debug.sethook()

return ipsCount / bogomipsDivider
end

function utils.calcIpsAndHookInterval()
local _hookInterval = 1000
local _ipsCount = calcIpsCount(_hookInterval)

-- Since our IPS might still be too generous (hookInterval needs to run at most
-- every 0.05 seconds), we divide it further by 10 relative to that.
_hookInterval = (_ipsCount * 0.005)

if _hookInterval < 1000 then _hookInterval = 1000 end

ipsCount = _ipsCount
hookInterval = _hookInterval

return _ipsCount, _hookInterval
end

local tooLongWithoutYielding = setmetatable({}, { __tostring = function() return "too long without yielding" end})

-- Timeout code adapted from OpenCompuers
function utils.pcallWithTimeout(f, timeout, ...)
if not timeout then
return pcall(f, ...)
end

-- Disable JIT optimization so that debug hooks work
jit.off(f, true)

-- Emergency initialization if needed
if not hookInterval then
utils.calcIpsAndHookInterval()
end

local deadline = math.huge
local function checkDeadline()
if love.timer.getTime() > deadline then
error(tooLongWithoutYielding)
end
end

deadline = love.timer.getTime() + timeout

debug.sethook(checkDeadline, "", hookInterval)
-- Very naïve but better than nothing
local res = {pcall(f, ...)}
debug.sethook()

return unpack(res)
end

return utils
5 changes: 5 additions & 0 deletions src/scenes/loading.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ end
function loadingScene:firstEnter()
local tasks = require("task")

local sandboxUtils = require("sandbox_utils")

local sceneHandler = require("scene_handler")
local toolHandler = require("tool_handler")
local entities = require("entities")
Expand All @@ -57,6 +59,9 @@ function loadingScene:firstEnter()

self:setText(language.scenes.loading.loading)

-- Microbenchmark to estimate instructions per second
sandboxUtils.calcIpsAndHookInterval()

-- Load assets and entity, trigger, effect etc modules
tasks.newTask(
function()
Expand Down
6 changes: 4 additions & 2 deletions src/serialize.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
-- TODO - Allow passing around settings table instead of using args?
-- Makes it easier to have "profiles" for serializing

local sandboxUtils = require("sandbox_utils")

local serialize = {}

local keywords = {
Expand Down Expand Up @@ -270,14 +272,14 @@ function serialize.serialize(t, pretty, sortKeys, useMetaKeys, seen, depth, succ
return success, "{" .. newline .. content.. newline .. closingPadding .. "}"
end

function serialize.unserialize(s, safe)
function serialize.unserialize(s, safe, timeout)
local func = assert(loadstring("return " .. s))

if safe ~= false then
setfenv(func, {})
end

return pcall(func)
return sandboxUtils.pcallWithTimeout(func, timeout)
end

return serialize
2 changes: 1 addition & 1 deletion src/tools/selection.lua
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ local function pasteItemsHotkey()
local clipboard = love.system.getClipboardText()

if validateClipboard(clipboard) then
local success, fromClipboard = utils.unserialize(clipboard)
local success, fromClipboard = utils.unserialize(clipboard, true, 3)

if success then
newPreviews = fromClipboard
Expand Down

0 comments on commit 736b14e

Please sign in to comment.