11

setfenvLua 5.2 での機能を再現するにはどうすればよいですか? _ENV新しい環境変数の使用方法を正確に理解するのに苦労しています。

Lua 5.1 ではsetfenv、任意の機能を非常に簡単にサンドボックス化できます。

--# Lua 5.1

print('_G', _G)             -- address of _G

local foo = function()  
    print('env', _G)        -- address of sandbox _G
    bar = 1
end

-- create a simple sandbox
local env = { print = print }
env._G = env

-- set the environment and call the function
setfenv(foo, env)
foo()

-- we should have global in our environment table but not in _G
print(bar, env.bar)

この例を実行すると、次の出力が表示されます。

_G    table: 0x62d6b0
env   table: 0x635d00
nil   1



この簡単な例を Lua 5.2 で再現したいと思います。以下は私の試みですが、上記の例のようには機能しません。

--# Lua 5.2

local function setfenv(f, env)
    local _ENV = env or {}       -- create the _ENV upvalue
    return function(...)
        print('upvalue', _ENV)   -- address of _ENV upvalue
        return f(...)
    end
end

local foo = function()
    print('_ENV', _ENV)          -- address of function _ENV
    bar = 1
end

-- create a simple sandbox
local env = { print = print }
env._G = env

-- set the environment and call the function
foo_env = setfenv(foo, env)
foo_env()

-- we should have global in our envoirnment table but not in _G
print(bar, env.bar)

この例を実行すると、次の出力が表示されます。

upvalue    table: 0x637e90
_ENV       table: 0x6305f0
1          nil



loadこの件に関して他にもいくつか質問があることは承知していますが、それらのほとんどは、Lua 5.2 で提供され た新しい機能を使用して非常にうまく機能する動的コード (ファイルまたは文字列) のロードを扱っているようです。ここでは、サンドボックスで任意の関数を実行するためのソリューションを具体的に求めています。debugライブラリを使用せずにこれを行いたいと思います。Lua のドキュメントによると、Luaに依存する必要はありません。

4

4 に答える 4

17

Lua 5.2 では、Lua からデバッグ ライブラリを使用せずに関数の環境を変更することはできません。関数が作成されると、それが環境になります。この環境を変更する唯一の方法は、デバッグ ライブラリを必要とする最初の上位値を変更することです。

Lua 5.2 の環境に関する一般的な考え方は、環境はトリッキー (つまり、デバッグ ライブラリ) 以外では不変であると見なされるべきだということです。環境で関数を作成します。そこに作成されると、それが環境です。永遠に。

これは Lua 5.1 で環境がよく使用される方法でしたが、簡単な関数呼び出しであらゆる環境を変更することは簡単であり、承認されていました。また、(ユーザーがサンドボックスを壊すのを防ぐために) Lua インタープリターが削除されsetfenvた場合、ユーザー コードは独自の関数の環境を内部的に設定できません。したがって、外側の世界にはサンドボックスがありますが、内側の世界はサンドボックスにサンドボックスを持つことはできません。

Lua 5.2 メカニズムにより、関数の作成後に環境を変更することが難しくなりますが、作成に環境を設定することはできます。これにより、サンドボックス内でサンドボックス化できます。

したがって、本当に必要なのは、次のようにコードを再配置することです。

local foo;

do
  local _ENV = { print = print }

  function foo()
    print('env', _ENV)
    bar = 1
  end
end

fooサンドボックス化されました。そして今、誰かがサンドボックスを破ることははるかに難しくなっています。

ご想像のとおり、これは Lua 開発者の間でいくつかの論争を引き起こしました。

于 2013-01-12T05:37:06.253 に答える
12

ちょっと高いけど、そんなに大事なら…

string.dumpを使用して、関数を適切な環境に再ロードしないのはなぜですか?

function setfenv(f, env)
    return load(string.dump(f), nil, nil, env)
end
function foo()
    herp(derp)
end

setfenv(foo, {herp = print, derp = "Hello, world!"})()
于 2013-01-21T22:09:59.817 に答える
4

Lua5.2 では、サンドボックス化可能な関数はそれ自体を指定する必要があります。使用できる簡単なパターンの 1 つは_ENV、引数として受け取ることです。

function(_ENV)
    ...
end

または、envを定義する何かの中にラップします

local mk_func(_ENV)
    return function()
        ...
    end
end

local f = mk_func({print = print})

ただし、この明示的な の使用は_ENV、サンドボックス化にはあまり役に立ちません_ENV。変数を持つことで他の関数が連携することを常に想定できるとは限らないためです。その場合、それはあなたが何をするかによって異なります。他のファイルからコードをロードしたいだけの場合はloadloadfile通常、サンドボックスに使用できるオプションの環境パラメーターを受け取ります。さらに、読み込もうとしているコードが文字列形式の場合、文字列操作を使用して_ENV自分で変数を追加できます (たとえば、関数を env パラメーターでラップすることによって)。

local code = 'return function(_ENV) return ' .. their_code .. 'end'

最後に、動的な関数環境操作が本当に必要な場合は、デバッグ ライブラリを使用して、関数の内部の の上位値を変更できます_ENV。デバッグ ライブラリを使用することは通常は推奨されませんが、他のすべての代替手段が適用されない場合は許容できると思います (この場合、関数の環境を変更することはすでにブードゥーの魔法であるため、デバッグ ライブラリを使用することはそれほど悪くないと思います)。

于 2013-01-12T14:20:45.287 に答える