Skip to content

Commit ba375fa

Browse files
committed
Don't trigger longjmp in rust.
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.
1 parent a7c6da0 commit ba375fa

34 files changed

+2547
-1052
lines changed

.github/workflows/main.yml

-22
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ jobs:
123123
target: ${{ matrix.target }}
124124
override: true
125125
- name: Run ${{ matrix.lua }} tests
126-
if: ${{ matrix.os != 'macos-latest' || matrix.lua != 'luajit' }}
127126
run: |
128127
cargo test --release --features "${{ matrix.lua }} vendored"
129128
cargo test --release --features "${{ matrix.lua }} vendored async send serialize"
@@ -135,27 +134,6 @@ jobs:
135134
TRYBUILD=overwrite cargo test --release --features "${{ matrix.lua }} vendored async send serialize" -- --ignored
136135
shell: bash
137136

138-
test_luajit_macos:
139-
name: Test LuaJIT on macOS
140-
runs-on: macos-latest
141-
needs: build
142-
steps:
143-
- uses: actions/checkout@v2
144-
- uses: actions-rs/toolchain@v1
145-
with:
146-
toolchain: nightly
147-
target: x86_64-apple-darwin
148-
override: true
149-
- name: Run LuaJIT 2.0.5 tests
150-
run: |
151-
brew install luajit
152-
cargo test --tests --release --features "luajit async send serialize" -- --test-threads=1
153-
shell: bash
154-
- name: Run LuaJIT vendored tests
155-
run: |
156-
cargo test --release --features "luajit vendored async send serialize"
157-
shell: bash
158-
159137
test_modules:
160138
name: Test modules
161139
runs-on: ${{ matrix.os }}

benches/benchmark.rs

-12
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,3 @@
1-
#![cfg_attr(
2-
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
3-
feature(link_args)
4-
)]
5-
6-
#[cfg_attr(
7-
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
8-
link_args = "-pagezero_size 10000 -image_base 100000000",
9-
allow(unused_attributes)
10-
)]
11-
extern "system" {}
12-
131
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
142
use std::time::Duration;
153
use tokio::runtime::Runtime;

build/main.rs

+17
Original file line numberDiff line numberDiff line change
@@ -237,4 +237,21 @@ fn main() {
237237
} else {
238238
build_glue(&include_dir);
239239
}
240+
241+
let mut shim_cc = cc::Build::new();
242+
shim_cc
243+
.include(include_dir)
244+
.define("COMPAT53_INCLUDE_SOURCE", None);
245+
#[cfg(feature = "luajit")]
246+
shim_cc.define("COMPAT53_LUAJIT", None);
247+
shim_cc.file("src/ffi/shim/shim.c").compile("shim");
248+
249+
println!("cargo:rerun-if-changed=src/ffi/shim/compat-5.3.c");
250+
println!("cargo:rerun-if-changed=src/ffi/shim/compat-5.3.h");
251+
println!("cargo:rerun-if-changed=src/ffi/shim/shim.c");
252+
253+
println!("cargo:rerun-if-changed=build/find_dummy.rs");
254+
println!("cargo:rerun-if-changed=build/find_normal.rs");
255+
println!("cargo:rerun-if-changed=build/find_vendored.rs");
256+
println!("cargo:rerun-if-changed=build/main.rs");
240257
}

src/ffi/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -288,3 +288,5 @@ mod lauxlib;
288288
mod lua;
289289
mod luaconf;
290290
mod lualib;
291+
292+
pub mod safe;

src/ffi/safe.rs

+265
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
use std::ffi::CString;
2+
use std::os::raw::{c_char, c_int, c_void};
3+
4+
use crate::error::Result;
5+
use crate::util::protect_lua;
6+
7+
use super::lua::{lua_CFunction, lua_Debug, lua_Integer, lua_State};
8+
9+
extern "C" {
10+
#[link_name = "MLUA_WRAPPED_ERROR_SIZE"]
11+
pub static mut WRAPPED_ERROR_SIZE: usize;
12+
#[link_name = "MLUA_WRAPPED_PANIC_SIZE"]
13+
pub static mut WRAPPED_PANIC_SIZE: usize;
14+
#[link_name = "MLUA_WRAPPED_ERROR_KEY"]
15+
pub static mut WRAPPED_ERROR_KEY: *const c_void;
16+
#[link_name = "MLUA_WRAPPED_PANIC_KEY"]
17+
pub static mut WRAPPED_PANIC_KEY: *const c_void;
18+
19+
pub fn lua_call_mlua_hook_proc(L: *mut lua_State, ar: *mut lua_Debug);
20+
21+
pub fn meta_index_impl(state: *mut lua_State) -> c_int;
22+
pub fn meta_newindex_impl(state: *mut lua_State) -> c_int;
23+
pub fn bind_call_impl(state: *mut lua_State) -> c_int;
24+
pub fn error_traceback(state: *mut lua_State) -> c_int;
25+
26+
fn lua_gc_s(L: *mut lua_State) -> c_int;
27+
fn luaL_ref_s(L: *mut lua_State) -> c_int;
28+
fn lua_pushlstring_s(L: *mut lua_State) -> c_int;
29+
fn lua_tolstring_s(L: *mut lua_State) -> c_int;
30+
fn lua_newthread_s(L: *mut lua_State) -> c_int;
31+
fn lua_newuserdata_s(L: *mut lua_State) -> c_int;
32+
fn lua_pushcclosure_s(L: *mut lua_State) -> c_int;
33+
fn lua_pushrclosure_s(L: *mut lua_State) -> c_int;
34+
fn luaL_requiref_s(L: *mut lua_State) -> c_int;
35+
fn error_traceback_s(L: *mut lua_State) -> c_int;
36+
37+
fn lua_createtable_s(L: *mut lua_State) -> c_int;
38+
fn lua_gettable_s(L: *mut lua_State) -> c_int;
39+
fn lua_settable_s(L: *mut lua_State) -> c_int;
40+
fn lua_geti_s(L: *mut lua_State) -> c_int;
41+
fn lua_rawset_s(L: *mut lua_State) -> c_int;
42+
fn lua_rawseti_s(L: *mut lua_State) -> c_int;
43+
fn lua_rawsetp_s(L: *mut lua_State) -> c_int;
44+
fn lua_rawsetfield_s(L: *mut lua_State) -> c_int;
45+
fn lua_rawinsert_s(L: *mut lua_State) -> c_int;
46+
fn lua_rawremove_s(L: *mut lua_State) -> c_int;
47+
fn luaL_len_s(L: *mut lua_State) -> c_int;
48+
fn lua_next_s(L: *mut lua_State) -> c_int;
49+
}
50+
51+
#[repr(C)]
52+
struct StringArg {
53+
data: *const c_char,
54+
len: usize,
55+
}
56+
57+
//
58+
// Common functions
59+
//
60+
61+
// Uses 4 stack spaces
62+
pub unsafe fn lua_gc(state: *mut lua_State, what: c_int, data: c_int) -> Result<c_int> {
63+
super::lua_pushinteger(state, what as lua_Integer);
64+
super::lua_pushinteger(state, data as lua_Integer);
65+
protect_lua(state, 2, lua_gc_s)?;
66+
let ret = super::lua_tointeger(state, -1) as c_int;
67+
super::lua_pop(state, 1);
68+
Ok(ret)
69+
}
70+
71+
// Uses 3 stack spaces
72+
pub unsafe fn luaL_ref(state: *mut lua_State, table: c_int) -> Result<c_int> {
73+
super::lua_pushvalue(state, table);
74+
super::lua_rotate(state, -2, 1);
75+
protect_lua(state, 2, luaL_ref_s)?;
76+
let ret = super::lua_tointeger(state, -1) as c_int;
77+
super::lua_pop(state, 1);
78+
Ok(ret)
79+
}
80+
81+
// Uses 3 stack spaces
82+
pub unsafe fn lua_pushstring<S: AsRef<[u8]> + ?Sized>(state: *mut lua_State, s: &S) -> Result<()> {
83+
let s = s.as_ref();
84+
let s = StringArg {
85+
data: s.as_ptr() as *const c_char,
86+
len: s.len(),
87+
};
88+
super::lua_pushlightuserdata(state, &s as *const StringArg as *mut c_void);
89+
protect_lua(state, 1, lua_pushlstring_s)
90+
}
91+
92+
// Uses 4 stack spaces
93+
pub unsafe fn lua_tolstring(
94+
state: *mut lua_State,
95+
index: c_int,
96+
len: *mut usize,
97+
) -> Result<*const c_char> {
98+
let index = super::lua_absindex(state, index);
99+
super::lua_pushvalue(state, index);
100+
super::lua_pushlightuserdata(state, len as *mut c_void);
101+
protect_lua(state, 2, lua_tolstring_s)?;
102+
let s = super::lua_touserdata(state, -1);
103+
super::lua_pop(state, 1);
104+
super::lua_replace(state, index);
105+
Ok(s as *const c_char)
106+
}
107+
108+
// Uses 2 stack spaces
109+
pub unsafe fn lua_newthread(state: *mut lua_State) -> Result<*mut lua_State> {
110+
protect_lua(state, 0, lua_newthread_s)?;
111+
Ok(super::lua_tothread(state, -1))
112+
}
113+
114+
// Uses 3 stack spaces
115+
pub unsafe fn lua_newuserdata(state: *mut lua_State, size: usize) -> Result<*mut c_void> {
116+
super::lua_pushinteger(state, size as lua_Integer);
117+
protect_lua(state, 1, lua_newuserdata_s)?;
118+
Ok(super::lua_touserdata(state, -1))
119+
}
120+
121+
// Uses 4 stack spaces
122+
pub unsafe fn lua_pushcclosure(state: *mut lua_State, f: lua_CFunction, n: c_int) -> Result<()> {
123+
super::lua_pushlightuserdata(state, f as *mut c_void);
124+
super::lua_pushinteger(state, n as lua_Integer);
125+
protect_lua(state, n + 2, lua_pushcclosure_s)
126+
}
127+
128+
// Uses 4 stack spaces
129+
pub unsafe fn lua_pushrclosure(state: *mut lua_State, f: lua_CFunction, n: c_int) -> Result<()> {
130+
super::lua_pushlightuserdata(state, f as *mut c_void);
131+
if n > 0 {
132+
super::lua_rotate(state, -n - 1, 1);
133+
}
134+
super::lua_pushinteger(state, n as lua_Integer + 1);
135+
protect_lua(state, n + 2, lua_pushrclosure_s)
136+
}
137+
138+
// Uses 5 stack spaces
139+
pub unsafe fn luaL_requiref<S: AsRef<[u8]> + ?Sized>(
140+
state: *mut lua_State,
141+
modname: &S,
142+
openf: lua_CFunction,
143+
glb: c_int,
144+
) -> Result<()> {
145+
let modname = mlua_expect!(CString::new(modname.as_ref()), "modname contains nil bytes");
146+
super::lua_pushlightuserdata(state, modname.as_ptr() as *mut c_void);
147+
super::lua_pushlightuserdata(state, openf as *mut c_void);
148+
super::lua_pushinteger(state, glb as lua_Integer);
149+
protect_lua(state, 3, luaL_requiref_s)
150+
}
151+
152+
// Uses 3 stack spaces
153+
pub unsafe fn error_traceback2(state: *mut lua_State, state2: *mut lua_State) -> Result<()> {
154+
mlua_assert!(
155+
state != state2,
156+
"error_traceback2 must be used with two different states"
157+
);
158+
super::lua_pushlightuserdata(state, state2);
159+
protect_lua(state, 1, error_traceback_s)
160+
}
161+
162+
//
163+
// Table functions
164+
//
165+
166+
// Uses 4 stack spaces
167+
pub unsafe fn lua_createtable(state: *mut lua_State, narr: c_int, nrec: c_int) -> Result<()> {
168+
super::lua_pushinteger(state, narr as lua_Integer);
169+
super::lua_pushinteger(state, nrec as lua_Integer);
170+
protect_lua(state, 2, lua_createtable_s)
171+
}
172+
173+
// Uses 3 stack spaces
174+
pub unsafe fn lua_gettable(state: *mut lua_State, table: c_int) -> Result<()> {
175+
super::lua_pushvalue(state, table);
176+
super::lua_rotate(state, -2, 1);
177+
protect_lua(state, 2, lua_gettable_s)
178+
}
179+
180+
// Uses 3 stack spaces
181+
pub unsafe fn lua_settable(state: *mut lua_State, table: c_int) -> Result<()> {
182+
super::lua_pushvalue(state, table);
183+
super::lua_rotate(state, -3, 1);
184+
protect_lua(state, 3, lua_settable_s)
185+
}
186+
187+
// Uses 4 stack spaces
188+
pub unsafe fn lua_geti(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<c_int> {
189+
super::lua_pushvalue(state, table);
190+
super::lua_pushinteger(state, i);
191+
protect_lua(state, 2, lua_geti_s).map(|_| super::lua_type(state, -1))
192+
}
193+
194+
// Uses 3 stack spaces
195+
pub unsafe fn lua_rawset(state: *mut lua_State, table: c_int) -> Result<()> {
196+
super::lua_pushvalue(state, table);
197+
super::lua_rotate(state, -3, 1);
198+
protect_lua(state, 3, lua_rawset_s)
199+
}
200+
201+
// Uses 4 stack spaces
202+
pub unsafe fn lua_rawseti(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<()> {
203+
super::lua_pushvalue(state, table);
204+
super::lua_rotate(state, -2, 1);
205+
super::lua_pushinteger(state, i);
206+
protect_lua(state, 3, lua_rawseti_s)
207+
}
208+
209+
// Uses 4 stack spaces
210+
pub unsafe fn lua_rawsetp(state: *mut lua_State, table: c_int, ptr: *const c_void) -> Result<()> {
211+
super::lua_pushvalue(state, table);
212+
super::lua_rotate(state, -2, 1);
213+
super::lua_pushlightuserdata(state, ptr as *mut c_void);
214+
protect_lua(state, 3, lua_rawsetp_s)
215+
}
216+
217+
// Uses 4 stack spaces
218+
pub unsafe fn lua_rawsetfield<S>(state: *mut lua_State, table: c_int, field: &S) -> Result<()>
219+
where
220+
S: AsRef<[u8]> + ?Sized,
221+
{
222+
let field = field.as_ref();
223+
let s = StringArg {
224+
data: field.as_ptr() as *const c_char,
225+
len: field.len(),
226+
};
227+
super::lua_pushvalue(state, table);
228+
super::lua_pushlightuserdata(state, &s as *const StringArg as *mut c_void);
229+
super::lua_rotate(state, -3, 2);
230+
protect_lua(state, 3, lua_rawsetfield_s)
231+
}
232+
233+
// Uses 4 stack spaces
234+
pub unsafe fn lua_rawinsert(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<()> {
235+
super::lua_pushvalue(state, table);
236+
super::lua_rotate(state, -2, 1);
237+
super::lua_pushinteger(state, i);
238+
protect_lua(state, 3, lua_rawinsert_s)
239+
}
240+
241+
// Uses 4 stack spaces
242+
pub unsafe fn lua_rawremove(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<()> {
243+
super::lua_pushvalue(state, table);
244+
super::lua_pushinteger(state, i);
245+
protect_lua(state, 2, lua_rawremove_s)
246+
}
247+
248+
// Uses 3 stack spaces
249+
pub unsafe fn luaL_len(state: *mut lua_State, table: c_int) -> Result<lua_Integer> {
250+
super::lua_pushvalue(state, table);
251+
protect_lua(state, 1, luaL_len_s)?;
252+
let ret = super::lua_tointeger(state, -1);
253+
super::lua_pop(state, 1);
254+
Ok(ret)
255+
}
256+
257+
// Uses 3 stack spaces
258+
pub unsafe fn lua_next(state: *mut lua_State, table: c_int) -> Result<lua_Integer> {
259+
super::lua_pushvalue(state, table);
260+
super::lua_rotate(state, -2, 1);
261+
protect_lua(state, 2, lua_next_s)?;
262+
let ret = super::lua_tointeger(state, -1);
263+
super::lua_pop(state, 1);
264+
Ok(ret)
265+
}

0 commit comments

Comments
 (0)