Live data from Hacker News

Safepoints and Fil-C

fil-c.org

21–30 of 45 posts

Re: Safepoints and Fil-C

#21

Earlier quoted context omitted.

> And if it does anything that isn't async-signal-safe, then all bets are off. > So, the Fil-C implementation of vfork(2) would have to have some way of checking (either statically or dynamically) that nothing async-signal-unsafe happens. That's the hard bit. That's why I think that implementing it is crazy. Not really. See, Fil-C already makes those things that would not be safe be safe except returning from the cal…

Simple example that breaks the world: void foo(void) { vfork(); } In this case, the vfork child is returning. Eventually it'll exit. In the meantime, it's clobbered the vfork parent's stack. Kaboom

Yes, _that_ is not to be allowed. I think you could have LLVM know about `vfork(2)` and treat that as an error -- heck, clang already knows how to do that, does it not?

Re: Safepoints and Fil-C

#22

Earlier quoted context omitted.

Yeah, but it would go a looong way to getting us (the public) to use Fil-C in production. Like I'd really like to be able to run OpenSSH using Fil-C and I really don't want to have to worry about the C-coded crypto being non-constant-time. What I suggest as a medium-term solution might be to have macros that expand to nothing if Fil-C is not used but which expand to a Fil-C macro decoration that indicates that the au…

That's a good point. Note that statically checked inline asm is very achievable, so those folks who do constant time crypto by concealing their math operators behind inline asm will get what they need. But I guess you really want the OpenSSL out-of-line assembly to work?

> But I guess you really want the OpenSSL out-of-line assembly to work?

Yes. And that code generally does not invoke the allocator or stray out of the bounds of the given buffers, so at the very least you could mark that assembly as trusted for this mode.

Re: Safepoints and Fil-C

#23

> Fil-C's pollchecks also support stop-the-world (via the FILC_THREAD_STATE_STOP_REQUESTED bit). This is used for: > - Implementing fork(2), which needs all threads to stop at a known-good point before the fork child jettisons them. Makes me wonder how it handles `vfork()`, but I think it needs just a safepoint and no stop-the-world since, after all, the child side of `vfork()` is executing in the same address space…

I don't support vfork(2) right now, only fork(2). I have some super crazy ideas about how to make vfork(2) work, but I don't particularly like any of them. I'll implement it if I find some situation where vfork(2) is strongly required (so far I haven't found even one such case). The closest is that I've found cases where I have to add the CLOEXEC pipe trick to do error handling the fork(2) way rather than the vfork(2…

Hi Fil! Congrats on all the amazing progress on Fil-C.

We needed to port all the user-level fork(2) calls to vfork(2) when working on uCLinux, a port of Linux to MMU-less microcontrollers[1]. It used to be that paging MMUs were kinda expensive (those TLBs! so much associativity!!!), and the CPU on your printer/ethernet card/etc. might not have that much grit. Nowadays not so much.

Still. A hard-and-fast use for vfork(2), as requested perhaps.

[1] http://www.ibiblio.org/lou/old/ViewStation/

Re: Safepoints and Fil-C

#24

Earlier quoted context omitted.

Simple example that breaks the world: void foo(void) { vfork(); } In this case, the vfork child is returning. Eventually it'll exit. In the meantime, it's clobbered the vfork parent's stack. Kaboom

Yes, _that_ is not to be allowed. I think you could have LLVM know about `vfork(2)` and treat that as an error -- heck, clang already knows how to do that, does it not?

That's just one example and the problem isn't just checking this one case, but any generalization of it. It can't be a fully static check because you could achieve similar things with longjmp or exceptions (both of which Fil-C supports).

And then there are similar issues with heap accesses and safepoints. The vfork child has to particular in safepoints except that it cannot quite (you can't use pthread_mutex/pthread_cond primitives in the vfork child to synchronize with threads in the vfork parent).

Anyway, I'm thought about this a lot, and I could keep coming at you with reasons why it's hard. It's not like I haven't implemented vfork(2) out of some abstract fear.

Re: Safepoints and Fil-C

#25
post #23

Earlier quoted context omitted.

I don't support vfork(2) right now, only fork(2). I have some super crazy ideas about how to make vfork(2) work, but I don't particularly like any of them. I'll implement it if I find some situation where vfork(2) is strongly required (so far I haven't found even one such case). The closest is that I've found cases where I have to add the CLOEXEC pipe trick to do error handling the fork(2) way rather than the vfork(2…

Hi Fil! Congrats on all the amazing progress on Fil-C. We needed to port all the user-level fork(2) calls to vfork(2) when working on uCLinux, a port of Linux to MMU-less microcontrollers[1]. It used to be that paging MMUs were kinda expensive (those TLBs! so much associativity!!!), and the CPU on your printer/ethernet card/etc. might not have that much grit. Nowadays not so much. Still. A hard-and-fast use for vfork…

It'll be a while before Fil-C is appropriate for use in MMU-less microcontrollers.

Re: Safepoints and Fil-C

#26

Earlier quoted context omitted.

That's a good point. Note that statically checked inline asm is very achievable, so those folks who do constant time crypto by concealing their math operators behind inline asm will get what they need. But I guess you really want the OpenSSL out-of-line assembly to work?

> But I guess you really want the OpenSSL out-of-line assembly to work? Yes. And that code generally does not invoke the allocator or stray out of the bounds of the given buffers, so at the very least you could mark that assembly as trusted for this mode.

Yeah, I'll have to come up with a decent way of allowing this, at some point.

But see https://fil-c.org/runtime

It's really about creating an FFI for Fil-C, because Fil-C has its own calling convention and symbol mangling. It could be a lot of work.

And, worst case, it ends up being a footgun

Re: Safepoints and Fil-C

#27

Earlier quoted context omitted.

Remember that the child side of `vfork(2)` can only do async-signal-safe things as `vfork(2)` is documented, and it's really as if it is executing in the same thread as the parent (down to thread-locals, I do believe), so really, `vfork(2)` shouldn't really be all that special for Fil-C! You might want to try it. As u/foota notes, it's important. The point of `vfork(2)` is that it's _fast_. https://news.ycombinator.c…

> Remember that the child side of `vfork(2)` can only do async-signal-safe things as `vfork(2)` is documented And if it does anything that isn't async-signal-safe, then all bets are off. So, the Fil-C implementation of vfork(2) would have to have some way of checking (either statically or dynamically) that nothing async-signal-unsafe happens. That's the hard bit. That's why I think that implementing it is crazy. I'd…

The other place it comes up is launchers and resource managers. We actually have a series of old issues and implementation work on flux (large scale resource manager for clusters) working around fork becoming a significant bottleneck in parallel launch. IIRC it showed up when we had ~1gb of memory in use and needed to spawn between 64 and 192 processes per node. That said, we actually didn’t pivot to vfork, we pivoted to posix_spawn for all but the case where we have to change working directory (had to support old glibc without the attr for that in spawn). If you’re interested I think we did some benchmarking with public results I could dredge up.

Anyway, much as I have cases where it matters I guess what I’m saying is I think you’re right that vfork is rarely actually necessary, especially since you’d probably have a much easier time getting a faster and still deterministic spawn if it ever actually becomes a bottleneck for something you care about.

Re: Safepoints and Fil-C

#28

Earlier quoted context omitted.

> But I guess you really want the OpenSSL out-of-line assembly to work? Yes. And that code generally does not invoke the allocator or stray out of the bounds of the given buffers, so at the very least you could mark that assembly as trusted for this mode.

Yeah, I'll have to come up with a decent way of allowing this, at some point. But see https://fil-c.org/runtime It's really about creating an FFI for Fil-C, because Fil-C has its own calling convention and symbol mangling. It could be a lot of work. And, worst case, it ends up being a footgun

I'm sure for many people's $WORK the ability to run OpenSSH built with Fil-C and constant-time crypto would be amazing, and it would be great advertising for Fil-C. But there is no way any of us would run OpenSSH built with Fil-C in production w/o constant-time crypto.

Re: Safepoints and Fil-C

#29

Earlier quoted context omitted.

Yeah, I'll have to come up with a decent way of allowing this, at some point. But see https://fil-c.org/runtime It's really about creating an FFI for Fil-C, because Fil-C has its own calling convention and symbol mangling. It could be a lot of work. And, worst case, it ends up being a footgun

I'm sure for many people's $WORK the ability to run OpenSSH built with Fil-C and constant-time crypto would be amazing, and it would be great advertising for Fil-C. But there is no way any of us would run OpenSSH built with Fil-C in production w/o constant-time crypto.

That's good to know.

If I made the assembly memory safe under Fil-C rules by running it through a transform that inserted additional instructions but did not otherwise change what was happening, would you trust that it's still constant-time?

Re: Safepoints and Fil-C

#30
post #14

Earlier quoted context omitted.

What other methods of stopping at safepoints do you think are viable? Another approach is code patching, but it's not possible on iOS, and it's a bad idea in general.

The most commonly used optimization is to have just a load, or just a store, rather than a load-and-branch. Basically you access a page that you mprotect to trigger the handshake. Unrolling loops is also super common. Recognizing loops that have a bounded runtime is also common. My favorite technique to try one day is: 1. just record where the pollcheck points are but don't emit code there 2. to handshake with a thre…

Memory protection games just don't scale. You're modifying global structures, resulting in tons of contention. That's also why compacting GCs prefer to use card marking rather than mprotect.

About 2. - that's exactly what I meant. The downside is that now you have to write a full x86 emulator, maybe including SIMD instructions. Good luck.

And that's also what I want to try to avoid. Imagine generating a copy of the innermost loops, with the nearest backbranch replaced with a jump into the GC code. Then from the signal handler you just need to find the offset between the current instruction pointer and the mirror copy, adjust the pointer, and exit from the signal handler.

Post reply on HN