This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
mrpoopy345's profile picture

Setfenv

Started by mrpoopy345, 04 May 2014 - 11:18 AM
mrpoopy345 #1
Posted 04 May 2014 - 01:18 PM
Hi, I am have been getting into OS development lately, but there is one thing I think is essential to OS's. Sandboxing. Now my good friends on the IRC told me to use setfenv for it, and I looked at the few tutorials out there about it, but I still cannot quite get it. What is setfenv? What does it do? And could someone give me a short example code for using it?

Thank you.
viluon #2
Posted 04 May 2014 - 01:29 PM
here is the PIL article about setfenv()

What does it do?
setfenv sets the environment of the currently running chunk or a function defined as setfenv's first argument.

What's that environment you are talking about?
Environment is basically a table containing all the functions and variables you have declared or have access to. Look here for more information at environment.


Example: create a sandbox with only the print() function allowed:

setfenv(shell.run("MyProgram"),{print=_G.print})
Note: I haven't tested this code.


Advice: look through the code of OSes on these forums and learn how others do sandboxing
Edited on 04 May 2014 - 01:41 PM
Engineer #3
Posted 04 May 2014 - 01:40 PM
with setfenv you can set the environment of the function, which can be a program (since you are probably loadstring'ing a programs' content).
By default the environment is only the _G table, but we can expand on that. Let's say we want to add a global log function to a program, but we do not want to load it globally, then we can do the following:

local file = fs.open( "somefile", "r" )
local content = file.readAll()
file.close()

local func, err = loadstring( content, "somefile" )
-- Assuming there are no runtime errors, add that checking yourself

-- Now we set the environment of the function, with our log function which isn't in the global table
setfenv( func, { log = function( s ) 
    -- Some function's content
end } )

-- Execute the function, so that it actually gets run
func()
-- Im assuming there are no runtime errors, you should add checking for that yourself

Now we run into a problem, we only set the environment of that function to the log function, but we also want to have the global table because that is quite essential.
For that we can use metatables, especially the __index metamethod. So then we can do the following:

setfenv( func, setmetatable( { log = function( s ) end }, { __index = _G }) )

Now the function can index global from that function. Now for sandboxing we want to override some api's, like the fs API. We can simply put that in the former table of the metatable, because then it's "overwritten", but only in that function. So, for instance we can do:

setfenv( func, setmetatable( {
   fs = {
      -- All functions
   }, {
     __index = _G
   }
)

Now if in the given function calls a global variable, it searches in the former table if it's available, and when not found it looks in the actual global table. So essentially we are overwriting _G.fs, but not really!

This is a vague/quick description of it, please ask questions if you dont understand it! :)/>
viluon #4
Posted 04 May 2014 - 01:42 PM
Thanks for expanding Engineer! :)/>
Engineer #5
Posted 04 May 2014 - 02:27 PM
– snip

setfenv(shell.run("MyProgram"),{print=_G.print})

This won't work, since you can set the environment of a function. Shell.run runs a function and does not return that, so this is the equivalent of (depending on the program, but regularly it's this):

setfenv( nil, {print=_G.print} )
Which obviously will error!
viluon #6
Posted 04 May 2014 - 03:39 PM
Yeah sorry for that
HometownPotato #7
Posted 04 May 2014 - 06:18 PM
Here's a very basic example (make all new added global variables a table for whatever reason)


local oldEnv = getfenv();
setfenv(1, setmetatable({}, {__index = oldEnv; __newindex = function(self, key, value) rawset(self, key, {value}) end;}));
Engineer #8
Posted 04 May 2014 - 09:31 PM
Here's a very basic example (make all new added global variables a table for whatever reason)


local oldEnv = getfenv();
setfenv(1, setmetatable({}, {__index = oldEnv; __newindex = function(self, key, value) rawset(self, key, {value}) end;}));
How is that sandboxing? 0o
That is just messing with metatables, nothing more
HometownPotato #9
Posted 04 May 2014 - 09:36 PM
I'm giving an example of setfenv.
And you do use metatables as well for sandboxing.
Engineer #10
Posted 04 May 2014 - 09:46 PM
I'm giving an example of setfenv.
And you do use metatables as well for sandboxing.
But not like that
HometownPotato #11
Posted 04 May 2014 - 09:57 PM
I know, I've created sandboxes before. But it wasn't my intention of creating any kind of sandbox with that script. I read the title and his first post and gave him an example of setfenv alone, not of sandboxing