Fix script kill to work also on scripts that use pcall (#8661)
pcall function runs another LUA function in protected mode, this means that any error will be caught by this function and will not stop the LUA execution. The script kill mechanism uses error to stop the running script. Scripts that uses pcall can catch the error raise by the script kill mechanism, this will cause a script like this to be unkillable: local f = function() while 1 do redis.call('ping') end end while 1 do pcall(f) end The fix is, when we want to kill the script, we set the hook function to be invoked after each line. This will promise that the execution will get another error before it is able to enter the pcall function again.
This commit is contained in:
parent
169be0426c
commit
9ae4f5c73d
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user