@@ -149,6 +149,67 @@ pub(crate) unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> E
149149 }
150150}
151151
152+ // Create C closures under `lua_cpcall` so an allocation failure doesn't escape
153+ #[ cfg( any( feature = "lua51" , feature = "luajit" ) ) ]
154+ unsafe fn push_protected_cfunctions ( state : * mut ffi:: lua_State , f : ffi:: lua_CFunction ) -> Result < ( ) > {
155+ if !MemoryState :: get ( state) . is_null ( ) {
156+ MemoryState :: relax_limit_with ( state, || {
157+ ffi:: lua_pushcfunction ( state, error_traceback) ;
158+ ffi:: lua_pushcfunction ( state, f) ;
159+ } ) ;
160+ return Ok ( ( ) ) ;
161+ }
162+
163+ static ERROR_TRACEBACK_KEY : u8 = 0 ;
164+ static FUNCTION_KEY : u8 = 0 ;
165+
166+ unsafe extern "C-unwind" fn do_push ( state : * mut ffi:: lua_State ) -> c_int {
167+ let f = ffi:: lua_tolightuserdata ( state, -1 ) as * const ffi:: lua_CFunction ;
168+ ffi:: lua_pop ( state, 1 ) ;
169+
170+ ffi:: lua_pushcfunction ( state, error_traceback) ;
171+ ffi:: lua_rawsetp (
172+ state,
173+ ffi:: LUA_REGISTRYINDEX ,
174+ & ERROR_TRACEBACK_KEY as * const u8 as * const c_void ,
175+ ) ;
176+ ffi:: lua_pushcfunction ( state, * f) ;
177+ ffi:: lua_rawsetp (
178+ state,
179+ ffi:: LUA_REGISTRYINDEX ,
180+ & FUNCTION_KEY as * const u8 as * const c_void ,
181+ ) ;
182+ 0
183+ }
184+
185+ let ret = ffi:: lua_cpcall ( state, do_push, & f as * const ffi:: lua_CFunction as * mut c_void ) ;
186+ if ret != ffi:: LUA_OK {
187+ return Err ( pop_error ( state, ret) ) ;
188+ }
189+
190+ ffi:: lua_rawgetp (
191+ state,
192+ ffi:: LUA_REGISTRYINDEX ,
193+ & ERROR_TRACEBACK_KEY as * const u8 as * const c_void ,
194+ ) ;
195+ ffi:: lua_rawgetp (
196+ state,
197+ ffi:: LUA_REGISTRYINDEX ,
198+ & FUNCTION_KEY as * const u8 as * const c_void ,
199+ ) ;
200+ Ok ( ( ) )
201+ }
202+
203+ #[ cfg( not( any( feature = "lua51" , feature = "luajit" ) ) ) ]
204+ #[ inline]
205+ unsafe fn push_protected_cfunctions ( state : * mut ffi:: lua_State , f : ffi:: lua_CFunction ) -> Result < ( ) > {
206+ MemoryState :: relax_limit_with ( state, || {
207+ ffi:: lua_pushcfunction ( state, error_traceback) ;
208+ ffi:: lua_pushcfunction ( state, f) ;
209+ } ) ;
210+ Ok ( ( ) )
211+ }
212+
152213// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
153214// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
154215// limited lua stack. `nargs` is the same as the the parameter to `lua_pcall`, and `nresults` is
@@ -162,10 +223,7 @@ pub(crate) unsafe fn protect_lua_call(
162223) -> Result < ( ) > {
163224 let stack_start = ffi:: lua_gettop ( state) - nargs;
164225
165- MemoryState :: relax_limit_with ( state, || {
166- ffi:: lua_pushcfunction ( state, error_traceback) ;
167- ffi:: lua_pushcfunction ( state, f) ;
168- } ) ;
226+ push_protected_cfunctions ( state, f) ?;
169227 if nargs > 0 {
170228 ffi:: lua_rotate ( state, stack_start + 1 , 2 ) ;
171229 }
@@ -223,10 +281,7 @@ where
223281
224282 let stack_start = ffi:: lua_gettop ( state) - nargs;
225283
226- MemoryState :: relax_limit_with ( state, || {
227- ffi:: lua_pushcfunction ( state, error_traceback) ;
228- ffi:: lua_pushcfunction ( state, do_call :: < F , R > ) ;
229- } ) ;
284+ push_protected_cfunctions ( state, do_call :: < F , R > ) ?;
230285 if nargs > 0 {
231286 ffi:: lua_rotate ( state, stack_start + 1 , 2 ) ;
232287 }
0 commit comments