logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·03.11.26
← Back to news

Memory Safe Context Switching

Original article
fil-c.org|191 points|29 comments|by modeless|Jun 30, 2026

Memory Safe Context Switching

Fil-C provides a robust implementation of context switching that prioritizes memory safety. While support for ucontext APIs was introduced in release 0.680, users wishing to experiment with setcontext, getcontext, makecontext, and swapcontext must download a binary build the project from source.

Overview of Supported APIs

Fil-C ensures that the following functions are implemented without compromising the system's capability model or risking stack corruption:

  • Exception Handling: setjmp and longjmp (often used for "throwing" exceptions from signal handlers).
  • Concurrency Primitives: The ucontext suite (getcontext, setcontext, makecontext, swapcontext), which are essential for building fibers and coroutines (e.g., as seen in the Boost library).

[!IMPORTANT] Note that ucontext APIs are less common today and have been deprecated\text{deprecated}deprecated on certain platforms, such as Darwin.

The Danger of Context Switching

The primary challenge in implementing these APIs is preventing the restoration of a dangling stack. Memory unsafety typically occurs through the following misuse patterns:

  1. Scope Escape: Calling setjmp or getcontext inside a function and then returning from that function. The saved context now points to a stack frame that no longer exists.
  2. Manual Deallocation: Using makecontext to assign a stack, freeing that stack, and subsequently attempting to swapcontext or setcontext back to it.
  3. Self-Swapping: Passing the currently executing context as the second argument to swapcontext (often due to argument transposition).

Comparison: Yolo-C vs. Fil-C

FeatureYolo-C BehaviorFil-C Behavior
Dangling StackMay result in execution on freed memory.Impossible.
Crash TypeConfusing crashes; no stack trace available.Immediate panic at the point of misuse.
SecurityPotential for arbitrary code execution.Guaranteed memory safety.
OutcomeUndefined Behavior\text{Undefined Behavior}Undefined BehaviorEither a panic or legal execution.

Memory Safety Concept

Deep Dive: The "Evil" Nature of setjmp

To understand how Fil-C secures these operations, we must first acknowledge why setjmp is fundamentally problematic.

"The depravity of setjmp lies in the fact that it returns twice."

Execution Flow Diagram

Analysis of a Simple Program

Consider the following implementation:

#include <setjmp.h>
#include <stdio.h>

int main(int argc, char** argv) {
    volatile int x = 42; // Must be volatile!
    jmp_buf jb;

    if (setjmp(jb)) {
        printf("x = %d\n", x);
        return 0;
    }

    x = 666;
    longjmp(jb, 1);
    printf("Should not get here.\n");
    return 1;
}

Expected Output: x = 666

The Role of volatile

Without the volatile keyword, the compiler may optimize the variable xxx, causing the program to print 42 instead of 666. This happens because:

  • Constant Folding: The compiler may simply replace xxx with the constant 424242.
  • Register Allocation: xxx might be stored in a callee-save register that was captured during the first setjmp call.
  • Spill Slot Splitting: At optimization levels above -O0, the compiler might treat x=42x = 42x=42 and x=666x = 666x=666 as two distinct variables with separate memory locations (spill slots). The printf call might reference the first slot because it dominates the control flow.

Critical Reflections on Stack Allocation

To ensure the updated value is observed, the compiler must treat xxx as a consistent stack allocation. This leads to several technical questions:

  • How does the compiler ensure a stack allocation isn't reused?
  • Why wouldn't the x=42x=42x=42 spill slot be overwritten by the time longjmp occurs?
  • Can passing a pointer to xxx force the compiler to use a single stack location?

The "Diabolical" Example

To prove the issue of spill slot splitting, one can force register pressure in gcc, clang, or filcc as follows:

#include <setjmp.h>
#include <stdio.h>

int main(int argc, char** argv) {
    int x = 42;
    asm volatile("" : "+r"(x));
    jmp_buf jb;
    
    // Inducing register pressure to force spilling
    int a = 1, b = 2, c = 3, d = 4, e = 5, f = 6, g = 7, h = 9, i = 10;
    asm volatile("" : "+r"(a), "+r"(b), "+r"(d), "+r"(e), "+r"(