41 posts
Posted 16 January 2016 - 11:36 PM
I'd like to sandbox all functions by embedding each of them inside a function where _ENV is set to the sandbox table.
Unfortunately, APIs often declare variables outside the function, which get excluded by my idea of using _ENV (and setfenv wouldn't help either).
My plan to work around this is to unload all APIs and reload them in the sandbox table.
Unfortunately, if I try to reload all of them (except term, window and those the computer can't access), the computer just shuts down.
How would I reload all the available APIs without the computer shutting down?
Edited on 17 January 2016 - 11:32 AM
7083 posts
Location
Tasmania (AU)
Posted 16 January 2016 - 11:44 PM
When an API is loaded, it gets its own environment table - they never share the environment your code is running in. Given this, I don't see how your sandbox creates a problem…?
Are you aware of the differences between _ENV and _G?
8543 posts
Posted 16 January 2016 - 11:47 PM
I think he's talking about APIs that use some sort of internal state tracking, so that if two different programs were trying to use an API that did different things depending on the internal hidden state, they might get unexpected results.
41 posts
Posted 16 January 2016 - 11:50 PM
When an API is loaded, it gets its own environment table - they never share the environment your code is running in. Given this, I don't see how your sandbox creates a problem…?
Are you aware of the differences between _ENV and _G?
I'm enclosing each function in a function whose _ENV is equal to the sandbox. Kind of like this:
sandbox[function] = function(...)
_ENV = sandbox
return _G[function](...)
end
However, APIs that define variables outside the function definition can no longer access those variables. And they
do use the sandbox environment, functions inherit their environment from the function above them in the stack.
Edited on 16 January 2016 - 10:51 PM
7083 posts
Location
Tasmania (AU)
Posted 17 January 2016 - 12:34 AM
Here's the source of os.loadAPI() (as defined in
bios.lua):
Spoiler
local tAPIsLoading = {}
function os.loadAPI( _sPath )
local sName = fs.getName( _sPath )
if tAPIsLoading[sName] == true then
printError( "API "..sName.." is already being loaded" )
return false
end
tAPIsLoading[sName] = true
local tEnv = {}
setmetatable( tEnv, { __index = _G } )
local fnAPI, err = loadfile( _sPath, tEnv )
if fnAPI then
local ok, err = pcall( fnAPI )
if not ok then
printError( err )
tAPIsLoading[sName] = nil
return false
end
else
printError( err )
tAPIsLoading[sName] = nil
return false
end
local tAPI = {}
for k,v in pairs( tEnv ) do
if k ~= "_ENV" then
tAPI[k] = v
end
end
_G[sName] = tAPI
tAPIsLoading[sName] = nil
return true
end
As you can see, the API is loaded as a function, and then the environment of that function is replaced with a custom one. The whole point of this is so that any globals the API defines can then be extracted and dumped into the API table which finally ends up in _G (that being the "loaded API").
That is to say, APIs can't touch _ENV. This is why they can't call "shell" or "multishell" functions: Those are loaded into the global environment area all our scripts use, _ENV, as opposed to the table APIs generally go into, _G.
(Edit: Point is, everything an API does is supposed to apply on a system-wide basis. If two different scripts asking one API to do something results in a conflict, then that's the fault of the API, and it's not your problem to fix. There's a reason why you can't define windows using the term API as a parent, for example.)
Edited on 16 January 2016 - 11:48 PM
41 posts
Posted 17 January 2016 - 06:58 AM
Here's the source of os.loadAPI() (as defined in
bios.lua):
Spoiler
local tAPIsLoading = {}
function os.loadAPI( _sPath )
local sName = fs.getName( _sPath )
if tAPIsLoading[sName] == true then
printError( "API "..sName.." is already being loaded" )
return false
end
tAPIsLoading[sName] = true
local tEnv = {}
setmetatable( tEnv, { __index = _G } )
local fnAPI, err = loadfile( _sPath, tEnv )
if fnAPI then
local ok, err = pcall( fnAPI )
if not ok then
printError( err )
tAPIsLoading[sName] = nil
return false
end
else
printError( err )
tAPIsLoading[sName] = nil
return false
end
local tAPI = {}
for k,v in pairs( tEnv ) do
if k ~= "_ENV" then
tAPI[k] = v
end
end
_G[sName] = tAPI
tAPIsLoading[sName] = nil
return true
end
As you can see, the API is loaded as a function, and then the environment of that function is replaced with a custom one. The whole point of this is so that any globals the API defines can then be extracted and dumped into the API table which finally ends up in _G (that being the "loaded API").
That is to say, APIs can't touch _ENV. This is why they can't call "shell" or "multishell" functions: Those are loaded into the global environment area all our scripts use, _ENV, as opposed to the table APIs generally go into, _G.
(Edit: Point is, everything an API does is supposed to apply on a system-wide basis. If two different scripts asking one API to do something results in a conflict, then that's the fault of the API, and it's not your problem to fix. There's a reason why you can't define windows using the term API as a parent, for example.)
There's a problem with that. I want to unload all the APIs bios.lua initialized and I want to reload those in the sandboxed filesystem.
I've already sandboxed os.loadAPI to load APIs into the sandboxed environment.
7083 posts
Location
Tasmania (AU)
Posted 17 January 2016 - 11:01 AM
I still don't get it. Even if you wanted to load fresh copies of all APIs in your sandboxed environment (what's this about a filesystem?), why would you want to unload the original APIs? Why not just leave them in the original _G table?
All that aside, if you want to know what's wrong with your code: post it. We can't very well comment on what we can't see. If you don't want to post the whole project, that's fine: just cobble together the bits that're causing you problems, make sure they're still causing said problems, and post that.
Edited on 17 January 2016 - 10:08 AM
41 posts
Posted 17 January 2016 - 12:32 PM
Somehow I got it to work… I guess unloading the APIs really is a bad idea.
The only API I had to reload was the IO API.
I have no idea how I got the others to work.
Here's the method I used (worked)
local sandbox = {--[[ Generate environment here ]]} -- Your sandbox environment
sandbox._G = sandbox
sandbox.func = function(...) -- Do this for each function
_ENV = sandbox
return func(...)
end
Thanks for your help.