validate captured env before reading locals in debug/stack (#1928)

This commit is contained in:
nabeel-dev21 authored and GitHub committed 2026-10-24 09:13:19 -05:00
1 parent dc0f243bb9
commit 4475a3d51c
2 files changed
+29

No files matched your search

+2
View File
@@ -23,6 +23,7 @@
#ifndef JANET_AMALG
#include "features.h"
#include <janet.h>
#include "fiber.h"
#include "gc.h"
#include "state.h"
#include "util.h"
@@ -347,6 +348,7 @@ static Janet doframe(JanetStackFrame *frame) {
if (jsm.death_pc < (uint32_t) def->environments_length) {
/* death_pc is in valid range */
JanetFuncEnv *env = frame->func->envs[jsm.death_pc];
janet_env_valid(env);
if (jsm.slot_index < (uint32_t) env->length) {
/* slot_index range good */
if (env->offset > 0) {
+27
View File
@@ -30,5 +30,32 @@
(debug/unfbreak map 1)
(map inc [1 2 3])
# Reading locals of an unmarshalled closure that captures an on-stack
# environment must validate the env first. The on-stack variant stores a
# negative, unvalidated offset after unmarshal; without validation debug/stack
# read the fiber pointer as a value array (out-of-bounds / type confusion).
(def outer
(fiber/new
(fn []
(def secret 0xBEEF)
(def capt (fn [] (yield :paused) secret))
(yield capt)
:done)
:y))
(def capt (resume outer))
(def inner (fiber/new capt :y))
(resume inner)
(def image (marshal inner))
# Disable collection so the unmarshalled env keeps its unvalidated offset until
# debug/stack touches it.
(def old-interval (gcinterval))
(gcsetinterval 0)
(def inner2 (unmarshal image))
(var locals nil)
(each fr (debug/stack inner2)
(when (fr :locals) (set locals (fr :locals))))
(gcsetinterval old-interval)
(assert (= 0xBEEF (get locals 'secret)) "unmarshalled on-stack env validated in debug/stack")
(end-suite)