jmp-scape 0.1.1

Safe Rust access to setjmp and sigsetjmp, forked from cee-scape.
Documentation

jmp-scape

The jmp-scape crate provides Rust access to setjmp and sigsetjmp functionality, via an interface that ensures LLVM won't miscompile things.

jmp-scape continues from cee-scape, with ongoing maintenance and fixes (for example Linux aarch64 jmp_buf layout). Full credit for the original design and implementation belongs to the upstream authors.

Example

You might imagine you have some C code that looks like this (vastly over simplified):

#include <setjmp.h>
#include <stdint.h>

uint32_t subtract_but_longjmp_if_underflow(jmp_buf env, uint32_t a, uint32_t b) {
    if (b > a) {
        longjmp(env, b - a);
    }
    return a - b;
}

If you want to call out to that C code from Rust, you need to establish a jump environment (the jmp_buf env parameter); but Rust does not provide setjmp.

Here's how you might use this crate to solve your problem:

use jmp_scape::call_with_setjmp;

// This invocation passes parameters that follow normal control flow.
assert_eq!(call_with_setjmp(|env| {
    (unsafe {
        subtract_but_longjmp_if_underflow(env, 10, 3)
    }) as c_int
}), 7);

// This invocation passes parameters that cause a non-local jump.
assert_eq!(call_with_setjmp(|env| {
    unsafe {
        subtract_but_longjmp_if_underflow(env, 3, 10);
        panic!("should never get here.");
    }
}), 7);

Why not just add setjmp itself as a function?

There has been significant discussion of that question amongst the Rust project.

See in particular rust-lang/libc PR 1216 and rust-lang/rfcs Issue 2625.

The answer is multifaceted.

First, adding support for calling setjmp would require adding extra support in the Rust compiler. Namely, support for marking functions as potentially returning multiple times, e.g. via a returns_twice attribute as discussed in rust-lang/rfcs Issue 2625.

Second, calls to the setjmp function would interact in strange ways with the Rust borrow checker; unless the borrow checker were extended to understand the meaning of a returns_twice attribute, it would invite immediate unsound behavior, such as moving an owned object multiple times from the same stack slot. This could cause the same value to be dropped multiple times (which would be unsound).

Third, the setjmp function itself only has well-defined behavior in very specific contexts according to the C standard (again discussed in rust-lang/rfcs Issue 2625); even just let x = setjmp(...) would be undefined behavior in Rust.

The methods offered by jmp-scape, such as call_with_setjmp, side-step the above issues by limiting the use of setjmp to a specific coding pattern:

call_with_setjmp(|env| { ... })

Within the dynamic extent of an invocation to call_with_setjmp, one can either return normally, or via a longjmp to the given jump environment env (which causes a return from the call_with_setjmp invocation). Either way, the outer call returns at most once. The given jump environment is only usable within that dynamic extent (and Rust's lifetime rules help enforce that constraint).

Why is it called jmp-scape?

It's a pun, carrying forward the spirit of the original cee-scape name: C's jump environments are also known as "escape continuations". This crate enables safe C escape jumps — hence, jmp-scape.

License

Same license as the upstream cee-scape crate.