Hacker News new | past | comments | ask | show | jobs | submit login

I don't think this meets the definition of "safe" in "safe" Rust: "safe" doesn't just mean "won't crash due to spatial memory errors," it means that the code is in fact spatially and temporally memory safe.

In other words: this won't detect memory unsafety that doesn't result in an abnormal exit or other detectable fault. If I'm writing an exploit, my entire goal is to perform memory corruption without causing a fault; that's why Rust's safety property is much stronger than crash-freeness.




Even better, this library, with its use of unsafe and fork underneath, introduces a whole new class of undefined behavior to a program by providing a safe interface over an unsafe API without actually enforcing the invariants necessary for safety.

In order for the fork() it calls to be safe, it needs to guarantee a bunch of properties of the program that it simply cannot. If this gets used in a multithreaded program that calls malloc, you've got UB. There's a long list of caveats with fork mentioned in some other comments here.

In my view, this is not serious code and should be regarded as a joke. There's no actual value in this type of isolation.


Yep. I wanted to start from the high-level point of "safe doesn't mean doesn't crash," but you're right that the technique itself is unsound.


In rust terminology, "safe" actually implies more frequent crashes on untrusted inputs.


No, "safe" implies that there's no undefined behavior across all inputs. Whether that's a crash or not is still up to the implementer of the code in question, same as any other language. It is your choice whether to use interfaces that crash or do not crash, that is not forced upon you by the language.


Why do you think this? The closest thing in “common” Rust would be unwraps/panics, but these are (1) not crashes per se, and (2) probably not more common than they would be in an equivalent C codebase.


"Panics are not crashes" is a new one. I'm referring to the fact that the rust code panics at the slightest sign of discomfort.

And they are very much more common than in most C codebases. C codebases are generally often overly permissive in what they accept (hence to security bugs). Rust made a different trade.


> "Panics are not crashes" is a new one. I'm referring to the fact that the rust code panics at the slightest sign of discomfort.

In this context, I'm using "crash" to mean something like a program fault, i.e. an uncontrolled termination orchestrated by the kernel rather than the program itself. Rust programs generally terminate in a controlled manner, even if that manner is analogous to an unchecked exception.

It's also not my experience that Rust code, on average, panics on abnormal inputs. I've seen it happen, but the presence of e.g. safe iterators and access APIs means that you see a lot less of the "crash from invalid offset or index" behavior you see in C codebases.

(However, as pointed out in the adjacent thread, none of this really has anything to do with what "safe" means in Rust; controlled termination is one way to preserve safety, but idiomatic Rust codebases tend to lean much more heavily in the "error and result types for everything" direction. This in and of itself is arguably non-ideal in some cases.)


> I'm referring to the fact that the rust code panics at the slightest sign of discomfort.

That's kind of up to you as the developer though. I generally avoid writing functions that can panic -- I'd even argue any non-test code that panics is simply poorly written, because you can't "catch" a panic like you can in a high-level language. Better to return an error result and let the calling code decide how to handle it. Which often means showing an error to the user, but that's better than an unexpected crash.


I agree with you that error results (and exceptions) are better than panics. I will point out, though, that we're talking about language proclivities.

It is entirely up to you as the developer to write memory-safe code in C, and it's possible to do so. Most programmers don't because it's hard to do that once you're doing anything nontrivial. It's also possible to write panic-free rust, but it's hard.


That's fair. I do wish error handling in Rust were easier (try blocks have been in "unstable" for almost a decade). Panicking probably shouldn't have existed in the first place.


Well, you can close all file descriptors (except the pipe used for sending the return value back to the parent), re-mmap all files with MAP_PRIVATE, and then use SECCOMP_SET_MODE_STRICT to isolate the child process. But at that point, what are you even doing? Probably nothing useful.

If there were a Quick Fix for safety, we'd probably have discovered it by now.


  > use SECCOMP_SET_MODE_STRICT to isolate the child process. But at that
  > point, what are you even doing? Probably nothing useful.
The classic example of a fully-seccomp'd subprocess is decoding / decompression. If you want to execute ffmpeg on untrusted user input then seccomp is a sandbox that allows full-power SIMD, and the code has no reason to perform syscalls other than read/write to its input/output stream.

On the client side there's font shaping, PDF rendering, image decoding -- historically rich hunting grounds for browser CVEs.


The classic example of a fully-seccomp'd subprocess is decoding / decompression.

Yes. I've run JPEG 2000 decoders in a subprocess for that reason.


Well, it seems that lately this kind of task wants to write/mmap to a GPU, and poke at font files and interpret them.


I've proposed these changes to shy away from the claims of "Run unsafe code safely" in this crate.

Let me know what you think, or if you have any additional suggestions.



It's not just that it won't crash, it means that an exploit in the unsafe code won't allow corrupting memory used by the rest of the program


This is pretty immaterial from an exploit development perspective:

1. The forked process has a copy of the program state. If I'm trying to steal in-process secrets, I can do it from the forked process.

2. The forked process is just as privileged as the original process. If I'm trying to obtain code execution, I don't care which process I'm in.

This is why Chrome at al. have full-fledged sandboxes that communicate over restricted IPC; they don't fork the same process and call it a day.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: