mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Scribunto
synced 2024-11-27 17:50:06 +00:00
Fixed setfenv() across a tail call
Fixed the issue noticed during testing ofda06273e
, and which resulted in satest.setfenv1() being disabled. It's not possible to protect environments by iterating through every stack level, calling getfenv() at each one, because if any of the stack levels is a tail call, an error is raised. Such a tail call was introduced inda06273e
, which is why the test broke. Instead, just protect the actual specified environments, not their callers. The callers will have to protect themselves. Change-Id: If39104010ff2663c1bae5105cc8d37e276532100
This commit is contained in:
parent
606aaf30ac
commit
41b93dd7e1
|
@ -136,16 +136,12 @@ function mw.makeProtectedEnvFuncs( protectedEnvironments, protectedFunctions )
|
|||
error( "'setfenv' cannot set an environment at a level greater than 10", 2 )
|
||||
end
|
||||
|
||||
|
||||
-- Add one because we are still in Lua and 1 is right here
|
||||
stackIndex = stackIndex + 1
|
||||
|
||||
local i
|
||||
for i = 2, stackIndex do
|
||||
local env = old_getfenv( i )
|
||||
if env == nil or protectedEnvironments[ env ] then
|
||||
error( "'setfenv' cannot set the requested environment, it is protected", 2 )
|
||||
end
|
||||
|
||||
local env = old_getfenv( stackIndex )
|
||||
if env == nil or protectedEnvironments[ env ] then
|
||||
error( "'setfenv' cannot set the requested environment, it is protected", 2 )
|
||||
end
|
||||
func = old_setfenv( stackIndex, newEnv )
|
||||
elseif type( func ) == 'function' then
|
||||
|
|
|
@ -56,12 +56,13 @@ function test.getTests( engine )
|
|||
{ 'setfenv1', { error = '%s cannot set the global %s' } },
|
||||
{ 'setfenv2', { error = '%s cannot set an environment %s' } },
|
||||
{ 'setfenv3', { error = '%s cannot set the requested environment%s' } },
|
||||
{ 'setfenv4', { error = '%s cannot set the requested environment%s' } },
|
||||
{ 'setfenv4', true },
|
||||
{ 'setfenv5', true },
|
||||
{ 'setfenv6', { error = '%s cannot be called on a protected function' } },
|
||||
{ 'setfenv7', { error = '%s can only be called with a function%s' } },
|
||||
{ 'getfenv1', true },
|
||||
{ 'getfenv2', { error = '%s cannot get the global environment' } },
|
||||
{ 'getfenv3', { error = '%Sno function environment for tail call %s' } },
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -119,11 +120,27 @@ function test.setfenv3()
|
|||
end
|
||||
|
||||
function test.setfenv4()
|
||||
local function jailbreak()
|
||||
(function() setfenv( 3, {} ) end )()
|
||||
-- Set an unprotected environment at a higher stack level than a protected
|
||||
-- environment. It's assumed that any higher-level environment will protect
|
||||
-- itself with its own setfenv wrapper, so this succeeds.
|
||||
local function level3()
|
||||
local function level2()
|
||||
local env = {setfenv = setfenv}
|
||||
local function level1()
|
||||
setfenv( 3, {} )
|
||||
end
|
||||
|
||||
setfenv( level1, env )()
|
||||
end
|
||||
local protected = {mw = mw}
|
||||
protected.setfenv, protected.getfenv = mw.makeProtectedEnvFuncs(
|
||||
{[protected] = true}, {} )
|
||||
setfenv( level2, protected )()
|
||||
end
|
||||
local new_setfenv, new_getfenv = mw.makeProtectedEnvFuncs( { [_G] = true }, {} )
|
||||
setfenv( jailbreak, {setfenv = new_setfenv} )()
|
||||
local unprotected = {setfenv = setfenv, getfenv = getfenv, mw = mw}
|
||||
setfenv( level3, unprotected )()
|
||||
assert( getfenv( level3 ) ~= unprotected )
|
||||
return true
|
||||
end
|
||||
|
||||
function test.setfenv5()
|
||||
|
@ -161,4 +178,16 @@ function test.getfenv2()
|
|||
getfenv( 0 )
|
||||
end
|
||||
|
||||
function test.getfenv3()
|
||||
local function foo()
|
||||
return getfenv( 2 )
|
||||
end
|
||||
|
||||
local function bar()
|
||||
return foo()
|
||||
end
|
||||
|
||||
bar()
|
||||
end
|
||||
|
||||
return test
|
||||
|
|
|
@ -3,13 +3,13 @@ local satest = {}
|
|||
|
||||
function satest.getTests()
|
||||
return {
|
||||
-- { 'setfenv1', { error = '%s cannot set the requested environment%s' } },
|
||||
{ 'setfenv1', { error = '%s cannot set the requested environment%s' } },
|
||||
{ 'getfenv1', true },
|
||||
}
|
||||
end
|
||||
|
||||
function satest.setfenv1()
|
||||
setfenv( 2, {} )
|
||||
setfenv( 3, {} )
|
||||
end
|
||||
|
||||
function satest.getfenv1()
|
||||
|
|
Loading…
Reference in a new issue