mruby-task: size the proc_set stack from the task's frame - #7308
Conversation
mrb_task_proc_set() grew the task's stack only when the proc's nregs exceeded the total capacity, stend - stbase. The proc is installed on the task's current frame, so the room that matters is above ci->stack. A task suspended mid-call-chain holds its frame at an offset, and a proc that fits the capacity but not the frame skipped the extend and the resumed task wrote past the stack. Compare against the room above the frame instead, allowing an exact fit so a proc the frame already holds does not touch the allocator. Bail out after a failed extend as well. A caller with no jmpbuf gets no re-raise from MRB_ENSURE, and the function went on to install the proc on the stack it just failed to grow, with mrb->exc set but unchecked. Add a regression test that suspends a task deep in a call chain before setting a larger proc. The probe task sleeps forever, so both the C helper and the Ruby test terminate it on every path. Leaving it suspended would keep a later Task.run from terminating. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The env resize in mrb_task_proc_set() read proc->body.irep->nlocals for any proc. body.irep aliases body.func for a cfunc proc and an mrb_sym for an alias proc, so both fed MRB_ENV_SET_LEN a value read through the wrong union member. The final install crashed on an alias proc too, CI_PROC_SET refuses aliases, and passing NULL died on the flag test before reaching CI_PROC_SET's NULL branch. Follow the alias chain to the real proc the way mrb_proc_arity() and mrb_proc_eql() do, tolerate NULL, and size and install the resolved proc. Widen the base env only while it is still backed by the task's stack. A heap env owns exactly MRB_ENV_LEN slots, so growing the length would send GC marking and the unshare copy past its allocation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review. 📝 WalkthroughWalkthrough
ChangesTask procedure replacement
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to This localized change addresses task stack sizing, error handling, proc alias resolution, and environment resizing; no actionable merge-blocking risk remains beyond normal checks and review. Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This PR is a follow-up to #7279, fixing two more issues in mrb_task_proc_set().
Problem
1. Out-of-bounds write in suspended tasks
The function determines whether the task's stack needs to grow by comparing the proc's
nregsagainst the total capacity (stend - stbase) instead of the available room above the current frame (ci->stack):A task suspended mid-call-chain holds its frame at an offset from the stack base. When the proc fits the total capacity but the
frame offset + procdoes not, the extension is skipped, and the resumed task writes past the stack. This is the edge case left open by #7279 (which only covered thereset_context+proc_setpath where the frame sits exactly at the stack base).Additionally, the error path has a related hole. A caller with no
jmpbufgets no re-raise fromMRB_ENSURE. Consequently, after a failed stack extension, the function previously went on to install the proc on the stack it just failed to grow, leavingmrb->excset but unchecked.2. Invalid memory access via union aliasing and heap envs
The
envresize logic below the stack growth blindly readsproc->body.irep->nlocalsfor any proc. Sincebodyis a union,body.irepholds a C function on acfuncproc and anmrb_symon analiasproc. Reading it in these cases feedsMRB_ENV_SET_LENan invalid value read through the wrong union member.Furthermore, the final install crashes on an
aliasproc becauseCI_PROC_SETrefuses aliases. PassingNULLalso dies on the flag test before reachingCI_PROC_SET'sNULLbranch.Finally, the block widens an
envthat may no longer be backed by the task's stack. A heapenvowns exactlyMRB_ENV_LENslots, so artificially growing its length sends GC marking and the unshare copy past its allocation boundaries.Fix
mrb_proc_arity()), tolerateNULL, and size/install the properly resolved proc.envonly while it is strictly backed by the task's stack.Testing
Added a regression test that suspends a task deep in a call chain and then sets a larger proc. Without the fix, it fails with the stack left at 64 slots, which cannot hold the required frame offset of 26 plus 43 registers.
Summary by CodeRabbit
Bug Fixes
Tests