Skip to content

Commit faf547c

Browse files
committed
Refactor Lua::inspect_stack and debug interface.
It was possible to cause a crash when getting a `Debug` instance and keeping it while deallocating the Lua stack frames.
1 parent 0de7cd1 commit faf547c

File tree

8 files changed

+67
-122
lines changed

8 files changed

+67
-122
lines changed

src/hook.rs renamed to src/debug.rs

Lines changed: 33 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
use std::borrow::Cow;
2-
use std::ops::Deref;
3-
#[cfg(not(feature = "luau"))]
4-
use std::ops::{BitOr, BitOrAssign};
52
use std::os::raw::c_int;
63

74
use ffi::lua_Debug;
85

96
use crate::state::RawLua;
10-
use crate::types::ReentrantMutexGuard;
117
use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
128

139
/// Contains information about currently executing Lua code.
@@ -20,51 +16,15 @@ use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str};
2016
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#lua_Debug
2117
/// [`Lua::set_hook`]: crate::Lua::set_hook
2218
pub struct Debug<'a> {
23-
lua: EitherLua<'a>,
24-
ar: ActivationRecord,
25-
#[cfg(feature = "luau")]
19+
lua: &'a RawLua,
20+
#[cfg_attr(not(feature = "luau"), allow(unused))]
2621
level: c_int,
27-
}
28-
29-
enum EitherLua<'a> {
30-
Owned(ReentrantMutexGuard<'a, RawLua>),
31-
#[cfg(not(feature = "luau"))]
32-
Borrowed(&'a RawLua),
33-
}
34-
35-
impl Deref for EitherLua<'_> {
36-
type Target = RawLua;
37-
38-
fn deref(&self) -> &Self::Target {
39-
match self {
40-
EitherLua::Owned(guard) => guard,
41-
#[cfg(not(feature = "luau"))]
42-
EitherLua::Borrowed(lua) => lua,
43-
}
44-
}
22+
ar: *mut lua_Debug,
4523
}
4624

4725
impl<'a> Debug<'a> {
48-
// We assume the lock is held when this function is called.
49-
#[cfg(not(feature = "luau"))]
50-
pub(crate) fn new(lua: &'a RawLua, ar: *mut lua_Debug) -> Self {
51-
Debug {
52-
lua: EitherLua::Borrowed(lua),
53-
ar: ActivationRecord(ar, false),
54-
}
55-
}
56-
57-
pub(crate) fn new_owned(
58-
guard: ReentrantMutexGuard<'a, RawLua>,
59-
_level: c_int,
60-
ar: Box<lua_Debug>,
61-
) -> Self {
62-
Debug {
63-
lua: EitherLua::Owned(guard),
64-
ar: ActivationRecord(Box::into_raw(ar), true),
65-
#[cfg(feature = "luau")]
66-
level: _level,
67-
}
26+
pub(crate) fn new(lua: &'a RawLua, level: c_int, ar: *mut lua_Debug) -> Self {
27+
Debug { lua, ar, level }
6828
}
6929

7030
/// Returns the specific event that triggered the hook.
@@ -77,7 +37,7 @@ impl<'a> Debug<'a> {
7737
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
7838
pub fn event(&self) -> DebugEvent {
7939
unsafe {
80-
match (*self.ar.get()).event {
40+
match (*self.ar).event {
8141
ffi::LUA_HOOKCALL => DebugEvent::Call,
8242
ffi::LUA_HOOKRET => DebugEvent::Ret,
8343
ffi::LUA_HOOKTAILCALL => DebugEvent::TailCall,
@@ -93,19 +53,19 @@ impl<'a> Debug<'a> {
9353
unsafe {
9454
#[cfg(not(feature = "luau"))]
9555
mlua_assert!(
96-
ffi::lua_getinfo(self.lua.state(), cstr!("n"), self.ar.get()) != 0,
56+
ffi::lua_getinfo(self.lua.state(), cstr!("n"), self.ar) != 0,
9757
"lua_getinfo failed with `n`"
9858
);
9959
#[cfg(feature = "luau")]
10060
mlua_assert!(
101-
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("n"), self.ar.get()) != 0,
61+
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("n"), self.ar) != 0,
10262
"lua_getinfo failed with `n`"
10363
);
10464

10565
DebugNames {
106-
name: ptr_to_lossy_str((*self.ar.get()).name),
66+
name: ptr_to_lossy_str((*self.ar).name),
10767
#[cfg(not(feature = "luau"))]
108-
name_what: match ptr_to_str((*self.ar.get()).namewhat) {
68+
name_what: match ptr_to_str((*self.ar).namewhat) {
10969
Some("") => None,
11070
val => val,
11171
},
@@ -120,27 +80,27 @@ impl<'a> Debug<'a> {
12080
unsafe {
12181
#[cfg(not(feature = "luau"))]
12282
mlua_assert!(
123-
ffi::lua_getinfo(self.lua.state(), cstr!("S"), self.ar.get()) != 0,
83+
ffi::lua_getinfo(self.lua.state(), cstr!("S"), self.ar) != 0,
12484
"lua_getinfo failed with `S`"
12585
);
12686
#[cfg(feature = "luau")]
12787
mlua_assert!(
128-
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("s"), self.ar.get()) != 0,
88+
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("s"), self.ar) != 0,
12989
"lua_getinfo failed with `s`"
13090
);
13191

13292
DebugSource {
133-
source: ptr_to_lossy_str((*self.ar.get()).source),
93+
source: ptr_to_lossy_str((*self.ar).source),
13494
#[cfg(not(feature = "luau"))]
135-
short_src: ptr_to_lossy_str((*self.ar.get()).short_src.as_ptr()),
95+
short_src: ptr_to_lossy_str((*self.ar).short_src.as_ptr()),
13696
#[cfg(feature = "luau")]
137-
short_src: ptr_to_lossy_str((*self.ar.get()).short_src),
138-
line_defined: linenumber_to_usize((*self.ar.get()).linedefined),
97+
short_src: ptr_to_lossy_str((*self.ar).short_src),
98+
line_defined: linenumber_to_usize((*self.ar).linedefined),
13999
#[cfg(not(feature = "luau"))]
140-
last_line_defined: linenumber_to_usize((*self.ar.get()).lastlinedefined),
100+
last_line_defined: linenumber_to_usize((*self.ar).lastlinedefined),
141101
#[cfg(feature = "luau")]
142102
last_line_defined: None,
143-
what: ptr_to_str((*self.ar.get()).what).unwrap_or("main"),
103+
what: ptr_to_str((*self.ar).what).unwrap_or("main"),
144104
}
145105
}
146106
}
@@ -150,16 +110,16 @@ impl<'a> Debug<'a> {
150110
unsafe {
151111
#[cfg(not(feature = "luau"))]
152112
mlua_assert!(
153-
ffi::lua_getinfo(self.lua.state(), cstr!("l"), self.ar.get()) != 0,
113+
ffi::lua_getinfo(self.lua.state(), cstr!("l"), self.ar) != 0,
154114
"lua_getinfo failed with `l`"
155115
);
156116
#[cfg(feature = "luau")]
157117
mlua_assert!(
158-
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("l"), self.ar.get()) != 0,
118+
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("l"), self.ar) != 0,
159119
"lua_getinfo failed with `l`"
160120
);
161121

162-
(*self.ar.get()).currentline
122+
(*self.ar).currentline
163123
}
164124
}
165125

@@ -170,10 +130,10 @@ impl<'a> Debug<'a> {
170130
pub fn is_tail_call(&self) -> bool {
171131
unsafe {
172132
mlua_assert!(
173-
ffi::lua_getinfo(self.lua.state(), cstr!("t"), self.ar.get()) != 0,
133+
ffi::lua_getinfo(self.lua.state(), cstr!("t"), self.ar) != 0,
174134
"lua_getinfo failed with `t`"
175135
);
176-
(*self.ar.get()).currentline != 0
136+
(*self.ar).currentline != 0
177137
}
178138
}
179139

@@ -182,51 +142,34 @@ impl<'a> Debug<'a> {
182142
unsafe {
183143
#[cfg(not(feature = "luau"))]
184144
mlua_assert!(
185-
ffi::lua_getinfo(self.lua.state(), cstr!("u"), self.ar.get()) != 0,
145+
ffi::lua_getinfo(self.lua.state(), cstr!("u"), self.ar) != 0,
186146
"lua_getinfo failed with `u`"
187147
);
188148
#[cfg(feature = "luau")]
189149
mlua_assert!(
190-
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("au"), self.ar.get()) != 0,
150+
ffi::lua_getinfo(self.lua.state(), self.level, cstr!("au"), self.ar) != 0,
191151
"lua_getinfo failed with `au`"
192152
);
193153

194154
#[cfg(not(feature = "luau"))]
195155
let stack = DebugStack {
196-
num_ups: (*self.ar.get()).nups as _,
156+
num_ups: (*self.ar).nups as _,
197157
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
198-
num_params: (*self.ar.get()).nparams as _,
158+
num_params: (*self.ar).nparams as _,
199159
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
200-
is_vararg: (*self.ar.get()).isvararg != 0,
160+
is_vararg: (*self.ar).isvararg != 0,
201161
};
202162
#[cfg(feature = "luau")]
203163
let stack = DebugStack {
204-
num_ups: (*self.ar.get()).nupvals,
205-
num_params: (*self.ar.get()).nparams,
206-
is_vararg: (*self.ar.get()).isvararg != 0,
164+
num_ups: (*self.ar).nupvals,
165+
num_params: (*self.ar).nparams,
166+
is_vararg: (*self.ar).isvararg != 0,
207167
};
208168
stack
209169
}
210170
}
211171
}
212172

213-
struct ActivationRecord(*mut lua_Debug, bool);
214-
215-
impl ActivationRecord {
216-
#[inline]
217-
fn get(&self) -> *mut lua_Debug {
218-
self.0
219-
}
220-
}
221-
222-
impl Drop for ActivationRecord {
223-
fn drop(&mut self) {
224-
if self.1 {
225-
drop(unsafe { Box::from_raw(self.0) });
226-
}
227-
}
228-
}
229-
230173
/// Represents a specific event that triggered the hook.
231174
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
232175
pub enum DebugEvent {
@@ -385,7 +328,7 @@ impl HookTriggers {
385328
}
386329

387330
#[cfg(not(feature = "luau"))]
388-
impl BitOr for HookTriggers {
331+
impl std::ops::BitOr for HookTriggers {
389332
type Output = Self;
390333

391334
fn bitor(mut self, rhs: Self) -> Self::Output {
@@ -400,7 +343,7 @@ impl BitOr for HookTriggers {
400343
}
401344

402345
#[cfg(not(feature = "luau"))]
403-
impl BitOrAssign for HookTriggers {
346+
impl std::ops::BitOrAssign for HookTriggers {
404347
fn bitor_assign(&mut self, rhs: Self) {
405348
*self = *self | rhs;
406349
}

src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ mod macros;
7575
mod buffer;
7676
mod chunk;
7777
mod conversion;
78+
mod debug;
7879
mod error;
7980
mod function;
80-
mod hook;
8181
#[cfg(any(feature = "luau", doc))]
8282
mod luau;
8383
mod memory;
@@ -101,9 +101,9 @@ pub use bstr::BString;
101101
pub use ffi::{self, lua_CFunction, lua_State};
102102

103103
pub use crate::chunk::{AsChunk, Chunk, ChunkMode};
104+
pub use crate::debug::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
104105
pub use crate::error::{Error, ErrorContext, ExternalError, ExternalResult, Result};
105106
pub use crate::function::{Function, FunctionInfo};
106-
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack};
107107
pub use crate::multi::{MultiValue, Variadic};
108108
pub use crate::scope::Scope;
109109
pub use crate::state::{GCMode, Lua, LuaOptions, WeakLua};
@@ -124,7 +124,7 @@ pub use crate::userdata::{
124124
pub use crate::value::{Nil, Value};
125125

126126
#[cfg(not(feature = "luau"))]
127-
pub use crate::hook::HookTriggers;
127+
pub use crate::debug::HookTriggers;
128128

129129
#[cfg(any(feature = "luau", doc))]
130130
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]

src/state.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ use std::result::Result as StdResult;
88
use std::{fmt, mem, ptr};
99

1010
use crate::chunk::{AsChunk, Chunk};
11+
use crate::debug::Debug;
1112
use crate::error::{Error, Result};
1213
use crate::function::Function;
13-
use crate::hook::Debug;
1414
use crate::memory::MemoryState;
1515
use crate::multi::MultiValue;
1616
use crate::scope::Scope;
@@ -28,7 +28,7 @@ use crate::util::{assert_stack, check_stack, protect_lua_closure, push_string, r
2828
use crate::value::{Nil, Value};
2929

3030
#[cfg(not(feature = "luau"))]
31-
use crate::{hook::HookTriggers, types::HookKind};
31+
use crate::{debug::HookTriggers, types::HookKind};
3232

3333
#[cfg(any(feature = "luau", doc))]
3434
use crate::{buffer::Buffer, chunk::Compiler};
@@ -869,28 +869,27 @@ impl Lua {
869869
}
870870
}
871871

872-
/// Gets information about the interpreter runtime stack.
872+
/// Gets information about the interpreter runtime stack at a given level.
873873
///
874-
/// This function returns [`Debug`] structure that can be used to get information about the
875-
/// function executing at a given level. Level `0` is the current running function, whereas
876-
/// level `n+1` is the function that has called level `n` (except for tail calls, which do
877-
/// not count in the stack).
878-
///
879-
/// [`Debug`]: crate::hook::Debug
880-
pub fn inspect_stack(&self, level: usize) -> Option<Debug<'_>> {
874+
/// This function calls callback `f`, passing the [`Debug`] structure that can be used to get
875+
/// information about the function executing at a given level.
876+
/// Level `0` is the current running function, whereas level `n+1` is the function that has
877+
/// called level `n` (except for tail calls, which do not count in the stack).
878+
pub fn inspect_stack<R>(&self, level: usize, f: impl FnOnce(Debug) -> R) -> Option<R> {
881879
let lua = self.lock();
882880
unsafe {
883-
let mut ar = Box::new(mem::zeroed::<ffi::lua_Debug>());
881+
let mut ar = mem::zeroed::<ffi::lua_Debug>();
884882
let level = level as c_int;
885883
#[cfg(not(feature = "luau"))]
886-
if ffi::lua_getstack(lua.state(), level, &mut *ar) == 0 {
884+
if ffi::lua_getstack(lua.state(), level, &mut ar) == 0 {
887885
return None;
888886
}
889887
#[cfg(feature = "luau")]
890-
if ffi::lua_getinfo(lua.state(), level, cstr!(""), &mut *ar) == 0 {
888+
if ffi::lua_getinfo(lua.state(), level, cstr!(""), &mut ar) == 0 {
891889
return None;
892890
}
893-
Some(Debug::new_owned(lua, level, ar))
891+
892+
Some(f(Debug::new(&lua, level, &mut ar)))
894893
}
895894
}
896895

src/state/extra.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ pub(crate) struct ExtraData {
7676
#[cfg(not(feature = "luau"))]
7777
pub(super) hook_callback: Option<crate::types::HookCallback>,
7878
#[cfg(not(feature = "luau"))]
79-
pub(super) hook_triggers: crate::hook::HookTriggers,
79+
pub(super) hook_triggers: crate::debug::HookTriggers,
8080
#[cfg(feature = "lua54")]
8181
pub(super) warn_callback: Option<crate::types::WarnCallback>,
8282
#[cfg(feature = "luau")]

src/state/raw.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use super::{Lua, LuaOptions, WeakLua};
3838

3939
#[cfg(not(feature = "luau"))]
4040
use crate::{
41-
hook::Debug,
41+
debug::Debug,
4242
types::{HookCallback, HookKind, VmState},
4343
};
4444

@@ -435,7 +435,7 @@ impl RawLua {
435435
match (*extra).hook_callback.clone() {
436436
Some(hook_callback) => {
437437
let rawlua = (*extra).raw_lua();
438-
let debug = Debug::new(rawlua, ar);
438+
let debug = Debug::new(rawlua, 0, ar);
439439
hook_callback((*extra).lua(), debug)
440440
}
441441
None => {
@@ -465,7 +465,7 @@ impl RawLua {
465465

466466
let status = callback_error_ext(state, ptr::null_mut(), false, |extra, _| {
467467
let rawlua = (*extra).raw_lua();
468-
let debug = Debug::new(rawlua, ar);
468+
let debug = Debug::new(rawlua, 0, ar);
469469
let hook_callback = (*hook_callback_ptr).clone();
470470
hook_callback((*extra).lua(), debug)
471471
});

src/thread.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard};
1010

1111
#[cfg(not(feature = "luau"))]
1212
use crate::{
13-
hook::{Debug, HookTriggers},
13+
debug::{Debug, HookTriggers},
1414
types::HookKind,
1515
};
1616

src/types.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use std::cell::UnsafeCell;
22
use std::os::raw::{c_int, c_void};
33

4-
use crate::error::Result;
54
#[cfg(not(feature = "luau"))]
6-
use crate::hook::{Debug, HookTriggers};
5+
use crate::debug::{Debug, HookTriggers};
6+
use crate::error::Result;
77
use crate::state::{ExtraData, Lua, RawLua};
88

99
// Re-export mutex wrappers

0 commit comments

Comments
 (0)