Hacker News new | ask | show | jobs
by nullc 1090 days ago
So 6 out of 174 CVEs could have been avoided this way. ... but how many more of the unavoided logic errors will be created by using a language which is far more complicated, less clear, and readable/reviewable to far fewer people?

That said, It's a great sign for that the tests that it was was comprehensive enough to find bugs in the original sudo. But a set of tests isn't really complete until it finds a compiler bug too. :)

7 comments

I think getting a fresh perspective from fresh developers is still a net positive, some of the CVEs are truly embarrassing for such a critical piece of code.

That being said I skimmed the list of CVE and yeah Rust wouldn't have prevented anything. The rust evangelism is exhausting.

getting fresh perspective, or repeating the same mistake.

Many logic error were discovered in real life use cases over many years

It's finally my turn to link this often linked essay: https://www.joelonsoftware.com/2000/04/06/things-you-should-...

I don't believe I've ever seen it shared in the context of rewrite-in-rust, but I think the views it expresses also have some value in this context.

On the other hand, part of the goal is to strip out accumulated feature creep. Sudo apparently has a lot of functionality which I never knew existed, all of which could lead to future bugs.

There are quite a few Unix tools which I think could use a similar refresh if not frozen in time thanks to POSIX.

No doubt 80% of the users only use 20% of the features... but do they use the same 20%? :)

I think sudo is often the wrong tool for what people are using it for today-- it's a tool to delegate on a multi-user system. But today people are most often applying it on a single(-ish) user system to raise or lower their permissions.

Ironically, most of the vulnerabilities that sudo has had aren't really of consequence in that modern single user usage: If the attacker can run code as the user, then they'll be able to take over any privilege raising process the user uses even if the 'sudo' tool were flawless.

Once you leave that use case the greater feature set of sudo (including, perhaps some of those crusty features you mention) has more applicability.

The thing to always keep in mind is that the programs cruft is substantially the body of its embedded knowledge. Some of that knowledge might be mistaken or outdated. If you really knew what parts were what-- you could just remove them.

If the rewrite isn't sure it can remove it and just replicates, it may not have understood the function's purpose enough to replicate it faithfully and could even introduce security problems (or cause users to introduce them by forcing them to bodge around configuration statements that no longer work).

The expressive type system and the borrow checker /also/ allow you to avoid many of the logic bugs that you’d have otherwise.
In the case of memory safety, we can say that absent compiler bugs, use of unsafe (including by dependencies), or callouts to other languages that rust defiantly eliminates essentially all memory safety bugs.

We can't make the same strong statement about the type system preventing other bugs. The type system in rust requires a LOT more boiler plate code an extra typing, some of which can introduce bugs.

I am not aware of any study that would support the claim that rust reduces the defect rate overall and I'm somewhat doubtful of claims that it does because of the absurd regularity that rust software immediately panics on me when I attempt to use it (and, the amount of firfox crashes I've experienced which of late are almost entirely rust code panicking under the hood). It's also possible that what I'm seeing are all defects that would have existed otherwise made slightly more visible, if so -- it's a good thing.

But we shouldn't let the fact that we can take for granted that rust avoids memory safety bugs by construction from meaning that its other properties which MAY reduce defects over all actually do. Hopefully it does. But writing software entails a lot of cost and risk to undertake for a hope and plenty of things that have been intended to make things better actually made them worse.

I know it's hard to make a strong argument about this, and also to gather data on it. But there is https://opensource.googleblog.com/2023/06/rust-fact-vs-ficti... , section "Rumor 5: Rust code is high quality – Confirmed!", which touches upon this.

That said, personal experience has shown me that modeling data using types (instead of abusing, say, string-to-string dicts as is common in Python, or passing "untyped" strings around for things that /are/ strings but that have higher meaning, like file system paths) is very beneficial to program correctness. Obviously this is doable in many languages, but Rust helps enforce this due to the lack of implicit promotions and general adoption of these idioms in standard/common libraries.

As one specific example, I originally wrote sandboxfs (a FUSE file system) in Go and then rewrote it in Rust -- part because I hit performance issues, and part because I wanted to learn Rust. Trying to reproduce the original structure of the code in Rust was not doable, and having to adapt it to Rust highlighted various logic and concurrency bugs that existed in the Go version but that silently went unnoticed.

That's a good testimonial. Also thanks for the link.

I'm a big fan of strongly typed software, but at the same time I've absolutely seen bugs introduced where people were forced to be explicit in conversions and got it wrong, when the automatic thing would have been the right thing. Maybe the answer to my concern is just that there is no replacement for care and competence and that bad software can be written regardless of the tools.

> but how many more of the unavoided logic errors will be created by using a language which is far more complicated, less clear, and readable/reviewable to far fewer people?

Can we stop pushing this false narrative about Rust being sooooo overly complicated? The real reason this narrative is pushed is because lazy developers don't want to take the time to learn a language that forces them to think. Yes I said it. Most of these developers want to go on autopilot just copying/pasting code from others and not even think about security in the slightest.

Rust is actually really shallow in what you have to learn to write safe code.

For me, languages like JavaScript, python, and C++ are the real complicated languages. Which is why I haven't "learned" (is that even possible) them.

Rust is a dramatically more complex language than C (what sudo is written in!).

If you force the comparison with C++ then your position has more merit, however, I think there is still an argument that the subset of C++ that you must deal with in a given codebase can be (and often is) simpler than the portion of rust you must deal with in any rust codebase. I'm not particularly confident of this position, however, and it's tangential to the point that rust is unambiguously more complex than the language used by sudo.

Aside, there is an enormous amount of thoughtless copy and paste in rust, just as there is in other language. Rust also comes with a culture of extremely promiscuous dependency use, it's not uncommon to build a rust program and watch the compilation download and build two different SSL libraries! -- even a program that you have no interest in using with HTTPS/SSL at all.

Maybe in spite of the bad dependency culture rust actually will mean an advance in software quality in practice. But I think we simply don't have evidence of that (yet), and it's overly hopeful to assume that this will be the outcome simply because it was the intent of the creators of rust.

The fact that any criticism or concern gets mobbed an that there is so much mindless advocacy from people that haven't even considered issues like the trusted-computing-base problem doesn't speak well for the prospects that rusts' own problems that limit its benefit will be addressed.

Which is why I didn't mention C along with those other languages that are spaghetti. C is simple, clean, and won't get in your way. That last part is why most serious bugs occur. Unless the language is built from the ground up to prevent the bugs from compiling then you will always have vulnerabilities.

I know that C is a beautiful language and Rust is ugly but at some point we have to realize that humans are error prone and will always produce buggy C code.

As for Rust dependency issues, here's a very simple article for beginners:

https://marketsplash.com/tutorials/rust/rust-dependencies/

There's also the cargo tree command to find duplicate dependencies:

cargo tree --duplicates

By far more better than the dependency hell that is Node.js :D

Duplicates aren't the main cause of dependency hell in Node.js, although they certainly don't help matters.

Other systems languages have kept simple, C-like syntax without the associated error rates. Removing a lot of implicit casting and having sane compiler standards is all you need, not a bad implementation of the HM type system.

sudo doesn't have 174 CVEs. People always link to a simple mitre keyword search, and many just mention sudo in passing; you don't have to look far: the two first items on that list are not about sudo at all.

The last time I looked at this[1] I found the most serious problems were logic issues, and not memory issues (although there are some of those a well).

[1]: https://news.ycombinator.com/item?id=35756093

> using a language which is far more complicated, less clear, and readable/reviewable to far fewer people?

By that reasoning we should rewrite all these tools in JavaScript or Ruby, Python or some such. Clearer (than C), more readable and readable/reviewable to far more people.

Edit: to be clear: I think that would be an extremely dumb idea.

> By that reasoning we should rewrite all these tools in JavaScript or Ruby, Python or some such. Clearer (than C), more readable and readable/reviewable to far more people.

> Edit: to be clear: I think that would be an extremely dumb idea.

I dunno if it really is that dumb.

Why can't sudo be rewritten in Go? The performance difference is not likely to be noticed and there's no runtime platform dependence (as with Python, et al).

And, with managed memory, you'll have even fewer memory-safety bugs than with Rust.

What exactly makes Rust a better fit for sudo than Go, bearing in mind that Go is a whole lot safer than Rust.

How is Go a lot safer than rust?
Any GC language is a lot safer than Rust.

Seriously, anytime you have to manage memory yourself it's going to include the danger that you mismanage it. Rust does not prevent all types of memory mismanagement.

What's more, the burden is on the programmer to manage memory; all the Rust compiler tells you is where there are potential (not actual) memory errors. The programmer still has to fix them, and even with that there are still a number of memory errors that will get through.

Compare to a GC - allocate an object and then forget about it. No problems at all.

All of JS, Ruby, and Python are vastly more complicated languages than C. They do have a shallower learning curve. It's unclear to me what degree they have a bigger review audience: there are an awful lot of write-only JS programmers! :)
Rust is far more readable and reviewable than C code. As attested by 1000 Googlers.
I wouldn't trust Googlers as a measure of any sort of quality, given their love of Go.

C code relying on tons of macros will be harder to read then C code that delegates preprocessing to another language. Rust code that leans heavily into the ML features and unwrap.parent.unwrap.parent.unwap... will be a lot more unpleasant than Rust code written to just get the bit shifting job done. I imagine it really depends on the code base.

You might be overestimating the popularity of Go within Google.

That said, a tool like sudo should be written almost entirely in a very high-level language that is easy to use and review. C, C++, or even Rust should be reserved for places where the system interface requires it, if there are any such points of interface.

On the flip side, languages like C,C++ and Rust have the major benefit of having next to no runtime component to it, allowing trace-driven fuzz testing to achieve a much higher level of confidence in its test coverage. For a tool like sudo, this can matter a lot.
I think this is really important and it's a big part of what makes it unrealistic to write critical software in Python or Java: It's too slow to get extremely deep testing, even with fancy tricks like snapshotting the execution state to avoid startup costs.

But, that said, Rust code compiled in debug mode which required to get integer overflow detection is slow enough that it severely degrades the ability to use fuzz testing on many codebases, FWIW. I believe the reason is that debug mode always disables numerous optimizations that are required to make rust performant at all because of all the boilerplate emitted by earlier stages of compilation.

AFAIK there isn't a way to get the equivalent of GCC's "-fsanitize=undefined" (or -ftrapv) for checking for unexpected overflows at a performance cost similar to "-fsanitize=undefined" performance cost on C code.

It's still a much better situation than python or java, I think-- but an area that could use improvement which won't be improved if rust is above criticism.

You can pretty trivially turn on overflow checks yet compile with all of the other release options as normal. It's one line in cargo.toml or one flag to rustc.
A job for Common Lisp!
Huh? Go has its tradeoffs, some people don't like it and that's fine. But nobody can deny that it is by far one of the most 'readable and reviewable' (the argument in question) mainstream languages.
The praise of Go didn't end with it being just readable, but also 'brutally pragmatic' and all that.

If Googlers think that Rust code is just as readable as Go, even though Rust obviously makes tradeoffs against readability for other features, I would be tempted to mark that as Googlers just having a culture about being overeager to think the tools they are using are the best.

Why can't I deny that it is readable and reviewable? I don't think it is, basically at all. It gets evangelized as if it is but I have yet to see a compelling argument that can convince me. I've had the displeasure of having to dive into some codebases and I specifically hate it for that reason.
Well, I can't speak for you, of course. I also haven't seen that code you reviewed. And I won't try to evangelize you. I'd say, in a nutshell, that Go is readable and reviewable because its grammar consists of the C-style basis that most every imperative language has, and not much more. It's the lack of features that makes it very 'WYSIWYG'.
I think java is way more readable (and safe) than C - we should all switch to. Proof by authority w/o any background is not useful
Solaris had a bit of Java in it, and it was a terrible fit on account of how slow the JVM startup was and how large its memory footprint. Now that was almost 20 years ago and by now there's ways to optimize the JVM for short-lived processes, but it's still a pain and it's still a huge installation just for a small program like `sudo`. No, something like `sudo` needs to be natively compiled, or if bytecompiled then to a tiny VM -- tiny in every way: tiny opcode set, tiny bytecode interpreter, tiny install size.
You're not wrong, yet I'd sooner kill myself.
I think your complaints about Rust would depend on the quality of the code. You can go wild, or you can keep things simple.