Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Gavin, there is no point in arguing with fanatics. (You have to know that none of them are actually going to dive into your code and possibly learn something new that didn't come from a vetted authority figure). They are incapable of listening to their own independent thoughts, assuming that they have them, and must attack anyone who counters their dogma because it risks shattering their core beliefs, which is intolerable. The funny thing is that the truth of rust is right there in its name. It is a corrosive process that ruins the iron that it touches.


I really like C. I did actually spend about an hour reading his code. It's very nice, and I genuinely believe that Gavin is probably capable of writing memory-safe C most of the time. Except when his sanitizer setup is accidentally disabled, of course.

I did find at least one case where a function in isolation is explicitly not memory-safe, but its safety depends on implicit and undocumented assumptions about how it's called. I didn't find a way to exploit it from the userland program. In `bc_parse_addNum`[1], a `char *` string is indexed into without checking the length. Callers are expected to provide strings of at least length 2. That expectation is not documented, though it is adhered to. But it is a landmine waiting to be tripped over should that assumption change.

But this is a sideshow. Experts have been insisting for decades that they can write crash-free C and yet we continue to regularly find exploitable memory issues in every large multi-author C project. It's plain as day that—for almost all of us—an increasingly indefensible amount of the time spent on C programs is in dealing with the types of bugs that languages like Rust prevent outright. Whether that's simply finding and fixing them, architecting programs to avoid them, incorporating those fixes into downstream projects, mitigating their impact in live production systems, or whatever, our industry pays an enormous ongoing cost. Even in projects that have never had a public release with a memory bug, significant engineering time is spent on them just during development iteration.

As a handy example, the Linux kernel is still trying to rid itself of `strlcpy`[2] which has been the source of multiple CVEs. This process has taken upwards of twenty years. And that's after spending god knows how much time and effort migrating to `strlcpy` from `strcpy` in the first place.

[1] https://git.yzena.com/gavin/bc/src/branch/master/src/parse.c...

[2] https://lwn.net/Articles/905777/


> I really like C. I did actually spend about an hour reading his code. It's very nice, and I genuinely believe that Gavin is probably capable of writing memory-safe C most of the time.

This is a nice compliment, thank you.

> Except when his sanitizer setup is accidentally disabled, of course.

Fair criticism. XD

> I did find at least one case where a function in isolation is explicitly not memory-safe, but its safety depends on implicit and undocumented assumptions about how it's called. I didn't find a way to exploit it from the userland program. In `bc_parse_addNum`[1], a `char` pointer string is indexed into without checking the length. Callers are expected to provide strings of at least length 2. That expectation is not documented, though it is adhered to. But it is a landmine waiting to be tripped over should that assumption change.

Callers are actually required to pass a string of length 1 including the nul terminator, but you are correct, this assumption is not documented in `bc_parse_addNum()`, and it should be.

By the way, `bc_parse_zero` and bc_parse_one` are constants, so I can make assumptions about them. This is why an empty string is fine: those constants do not have nul terminators at index 0, so if the string parameter does, the short-circuit operators will short-circuit.

I've pushed commit e4b31620f181 to document that assumption, along with a note that assuming a non-empty string might be useful too.

> But this is a sideshow. Experts have been insisting for decades that they can write crash-free C and yet we continue to regularly find exploitable memory issues in every large multi-author C project. It's plain as day that—for almost all of us—an increasingly indefensible amount of the time spent on C programs is in dealing with the types of bugs that languages like Rust prevent outright. Whether that's simply finding and fixing them, architecting programs to avoid them, incorporating those fixes into downstream projects, mitigating their impact in live production systems, or whatever, our industry pays an enormous ongoing cost. Even in projects that have never had a public release with a memory bug, significant engineering time is spent on them just during development iteration.

I agree with this. I never said people should use C by default; absolutely not. They should use Rust by default.

But as I said in another of my comments [1], I think I personally would write buggier code in Rust than in C.

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


> I've pushed commit e4b31620f181 to document that assumption, along with a note that assuming a non-empty string might be useful too.

Nice!

> By the way, `bc_parse_zero` and bc_parse_one` are constants, so I can make assumptions about them. This is why an empty string is fine: those constants do not have nul terminators at index 0, so if the string parameter does, the short-circuit operators will short-circuit.

Ah, of course. I knew that they were constants, but I didn't consider the short-circuiting of the comparison before indexing further into the string.

> But as I said in another of my comments [1], I think I personally would write buggier code in Rust than in C.

I hope you understand that—all snark aside—I truly, genuinely believe you. I do suspect that if you had invested the just time you've spent figuring out how to work around C's memory-safety shortcomings, your proficiency would be within spitting distance. But obviously those costs are already sunk. And at the end of the day, if you just love C and only want to write C then it doesn't matter what sorts of things another language provides.

I love C, but I've also come to love Rust. Sum types and exhaustive pattern matching are the sorts of things that are hard to live without once you've had them, and now languages that don't offer them are pretty much ruined for me. But at the same time I'm not a fan of the async/await paradigm, though I begrudgingly understand why it was necessary and why it was designed the way it was. For now I simply avoid it, but it makes me sad that we were never able to find something better.


> Ah, of course. I knew that they were constants, but I didn't consider the short-circuiting of the comparison before indexing further into the string.

No worries! That's why I specifically wrote it that way instead of a `strcmp()` call. I may be a little obsessive over getting my code right.

> I hope you understand that—all snark aside—I truly, genuinely believe you.

Thank you.

> I do suspect that if you had invested the just time you've spent figuring out how to work around C's memory-safety shortcomings, your proficiency would be within spitting distance. But obviously those costs are already sunk.

I actually don't know. It's possible, even plausible, that you are correct.

But I've spent gobs of time trying to understand async/await and failed. It wasn't as much time as I spent honing my C, but it was a lot. And I did not seem to make progress.

However, I might have been making progress and didn't realize it. Or maybe an epiphany like the one I experienced with Haskell would have been in the future.

Or maybe my brain just doesn't work in a compatible way with async models. I don't know.

It is one of my regrets that I did not try harder. Oh well; I guess I loved the visible progress of fewer and fewer crashes in a fuzzer more, even if it was slower.

> And at the end of the day, if you just love C and only want to write C then it doesn't matter what sorts of things another language provides.

As long as I am not negligent! I'll add more about this in a reply to another of your comments, though.

> I love C, but I've also come to love Rust. Sum types and exhaustive pattern matching are the sorts of things that are hard to live without once you've had them, and now languages that don't offer them are pretty much ruined for me.

I agree. I love them too, so much so that I run clang with -Weverything to get exhaustive matching in switch statements. And I emulate sum types with enums and unions. I always use switch statements to match on the enum value, which sort of gives me the experience of Rust sum types and exhaustive pattern matching. Sort of.

My language will have them for sure.

> But at the same time I'm not a fan of the async/await paradigm, though I begrudgingly understand why it was necessary and why it was designed the way it was. For now I simply avoid it, but it makes me sad that we were never able to find something better.

I agree. I'm sad about it too. I couldn't really reject Rust if it didn't have async/await, and I wish I didn't have to reject Rust.

(Though the RESF is still off-putting.)

I'm hoping that my language will be a competitor to Rust but allow people to try structured concurrency instead. If Rust still "wins," that's fine; people want async/await. But if they coexist or my language "wins," that's good too.

Eh, sorry for the long-winded comments.


> But I've spent gobs of time trying to understand async/await and failed. It wasn't as much time as I spent honing my C, but it was a lot. And I did not seem to make progress.

I've said this elsewhere but as a long-time Rust user I have literally never used or even encountered async/await. It exists, and I could use it if I needed to, but I never have nor have I had to spend effort avoiding it or thinking about it. It as far as I can tell a feature you can essentially entirely forget exists.


I'm glad you said this because my impression of Rust async/await until now was that it was unavoidable, mostly because I heard that all of the big-name crates, the ones you would use in just about every project, use async/await.


I think you hear about them more because there’s been a lot of pent up demand for async and people who’ve been waiting for it are excited. But I can count on zero hands the number of times I’ve wanted to use a crate but the only one available required async.


Big-name crates that do IO often use async by default, but I haven't seen one so far that doesn't provide a sync/blocking api as well. Usually this is done by enabling a feature for the crate.




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

Search: