4

LuaJ を使用して、ユーザーが作成した Lua スクリプトを Java で実行しています。ただし、戻らない Lua スクリプトを実行すると、Java スレッドがフリーズします。これにより、スレッドが中断できなくなります。Lua スクリプトを次のように実行します。

JsePlatform.standardGlobals().loadFile("badscript.lua").call();

badscript.luaが含まれていますwhile true do end

ゆるぎないループに陥っているスクリプトを自動的に終了させ、ユーザーが実行中に Lua スクリプトを手動で終了できるようにしたいと考えています。debug.sethookpcallについて読んだことがありますが、それらを自分の目的に適切に使用する方法はわかりません。また、サンドボックスがより良い代替手段であると聞いたことがありますが、それは私の手の届かないところにあります.

この問題は、Java スレッドだけに拡張される可能性もあります。でスタックしている Java スレッドの中断に関する決定的な情報は見つかりませんでしたwhile (true);

オンラインのLua デモ非常に有望でしたが、「悪い」スクリプトの検出と終了は、Lua ではなく CGI スクリプトで行われるようです。Java を使用して、Lua スクリプトを呼び出す CGI スクリプトを呼び出すことはできますか? ただし、ユーザーがスクリプトを手動で終了できるかどうかはわかりません。Lua デモ ソース コードのリンクを紛失しましたが、手元にあります。これは魔法の行です:

tee -a $LOG | (ulimit -t 1 ; $LUA demo.lua 2>&1 | head -c 8k)

誰かが私を正しい方向に向けることができますか?

いくつかの情報源:

4

4 に答える 4

6

私は同じ問題に苦労し、デバッグライブラリの実装を掘り下げた後、David Lewis によって提案されたものと同様のソリューションを作成しましたが、独自の DebugLibrary を提供することでそうしました:

package org.luaj.vm2.lib;

import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;

public class CustomDebugLib extends DebugLib {
    public boolean interrupted = false;

    @Override
    public void onInstruction(int pc, Varargs v, int top) {
        if (interrupted) {
            throw new ScriptInterruptException();
        }
        super.onInstruction(pc, v, top);
    }

    public static class ScriptInterruptException extends RuntimeException {}
}

新しいスレッド内からスクリプトを実行し、interruptedtrue に設定して実行を停止するだけです。LuaError例外は、スローされたときの原因としてカプセル化されます。

于 2015-04-03T19:30:04.913 に答える
2

問題がありますが、これはあなたの質問に答えるのに大いに役立ちます。

次の概念実証は、任意のユーザー コードのサンドボックス化とスロットリングの基本レベルを示しています。巧妙に細工されていない「ユーザー入力」の約 250 の命令を実行し、コルーチンを破棄します。この回答のようなメカニズムを使用してJava にクエリを実行し、毎回生成するのではなく、フック関数内で条件付きで生成することができます。

SandboxTest.java:

public static void main(String[] args) {
    Globals globals = JsePlatform.debugGlobals();

    LuaValue chunk = globals.loadfile("res/test.lua");

    chunk.call();
}

res/test.lua:

function sandbox(fn)
    -- read script and set the environment
    f = loadfile(fn, "t")
    debug.setupvalue(f, 1, {print = print})

    -- create a coroutine and have it yield every 50 instructions
    local co = coroutine.create(f)
    debug.sethook(co, coroutine.yield, "", 50)

    -- demonstrate stepped execution, 5 'ticks'
    for i = 1, 5 do
        print("tick")
        coroutine.resume(co)
    end
end

sandbox("res/badfile.lua")

res/badfile.lua:

while 1 do
    print("", "badfile")
end

残念ながら、制御フローは意図したとおりに機能しますが、「放棄された」コルーチンのガベージ コレクションが正しく機能していません。Java で対応するものLuaThreadは、待機ループで永遠にハングアップし、プロセスを存続させます。詳細はこちら:

LuaJコルーチンLuaThreadを放棄するにはどうすればよいですか?

于 2014-07-05T10:36:00.730 に答える
0

編集: LuaJ自体を変更せずにLuaJのスレッドを安全に終了する方法が見つかりませんでした。LuaJでは動作しませんが、私が思いついたのは次のとおりです。ただし、純粋な Lua で機能するように簡単に変更できます。LuaJ スレッドは非常に問題があるため、Java 用の Python バインディングに切り替えている可能性があります。

--- 以下を思いついたのですが、LuaJ では動作しません ---

これが可能な解決策です。「カウント」イベントでトリガーされる debug.sethook を使用してフックを登録します (これらのイベントは でさえ発生しますwhile true do end)。また、スクリプトを終了するかどうかを示すブール値フラグを含む、作成したカスタム「ScriptState」Java オブジェクトも渡します。Java オブジェクトは、フラグが設定されている場合、エラーをスローしてスクリプトを閉じる Lua フックで照会されます (編集: エラーをスローしても、実際にはスクリプトは終了しません)。終了フラグは、Lua スクリプト内から設定することもできます。

揺るぎない無限ループを自動的に終了したい場合は、ScriptState への呼び出しが最後に行われた時間を記録するタイマー システムを実装し、API 呼び出しなしで十分な時間が経過した場合にスクリプトを自動的に終了するのは簡単です(編集: これでのみ機能します)。スレッドを中断できる場合) . 無限ループを強制終了したいが、特定のブロック操作を中断したくない場合は、ScriptState オブジェクトを調整して、自動終了などを一時的に一時停止できる他の状態情報を含めることができます。

interpreter.luaこれは、必要に応じて別のスクリプトを呼び出して中断するために使用できるmyです。Javaメソッドを呼び出すため、変更しない限りLuaJ(または他のLua-Javaライブラリ)なしでは実行されません(編集:再び、純粋なLuaで動作するように簡単に変更できます)

function hook_line(e)
    if jthread:getDone() then
        -- I saw someone else use error(), but an infinite loop still seems to evade it.
        -- os.exit() seems to take care of it well.
        os.exit()
    end
end

function inithook()
    -- the hook will run every 100 million instructions.
    -- the time it takes for 100 million instructions to occur
    --   is based on computer speed and the calling environment
    debug.sethook(hook_line, "", 1e8)
    local ret = dofile(jLuaScript)
    debug.sethook()
    return ret
end

args = { ... }
if jthread == nil then
    error("jthread object is nil. Please set it in the Java environment.",2)
elseif jLuaScript == nil then
    error("jLuaScript not set. Please set it in the Java environment.",2)
else
    local x,y = xpcall(inithook, debug.traceback)
end

フラグとデモンストレーションを格納するScriptStateクラスは次のとおりです。main()

public class ScriptState {

    private AtomicBoolean isDone = new AtomicBoolean(true);
    public boolean getDone() { return isDone.get(); }
    public void setDone(boolean v) { isDone.set(v); }

    public static void main(String[] args) {
        Thread t = new Thread() {
            public void run() {
                System.out.println("J: Lua script started.");
                ScriptState s = new ScriptState();
                Globals g = JsePlatform.debugGlobals();
                g.set("jLuaScript", "res/main.lua");
                g.set("jthread", CoerceJavaToLua.coerce(s));
                try {
                    g.loadFile("res/_interpreter.lua").call();
                } catch (Exception e) {
                    System.err.println("There was a Lua error!");
                    e.printStackTrace();
                }
            }
        };
        t.start();
        try { t.join(); } catch (Exception e) { System.err.println("Error waiting for thread"); }
        System.out.println("J: End main");
    }
}

res/main.lua実行するターゲット Lua コードが含まれています。通常どおり、環境変数またはパラメーターを使用して、追加情報をスクリプトに渡します。Lua でライブラリを使用する場合は、JsePlatform.debugGlobals()代わりにを使用することを忘れないでください。JsePlatform.standardGlobals()debug

os.exit()編集: Lua スクリプトだけでなく、呼び出しプロセスも終了することに気付きました。と同等のようですSystem.exit()error()エラーがスローされますが、Lua スクリプトが終了することはありません。私は今、これに対する解決策を見つけようとしています。

于 2013-07-07T22:12:29.863 に答える