VYPR
Low severity3.3NVD Advisory· Published Jun 1, 2026· Updated Jun 1, 2026

CVE-2026-10267

CVE-2026-10267

Description

An out-of-bounds read vulnerability in janet-lang up to 1.41.0 allows local attackers to read heap memory via a crafted fiber during debugging.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

An out-of-bounds read vulnerability in janet-lang up to 1.41.0 allows local attackers to read heap memory via a crafted fiber during debugging.

Vulnerability

A heap-based out-of-bounds read exists in the doframe function within src/core/debug.c of janet-lang versions up to 1.41.0 [2], [3]. The vulnerability occurs because the slot_index value, which is derived from untrusted marshaled data, is not validated against the slotcount of the function definition before being used to index into the stack [2], [4].

Exploitation

An attacker must be able to execute code locally to trigger this vulnerability [2]. Exploitation requires providing a crafted marshal payload to the application, which is then processed by the unmarshal function [3], [4]. When the debug/stack function is subsequently called on a fiber containing the malicious function definition, the unchecked slot_index causes the runtime to read memory outside the intended stack frame allocation [2], [4].

Impact

Successful exploitation of this vulnerability allows an attacker to perform an out-of-bounds read of heap memory [2], [4]. This can lead to the disclosure of sensitive information residing in adjacent memory locations, potentially compromising the confidentiality of the application's runtime state [3].

Mitigation

The vulnerability is addressed in commit ed17dd2c5913a23fb1107251e44a9410a3c30cf5, which introduces the necessary bounds checking for slot_index [2]. Users are advised to update to a version containing this fix.

AI Insight generated on Jun 1, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

1
ed17dd2c5913

Add runtime checks for bug #1743

https://github.com/janet-lang/janetCalvin RoseApr 26, 2026via nvd-ref
3 files changed · +42 7
  • src/core/bytecode.c+24 0 modified
    @@ -496,6 +496,30 @@ int janet_verify(JanetFuncDef *def) {
             }
         }
     
    +    /* Verify debug info - slotmapping, etc. */
    +    for (int32_t i = def->symbolmap_length - 1; i >= 0; i--) {
    +        JanetSymbolMap jsm = def->symbolmap[i];
    +        if (jsm.birth_pc == UINT32_MAX) {
    +            if (jsm.death_pc >= (uint32_t) def->environments_length) {
    +                return 10;
    +                /* We should also check jsm.slot_index */
    +            }
    +        } else {
    +            if (jsm.slot_index >= (uint32_t) def->slotcount) {
    +                return 11;
    +            }
    +            if (jsm.birth_pc != UINT32_MAX && jsm.birth_pc >= (uint32_t) def->bytecode_length) {
    +                return 12;
    +            }
    +            if (jsm.death_pc != UINT32_MAX && jsm.death_pc > (uint32_t) def->bytecode_length) {
    +                return 13;
    +            }
    +        }
    +        if (jsm.symbol == NULL) {
    +            return 14;
    +        }
    +    }
    +
         return 0;
     }
     
    
  • src/core/compile.c+1 1 modified
    @@ -1082,7 +1082,7 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
                     if (pair.sym2) {
                         JanetSymbolMap jsm;
                         jsm.birth_pc = UINT32_MAX;
    -                    jsm.death_pc = j;
    +                    jsm.death_pc = j; /* Use death_pc to encode environment index */
                         jsm.slot_index = pair.slot.index;
                         jsm.symbol = pair.sym2;
                         janet_v_push(locals, jsm);
    
  • src/core/debug.c+17 6 modified
    @@ -343,14 +343,25 @@ static Janet doframe(JanetStackFrame *frame) {
                     Janet value = janet_wrap_nil();
                     uint32_t pc = (uint32_t)(frame->pc - def->bytecode);
                     if (jsm.birth_pc == UINT32_MAX) {
    -                    JanetFuncEnv *env = frame->func->envs[jsm.death_pc];
    -                    if (env->offset > 0) {
    -                        value = env->as.fiber->data[env->offset + jsm.slot_index];
    -                    } else {
    -                        value = env->as.values[jsm.slot_index];
    +                    /* death_pc has secondary meaning here, we use it to encode the environment index */
    +                    if (jsm.death_pc < (uint32_t) def->environments_length) {
    +                        /* death_pc is in valid range */
    +                        JanetFuncEnv *env = frame->func->envs[jsm.death_pc];
    +                        if (jsm.slot_index < (uint32_t) env->length) {
    +                            /* slot_index range good */
    +                            if (env->offset > 0) {
    +                                /* On stack */
    +                                value = env->as.fiber->data[env->offset + jsm.slot_index];
    +                            } else {
    +                                /* Off stack */
    +                                value = env->as.values[jsm.slot_index];
    +                            }
    +                        }
                         }
                     } else if (pc >= jsm.birth_pc && pc < jsm.death_pc) {
    -                    value = stack[jsm.slot_index];
    +                    if (jsm.slot_index < (uint32_t) def->slotcount) {
    +                        value = stack[jsm.slot_index];
    +                    }
                     }
                     janet_table_put(local_bindings, janet_wrap_symbol(jsm.symbol), value);
                 }
    

Vulnerability mechanics

Root cause

"Missing bounds check in the doframe function of src/core/debug.c allows an out-of-bounds read when inspecting stack frames."

Attack vector

A local attacker with the ability to execute arbitrary Janet code can trigger an out-of-bounds read in the `doframe` function of `src/core/debug.c`. The attacker does not need special privileges beyond being able to run a Janet script. By crafting a script that forces the debugger to inspect a stack frame with an invalid or manipulated frame pointer, the function reads memory outside the intended buffer, leaking a limited amount of sensitive data. The CVSS vector confirms the attack is local, low-complexity, requires low privileges, and yields low confidentiality impact with no integrity or availability impact.

Affected code

The vulnerability resides in the function `doframe` in `src/core/debug.c`. This function is part of the Janet debugger and is reachable when a local attacker runs a crafted Janet script that triggers the debugger's frame inspection logic.

What the fix does

The patch [patch_id=4264815] modifies the `doframe` function in `src/core/debug.c` to add bounds checking before accessing frame data. The commit ensures that the frame index is validated against the actual number of available frames, preventing the function from reading beyond the allocated stack array. Without this check, an attacker could supply a frame index that causes an out-of-bounds read, leaking memory contents.

Preconditions

  • authAttacker must be able to execute arbitrary Janet code on the local system.
  • configThe Janet debugger must be active (e.g., via the REPL or a script that triggers debugger frame inspection).
  • networkAttack is local only; no network vector is involved.

Generated on Jun 1, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.