logoalt Hacker News

Memory Safe Context Switching

198 pointsby modelesstoday at 12:38 AM30 commentsview on HN

Comments

matheusmoreiratoday at 2:10 AM

This is an article I wish I could have read many months ago.

> Hence, the most basic safety issue with setjmp is that if we call it and then return from the function that had called it, the context saved by setjmp is not valid to longjmp to.

> longjmp is only safe if it's called at a time when the stack frame used by setjmp could not have possibly been overwritten, since that is the only way to guarantee that the register state restored by longjmp matches the stack frame that the stack pointer points to.

That limitation could be lifted by simply copying the stack frames somewhere else prior to long jumping, and then spilling that entire thing on top of the current stack instead of just restoring the registers from the jump buffer. This is how delimited continuations work! What ruins this for C is the existence of pointers. Stacks aren't freely relocatable since pointers into the stack could exist. Other languages don't have this problem.

So much fun stuff in this article! The "fibers with ucontext", essentially swapping stack pointers back and forth, are how I implemented generators! I too reached for musl source code in order to understand setjmp, but for a different reason: its ability to spill the registers onto the stack was instrumental for my garbage collector.

Blogged about all of these things too, in case anyone is curious:

https://www.matheusmoreira.com/articles/delimited-continuati...

https://www.matheusmoreira.com/articles/generators-in-lone-l...

https://www.matheusmoreira.com/articles/babys-second-garbage...

show 3 replies
nanolithtoday at 2:04 AM

> For example, Boost uses ucontext as part of its fiber implementation.

Maybe for the incredibly slow fallback, it does. Boost context and Boost fiber has ABI support for *nix / MacOS / Windows for x86_64 and ARM/ARM64. The overhead for a fiber switch using this support is about as heavy as a virtual function call. In comparison, ucontext is very heavy.

I wrote my own fiber library for C. I got the idea from an old implementation I saw that used setjmp and longjmp, which took me down the rabbit hole of figuring out how to do this more efficiently and with an improved margin of safety. I chose to follow Boost's example, and in fact, used some of their fiber switch assembler with attribution in my library.

show 1 reply
anitiltoday at 1:46 AM

How interesting! I thought that setjmp and longjmp were probably incompatible with Fil-C. And I'd somehow never heard of ucontext at all.

I suppose managing the stack is still managing memory after all, even if we typically don't think of it that way, so Fil-C has something to add here.

It's really worth reading the section here about the complexity of setjmp/longjmp and how they interact with register allocation and stack spilling. I knew they're tricky, but going in to the specifics is delicious.

toast0today at 5:29 AM

> longjmp panics unless it is called from a stack frame that is an ancestor of a stack frame that considers the zjmp_buf to be valid.

Maybe I have things backwards, but I think you mean descendent and not ancestor here?

If a() calls b(), I wouldn't think the stack frame while running b is the ancestor of a.

gruntled-workertoday at 1:37 AM

No complaints about this in particular, but code that uses setjmp/longjmp often has a risk profile that's way bigger than memory safety alone. If you're stuck with them then by all means, mitigate all you can.

show 1 reply
infogulchtoday at 2:13 PM

Couldn't we just permanently solve the stack copying issue by always using a per-stack base pointer + offset for all stack objects? Copy stack -> update base pointer -> done. Trying to access a stack object on a different thread? That's retarded don't do that allocate it on the heap instead.

show 1 reply
erichoceantoday at 8:48 AM

My depraved mind wants to see Jank [0] running on Fil-C, using Fil-C's garbage collector.

[0] https://jank-lang.org/

brcmthrowawaytoday at 1:35 AM

Is Fil-C now using Claude for dev?

show 2 replies
z0ltantoday at 2:56 AM

[dead]

lstoddtoday at 1:38 AM

longjmp, setjmp, setcontext, getcontext, makecontext, and swapcontext and whatever have no bearing on safety, memory or otherwise. What you have to deal with is what is represented by sigaction(2) and only and much later then by what you use to drive the context switch, be it io, or preemptive.

show 2 replies