@@ -833,6 +833,11 @@ struct SuspendedFrame {
833833 /// `vm.pending_tailcall_refs` when the callee's TailCall is consumed.
834834 /// Dropped when this SuspendedFrame is popped (after callee returns/errors).
835835 owned_refs : Vec < PyObjectRef > ,
836+ /// True for the initial frame passed into the trampoline by the caller.
837+ /// The caller owns the datastack allocation for the entry frame, so the
838+ /// trampoline must NOT release it — only callee-allocated frames are
839+ /// released here.
840+ is_entry : bool ,
836841}
837842
838843impl VirtualMachine {
@@ -1469,6 +1474,7 @@ impl VirtualMachine {
14691474 iframe : iframe as * mut crate :: frame:: InterpreterFrame ,
14701475 entry_state,
14711476 owned_refs : initial_refs,
1477+ is_entry : true ,
14721478 } ) ;
14731479 let mut action = Action :: EnterCallee ( initial_ptr) ;
14741480
@@ -1499,6 +1505,7 @@ impl VirtualMachine {
14991505 iframe : callee_ptr,
15001506 entry_state : callee_entry,
15011507 owned_refs : refs,
1508+ is_entry : false ,
15021509 } ) ;
15031510 action = Action :: EnterCallee ( self . take_pending_tailcall ( ) ) ;
15041511 }
@@ -1533,6 +1540,7 @@ impl VirtualMachine {
15331540 iframe : caller_iframe_ptr,
15341541 entry_state : caller_entry,
15351542 owned_refs : _caller_refs,
1543+ is_entry : caller_is_entry,
15361544 } = caller;
15371545 let caller_iframe = unsafe { & mut * caller_iframe_ptr } ;
15381546 caller_iframe. localsplus . push_stack ( value) ;
@@ -1548,15 +1556,18 @@ impl VirtualMachine {
15481556 iframe : caller_iframe_ptr,
15491557 entry_state : caller_entry,
15501558 owned_refs : refs,
1559+ is_entry : caller_is_entry,
15511560 } ) ;
15521561 action = Action :: EnterCallee ( self . take_pending_tailcall ( ) ) ;
15531562 }
15541563 Ok ( ExecutionResult :: Return ( value) ) => {
15551564 drop ( _caller_refs) ;
15561565 self . exit_iframe ( caller_entry) ;
1557- unsafe {
1558- if let Some ( base) = caller_iframe. release_datastack_frame ( ) {
1559- self . datastack_pop ( base) ;
1566+ if !caller_is_entry {
1567+ unsafe {
1568+ if let Some ( base) = caller_iframe. release_datastack_frame ( ) {
1569+ self . datastack_pop ( base) ;
1570+ }
15601571 }
15611572 }
15621573 action = Action :: ReturnValue ( value) ;
@@ -1565,9 +1576,11 @@ impl VirtualMachine {
15651576 Err ( exc) => {
15661577 drop ( _caller_refs) ;
15671578 self . exit_iframe ( caller_entry) ;
1568- unsafe {
1569- if let Some ( base) = caller_iframe. release_datastack_frame ( ) {
1570- self . datastack_pop ( base) ;
1579+ if !caller_is_entry {
1580+ unsafe {
1581+ if let Some ( base) = caller_iframe. release_datastack_frame ( ) {
1582+ self . datastack_pop ( base) ;
1583+ }
15711584 }
15721585 }
15731586 action = Action :: Unwind ( exc) ;
@@ -1583,6 +1596,7 @@ impl VirtualMachine {
15831596 iframe : caller_iframe_ptr,
15841597 entry_state : caller_entry,
15851598 owned_refs : _caller_refs,
1599+ is_entry : caller_is_entry,
15861600 } = caller;
15871601 let caller_iframe = unsafe { & mut * caller_iframe_ptr } ;
15881602
@@ -1603,16 +1617,20 @@ impl VirtualMachine {
16031617 iframe : caller_iframe_ptr,
16041618 entry_state : caller_entry,
16051619 owned_refs : refs,
1620+ is_entry : caller_is_entry,
16061621 } ) ;
16071622 action = Action :: EnterCallee ( self . take_pending_tailcall ( ) ) ;
16081623 }
16091624 Ok ( ExecutionResult :: Return ( value) ) => {
16101625 drop ( _caller_refs) ;
16111626 self . exit_iframe ( caller_entry) ;
1612- unsafe {
1613- if let Some ( base) = caller_iframe. release_datastack_frame ( )
1614- {
1615- self . datastack_pop ( base) ;
1627+ if !caller_is_entry {
1628+ unsafe {
1629+ if let Some ( base) =
1630+ caller_iframe. release_datastack_frame ( )
1631+ {
1632+ self . datastack_pop ( base) ;
1633+ }
16161634 }
16171635 }
16181636 action = Action :: ReturnValue ( value) ;
@@ -1623,10 +1641,13 @@ impl VirtualMachine {
16231641 Err ( new_exc) => {
16241642 drop ( _caller_refs) ;
16251643 self . exit_iframe ( caller_entry) ;
1626- unsafe {
1627- if let Some ( base) = caller_iframe. release_datastack_frame ( )
1628- {
1629- self . datastack_pop ( base) ;
1644+ if !caller_is_entry {
1645+ unsafe {
1646+ if let Some ( base) =
1647+ caller_iframe. release_datastack_frame ( )
1648+ {
1649+ self . datastack_pop ( base) ;
1650+ }
16301651 }
16311652 }
16321653 action = Action :: Unwind ( new_exc) ;
@@ -1636,9 +1657,11 @@ impl VirtualMachine {
16361657 Ok ( Some ( ExecutionResult :: Return ( value) ) ) => {
16371658 drop ( _caller_refs) ;
16381659 self . exit_iframe ( caller_entry) ;
1639- unsafe {
1640- if let Some ( base) = caller_iframe. release_datastack_frame ( ) {
1641- self . datastack_pop ( base) ;
1660+ if !caller_is_entry {
1661+ unsafe {
1662+ if let Some ( base) = caller_iframe. release_datastack_frame ( ) {
1663+ self . datastack_pop ( base) ;
1664+ }
16421665 }
16431666 }
16441667 action = Action :: ReturnValue ( value) ;
@@ -1649,9 +1672,11 @@ impl VirtualMachine {
16491672 Err ( new_exc) => {
16501673 drop ( _caller_refs) ;
16511674 self . exit_iframe ( caller_entry) ;
1652- unsafe {
1653- if let Some ( base) = caller_iframe. release_datastack_frame ( ) {
1654- self . datastack_pop ( base) ;
1675+ if !caller_is_entry {
1676+ unsafe {
1677+ if let Some ( base) = caller_iframe. release_datastack_frame ( ) {
1678+ self . datastack_pop ( base) ;
1679+ }
16551680 }
16561681 }
16571682 action = Action :: Unwind ( new_exc) ;
@@ -2194,13 +2219,6 @@ impl VirtualMachine {
21942219 save_exc,
21952220 } = state;
21962221
2197- // Read the materialized pointer once via read_volatile (bypasses
2198- // LLVM's noalias on the &mut iframe borrow).
2199- let mat_ptr = unsafe {
2200- let field_ptr = core:: ptr:: addr_of!( ( * iframe_ptr) . materialized) ;
2201- core:: ptr:: read_volatile ( field_ptr as * const usize )
2202- } ;
2203-
22042222 // If this iframe was materialized, capture f_back so that code
22052223 // holding a reference to the FrameObject can walk the chain after
22062224 // return. Read materialized through read_volatile to bypass
@@ -2237,10 +2255,22 @@ impl VirtualMachine {
22372255 core:: sync:: atomic:: Ordering :: Release ,
22382256 ) ;
22392257 }
2258+ }
22402259
22412260 if save_exc {
22422261 self . restore_exception ( saved_exc) ;
22432262 }
2263+ // Clear previous before popping — it may point to a stack-allocated
2264+ // iframe that will be freed when the caller's with_iframe exits.
2265+ {
2266+ #[ allow( unused_imports) ]
2267+ use rustpython_common:: atomic:: Radium ;
2268+ unsafe {
2269+ ( * iframe_ptr)
2270+ . previous
2271+ . store ( 0 , core:: sync:: atomic:: Ordering :: Relaxed ) ;
2272+ }
2273+ }
22442274 let _ = crate :: vm:: thread:: set_current_frame ( old_chain) ;
22452275 self . recursion_depth . update ( |d| d - 1 ) ;
22462276
@@ -2269,7 +2299,10 @@ impl VirtualMachine {
22692299 f : impl FnOnce ( & mut crate :: frame:: InterpreterFrame ) -> PyResult < R > ,
22702300 ) -> PyResult < R > {
22712301 let state = self . enter_iframe ( iframe) ?;
2302+ // Ensure exit_iframe runs even if f(iframe) panics.
2303+ let guard = scopeguard:: guard ( state, |s| self . exit_iframe ( s) ) ;
22722304 let result = f ( iframe) ;
2305+ let state = scopeguard:: ScopeGuard :: into_inner ( guard) ;
22732306 self . exit_iframe ( state) ;
22742307 result
22752308 }
0 commit comments