Attempt to fix

This commit is contained in:
zeng-github01 2025-08-30 02:33:18 +08:00
parent 7d9eac5584
commit 211b81fdd0
3 changed files with 1497 additions and 1370 deletions

View File

@ -47,11 +47,14 @@ local stats = {
}
local function doSleep()
local epsilon = 1e-6
local deadline = computer.uptime() + (tonumber(options.i) or tonumber(options.interval) or 1)
repeat
event.pull(deadline - computer.uptime())
until computer.uptime() >= deadline
local remaining = deadline - computer.uptime()
if remaining <= epsilon then break end
event.pull(math.max(remaining, 0.001))
until computer.uptime() >= deadline - epsilon
end
local function doPing()

View File

@ -25,9 +25,13 @@ end
function os.sleep(timeout)
checkArg(1, timeout, "number", "nil")
local deadline = computer.uptime() + (timeout or 0)
local epsilon = 1e-6
repeat
event.pull(deadline - computer.uptime())
until computer.uptime() >= deadline
local remaining = deadline - computer.uptime()
if remaining <= epsilon then break end
event.pull(math.max(remaining, 0.001))
until computer.uptime() >= deadline - epsilon
end
os.setenv("PATH", "/bin:/usr/bin:/home/bin:.")

View File

@ -37,11 +37,15 @@ local ipsCount = calcHookInterval()
-- 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
if hookInterval < 1000 then
hookInterval = 1000
end
local deadline = math.huge
local hitDeadline = false
local tooLongWithoutYielding = setmetatable({}, { __tostring = function() return "too long without yielding" end})
local tooLongWithoutYielding = setmetatable({}, { __tostring = function()
return "too long without yielding"
end })
local function checkDeadline()
if computer.realTime() > deadline then
debug.sethook(coroutine.running(), checkDeadline, "", 1)
@ -96,8 +100,7 @@ do
local SPECIALS = "^$*+?.([%-"
local SHORT_STRING = 500 -- use native implementations for short strings
local string_find, string_lower, string_match, string_gmatch, string_gsub =
string.find, string.lower, string.match, string.gmatch, string.gsub
local string_find, string_lower, string_match, string_gmatch, string_gsub = string.find, string.lower, string.match, string.gmatch, string.gsub
local match -- forward declaration
@ -157,22 +160,45 @@ do
}, strptr_mt)
end
local function islower(b) return b >= 'a' and b <= 'z' end
local function isupper(b) return b >= 'A' and b <= 'Z' end
local function isalpha(b) return islower(b) or isupper(b) end
local function iscntrl(b) return b <= '\007' or (b >= '\010' and b <= '\017') or (b >= '\020' and b <= '\027') or (b >= '\030' and b <= '\037' and b ~= ' ') or b == '\177' end
local function isdigit(b) return b >= '0' and b <= '9' end
local function ispunct(b) return (b >= '{' and b <= '~') or (b == '`') or (b >= '[' and b <= '_') or (b == '@') or (b >= ':' and b <= '?') or (b >= '(' and b <= '/') or (b >= '!' and b <= '\'') end
local function isspace(b) return b == '\t' or b == '\n' or b == '\v' or b == '\f' or b == '\r' or b == ' ' end
local function isalnum(b) return isalpha(b) or isdigit(b) end
local function isxdigit(b) return isdigit(b) or (b >= 'a' and b <= 'f') or (b >= 'A' and b <= 'F') end
local function isgraph(b) return not iscntrl(b) and not isspace(b) end
local function islower(b)
return b >= 'a' and b <= 'z'
end
local function isupper(b)
return b >= 'A' and b <= 'Z'
end
local function isalpha(b)
return islower(b) or isupper(b)
end
local function iscntrl(b)
return b <= '\007' or (b >= '\010' and b <= '\017') or (b >= '\020' and b <= '\027') or (b >= '\030' and b <= '\037' and b ~= ' ') or b == '\177'
end
local function isdigit(b)
return b >= '0' and b <= '9'
end
local function ispunct(b)
return (b >= '{' and b <= '~') or (b == '`') or (b >= '[' and b <= '_') or (b == '@') or (b >= ':' and b <= '?') or (b >= '(' and b <= '/') or (b >= '!' and b <= '\'')
end
local function isspace(b)
return b == '\t' or b == '\n' or b == '\v' or b == '\f' or b == '\r' or b == ' '
end
local function isalnum(b)
return isalpha(b) or isdigit(b)
end
local function isxdigit(b)
return isdigit(b) or (b >= 'a' and b <= 'f') or (b >= 'A' and b <= 'F')
end
local function isgraph(b)
return not iscntrl(b) and not isspace(b)
end
-- translate a relative string position: negative means back from end
local function posrelat(pos, len)
if pos >= 0 then return pos
elseif -pos > len then return 0
else return len + pos + 1
if pos >= 0 then
return pos
elseif -pos > len then
return 0
else
return len + pos + 1
end
end
@ -196,7 +222,8 @@ do
end
local function classend(ms, p)
local p0 = p:char() p = p:copy(1)
local p0 = p:char()
p = p:copy(1)
if p0 == L_ESC then
if p == ms.p_end then
error("malformed pattern (ends with %)")
@ -226,21 +253,35 @@ do
local function match_class(c, cl)
local res
local cll = string_lower(cl)
if cll == 'a' then res = isalpha(c)
elseif cll == 'c' then res = iscntrl(c)
elseif cll == 'd' then res = isdigit(c)
elseif cll == 'g' then res = isgraph(c)
elseif cll == 'l' then res = islower(c)
elseif cll == 'p' then res = ispunct(c)
elseif cll == 's' then res = isspace(c)
elseif cll == 'u' then res = isupper(c)
elseif cll == 'w' then res = isalnum(c)
elseif cll == 'x' then res = isxdigit(c)
elseif cll == 'z' then res = c == '\0' -- deprecated option
else return cl == c
if cll == 'a' then
res = isalpha(c)
elseif cll == 'c' then
res = iscntrl(c)
elseif cll == 'd' then
res = isdigit(c)
elseif cll == 'g' then
res = isgraph(c)
elseif cll == 'l' then
res = islower(c)
elseif cll == 'p' then
res = ispunct(c)
elseif cll == 's' then
res = isspace(c)
elseif cll == 'u' then
res = isupper(c)
elseif cll == 'w' then
res = isalnum(c)
elseif cll == 'x' then
res = isxdigit(c)
elseif cll == 'z' then
res = c == '\0' -- deprecated option
else
return cl == c
end
if islower(cl) then return res
else return not res
if islower(cl) then
return res
else
return not res
end
end
@ -275,10 +316,14 @@ do
return false
end
local p0 = p:char()
if p0 == '.' then return true -- matches any char
elseif p0 == L_ESC then return match_class(s:char(), p:char(1))
elseif p0 == '[' then return matchbracketclass(s:char(), p, ep:copy(-1))
else return p:char() == s:char()
if p0 == '.' then
return true -- matches any char
elseif p0 == L_ESC then
return match_class(s:char(), p:char(1))
elseif p0 == '[' then
return matchbracketclass(s:char(), p, ep:copy(-1))
else
return p:char() == s:char()
end
end
@ -286,7 +331,9 @@ do
if p >= ms.p_end - 1 then
error("malformed pattern (missing arguments to %b)")
end
if s:char() ~= p:char() then return nil end
if s:char() ~= p:char() then
return nil
end
local b = p:char()
local e = p:char(1)
local cont = 1
@ -294,7 +341,9 @@ do
while s:step() < ms.src_end do
if s:char() == e then
cont = cont - 1
if cont == 0 then return s:step() end
if cont == 0 then
return s:step()
end
elseif s:char() == b then
cont = cont + 1
end
@ -310,7 +359,9 @@ do
-- keeps trying to match with the maximum repetitions
while i >= 0 do
local res = match(ms, s:copy(i), ep:copy(1))
if res then return res end
if res then
return res
end
i = i - 1 -- else didn't match; reduce 1 repetition to try again
end
return nil
@ -324,7 +375,8 @@ do
return res
elseif singlematch(ms, s, p, ep) then
s:step() -- try with one more repetition
else return nil
else
return nil
end
end
end
@ -336,7 +388,8 @@ do
ms.capture[level].len = what
ms.level = level + 1
local res = match(ms, s, p)
if res == nil then -- match failed?
if res == nil then
-- match failed?
ms.level = ms.level - 1 -- undo capture
end
return res
@ -346,7 +399,8 @@ do
local l = capture_to_close(ms)
ms.capture[l].len = s - ms.capture[l].init -- close capture
local res = match(ms, s, p)
if res == nil then -- match failed?
if res == nil then
-- match failed?
ms.capture[l].len = CAP_UNFINISHED -- undo capture
end
return res
@ -359,7 +413,8 @@ do
ms.capture[l].init:head(len) == s:head(len)
then
return s:copy(len)
else return nil
else
return nil
end
end
@ -369,32 +424,39 @@ do
:: init :: -- using goto's to optimize tail recursion
if p ~= ms.p_end then
local p0 = p:char()
if p0 == '(' then -- start capture
if p:char(1) == ')' then -- position capture?
if p0 == '(' then
-- start capture
if p:char(1) == ')' then
-- position capture?
s = start_capture(ms, s, p:copy(2), CAP_POSITION)
else
s = start_capture(ms, s, p:copy(1), CAP_UNFINISHED)
end
goto brk
elseif p0 == ')' then -- end capture
elseif p0 == ')' then
-- end capture
s = end_capture(ms, s, p:copy(1))
goto brk
elseif p0 == '$' then
if p + 1 ~= ms.p_end then -- is the `$' the last char in pattern?
if p + 1 ~= ms.p_end then
-- is the `$' the last char in pattern?
goto dflt -- no; go to default
end
s = (s == ms.src_end) and s or nil -- check end of string
goto brk
elseif p0 == L_ESC then -- escaped sequences not in the format class[*+?-]?
elseif p0 == L_ESC then
-- escaped sequences not in the format class[*+?-]?
local p1 = p:char(1)
if p1 == 'b' then -- balanced string?
if p1 == 'b' then
-- balanced string?
s = matchbalance(ms, s, p:copy(2))
if s ~= nil then
p:step(4)
goto init -- return match(ms, s, p + 4)
end
-- else fail (s == nil)
elseif p1 == 'f' then -- frontier?
elseif p1 == 'f' then
-- frontier?
p:step(2)
if p:char() ~= '[' then
error("missing [ after %f in pattern")
@ -408,7 +470,8 @@ do
goto init -- return match(ms, s, ep)
end
s = nil -- match failed
elseif isdigit(p:char(1)) then -- capture results (%0-%9)?
elseif isdigit(p:char(1)) then
-- capture results (%0-%9)?
s = match_capture(ms, s, p:char(1))
if s ~= nil then
p:step(2)
@ -419,18 +482,23 @@ do
end
goto brk
end
::dflt:: do
:: dflt ::
do
local ep = classend(ms, p) -- points to what is next
local ep0 = ep:char()
if not singlematch(ms, s, p, ep) then
if ep0 == '*' or ep0 == '?' or ep0 == '-' then -- accept empty?
if ep0 == '*' or ep0 == '?' or ep0 == '-' then
-- accept empty?
p = ep:copy(1)
goto init -- return match(ms, s, ep + 1)
else -- '+' or no suffix
else
-- '+' or no suffix
s = nil -- fail
end
else -- matched once
if ep0 == '?' then -- optional
else
-- matched once
if ep0 == '?' then
-- optional
local res = match(ms, s:copy(1), ep:copy(1))
if res ~= nil then
s = res
@ -438,11 +506,14 @@ do
p = ep:copy(1)
goto init -- else return match(ms, s, ep + 1)
end
elseif ep0 == '+' then -- 1 or more repetitions
elseif ep0 == '+' then
-- 1 or more repetitions
s = max_expand(ms, s:copy(1), p, ep) -- 1 match already done
elseif ep0 == '*' then -- 0 or more repetitions
elseif ep0 == '*' then
-- 0 or more repetitions
s = max_expand(ms, s, p, ep)
elseif ep0 == '-' then -- 0 or more repetitions (minimum)
elseif ep0 == '-' then
-- 0 or more repetitions (minimum)
s = min_expand(ms, s, p, ep)
else
s:step()
@ -458,14 +529,17 @@ do
local function push_onecapture(ms, i, s, e)
if i >= ms.level then
if i == 0 then -- ms->level == 0, too
if i == 0 then
-- ms->level == 0, too
return s:head(e - s) -- add whole match
else
error("invalid capture index")
end
else
local l = ms.capture[i].len;
if l == CAP_UNFINISHED then error("unfinished capture") end
if l == CAP_UNFINISHED then
error("unfinished capture")
end
if l == CAP_POSITION then
return ms.capture[i].init - ms.src_init + 1
else
@ -507,8 +581,10 @@ do
local s = strptr(str)
local p = strptr(pattern)
local init = posrelat(init or 1, #str)
if init < 1 then init = 1
elseif init > #str + 1 then -- start after string's end?
if init < 1 then
init = 1
elseif init > #str + 1 then
-- start after string's end?
return nil -- cannot find anything
end
-- explicit request or no special characters?
@ -521,7 +597,9 @@ do
else
local s1 = s:copy(init - 1)
local anchor = p:char() == '^'
if anchor then p:step() end
if anchor then
p:step()
end
local ms = {
src_init = s,
src_end = s:copy(s:len()),
@ -564,9 +642,13 @@ do
checkArg(3, init, "number", "nil")
if init ~= nil then
start = posrelat(init, #s)
if start < 1 then start = 0
elseif start > #s + 1 then start = #s + 1
else start = start - 1 end
if start < 1 then
start = 0
elseif start > #s + 1 then
start = #s + 1
else
start = start - 1
end
end
end
@ -585,7 +667,9 @@ do
local e = match(ms, src, p)
if e ~= nil then
local newstart = e - s
if e == src then newstart = newstart + 1 end -- empty match? go at least one position
if e == src then
newstart = newstart + 1
end -- empty match? go at least one position
start = newstart
return push_captures(ms, src, e)
end
@ -621,10 +705,12 @@ do
res = r(push_captures(ms, s, e))
elseif tr == "table" then
res = r[push_onecapture(ms, 0, s, e)]
else -- LUA_TNUMBER or LUA_TSTRING
else
-- LUA_TNUMBER or LUA_TSTRING
return add_s(ms, b, s, e, r)
end
if not res then -- nil or false?
if not res then
-- nil or false?
res = s:head(e - s) -- keep original text
elseif type(res) ~= "string" and type(res) ~= "number" then
error("invalid replacement value (a " .. type(res) .. ")")
@ -666,14 +752,18 @@ do
n = n + 1
b = add_value(ms, b, src, e, repl, tr)
end
if e and e > src then -- non empty match?
if e and e > src then
-- non empty match?
src = e -- skip it
elseif src < ms.src_end then
b = b .. src:char()
src:step()
else break
else
break
end
if anchor then
break
end
if anchor then break end
end
b = b .. src:head()
return b, n -- number of substitutions
@ -740,7 +830,8 @@ sandbox = {
error = error,
_G = nil, -- see below
getmetatable = function(t)
if type(t) == "string" then -- don't allow messing with the string mt
if type(t) == "string" then
-- don't allow messing with the string mt
return nil
end
local result = getmetatable(t)
@ -763,7 +854,9 @@ sandbox = {
pcall = function(...)
-- prevent infinite pcall() loops by checking deadline before pcall()
local status, err = pcall(checkDeadline)
if not status then return false, err end
if not status then
return false, err
end
return pcallTimeoutCheck(pcall(...))
end,
@ -777,7 +870,8 @@ sandbox = {
if type(mt) ~= "table" then
return setmetatable(t, mt)
end
if rawget(mt, "__gc") ~= nil then -- If __gc is set to ANYTHING not `nil`, we're gonna have issues
if rawget(mt, "__gc") ~= nil then
-- If __gc is set to ANYTHING not `nil`, we're gonna have issues
-- Garbage collector callbacks apparently can't be sandboxed after
-- all, because hooks are disabled while they're running. So we just
-- disable them altogether by default.
@ -800,7 +894,9 @@ sandbox = {
rawset(mt, "__gc", nil) -- remove __gc
local ret = table.pack(pcall(setmetatable, t, mt))
rawset(mt, "__gc", gc) -- restore __gc
if not ret[1] then error(ret[2], 0) end
if not ret[1] then
error(ret[2], 0)
end
return table.unpack(ret, 2, ret.n)
end
end
@ -818,7 +914,9 @@ sandbox = {
errorCapture = function(ff, ...)
-- prevent infinite xpcall() loops by checking deadline before xpcall()
local status, err = pcall(checkDeadline)
if not status then return false, err end
if not status then
return false, err
end
return xpcall(ff, function(...)
if rawequal((...), tooLongWithoutYielding) then
@ -840,31 +938,39 @@ sandbox = {
coroutine = {
create = coroutine.create,
resume = function(co, ...) -- custom resume part for bubbling sysyields
resume = function(co, ...)
-- custom resume part for bubbling sysyields
checkArg(1, co, "thread")
local args = table.pack(...)
while true do -- for consecutive sysyields
while true do
-- for consecutive sysyields
debug.sethook(co, checkDeadline, "", hookInterval)
local result = table.pack(
coroutine.resume(co, table.unpack(args, 1, args.n)))
debug.sethook(co) -- avoid gc issues
checkDeadline()
if result[1] then -- success: (true, sysval?, ...?)
if coroutine.status(co) == "dead" then -- return: (true, ...)
if result[1] then
-- success: (true, sysval?, ...?)
if coroutine.status(co) == "dead" then
-- return: (true, ...)
return true, table.unpack(result, 2, result.n)
elseif result[2] ~= nil then -- yield: (true, sysval)
elseif result[2] ~= nil then
-- yield: (true, sysval)
args = table.pack(coroutine.yield(result[2]))
else -- yield: (true, nil, ...)
else
-- yield: (true, nil, ...)
return true, table.unpack(result, 3, result.n)
end
else -- error: result = (false, string)
else
-- error: result = (false, string)
return false, result[2]
end
end
end,
running = coroutine.running,
status = coroutine.status,
wrap = function(f) -- for bubbling coroutine.resume
wrap = function(f)
-- for bubbling coroutine.resume
local co = coroutine.create(f)
return function(...)
local result = table.pack(sandbox.coroutine.resume(co, ...))
@ -875,7 +981,8 @@ sandbox = {
end
end
end,
yield = function(...) -- custom yield part for bubbling sysyields
yield = function(...)
-- custom yield part for bubbling sysyields
return coroutine.yield(nil, ...)
end,
-- Lua 5.3.
@ -929,7 +1036,8 @@ sandbox = {
fmod = math.fmod,
frexp = math.frexp, -- Deprecated in Lua 5.3
huge = math.huge,
ldexp = math.ldexp or function(a, e) -- Deprecated in Lua 5.3
ldexp = math.ldexp or function(a, e)
-- Deprecated in Lua 5.3
return a * (2.0 ^ e)
end,
log = math.log,
@ -937,7 +1045,8 @@ sandbox = {
min = math.min,
modf = math.modf,
pi = math.pi,
pow = math.pow or function(a, b) -- Deprecated in Lua 5.3
pow = math.pow or function(a, b)
-- Deprecated in Lua 5.3
return a ^ b
end,
rad = math.rad,
@ -1025,8 +1134,12 @@ sandbox = {
-- e.g. (1, 2) is only (1), the 2 is not returned
-- this is critically important here because the 2nd return value from these
-- debug methods is the value itself, which opens a door to exploit the sandbox
getlocal = function(...) return (debug.getlocal(...)) end,
getupvalue = function(...) return (debug.getupvalue(...)) end,
getlocal = function(...)
return (debug.getlocal(...))
end,
getupvalue = function(...)
return (debug.getupvalue(...))
end,
},
-- Lua 5.3.
@ -1070,9 +1183,11 @@ local wrappedUserdata = setmetatable({}, wrappedUserdataMeta)
local function processResult(result)
result = wrapUserdata(result) -- needed for metamethods.
if not result[1] then -- error that should be re-thrown.
if not result[1] then
-- error that should be re-thrown.
error(result[2], 0)
else -- success or already processed error.
else
-- success or already processed error.
return table.unpack(result, 2, result.n)
end
end
@ -1084,7 +1199,8 @@ local function invoke(target, direct, ...)
args = unwrapUserdata(args)
result = table.pack(target.invoke(table.unpack(args, 1, args.n)))
args = nil -- clear upvalue, avoids trying to persist it
if result.n == 0 then -- limit for direct calls reached
if result.n == 0 then
-- limit for direct calls reached
result = nil
end
-- no need to wrap here, will be wrapped in processResult
@ -1411,14 +1527,17 @@ local libcomputer = {
return spcall(computer.pushSignal, ...)
end,
pullSignal = function(timeout)
local deadline = computer.uptime() +
(type(timeout) == "number" and timeout or math.huge)
local epsilon = 1e-6
local deadline = computer.uptime() + (type(timeout) == "number" and timeout or math.huge)
repeat
local signal = table.pack(coroutine.yield(deadline - computer.uptime()))
local remaining = deadline - computer.uptime()
if remaining <= epsilon then break end
local signal = table.pack(coroutine.yield(math.max(remaining, 0.001)))
if signal.n > 0 then
return table.unpack(signal, 1, signal.n)
end
until computer.uptime() >= deadline
until computer.uptime() >= deadline - epsilon
end,
beep = function(...)
@ -1520,7 +1639,8 @@ local function main()
-- NOTE: since this is run in an executor thread and we enforce timeouts
-- in user-defined garbage collector callbacks this should be safe.
if persistKey then -- otherwise we're in LuaJ
if persistKey then
-- otherwise we're in LuaJ
forceGC = forceGC - 1
if forceGC < 1 then
collectgarbage("collect")