YetAnotherForum
Welcome Guest Search | Active Topics | Log In | Register

Sandbox in Squirrel?
serge
#1 Posted : Wednesday, July 06, 2005 9:10:23 AM(UTC)
Rank: Member

Groups: Registered
Joined: 7/6/2005(UTC)
Posts: 22

Thanks: 0 times
Was thanked: 0 time(s) in 0 post(s)
<span id="_ctl0_PostForm_PreviewBody">I have just found Squirrel and it
seems like a great language. It is lightweight like Lua and has C-like
syntax which I like better.



I'm seriously considering using Squrrel instead of Lua, but I wouder if
the following is possible. Let's suppose we have a game engine which
uses scripting for configuration purposes. It loads and executes
scripts which can specify some settings, register user made callbacks
and allow to extend the game. Naturally as such scripts work as
plugins, can be created by third party persons and distributed
independently, scriptable plugins can be used by malicious people to
harm the users - for example delete some important files. So we need to
add some restrictions. On the other hand, some code written in a
scripting language can be a part of the engine and can provide some
supplementary functions, validate data and do other things. For maximum
flexibility, a scripts which are a part of the engine should be
unrestricted as they can be trusted.



In the following example (written in Lua), 'selected_fruits' is a table
of options which is read by C++ engine, all the validation is performed
by Lua functions which are part of the engine and ensure that values in
'selected_fruits' table are valid not matter what configuration scripts
are loaded. 'untrusted_script' is some Lua code which is loaded from
external files, it can contain just anything from correct configuration
to the code harmful for the system. Restriction for file API is not
enough, for example user scripts can intentially or unintentiallt
damage 'selected_fruits' table by adding invalid values to it, removing
values which they did not add there, etc.



Could you please help to make Sqirrel replacement for this example? I
did not find anything in Squirrel manual yet. Or probably tell me which
parts of the manual I should start reading.





-- part of game engine written in a scripted language

local valid_fruits = {

["apple"] = true,

["orange"] = true,

["pear"] = true,

["banana"] = true,

}



selected_fruits = {}



-- the only API function available to 'untrusted' scripts

function add_fruit(fruit)

if not valid_fruits[fruit] then return end

selected_fruits[fruit] = true

end



-- function using which 'untrusted' scripts are executed

function sandbox_call(untrusted_code)

local restricted_sandbox = { add_fruit = add_fruit }

restricted_sandbox = setmetatable({}, {

__index = restricted_sandbox,

__newindex = function(table, key, value)

error("Sandbox violation: attempt to modify global variable")

end

})

setfenv(untrusted_code, restricted_sandbox)

return pcall(untrusted_code)

end



-- come code probably loaded from external files, received from network, etc

-- it should selections a set of fruits from a predefined list, this list is

-- used by the main program as a configuration setting

function untrusted_script()

add_fruit("apple")

add_fruit("badfruit")

add_fruit("pear")

-- try to do something weird ()

os.remove("c:\\autoexec.bat")

end



local result, errmsg = sandbox_call(untrusted_script)

if errmsg then print("Error detected:") print(errmsg) end

print("List of selected fruits:")

for fruit in selected_fruits do print(fruit) end

</span>------------------------------------------------------------------------------------



It produces the following:

Error detected:

1.lua:38: attempt to index global `os' (a nil value)

List of selected fruits:

apple

pear



fagiano
#2 Posted : Wednesday, July 06, 2005 11:47:24 AM(UTC)
Rank: Advanced Member

Groups: Registered, Administrators
Joined: 6/11/2005(UTC)
Posts: 827

Thanks: 0 times
Was thanked: 36 time(s) in 29 post(s)
The following code is a literal translation Lua-&gt;Squirrel. Squirrel has a wide range of features, I suggest to explore the doc, maybe there is a cooler way. However the following code works.

<PRE>local valid_fruits = {
["apple"] = true,
["orange"] = true,
["pear"] = true,
["banana"] = true,
}</PRE><PRE>selected_fruits &lt;- {}</PRE><PRE>function add_fruit(fruit) : (valid_fruits,selected_fruits) //binds the 2 free-variables to the closure
{
if(fruit in valid_fruits)
selected_fruits[fruit] &lt;- true
}</PRE><PRE>function sandbox_call(untrusted_code) {
local restricted_sandbox = delegate {
add_fruit = add_fruit
function _newslot(key, value)
{
throw "Sandbox violation: attempt to modify global variable"
//notice this wont stop the execution(I'm considering to change this behaviour)
}
} : { }
local oldroot = getroottable(); // this is avoidable if you create a 2nd thread(VM that can share data)
setroottable(restricted_sandbox);
local ret;
try {
ret = untrusted_code.call(restricted_sandbox); //passes restricted_sandbox as 'this' parameter
}
catch(err)
{
setroottable(oldroot);
::print("\nerror : "+err+"\n");
}
setroottable(oldroot); //restore old root table
return ret;
}</PRE><PRE>function untrusted_script() {
add_fruit("apple")
add_fruit("badfruit")
add_fruit("pear")
// try to do something weird ()
remove("c:\\test.txt")
}</PRE><PRE>
sandbox_call(untrusted_script);
foreach(idx,val in selected_fruits)
::print(idx+"\n");</PRE><PRE> </PRE><PRE>ciao</PRE><PRE>Alberto</PRE>
serge
#3 Posted : Friday, July 08, 2005 7:55:44 AM(UTC)
Rank: Member

Groups: Registered
Joined: 7/6/2005(UTC)
Posts: 22

Thanks: 0 times
Was thanked: 0 time(s) in 0 post(s)
Thanks, your example really helps a lot.





Though I would like global evironment assigned to each function just
like in Lua :) That way you would not have to bind any free variables
to 'trusted' api functions, maintaining of any more complicated code
can become a nighmare, unless each API function automatically switches
global environment back on entry and restoring to a sandboxed global
environment on exit.





An interesting demo with sandboxing usage would be to make a simple
game engine (reversi game for example) with two AI opponents fighting
each other, everything written entirely in Squirrel. The player who
submits the best AI script wins. This way, each AI code is better to be
executed in a sandbox in order to prevent cheating and accessing each
other's data or messing with the engine.





Anyway, I will try to learn Squirrel better to see what can be done
currently and what can't before posting any suggestions or feature
requests :)

fagiano
#4 Posted : Friday, July 08, 2005 11:56:20 AM(UTC)
Rank: Advanced Member

Groups: Registered, Administrators
Joined: 6/11/2005(UTC)
Posts: 827

Thanks: 0 times
Was thanked: 36 time(s) in 29 post(s)
I personally don't really like lua 5.x environment approach. Squirrel already since ver 1.x heads in a complete different direction. Squirrel's functions are completely context free, plus free-variables are explicit. Another thing to keep in mind, is that Squirrel differentiate between function environment (aka 'this') and root table (global environment obj).


for instance in squirrel:

<PRE>something = 10
::something = 10</PRE>
don't necessary mean the same thing(they mean the same thing only if 'this' and the roottable are the same object). The following code is an sample of how it behaves(give it a run).

<PRE>function printsomething()
{
::print(something+"\n") //equivalent of 'this.something' (if not found falls back to the root)
::print(::something+"\n") //fetches from the root table explicityly
}</PRE><PRE>local table = {
test_in_the_table = printsomething
something = "I'm in the table"
}</PRE><PRE>something &lt;- "I'm in the root"</PRE><PRE>
::print("\n&gt;called from root\n")
printsomething()</PRE><PRE>//shows how squirrel automatically assing the 'this'
::print("\n&gt;called from table\n")
table.test_in_the_table();</PRE><PRE>::print("\n&gt;explicit this\n")
printsomething.call(table);</PRE><PRE> </PRE>
Personally if I'd have to sandbox some code, I'd create a second VM with (sq_newthread()) get a C reference (sq_getstackobj) and call system scripts in the main VM and unsafe scripts in the second (with a read-only roottable) so I don't have to mess around with changing roots etc... and you can still share all the data. Is just the first solution that comes to mind at the moment.


I'm looking forward for your idea/suggestions :)


I hope this helps


ciao


Alberto

Users browsing this topic
Guest
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.

Clean Slate theme by Jaben Cargman (Tiny Gecko)
Powered by YAF 1.9.4 | YAF © 2003-2010, Yet Another Forum.NET
This page was generated in 0.188 seconds.