lucy-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Marvin Humphrey <mar...@rectangular.com>
Subject [lucy-dev] Trap exceptions from C
Date Sun, 15 Apr 2012 18:50:29 GMT
Greets,

I've opened an issue and uploaded some patches regarding trapping exceptions
from our C code:

    https://issues.apache.org/jira/browse/LUCY-230

    Trap Exceptions from C

    The Clownfish exception mechanism wraps the host language's exception
    mechanism, making it possible to throw host exceptions from C code without
    knowledge of the host language. We need to augment this capability
    with a way to trap exceptions from C.

    This new feature will enable us to clean up a couple places in the
code base,
    such as opening an index reader. More importantly, though, it will make it
    possible to write tests in C which verify error handling.

It's been a challenge to design generic exception handling for Clownfish given
that host languages have very different exception models, and given that C
does not support nested functions so we must pass pointers to concrete
functions.

For reference, here's Wikipedia's survey of exception handling syntax:

    http://en.wikipedia.org/wiki/Exception_handling_syntax

Here's the provisional API for the new feature Err#trap:

    /** Run <code>routine</code> within the host's exception handling
     * environment, catching and returning any errors that occur.
     *
     * If an unrecognized host exception is trapped, it will be wrapped in an
     * Err so that it can be handled by Clownfish code.
     *
     * @return an Err, or NULL if no exception occurs.
     */
    public inert incremented nullable Err*
    trap(cfish_Err_attempt_t routine, void *context);

Here's sample usage, where we use Err#trap to implement a "Safe_Do_Stuff"
method which traps "MyError" exceptions which might be thrown by "Do_Stuff":

    struct do_stuff_context {
        Foo        *foo;
        int64_t     number;
        const char *string;
        int64_t     result;
    };

    static void
    S_run_do_stuff(void *context) {
        struct do_stuff_context *args = (struct do_stuff_context*)context;
        args.result = Foo_Do_Stuff(args->foo, args->number, args->string);
    }

    int64_t
    Foo_safe_do_stuff(Foo *self, int64_t number, const char *string) {
        struct do_stuff_context context;
        context.foo    = self;
        context.number = number;
        context.string = string;

        Err *error = Err_trap(S_run_do_stuff, &context);
        if (error) {
            if (Err_Is_A(error, MYERROR)) {
                Err_set_error(error);
                return -1;
            }
            else {
                RETHROW(error);
            }
        }
        else {
            return args.result;
        }
    }

Thoughts?

Instead of Err_trap(), we could have gone with Err_try() -- but it would not
have had the same semantics as "try" in other languages.  It would have been
just like trap(), only returning a boolean indicating whether an exception
occurred and storing the exception object in Err_error rather than returning
it.

The one thing I'm not settled about in the provisional implementation is the
behavior of the XS helper function Err#run, which is where we extract an
arbitrary C function pointer out of a scalar and run it blindly.

    void
    run(routine_sv, context_sv)
        SV *routine_sv;
        SV *context_sv;
    PPCODE:
        IV routine_iv = SvIV(routine_sv);
        IV context_iv = SvIV(context_sv);
        cfish_Err_attempt_t routine = INT2PTR(cfish_Err_attempt_t, routine_iv);
        void *context               = INT2PTR(void*, context_iv);
        routine(context);

I'm concerned that there might be an attack vector where a malicious user gets
shellcode into a scalar somewhere (easy), and then gets the first argument to
Err#run to point at it (hard, I think).  I haven't yet figured out how to
complete that attack, though.  Anybody see a problem?

Marvin Humphrey

Mime
View raw message