fixes exploits with infinite loops and better sandbox code checks

This commit is contained in:
rnd1 2016-11-26 19:07:01 +01:00
parent 374c1bed5c
commit e75ba494c7

173
init.lua
View File

@ -10,7 +10,7 @@ basic_robot.maxdig = 1; -- how many digs allowed per execution, 0 = unlimited
basic_robot.version = "11/22a"; basic_robot.version = "11/26a";
basic_robot.data = {}; basic_robot.data = {};
@ -99,6 +99,28 @@ function getSandboxEnv (name)
end end
end, end,
listen_msg = function()
local msg = basic_robot.data[name].listen_msg;
local speaker = basic_robot.data[name].listen_speaker;
basic_robot.data[name].listen_msg = nil;
basic_robot.data[name].listen_speaker = nil;
return speaker,msg
end,
read_mail = function()
local mail = basic_robot.data[name].listen_mail;
local sender = basic_robot.data[name].listen_sender;
basic_robot.data[name].listen_mail = nil;
basic_robot.data[name].listen_sender = nil;
return sender,mail
end,
send_mail = function(target,mail)
if not basic_robot.data[target] then return false end
basic_robot.data[target].listen_mail = mail;
basic_robot.data[target].listen_sender = name;
end,
remove = function() remove = function()
basic_robot.data[name].obj:remove(); basic_robot.data[name].obj:remove();
basic_robot.data[name].obj=nil; basic_robot.data[name].obj=nil;
@ -111,6 +133,41 @@ function getSandboxEnv (name)
basic_robot.data[name].allow_spam = nil basic_robot.data[name].allow_spam = nil
end end
end, end,
fire = function(speed, pitch,gravity) -- experimental: fires an projectile
local obj = basic_robot.data[name].obj;
local pos = obj:getpos();
local yaw = obj:getyaw();
pitch = pitch*math.pi/180
local velocity = {x=speed*math.cos(yaw)*math.cos(pitch), y=speed*math.sin(pitch),z=speed*math.sin(yaw)*math.cos(pitch)};
-- fire particle
-- minetest.add_particle(
-- {
-- pos = pos,
-- expirationtime = 10,
-- velocity = {x=speed*math.cos(yaw)*math.cos(pitch), y=speed*math.sin(pitch),z=speed*math.sin(yaw)*math.cos(pitch)},
-- size = 5,
-- texture = "default_apple.png",
-- acceleration = {x=0,y=-gravity,z=0},
-- collisiondetection = true,
-- collision_removal = true,
-- }
--);
local obj = minetest.add_entity(pos, "basic_robot:projectile");
if not obj then return end
obj:setvelocity(velocity);
obj:setacceleration({x=0,y=-gravity,z=0});
local luaent = obj:get_luaentity();
luaent.owner = name;
end,
fire_pos = function()
local fire_pos = basic_robot.data[name].fire_pos;
basic_robot.data[name].fire_pos = nil;
return fire_pos
end,
}, },
find_nodes = find_nodes =
@ -169,48 +226,6 @@ function getSandboxEnv (name)
end end
end, end,
listen_msg = function()
local msg = basic_robot.data[name].listen_msg;
local speaker = basic_robot.data[name].listen_speaker;
basic_robot.data[name].listen_msg = nil;
basic_robot.data[name].listen_speaker = nil;
return speaker,msg
end,
fire = function(speed, pitch,gravity) -- experimental: fires an projectile
local obj = basic_robot.data[name].obj;
local pos = obj:getpos();
local yaw = obj:getyaw();
pitch = pitch*math.pi/180
local velocity = {x=speed*math.cos(yaw)*math.cos(pitch), y=speed*math.sin(pitch),z=speed*math.sin(yaw)*math.cos(pitch)};
-- fire particle
-- minetest.add_particle(
-- {
-- pos = pos,
-- expirationtime = 10,
-- velocity = {x=speed*math.cos(yaw)*math.cos(pitch), y=speed*math.sin(pitch),z=speed*math.sin(yaw)*math.cos(pitch)},
-- size = 5,
-- texture = "default_apple.png",
-- acceleration = {x=0,y=-gravity,z=0},
-- collisiondetection = true,
-- collision_removal = true,
-- }
--);
local obj = minetest.add_entity(pos, "basic_robot:projectile");
if not obj then return end
obj:setvelocity(velocity);
obj:setacceleration({x=0,y=-gravity,z=0});
local luaent = obj:get_luaentity();
luaent.owner = name;
end,
fire_pos = function()
local fire_pos = basic_robot.data[name].fire_pos;
basic_robot.data[name].fire_pos = nil;
return fire_pos
end,
book = { book = {
read = function(i) read = function(i)
@ -248,7 +263,7 @@ function getSandboxEnv (name)
local ScriptFunc, CompileError = loadstring( script ) local ScriptFunc, CompileError = loadstring( script )
if CompileError then if CompileError then
minetest.chat_send_player(name, "#code.run: compile error " .. CompileError ) minetest.chat_send_player(name, "#code.run: compile error " .. CompileError )
return return false
end end
setfenv( ScriptFunc, basic_robot.data[name].sandbox ) setfenv( ScriptFunc, basic_robot.data[name].sandbox )
@ -256,8 +271,9 @@ function getSandboxEnv (name)
local Result, RuntimeError = pcall( ScriptFunc ); local Result, RuntimeError = pcall( ScriptFunc );
if RuntimeError then if RuntimeError then
minetest.chat_send_player(name, "#code.run: run error " .. RuntimeError ) minetest.chat_send_player(name, "#code.run: run error " .. RuntimeError )
return return false
end end
return true
end end
}, },
@ -325,7 +341,7 @@ end
local function check_code(code) local function check_code(code)
--"while ", "for ", "do ","goto ", --"while ", "for ", "do ","goto ",
local bad_code = {"repeat ", "until ", "_ccounter"} local bad_code = {"repeat ", "until ", "_ccounter", "_G", "while%(", "while{", "pcall"}
for _, v in pairs(bad_code) do for _, v in pairs(bad_code) do
if string.find(code, v) then if string.find(code, v) then
@ -335,6 +351,34 @@ local function check_code(code)
end end
local function is_inside_string(pos,script)
local i1=string.find (script, "\"", 1);
if not i1 then
return false
end
local i2=0;
local par = 1;
if pos<i1 then
return false
end
while i1 do
i2=string.find(script,"\"",i1+1);
if i2 then
par = 1 - par;
end
if par == 0 then
if i1<pos and pos<i2 then
return true
end
end
i1=i2;
end
return false;
end
local function CompileCode ( script ) local function CompileCode ( script )
--[[ idea: in each local a = function (args) ... end insert counter like: --[[ idea: in each local a = function (args) ... end insert counter like:
@ -342,7 +386,9 @@ local function CompileCode ( script )
when counter exceeds limit exit with error when counter exceeds limit exit with error
--]] --]]
script="_ccounter = 0; " .. script; script="_ccounter = 0; " .. script;
local i1 -- process script to insert call counter in every function local i1 -- process script to insert call counter in every function
local insert_code = " increase_ccounter(); "; local insert_code = " increase_ccounter(); ";
@ -350,45 +396,56 @@ local function CompileCode ( script )
while (i2) do -- PROCESS SCRIPT AND INSERT COUNTER AT PROBLEMATIC SPOTS while (i2) do -- PROCESS SCRIPT AND INSERT COUNTER AT PROBLEMATIC SPOTS
i2 = nil; i2 = nil;
i2=string.find (script, "function", i1) -- fix functions i2=string.find (script, "function", i1) -- fix functions
if i2 then if i2 then
if not is_inside_string(i2,script) then
i2=string.find(script, ")", i2); i2=string.find(script, ")", i2);
if i2 then if i2 then
script = script.sub(script,1, i2) .. insert_code .. script.sub(script, i2+1); script = script.sub(script,1, i2) .. insert_code .. script.sub(script, i2+1);
i1=i2+string.len(insert_code); i1=i2+string.len(insert_code);
end end
end
end end
i2=string.find (script, "for ", i1) -- fix for OK i2=string.find (script, "for ", i1) -- fix for OK
if i2 then if i2 then
if not is_inside_string(i2,script) then
i2=string.find(script, "do ", i2); i2=string.find(script, "do ", i2);
if i2 then if i2 then
script = script.sub(script,1, i2+1) .. insert_code .. script.sub(script, i2+2); script = script.sub(script,1, i2+1) .. insert_code .. script.sub(script, i2+2);
i1=i2+string.len(insert_code); i1=i2+string.len(insert_code);
end end
end
end end
i2=string.find (script, "while ", i1) -- fix while OK i2=string.find (script, "while ", i1) -- fix while OK
if i2 then if i2 then
if not is_inside_string(i2,script) then
i2=string.find(script, "do ", i2); i2=string.find(script, "do ", i2);
if i2 then if i2 then
script = script.sub(script,1, i2+1) .. insert_code .. script.sub(script, i2+2); script = script.sub(script,1, i2+1) .. insert_code .. script.sub(script, i2+2);
i1=i2+string.len(insert_code); i1=i2+string.len(insert_code);
end end
end end
end
i2=string.find (script, "goto ", i1) -- fix goto OK i2=string.find (script, "goto ", i1) -- fix goto OK
if i2 then if i2 then
if not is_inside_string(i2,script) then
script = script.sub(script,1, i2-1) .. insert_code .. script.sub(script, i2); script = script.sub(script,1, i2-1) .. insert_code .. script.sub(script, i2);
i1=i2+string.len(insert_code)+5; -- insert + skip goto i1=i2+string.len(insert_code)+5; -- insert + skip goto
end end
end
end end
--minetest.chat_send_all(script)
--if true then return nil, "" end
local ScriptFunc, CompileError = loadstring( script ) local ScriptFunc, CompileError = loadstring( script )
if CompileError then if CompileError then
return nil, CompileError return nil, CompileError
@ -657,8 +714,9 @@ local spawn_robot = function(pos,node,ttl)
-- if robot already exists do nothing -- if robot already exists do nothing
if basic_robot.data[owner] and basic_robot.data[owner].obj then if basic_robot.data[owner] and basic_robot.data[owner].obj then
minetest.chat_send_player(owner,"#ROBOT ERROR : robot already active") minetest.chat_send_player(owner,"#ROBOT: robot already active, removing")
return basic_robot.data[owner].obj:remove();
basic_robot.data[owner].obj = nil;
end end
local obj = minetest.add_entity(pos,"basic_robot:robot"); local obj = minetest.add_entity(pos,"basic_robot:robot");
@ -721,14 +779,16 @@ local on_receive_robot_form = function(pos, formname, fields, sender)
"**ROBOT\n".. "**ROBOT\n"..
"say(\"hello\") will speak\n".. "say(\"hello\") will speak\n"..
"self.listen(0/1) (de)attaches chat listener to robot\n".. "self.listen(0/1) (de)attaches chat listener to robot\n"..
"speaker, msg = listen_msg() retrieves last chat message if robot listens\n".. "speaker, msg = self.listen_msg() retrieves last chat message if robot listens\n"..
"self.send_mail(target,mail) sends mail to target robot\n"..
"sender,mail = read_mail() reads mail, if any\n" ..
"self.pos() returns table {x=pos.x,y=pos.y,z=pos.z}\n".. "self.pos() returns table {x=pos.x,y=pos.y,z=pos.z}\n"..
"self.spam(0/1) (dis)enable message repeat to all\n".. "self.spam(0/1) (dis)enable message repeat to all\n"..
"self.remove() removes robot\n".. "self.remove() removes robot\n"..
"self.spawnpos() returns position of spawner block\n".. "self.spawnpos() returns position of spawner block\n"..
"self.viewdir() returns vector of view for robot\n".. "self.viewdir() returns vector of view for robot\n"..
"fire(speed, pitch,gravity) fires a projectile from robot\n".. "self.fire(speed, pitch,gravity) fires a projectile from robot\n"..
"fire_pos() returns last hit position\n "; "self.fire_pos() returns last hit position\n ";
text = minetest.formspec_escape(text); text = minetest.formspec_escape(text);
@ -838,13 +898,15 @@ minetest.register_on_player_receive_fields(
elseif fields.right then elseif fields.right then
pcall(function () commands.move(name,2) end) pcall(function () commands.move(name,2) end)
elseif fields.dig then elseif fields.dig then
pcall(function () commands.dig(name,3) end) pcall(function () basic_robot.data[name].digcount = 1; commands.dig(name,3) end)
elseif fields.up then elseif fields.up then
pcall(function () commands.move(name,5) end) pcall(function () commands.move(name,5) end)
elseif fields.down then elseif fields.down then
pcall(function () commands.move(name,6) end) pcall(function () commands.move(name,6) end)
elseif fields.digdown then elseif fields.digdown then
pcall(function () commands.dig(name,6) end) pcall(function () basic_robot.data[name].digcount = 1; commands.dig(name,6) end)
elseif fields.digup then
pcall(function () basic_robot.data[name].digcount = 1; commands.dig(name,5) end)
end end
return return
end end
@ -936,7 +998,8 @@ local get_manual_control_form = function(name)
"button[0.75,1.75;1.,1;back;BACK]".. "button[0.75,1.75;1.,1;back;BACK]"..
"button[1.75,1.75;1.,1;up;UP]".. "button[1.75,1.75;1.,1;up;UP]"..
"button[0.75,2.75;1.,1;digdown;DDown]"; "button[-0.25,2.65;1.,1;digdown;DDown]"..
"button[1.75,2.65;1.,1;digup;DUp]";
return form; return form;
end end