diff --git a/mesecons_luacontroller/init.lua b/mesecons_luacontroller/init.lua
index 7df2bdc943ff8a0328427eb7628e71c360fd2529..e669f93776f9c40719117aaa7edf838e8221bd40 100644
--- a/mesecons_luacontroller/init.lua
+++ b/mesecons_luacontroller/init.lua
@@ -216,6 +216,17 @@ local function safe_string_rep(str, n)
 	return string.rep(str, n)
 end
 
+-- string.find with a pattern can be used to DoS the server.
+-- Therefore, limit string.find to patternless matching.
+local function safe_string_find(...)
+	if (select(4, ...)) ~= true then
+		debug.sethook() -- Clear hook
+		error("string.find: 'plain' (fourth parameter) must always be true in a LuaController")
+	end
+
+	return string.find(...)
+end
+
 local function remove_functions(x)
 	local tp = type(x)
 	if tp == "table" then
@@ -292,6 +303,7 @@ local function create_environment(pos, mem, event)
 			rep = safe_string_rep,
 			reverse = string.reverse,
 			sub = string.sub,
+			find = safe_string_find,
 		},
 		math = {
 			abs = math.abs,
@@ -354,22 +366,6 @@ local function timeout()
 end
 
 
-local function code_prohibited(code)
-	-- LuaJIT doesn't increment the instruction counter when running
-	-- loops, so we have to sanitize inputs if we're using LuaJIT.
-	if not rawget(_G, "jit") then
-		return false
-	end
-	local prohibited = {"while", "for", "repeat", "until", "goto"}
-	code = " "..code.." "
-	for _, p in ipairs(prohibited) do
-		if string.find(code, "[^%w_]"..p.."[^%w_]") then
-			return "Prohibited command: "..p
-		end
-	end
-end
-
-
 local function create_sandbox(code, env)
 	if code:byte(1) == 27 then
 		return nil, "Binary code prohibited."
@@ -378,24 +374,17 @@ local function create_sandbox(code, env)
 	if not f then return nil, msg end
 	setfenv(f, env)
 
+	-- Turn off JIT optimization for user code so that count
+	-- events are generated when adding debug hooks
+	if rawget(_G, "jit") then
+		jit.off(f, true)
+	end
+
 	return function(...)
-		-- Normal Lua: Use instruction counter to stop execution
-		-- after luacontroller_maxevents.
-		-- LuaJIT: Count function calls instead of instructions, allows usage
-		-- of function keyword. However, LuaJIT still doesn't trigger
-		-- lines events when using infinite loops.
+		-- Use instruction counter to stop execution
+		-- after luacontroller_maxevents
 		local maxevents = mesecon.setting("luacontroller_maxevents", 10000)
-		if not rawget(_G, "jit") then
-			debug.sethook(timeout, "", maxevents)
-		else
-			local events = 0
-			debug.sethook(function ()
-				events = events + 1
-				if events > maxevents then
-					timeout()
-				end
-			end, "c")
-		end
+		debug.sethook(timeout, "", maxevents)
 		local ok, ret = pcall(f, ...)
 		debug.sethook()  -- Clear hook
 		if not ok then error(ret, 0) end
@@ -432,9 +421,6 @@ local function run(pos, event)
 	local mem  = load_memory(meta)
 	local code = meta:get_string("code")
 
-	local err = code_prohibited(code)
-	if err then return err end
-
 	-- Create environment
 	local env = create_environment(pos, mem, event)