Skip to content

Commit

Permalink
Don't trigger longjmp in rust.
Browse files Browse the repository at this point in the history
Motivation behind this change is upcoming breaking change in Rust
compiler v1.52.0 to prevent unwinding across FFI boundaries.
rust-lang/rust#76570
The new functionality requires nightly compiler to declare FFI
functions as "C-unwind".
The fundamental solution is to use C shim to wrap "e" and "m"
Lua functions in pcall.
Additionally define Rust calling convention to trigger lua_error
on Rust behalf.
  • Loading branch information
khvzak committed Apr 10, 2021
1 parent a7c6da0 commit ba375fa
Show file tree
Hide file tree
Showing 34 changed files with 2,547 additions and 1,052 deletions.
22 changes: 0 additions & 22 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ jobs:
target: ${{ matrix.target }}
override: true
- name: Run ${{ matrix.lua }} tests
if: ${{ matrix.os != 'macos-latest' || matrix.lua != 'luajit' }}
run: |
cargo test --release --features "${{ matrix.lua }} vendored"
cargo test --release --features "${{ matrix.lua }} vendored async send serialize"
Expand All @@ -135,27 +134,6 @@ jobs:
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }} vendored async send serialize" -- --ignored
shell: bash

test_luajit_macos:
name: Test LuaJIT on macOS
runs-on: macos-latest
needs: build
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
target: x86_64-apple-darwin
override: true
- name: Run LuaJIT 2.0.5 tests
run: |
brew install luajit
cargo test --tests --release --features "luajit async send serialize" -- --test-threads=1
shell: bash
- name: Run LuaJIT vendored tests
run: |
cargo test --release --features "luajit vendored async send serialize"
shell: bash

test_modules:
name: Test modules
runs-on: ${{ matrix.os }}
Expand Down
12 changes: 0 additions & 12 deletions benches/benchmark.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]

#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000",
allow(unused_attributes)
)]
extern "system" {}

use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use std::time::Duration;
use tokio::runtime::Runtime;
Expand Down
17 changes: 17 additions & 0 deletions build/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,4 +237,21 @@ fn main() {
} else {
build_glue(&include_dir);
}

let mut shim_cc = cc::Build::new();
shim_cc
.include(include_dir)
.define("COMPAT53_INCLUDE_SOURCE", None);
#[cfg(feature = "luajit")]
shim_cc.define("COMPAT53_LUAJIT", None);
shim_cc.file("src/ffi/shim/shim.c").compile("shim");

println!("cargo:rerun-if-changed=src/ffi/shim/compat-5.3.c");
println!("cargo:rerun-if-changed=src/ffi/shim/compat-5.3.h");
println!("cargo:rerun-if-changed=src/ffi/shim/shim.c");

println!("cargo:rerun-if-changed=build/find_dummy.rs");
println!("cargo:rerun-if-changed=build/find_normal.rs");
println!("cargo:rerun-if-changed=build/find_vendored.rs");
println!("cargo:rerun-if-changed=build/main.rs");
}
2 changes: 2 additions & 0 deletions src/ffi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,5 @@ mod lauxlib;
mod lua;
mod luaconf;
mod lualib;

pub mod safe;
265 changes: 265 additions & 0 deletions src/ffi/safe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
use std::ffi::CString;
use std::os::raw::{c_char, c_int, c_void};

use crate::error::Result;
use crate::util::protect_lua;

use super::lua::{lua_CFunction, lua_Debug, lua_Integer, lua_State};

extern "C" {
#[link_name = "MLUA_WRAPPED_ERROR_SIZE"]
pub static mut WRAPPED_ERROR_SIZE: usize;
#[link_name = "MLUA_WRAPPED_PANIC_SIZE"]
pub static mut WRAPPED_PANIC_SIZE: usize;
#[link_name = "MLUA_WRAPPED_ERROR_KEY"]
pub static mut WRAPPED_ERROR_KEY: *const c_void;
#[link_name = "MLUA_WRAPPED_PANIC_KEY"]
pub static mut WRAPPED_PANIC_KEY: *const c_void;

pub fn lua_call_mlua_hook_proc(L: *mut lua_State, ar: *mut lua_Debug);

pub fn meta_index_impl(state: *mut lua_State) -> c_int;
pub fn meta_newindex_impl(state: *mut lua_State) -> c_int;
pub fn bind_call_impl(state: *mut lua_State) -> c_int;
pub fn error_traceback(state: *mut lua_State) -> c_int;

fn lua_gc_s(L: *mut lua_State) -> c_int;
fn luaL_ref_s(L: *mut lua_State) -> c_int;
fn lua_pushlstring_s(L: *mut lua_State) -> c_int;
fn lua_tolstring_s(L: *mut lua_State) -> c_int;
fn lua_newthread_s(L: *mut lua_State) -> c_int;
fn lua_newuserdata_s(L: *mut lua_State) -> c_int;
fn lua_pushcclosure_s(L: *mut lua_State) -> c_int;
fn lua_pushrclosure_s(L: *mut lua_State) -> c_int;
fn luaL_requiref_s(L: *mut lua_State) -> c_int;
fn error_traceback_s(L: *mut lua_State) -> c_int;

fn lua_createtable_s(L: *mut lua_State) -> c_int;
fn lua_gettable_s(L: *mut lua_State) -> c_int;
fn lua_settable_s(L: *mut lua_State) -> c_int;
fn lua_geti_s(L: *mut lua_State) -> c_int;
fn lua_rawset_s(L: *mut lua_State) -> c_int;
fn lua_rawseti_s(L: *mut lua_State) -> c_int;
fn lua_rawsetp_s(L: *mut lua_State) -> c_int;
fn lua_rawsetfield_s(L: *mut lua_State) -> c_int;
fn lua_rawinsert_s(L: *mut lua_State) -> c_int;
fn lua_rawremove_s(L: *mut lua_State) -> c_int;
fn luaL_len_s(L: *mut lua_State) -> c_int;
fn lua_next_s(L: *mut lua_State) -> c_int;
}

#[repr(C)]
struct StringArg {
data: *const c_char,
len: usize,
}

//
// Common functions
//

// Uses 4 stack spaces
pub unsafe fn lua_gc(state: *mut lua_State, what: c_int, data: c_int) -> Result<c_int> {
super::lua_pushinteger(state, what as lua_Integer);
super::lua_pushinteger(state, data as lua_Integer);
protect_lua(state, 2, lua_gc_s)?;
let ret = super::lua_tointeger(state, -1) as c_int;
super::lua_pop(state, 1);
Ok(ret)
}

// Uses 3 stack spaces
pub unsafe fn luaL_ref(state: *mut lua_State, table: c_int) -> Result<c_int> {
super::lua_pushvalue(state, table);
super::lua_rotate(state, -2, 1);
protect_lua(state, 2, luaL_ref_s)?;
let ret = super::lua_tointeger(state, -1) as c_int;
super::lua_pop(state, 1);
Ok(ret)
}

// Uses 3 stack spaces
pub unsafe fn lua_pushstring<S: AsRef<[u8]> + ?Sized>(state: *mut lua_State, s: &S) -> Result<()> {
let s = s.as_ref();
let s = StringArg {
data: s.as_ptr() as *const c_char,
len: s.len(),
};
super::lua_pushlightuserdata(state, &s as *const StringArg as *mut c_void);
protect_lua(state, 1, lua_pushlstring_s)
}

// Uses 4 stack spaces
pub unsafe fn lua_tolstring(
state: *mut lua_State,
index: c_int,
len: *mut usize,
) -> Result<*const c_char> {
let index = super::lua_absindex(state, index);
super::lua_pushvalue(state, index);
super::lua_pushlightuserdata(state, len as *mut c_void);
protect_lua(state, 2, lua_tolstring_s)?;
let s = super::lua_touserdata(state, -1);
super::lua_pop(state, 1);
super::lua_replace(state, index);
Ok(s as *const c_char)
}

// Uses 2 stack spaces
pub unsafe fn lua_newthread(state: *mut lua_State) -> Result<*mut lua_State> {
protect_lua(state, 0, lua_newthread_s)?;
Ok(super::lua_tothread(state, -1))
}

// Uses 3 stack spaces
pub unsafe fn lua_newuserdata(state: *mut lua_State, size: usize) -> Result<*mut c_void> {
super::lua_pushinteger(state, size as lua_Integer);
protect_lua(state, 1, lua_newuserdata_s)?;
Ok(super::lua_touserdata(state, -1))
}

// Uses 4 stack spaces
pub unsafe fn lua_pushcclosure(state: *mut lua_State, f: lua_CFunction, n: c_int) -> Result<()> {
super::lua_pushlightuserdata(state, f as *mut c_void);
super::lua_pushinteger(state, n as lua_Integer);
protect_lua(state, n + 2, lua_pushcclosure_s)
}

// Uses 4 stack spaces
pub unsafe fn lua_pushrclosure(state: *mut lua_State, f: lua_CFunction, n: c_int) -> Result<()> {
super::lua_pushlightuserdata(state, f as *mut c_void);
if n > 0 {
super::lua_rotate(state, -n - 1, 1);
}
super::lua_pushinteger(state, n as lua_Integer + 1);
protect_lua(state, n + 2, lua_pushrclosure_s)
}

// Uses 5 stack spaces
pub unsafe fn luaL_requiref<S: AsRef<[u8]> + ?Sized>(
state: *mut lua_State,
modname: &S,
openf: lua_CFunction,
glb: c_int,
) -> Result<()> {
let modname = mlua_expect!(CString::new(modname.as_ref()), "modname contains nil bytes");
super::lua_pushlightuserdata(state, modname.as_ptr() as *mut c_void);
super::lua_pushlightuserdata(state, openf as *mut c_void);
super::lua_pushinteger(state, glb as lua_Integer);
protect_lua(state, 3, luaL_requiref_s)
}

// Uses 3 stack spaces
pub unsafe fn error_traceback2(state: *mut lua_State, state2: *mut lua_State) -> Result<()> {
mlua_assert!(
state != state2,
"error_traceback2 must be used with two different states"
);
super::lua_pushlightuserdata(state, state2);
protect_lua(state, 1, error_traceback_s)
}

//
// Table functions
//

// Uses 4 stack spaces
pub unsafe fn lua_createtable(state: *mut lua_State, narr: c_int, nrec: c_int) -> Result<()> {
super::lua_pushinteger(state, narr as lua_Integer);
super::lua_pushinteger(state, nrec as lua_Integer);
protect_lua(state, 2, lua_createtable_s)
}

// Uses 3 stack spaces
pub unsafe fn lua_gettable(state: *mut lua_State, table: c_int) -> Result<()> {
super::lua_pushvalue(state, table);
super::lua_rotate(state, -2, 1);
protect_lua(state, 2, lua_gettable_s)
}

// Uses 3 stack spaces
pub unsafe fn lua_settable(state: *mut lua_State, table: c_int) -> Result<()> {
super::lua_pushvalue(state, table);
super::lua_rotate(state, -3, 1);
protect_lua(state, 3, lua_settable_s)
}

// Uses 4 stack spaces
pub unsafe fn lua_geti(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<c_int> {
super::lua_pushvalue(state, table);
super::lua_pushinteger(state, i);
protect_lua(state, 2, lua_geti_s).map(|_| super::lua_type(state, -1))
}

// Uses 3 stack spaces
pub unsafe fn lua_rawset(state: *mut lua_State, table: c_int) -> Result<()> {
super::lua_pushvalue(state, table);
super::lua_rotate(state, -3, 1);
protect_lua(state, 3, lua_rawset_s)
}

// Uses 4 stack spaces
pub unsafe fn lua_rawseti(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<()> {
super::lua_pushvalue(state, table);
super::lua_rotate(state, -2, 1);
super::lua_pushinteger(state, i);
protect_lua(state, 3, lua_rawseti_s)
}

// Uses 4 stack spaces
pub unsafe fn lua_rawsetp(state: *mut lua_State, table: c_int, ptr: *const c_void) -> Result<()> {
super::lua_pushvalue(state, table);
super::lua_rotate(state, -2, 1);
super::lua_pushlightuserdata(state, ptr as *mut c_void);
protect_lua(state, 3, lua_rawsetp_s)
}

// Uses 4 stack spaces
pub unsafe fn lua_rawsetfield<S>(state: *mut lua_State, table: c_int, field: &S) -> Result<()>
where
S: AsRef<[u8]> + ?Sized,
{
let field = field.as_ref();
let s = StringArg {
data: field.as_ptr() as *const c_char,
len: field.len(),
};
super::lua_pushvalue(state, table);
super::lua_pushlightuserdata(state, &s as *const StringArg as *mut c_void);
super::lua_rotate(state, -3, 2);
protect_lua(state, 3, lua_rawsetfield_s)
}

// Uses 4 stack spaces
pub unsafe fn lua_rawinsert(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<()> {
super::lua_pushvalue(state, table);
super::lua_rotate(state, -2, 1);
super::lua_pushinteger(state, i);
protect_lua(state, 3, lua_rawinsert_s)
}

// Uses 4 stack spaces
pub unsafe fn lua_rawremove(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<()> {
super::lua_pushvalue(state, table);
super::lua_pushinteger(state, i);
protect_lua(state, 2, lua_rawremove_s)
}

// Uses 3 stack spaces
pub unsafe fn luaL_len(state: *mut lua_State, table: c_int) -> Result<lua_Integer> {
super::lua_pushvalue(state, table);
protect_lua(state, 1, luaL_len_s)?;
let ret = super::lua_tointeger(state, -1);
super::lua_pop(state, 1);
Ok(ret)
}

// Uses 3 stack spaces
pub unsafe fn lua_next(state: *mut lua_State, table: c_int) -> Result<lua_Integer> {
super::lua_pushvalue(state, table);
super::lua_rotate(state, -2, 1);
protect_lua(state, 2, lua_next_s)?;
let ret = super::lua_tointeger(state, -1);
super::lua_pop(state, 1);
Ok(ret)
}
Loading

0 comments on commit ba375fa

Please sign in to comment.