Skip to content

Commit 12e61e0

Browse files
committed
Restrict access to Luau VM from UserData destructors.
It's unsafe to make almost any Lua calls when userdata destructor is running. This can cause recursive GC run and crash. See luau-lang/luau#510 for some details.
1 parent 53ff494 commit 12e61e0

File tree

10 files changed

+40
-19
lines changed

10 files changed

+40
-19
lines changed

mlua-sys/src/lua51/lua.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,10 @@ pub type lua_Reader =
8484
pub type lua_Writer =
8585
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
8686

87-
/// Type for memory-allocation functions
87+
/// Type for memory-allocation functions (no unwinding)
8888
#[rustfmt::skip]
8989
pub type lua_Alloc =
90-
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
90+
unsafe extern "C" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
9191

9292
#[cfg_attr(all(windows, raw_dylib), link(name = "lua51", kind = "raw-dylib"))]
9393
extern "C-unwind" {

mlua-sys/src/lua52/lua.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,10 @@ pub type lua_Reader =
8989
pub type lua_Writer =
9090
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
9191

92-
/// Type for memory-allocation functions
92+
/// Type for memory-allocation functions (no unwinding)
9393
#[rustfmt::skip]
9494
pub type lua_Alloc =
95-
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
95+
unsafe extern "C" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
9696

9797
#[cfg_attr(all(windows, raw_dylib), link(name = "lua52", kind = "raw-dylib"))]
9898
extern "C-unwind" {

mlua-sys/src/lua53/lua.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@ pub type lua_Reader =
9696
pub type lua_Writer =
9797
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
9898

99-
/// Type for memory-allocation functions
99+
/// Type for memory-allocation functions (no unwinding)
100100
#[rustfmt::skip]
101101
pub type lua_Alloc =
102-
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
102+
unsafe extern "C" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
103103

104104
#[cfg_attr(all(windows, raw_dylib), link(name = "lua53", kind = "raw-dylib"))]
105105
extern "C-unwind" {

mlua-sys/src/lua54/lua.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,10 @@ pub type lua_Reader =
9595
pub type lua_Writer =
9696
unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int;
9797

98-
/// Type for memory-allocation functions
98+
/// Type for memory-allocation functions (no unwinding)
9999
#[rustfmt::skip]
100100
pub type lua_Alloc =
101-
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
101+
unsafe extern "C" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
102102

103103
/// Type for warning functions
104104
pub type lua_WarnFunction = unsafe extern "C-unwind" fn(ud: *mut c_void, msg: *const c_char, tocont: c_int);

mlua-sys/src/luau/compat.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ pub unsafe fn luaL_loadbufferenv(
372372
fn free(p: *mut c_void);
373373
}
374374

375-
unsafe extern "C-unwind" fn data_dtor(_: *mut lua_State, data: *mut c_void) {
375+
unsafe extern "C" fn data_dtor(_: *mut lua_State, data: *mut c_void) {
376376
free(*(data as *mut *mut c_char) as *mut c_void);
377377
}
378378

mlua-sys/src/luau/lua.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,12 @@ pub type lua_Unsigned = c_uint;
8383
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
8484
pub type lua_Continuation = unsafe extern "C-unwind" fn(L: *mut lua_State, status: c_int) -> c_int;
8585

86-
/// Type for userdata destructor functions.
87-
pub type lua_Destructor = unsafe extern "C-unwind" fn(L: *mut lua_State, *mut c_void);
86+
/// Type for userdata destructor functions (no unwinding).
87+
pub type lua_Destructor = unsafe extern "C" fn(L: *mut lua_State, *mut c_void);
8888

89-
/// Type for memory-allocation functions.
89+
/// Type for memory-allocation functions (no unwinding).
9090
pub type lua_Alloc =
91-
unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
91+
unsafe extern "C" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void;
9292

9393
/// Returns Luau release version (eg. `0.xxx`).
9494
pub const fn luau_version() -> Option<&'static str> {
@@ -345,7 +345,7 @@ pub unsafe fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void {
345345

346346
#[inline(always)]
347347
pub unsafe fn lua_newuserdata_t<T>(L: *mut lua_State, data: T) -> *mut T {
348-
unsafe extern "C-unwind" fn destructor<T>(_: *mut lua_State, ud: *mut c_void) {
348+
unsafe extern "C" fn destructor<T>(_: *mut lua_State, ud: *mut c_void) {
349349
ptr::drop_in_place(ud as *mut T);
350350
}
351351

src/memory.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ impl MemoryState {
9696
}
9797
}
9898

99-
unsafe extern "C-unwind" fn allocator(
99+
unsafe extern "C" fn allocator(
100100
extra: *mut c_void,
101101
ptr: *mut c_void,
102102
osize: usize,

src/state.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -2025,7 +2025,12 @@ impl Lua {
20252025

20262026
#[inline(always)]
20272027
pub(crate) fn lock(&self) -> ReentrantMutexGuard<RawLua> {
2028-
self.raw.lock()
2028+
let rawlua = self.raw.lock();
2029+
#[cfg(feature = "luau")]
2030+
if unsafe { (*rawlua.extra.get()).running_userdata_gc } {
2031+
panic!("Luau VM is suspended while userdata destructor is running");
2032+
}
2033+
rawlua
20292034
}
20302035

20312036
#[inline(always)]
@@ -2052,7 +2057,12 @@ impl WeakLua {
20522057
#[track_caller]
20532058
#[inline(always)]
20542059
pub(crate) fn lock(&self) -> LuaGuard {
2055-
LuaGuard::new(self.0.upgrade().expect("Lua instance is destroyed"))
2060+
let guard = LuaGuard::new(self.0.upgrade().expect("Lua instance is destroyed"));
2061+
#[cfg(feature = "luau")]
2062+
if unsafe { (*guard.extra.get()).running_userdata_gc } {
2063+
panic!("Luau VM is suspended while userdata destructor is running");
2064+
}
2065+
guard
20562066
}
20572067

20582068
#[inline(always)]

src/state/extra.rs

+4
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ pub(crate) struct ExtraData {
8383
#[cfg(feature = "luau")]
8484
pub(super) userthread_callback: Option<crate::types::UserThreadCallback>,
8585

86+
#[cfg(feature = "luau")]
87+
pub(crate) running_userdata_gc: bool,
8688
#[cfg(feature = "luau")]
8789
pub(super) sandboxed: bool,
8890
#[cfg(feature = "luau")]
@@ -186,6 +188,8 @@ impl ExtraData {
186188
compiler: None,
187189
#[cfg(feature = "luau-jit")]
188190
enable_jit: true,
191+
#[cfg(feature = "luau")]
192+
running_userdata_gc: false,
189193
}));
190194

191195
// Store it in the registry

src/userdata/util.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -436,11 +436,18 @@ pub(crate) unsafe extern "C-unwind" fn collect_userdata<T>(state: *mut ffi::lua_
436436

437437
// This method is called by Luau GC when it's time to collect the userdata.
438438
#[cfg(feature = "luau")]
439-
pub(crate) unsafe extern "C-unwind" fn collect_userdata<T>(
440-
_state: *mut ffi::lua_State,
439+
pub(crate) unsafe extern "C" fn collect_userdata<T>(
440+
state: *mut ffi::lua_State,
441441
ud: *mut std::os::raw::c_void,
442442
) {
443+
// Almost none Lua operations are allowed when destructor is running,
444+
// so we need to set a flag to prevent calling any Lua functions
445+
let extra = (*ffi::lua_callbacks(state)).userdata as *mut crate::state::ExtraData;
446+
(*extra).running_userdata_gc = true;
447+
// Luau does not support _any_ panics in destructors (they are declared as "C", NOT as "C-unwind"),
448+
// so any panics will trigger `abort()`.
443449
ptr::drop_in_place(ud as *mut T);
450+
(*extra).running_userdata_gc = false;
444451
}
445452

446453
// This method can be called by user or Lua GC to destroy the userdata.

0 commit comments

Comments
 (0)