diff --git a/src/scripting.c b/src/scripting.c index 345feda2f..4276190ce 100644 --- a/src/scripting.c +++ b/src/scripting.c @@ -1453,6 +1453,14 @@ void luaMaskCountHook(lua_State *lua, lua_Debug *ar) { if (server.lua_timedout) processEventsWhileBlocked(); if (server.lua_kill) { serverLog(LL_WARNING,"Lua script killed by user with SCRIPT KILL."); + + /* + * Set the hook to invoke all the time so the user +         * will not be able to catch the error with pcall and invoke +         * pcall again which will prevent the script from ever been killed + */ + lua_sethook(lua, luaMaskCountHook, LUA_MASKLINE, 0); + lua_pushstring(lua,"Script killed by user with SCRIPT KILL..."); lua_error(lua); } diff --git a/tests/unit/scripting.tcl b/tests/unit/scripting.tcl index c44ec74f5..92ca05bdc 100644 --- a/tests/unit/scripting.tcl +++ b/tests/unit/scripting.tcl @@ -612,6 +612,34 @@ start_server {tags {"scripting"}} { assert_equal [r ping] "PONG" } + test {Timedout read-only scripts can be killed by SCRIPT KILL even when use pcall} { + set rd [redis_deferring_client] + r config set lua-time-limit 10 + $rd eval {local f = function() while 1 do redis.call('ping') end end while 1 do pcall(f) end} 0 + + wait_for_condition 50 100 { + [catch {r ping} e] == 1 + } else { + fail "Can't wait for script to start running" + } + catch {r ping} e + assert_match {BUSY*} $e + + r script kill + + wait_for_condition 50 100 { + [catch {r ping} e] == 0 + } else { + fail "Can't wait for script to be killed" + } + assert_equal [r ping] "PONG" + + catch {$rd read} res + $rd close + + assert_match {*killed by user*} $res + } + test {Timedout script link is still usable after Lua returns} { r config set lua-time-limit 10 r eval {for i=1,100000 do redis.call('ping') end return 'ok'} 0