Here is an implementation of the example I described in my previous
message, hopefully making clear the distinction I am making between
directly and indirectly invoked functions on the one hand, and
"unnamed code X" functions on the other.
#!/usr/bin/env lua5.3
-- Filename: example.lua
function A (x) return B(x) end
function B (x) return math.random(x) end
-- this environment is probably more generous than it needs to be for this
-- example, but just in case...
local environment = {
A = A,
B = B,
math = math,
print = print,
}
local snippet = string.format('print(A(%s))', arg[1])
local loaded_chunk = assert(load(snippet, "=(load)", "t", environment))
local whitelist = {
[loaded_chunk] = true,
[A] = true,
[B] = true,
[math.random] = true,
[print] = true,
[tostring] = true,
}
local function callhook (event)
local info = debug.getinfo(2, "fnS")
print(string.format("%-15s\t%-15s\t%s",
info.source,
info.name or "?", info.func))
if not whitelist[info.func] then
error(string.format("calling disallowed function (%s:%d): %s (%s)",
info.short_src, info.linedefined, (
info.name or "?"),
info.func))
end
end
debug.sethook(callhook, "c")
loaded_chunk()
If I run this script with argument, say, "1000", I get the following
output:
=(load) loaded_chunk function: 0x5588e62a2b80
@./example.lua A function: 0x5588e62a2260
@./example.lua ? function: 0x5588e62a2780
=[C] random function: 0x5588e59bba80
=[C] print function: 0x5588e59b7cc0
=[C] ? function: 0x5588e59b7830
841
...where 841 is, basically, `math.random(1000)`. This confirms that
the loaded chunk does not invoke, *directly or indirectly*, any
function that is not in `whitelist`.
But if I run the script with argument "-1", I get the following
output
=(load) loaded_chunk function: 0x5565a23f3b80
@./example.lua A function: 0x5565a23f3260
@./example.lua ? function: 0x5565a23f3780
=[C] random function: 0x5565a10c9a80
=[C] ? function: 0x5565a10adef0
lua5.3: ./example.lua:34: calling disallowed function ([C]:-1): ? (function: 0x5565a10adef0)
stack traceback:
[C]: in function 'error'
./example.lua:34: in function <./example.lua:29>
[C]: in ?
[C]: in function 'math.random'
./example.lua:6: in function 'B'
(...tail calls...)
(load):1: in local 'loaded_chunk'
./example.lua:41: in main chunk
[C]: in ?
Whatever is triggering the error whose traceback is shown above is a
call to some "unnamed code X" function, which occurred when Lua tried
to evaluate `math.random(-1)`.
My one and only point is that in order to properly implement a
whitelist strategy, one needs to include functions like this "unnamed
code X" function in `whitelist`. I am trying to find out whether this
is at all possible, and if so, how to do it.
kj