HN Debrief

Memory Safe Context Switching

  • Programming
  • Security
  • Developer Tools
  • Open Source

The post is a deep dive into why old C control-flow primitives are far more dangerous than they look. setjmp and longjmp do not just jump between functions. They restore register state against whatever stack frame is still there, which means jumping back after the original frame is gone can land on junk memory. The article argues that this is a direct memory-safety problem, not just a portability footgun, and explains how Fil-C constrains these APIs so a jump can only unwind the stack in ways that preserve its capability model. It also covers ucontext and related calls as a route to fibers and user-space context switching, but through the same lens of keeping stack manipulation memory safe.

If your runtime, coroutine, or error-handling design still leans on raw setjmp/longjmp or ucontext, treat them as capability-breaking primitives and put strict invariants around them. If performance matters, plan for a custom context-switch backend rather than assuming libc’s implementation is representative.

Discussion mood

Mostly positive and curious. People liked the article’s concrete explanation of why setjmp/longjmp are dangerous, and several commenters brought hands-on experience with continuations, generators, and fibers. The main caution was that memory safety only solves one class of failure, not the broader operational hazards of nonlocal jumps.

Key insights

  1. 01

    Why movable stacks fight C pointers

    Movable stacks become practical only when the runtime knows exactly what a stack reference is and how to update it. In C, raw pointers can point into the stack with no metadata, so copying or relocating frames risks leaving behind dangling addresses that look perfectly valid. A workable trick is to save stack chunks elsewhere but always restore them to the same original address range, which avoids pointer rewriting entirely. That is much closer to how continuations in managed runtimes work than to ordinary C calling conventions.

    If you want continuations, segmented stacks, or stack growth in C-compatible code, choose early between exact pointer tracking and strict restore-to-same-address rules. If you need ABI compatibility with existing C code, assume full stack relocation will stay painful.

      Attribution:
    • matheusmoreira #1 #2 #3 #4
    • pizlonator #1
    • Onavo #1
  2. 02

    ucontext overhead is mostly signal state

    The big performance knock on ucontext is not that context switching must be slow. It is that glibc’s implementation saves and restores signal masks along with execution state, which is expensive and often unnecessary for cooperative fibers. That makes libc a poor proxy for what user-space scheduling can cost. Boost-style assembly backends skip that extra work and get much closer to a function-call-scale switch.

    Do not benchmark fibers by testing swapcontext and calling it a day. If you are building a scheduler or coroutine runtime, separate safety policy from the low-level switch path so you can swap in a lean backend later.

      Attribution:
    • nanolith #1 #2
    • pizlonator #1
  3. 03

    Safe jumps still leave operational hazards

    Preventing memory corruption does not make longjmp behave like a modern structured exception system. It can still skip cleanup, cross library boundaries that were never written to tolerate nonlocal exit, or fail to restore machine state that some code quietly depends on. Those are not edge cases in low-level systems code. They are the other half of why these APIs have such a bad reputation.

    Even in a memory-safe runtime, audit every nonlocal jump like a control-flow escape hatch. Add cleanup rules, boundary checks, and tests around foreign code before treating it as a normal error path.

      Attribution:
    • gruntled-worker #1 #2
    • pizlonator #1 #2
  4. 04

    Capability safety depends on controlling unwinds

    The key Fil-C claim is not just that bad longjmp calls crash programs. It is that unrestricted stack rewinds could let code escape the runtime’s capability checks and reach memory it should no longer be allowed to touch. That turns an old C footgun into a direct threat to the language’s safety model. Restricting jumps to the current frame or deeper frames is what keeps the runtime’s view of live stack state aligned with reality.

    If you are designing a memory-safe layer on top of C, treat stack-control primitives as part of the trusted computing base. They need the same enforcement mindset you would apply to allocators, foreign-function boundaries, and signal handlers.

      Attribution:
    • lstodd #1
    • pizlonator #1 #2

Against the grain

  1. 01

    Offset-based stack references need a new ABI

    Using base-pointer-plus-offset for all stack objects would make relocation much easier in theory. The catch is that C’s existing binary interfaces assume ordinary pointers and ordinary stack layouts. That means the idea is less a tweak to stack handling and more a new calling convention that old code would not understand.

    If you are tempted to solve stack relocation by redesigning pointer representation, scope it as a language and ABI fork. Do not assume you can slip it under existing native libraries.

      Attribution:
    • infogulch #1
    • matheusmoreira #1
  2. 02

    Segmentation would not have saved this

    The thought experiment that old x86 segmentation could have made relocation painless got pushback. Changing one base register only helps if you can treat memory as a single movable object, while real systems need many independently relocatable regions like shared libraries and multiple stacks. Even modern tricks like mmap with MAP_FIXED do not remove address-space conflicts inside one process.

    Do not look for a hardware-era shortcut to make stack relocation free. The hard part is managing many live address-bearing objects at once, not just rebasing one chunk of memory.

      Attribution:
    • quotemstr #1 #2
    • dzaima #1
    • russdill #1

In plain english

Boost
A widely used collection of portable C++ libraries.
Boost.Context
A Boost library that provides low-level user-space context switching primitives, often used to implement fibers or coroutines.
capability model
A safety model where access to memory or resources is controlled through explicit references that carry authority.
fiber
A lightweight user-space thread that cooperatively yields control rather than being preempted by the operating system.
Fil-C
A project that aims to make C memory safe by enforcing a capability-based model around pointers and low-level operations.
glibc
The GNU C Library, the standard C runtime library used on many Linux systems.
longjmp
A C library function that restores a state saved by setjmp and resumes execution there, skipping normal returns.
MAP_FIXED
An mmap option that requests mapping memory at an exact virtual address, replacing existing mappings if needed.
mmap
A Unix system call that maps files or anonymous memory into a process address space.
setjmp
A C library function that saves the current execution state so code can later jump back to that point.
ucontext
A family of POSIX context-switching APIs such as getcontext, setcontext, makecontext, and swapcontext for saving and restoring user-space execution state.

Reference links

Continuations and stack relocation

Runtime implementation examples

  • Generators in Lone Lisp
    Example linked by a commenter who used stack-switching ideas similar to fibers to implement generators.
  • Baby’s Second Garbage Collector
    Linked as a practical example of using saved register state and stack handling in a custom garbage collector.
  • Lone Lisp heap
    Linked to show an offset-based object and reference model that makes relocation easier than raw C pointers do.
  • Jank language
    Mentioned as an interesting candidate to combine with Fil-C and its garbage collector.

Background on user-space context switching

Project and tooling references

  • Fil-C CLAUDE.md
    Referenced in a side discussion about whether LLM tools are being used in Fil-C development.