←back to thread

218 points signa11 | 1 comments | | HN request time: 0.207s | source
Show context
lou1306 ◴[] No.43681059[source]
> Using a stricter language helps with reducing some classes of bugs, at the cost of reduced flexibility in expressing a solution and increased effort creating the software.

First of all, those languages do not "help" "reducing" some classes of bugs. They often entirely remove them.

Then, even assuming that any safe language with unsafe regions (Rust, C#, etc) would not give you comparable flexibility at a fraction of the risk... if your flexible, effortless solution contains entire classes of bugs, then there is no point in comparing "effort". You should at least take into account the effort in providing a software with a high confidence that those bugs are not there.

replies(3): >>43681118 #>>43681160 #>>43684056 #
immibis ◴[] No.43681118[source]
If the language has unsafe regions, it doesn't entirely remove classes of bugs, since they can still occur in unsafe regions.

(Predictable response: "But they can only occur in unsafe regions which you can grep for" and my response to that: "so?")

replies(4): >>43681325 #>>43681630 #>>43682375 #>>43682796 #
oconnor663 ◴[] No.43682796[source]
> Predictable response: "But they can only occur in unsafe regions which you can grep for" and my response to that: "so?"

The situation is both worse than this and better than this. Consider the .set_len() method on Rust's Vec. It's unsafe, because you could just .set_len(1_000_000) and then the Vec would happily let you try to read the nonexistent millionth element and segfault. However, if you could edit the standard library sources, you could add this new method to Vec without touching any unsafe code:

    pub fn set_len_totally_safe_i_promise(&mut self, new_len: usize) {
        self.len = new_len;
    }
This is exactly the same as the real set_len, except it's a "fn" instead of an "unsafe fn". Now the Vec API is totally broken, and safe callers can corrupt memory. Also critically, we didn't write any unsafe code in "set_len_totally_safe_i_promise". The key detail is that this new method has access to the private self.len field of Vec that unsafe blocks in the same module rely on.

In other words, grepping for all the unsafe blocks isn't sufficient for saying that a program is UB-free. You also have to make sure that none of the safe code ever violates an invariant that the unsafe blocks rely on. Read the comments, think really hard, etc.

So...what's the point of all this? The point is that it lets us define a notion of "soundness", such that if I only write safe code, and I only use libraries that are "sound", we can guarantee that my program is UB-free. In other words, any UB in my program would necessarily point to a bug in one of my dependencies, in the stdlib, or in the compiler. (Or you know, in the hardware, or in mathematics itself.) In other other words, instead of auditing my entire gigantic (safe) program for UB, we can reduce the problem to auditing my dependencies for soundness. Crucially, this decouples the difficulty of the problem from the size of my program. This wouldn't be very interesting if "safe code" was some impoverished subset, like "unsigned integer arithmetic only". But in fact safe code can use pointers, tagged unions, pointers into tagged unions, heap allocation/freeing, and multithreading. Lots of large, complicated, useful, real-world programs are written in 100% safe code. Here the version of this story with all the caveats and footnotes: https://jacko.io/safety_and_soundness.html

replies(1): >>43683212 #
1. uecker ◴[] No.43683212[source]
You still need to audit the safe part for other bugs...

But yes, this is nice and we should (and probably will) have a safe mode in C too.