Skip to content

Find a way to apply a execution timeout even for code in protected regions #85

@kpreisser

Description

@kpreisser

Hi,

some time ago I opened #42 to write a wiki page that shows how to apply a timeout to script execution.
Just now I was looking at the emit for the TryCatchFinallyStatement and realized that it emits JS catch and finally blocks as .NET catch and finally blocks. This means the timeout (which is done by Thread.Abort()) can easily be bypassed:

try {} finally {
    while (true);
}

Drat!

Also, when the script calls an API in the catch or finally block which uses ScriptTimeoutHelper.EnterCriticalSection, it will lead to a deadlock as that method waits for a ThreadAbortException, which will not happen since the code is currently in a protected region.

Further, this means that JS code in a finally block is executed even when non-JavaScriptExceptions, e.g. a ThreadAbortException or a StackOverflowException from the recursion depth check, are thrown (whereas in my case I would like to ensure no more script code is executed when throwing a non-JavaScriptException).

Unfortunately, I'm not sure if it is somehow possible to mark generated catch and finally blocks as non-protected region, so that the CLR does not delay thread aborts for these regions. If this is not possible, the only way I can think of which would still be practically, is to generate functions for the contents of a try and catch statement, and catching an exception before running the code for a catch and finally clause.

For example, if the JS is this:

function myFunc() {
    for (var i = 0; i < 20; i++) {
        try {
            console.log("Try!");

            if (i == 10)
                break;
            else if (i == 11)
                continue;
            else if (i == 12)
                return "i: " + i;

        }
        catch (e) {
            console.log("Catch!");
        }
        finally {
            console.log("Finally!");
        }
    }
}

the generated code would look in C# like this:

        private static object JS_myFunc()
        {
            for (var i = 0; i < 20; i++)
            {
                // Try
                ExceptionDispatchInfo caughtException1 = null;
                int state1 = 0;
                object returnValue1 = null;
                try
                {
                    state1 = TryFunction1(ref returnValue1, ref i);
                }
                catch (Exception ex) when (ex is JavaScriptException)
                {
                    caughtException1 = ExceptionDispatchInfo.Capture(ex);
                }
                // Finally
                Console.WriteLine("Finally!");
                if (caughtException1 != null)
                    caughtException1.Throw();
                // Handle return state
                if (state1 == 0)
                    break;
                else if (state1 == 1)
                    continue;
                else if (state1 == 2)
                    return returnValue1;
            }

            return null;
        }
        private static int TryFunction1(ref object returnValue, ref int i)
        {
            Exception caughtException1 = null;
            try
            {
                Console.WriteLine("Try!");

                if (i == 10)
                    return 0;
                else if (i == 11)
                    return 1;
                else if (i == 12)
                {
                    returnValue = "i: " + i;
                    return 2;
                }
            }
            catch (Exception ex) when (ex is JavaScriptException)
            {
                caughtException1 = ex;
            }
            if (caughtException1 != null)
            {
                Console.WriteLine("Catch!");
            }
            return 0;
        }

(although that seems already quite a bit complicated).
Unfortunately, I also don't know much of the internals of CIL, so I'm not sure if I can do this.

Do you have any other idea how this could be solved?
Thanks!

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions