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

The true test of what I said is "show me the code."

The code is my `bc`. [1]

Find a memory bug, any memory bug, in my `bc` after 1.0.

Rust has occasional memory bugs too; I mentioned that specifically. It still happens.

So if you really are correct, break my `bc`.

[1]: https://git.yzena.com/gavin/bc




>Find a memory bug, any memory bug, in my `bc` after 1.0.

I'm surprised by this confidence given one of the commits on the first page is "Fix a double-free when using expressions and sending SIGINT". And going through the commit history quickly reveals "Fix memory bugs in bcl" (a value not being initialized) and "Fix memory leaks in bcl" (missing dealloc), all within the last two months and all which are trivially not a problem in Rust.


Notice that I said to find a memory bug in a release, not just any commit.

Yes, there will be memory bugs during development, but I usually find them before release. There will definitely be commits fixing memory bugs during development.

The bcl library is an exceptional case, where my test suite did not have sanitizers and Valgrind properly hooked up, and that one was because it went from a global (guaranteed to be zeroed) to allocated.

But I also said to find one in the program, not the library.

I issue that challenge again: find a memory bug in the `bc` or `dc` program in a release after 1.0.

Oh, and the double-free with `SIGINT`? Rust isn't going to help you much there. Signals are not part of Rust's model.


>Notice that I said to find a memory bug in a release, not just any commit.

You did not say anything of the sort.

>But I also said to find one in the program, not the library.

You did not say anything of the sort.

You said:

>>The code is my `bc`. [1]

>>Find a memory bug, any memory bug, in my `bc` after 1.0.

>>[1]: https://git.yzena.com/gavin/bc

But okay, let's acknowledge the moved goalposts.

>I issue that challenge again: find a memory bug in the `bc` or `dc` program in a release after 1.0.

Tell me what subset of the repo named "bc" you consider to be "the program"

>Oh, and the double-free with `SIGINT`? Rust isn't going to help you much there. Signals are not part of Rust's model.

Crates like tokio::signal allow a signal to be converted to a stream of events, which can be handled along with any other events in the program, instead of interrupting the current fn in the middle and necessitating longjmp shenanigans. Since the existing stack is not interrupted in any special way, dtors fire as they're supposed to.


> Tell me what subset of the repo named "bc" you consider to be "the program"

Clearly, the parts where you aren't able to find memory bugs.


Anything built without the -a option to configure.sh. You can try the library too, but I did mess up that one time that you already know about.


I realize I should answer the signal part.

tokio::signal-type code would not work for `bc` signals.

`bc` is a special program; most programs are I/O-bound, but `bc` is both I/O- and CPU-bound. And it's interactive, so it's got to respond to the user as fast as possible.

It's quite possible for a user to enter an expression, not realizing how expensive it is to compute (since `bc`'s numbers can be arbitrarily large). In that case, a `SIGINT` should be responded to instantly.

Turning signals into an event stream to read would not do that because the signal handler literally has to `longjmp()` out of itself.

It won't `longjmp()` out if it's not safe to do so, but it can be safe to do so. And when it is not safe to do so, it will set a flag and return normally.

Then the code that wasn't safe to jump around finishes, it will check the flag and jump itself once it is safe.

This is what keeps my `bc` responsive even if you have a long-running computation.

To see this, compile and run my `bc`, and give it this input:

    >>> 2^2^32
It will hang because it's calculating a LARGE number.

Press Ctrl+C. It will instantly return to the input prompt.

I might be wrong that tokio::signal cannot do that, and if so, I apologize. But it doesn't appear so from your description.

Also, I have the same sort of code in my new project to turn signals into an event stream in C.


Okay, so I didn't say a release at first, but I meant to. That's only fair.

Anything built without the -a option to configure.sh is "the program." That's the vast majority. You can try the library too, but there is that one mistake you already know about. I don't think there are any others though.

And again, even with the things you all have found that are in random commits, it doesn't really matter; Rust isn't completely memory safe either. It's close, but not quite.


"Find a memory bug in this repo. No, not those bugs. No, the ones in that part of the program are off limits. The ones I pushed but caught just before tagging don't count either because I noticed them just in time. Okay there was this one time where it happened when my complicated sanitizer setup broke without me realizing but that was a total fluke. And that other one also doesn't count because it definitely wouldn't have been stopped by Rust[1]."

I'm almost tempted to believe this entire exchange was an elaborate setup to convince people to use memory-safe languages.

[1] except I'm… pretty sure it would have been


Seriously?

Just try it. Break my `bc` in a release. I dare you. Until any of you do, you're just trolls or the RESF.


To what end? You’ve already stated elsewhere that your only point is that you can, under an increasingly strict set of conditions, write code that is (almost always) free of memory bugs. Great!

Nobody is hiding around the corner waiting to take `gcc` or `clang` away from you if you want to keep using them. Nobody's threatening to take away your software engineering license if we catch you writing a memory bug.

This whole thread is ridiculous. “The safety improvements in modern cars are substantial. If you're buying a new car these days, you should probably buy one made after 2018.” “Well I'm still driving my 1969 Malibu. Plus I’ve never been in an accident so I don’t need those safety features.” “Okay but… what are those dents in your car?” “I said no accidents on public roads. Prove me wrong or STFU.”


The RESF seems to want to take C away from me. They seem to think I am a bad person for using C. They seem to think I am a bad programmer for using C.

To refute all of that, I point people at some code that they would probably struggle to break.


I'm sorry, but this is an entirely imagined persecution. Nobody is taking your C compiler away from you. Nobody thinks you're a bad person for using C. Nobody thinks you're a bad programmer for using C.

Many of us think there are better alternatives. We're excited about these better alternatives. We want people to use these better alternatives. We think these better alternative solve a lot of problems that many people spend a lot of time dealing with. We think these better alternatives prevent many of the types of bugs that regularly ruin people's afternoons, evenings, and weekends. We really think you should try it out, but if you don't like it, literally nobody is coming to take your compiler away from you.

We do think a lot of the common arguments against moving to these languages are bullshit though. "Rust can't do self-referential data structures" is one of them. "I have a ton of expertise in C and it would take more time than I'm willing to spend to get to that level in something else", "this project is already written in C and mature", and "I write code for unsupported platforms" aren't bullshit, but they are ones that naturally decay in how compelling they are over time.

And as much as I believe you personally can write memory-safe programs in C, I would be willing to bet this guarantee would go out the window the moment you add a second maintainer who can push and release code without you vetting every commit.


> I'm sorry, but this is an entirely imagined persecution. Nobody is taking your C compiler away from you. Nobody thinks you're a bad person for using C. Nobody thinks you're a bad programmer for using C.

I've had people say all of these things to me in various ways.

"You should be ashamed of yourself," is a typical phrase used.

> Many of us think there are better alternatives. We're excited about these better alternatives. We want people to use these better alternatives. We think these better alternative solve a lot of problems that many people spend a lot of time dealing with. We think these better alternatives prevent many of the types of bugs that regularly ruin people's afternoons, evenings, and weekends.

This excitement has been over-the-top and as mentioned by one or more people in this thread, it has actually driven people away from Rust. As much as I don't like Rust, I don't want that to happen.

> We really think you should try it out, but if you don't like it, literally nobody is coming to take your compiler away from you.

When people say they want to deprecate C, that's what they mean. If you deprecate C, the compilers will be abandoned, which in the software world converges to the same effect as taking it away because compilers need ongoing maintenance to keep up with new platforms and such.

If a new platform becomes mainstream, and there's no C compiler for it, then my C compiler has effectively been taken away.

This is one reason I'm making my C replacement language: I do think that C will eventually be abandoned on the major platforms, and I want to have switched by that time.

> We do think a lot of the common arguments against moving to these languages are bull** though.

Which is fair. You didn't say all of them are, and yes, a lot of the common arguments are crud.

> "Rust can't do self-referential data structures" is one of them.

I actually agree with you; this is a crud argument. In order to make my C as safe as possible, I don't use self-referential data structures either; I use a parent data structure.

I did this with linked lists [1]. The linked list is a parent data structure that owns everything, and the items in the linked list just have indices to their neighbors. This allows me to tie Rust-like lifetimes to the items.

And any self-referential data structure can be expressed in terms of a parent data structure and children data structures.

So yes, you are right: this argument is crud.

> "I have a ton of expertise in C and it would take more time than I'm willing to spend to get to that level in something else", "this project is already written in C and mature", and "I write code for unsupported platforms" aren't bull**, but they are ones that naturally decay in how compelling they are over time.

I agree. This is another reason I'm still writing a C replacement for myself.

> And as much as I believe you personally can write memory-safe programs in C, I would be willing to bet this guarantee would go out the window the moment you add a second maintainer who can push and release code without you vetting every commit.

Yep. I agree. Even if it's Linus Torvalds, Ken Thompson, or anyone.

I still vet every commit, or just not accept anything.

It also helps me keep the scope manageable: if I can't do it myself, the scope is too large.

[1]: https://git.yzena.com/Yzena/Yc/src/branch/master/src/contain...


> "You should be ashamed of yourself," is a typical phrase used.

I'm trying very hard to not "no true scotsman" myself here. But I tend to think of it like this: there's people, and then there's Twitter personas. There's a lot of very strong, absurd opinions that Twitter personas have but that I don't ever seem to encounter in the real world.

Maybe another way of putting it is that if I dropped you on stage in the middle of a RustConf keynote, nobody would actually have these types of opinions. And if anyone did express such a thing, there would be overwhelming opposition to it. But of course I can't prove this, and I'd be willing to admit I'm wrong. And none of this discounts the fact that you've apparently seen these types of opinions firsthand.

> > We really think you should try it out, but if you don't like it, literally nobody is coming to take your compiler away from you.

> When people say they want to deprecate C, that's what they mean.

I don't think that's a reasonable take at all. I'd be willing to bet that what Mark Russinovich was saying was exactly what you just stated in another thread: "I agree with this. I never said people should use C by default; absolutely not. They should use Rust by default."

> If a new platform becomes mainstream, and there's no C compiler for it, then my C compiler has effectively been taken away.

Being taken away is very, very different from other people not being sufficiently motivated to build something you want to exist. Nobody has "taken away" COBOL from folks by not porting compilers to arm64 (I have no idea if arm64 COBOL compilers exist, feel free to substitute a suitable arch/lang).

> This is one reason I'm making my C replacement language: I do think that C will eventually be abandoned on the major platforms, and I want to have switched by that time.

I'd be willing to bet substantial sums of money that "eventually" will not include either of our lifetimes. And my guess is it would be far less work to port C to (or write an LLVM backend for) x86-128 or aarch256 or RISC IX than to maintain your own language and keep it running on all the major platforms. But good luck to you nonetheless.

> Yep. I agree. Even if it's Linus Torvalds, Ken Thompson, or anyone.

I'm pleased you pointed this out! I'd debated saying this but opted not to, it's great that we agree on that extra detail.


> I'm trying very hard to not "no true scotsman" myself here. But I tend to think of it like this: there's people, and then there's Twitter personas. There's a lot of very strong, absurd opinions that Twitter personas have but that I don't ever seem to encounter in the real world.

If we expand that to Reddit and Hacker News, I have to agree, so it might have just been personas I went up against.

> Maybe another way of putting it is that if I dropped you on stage in the middle of a RustConf keynote, nobody would actually have these types of opinions. And if anyone did express such a thing, there would be overwhelming opposition to it. But of course I can't prove this, and I'd be willing to admit I'm wrong.

But I've never been to RustConf, so I can't dispute this. I'll simply have to trust you on this one.

Though dropping me of all people into a RustConf keynote would be a wild prank. Hilarious.

> And none of this discounts the fact that you've apparently seen these types of opinions firsthand.

Unless I've only seen personas, as you suspect, and I can't refute that.

> I don't think that's a reasonable take at all. I'd be willing to bet that what Mark Russinovich was saying was exactly what you just stated in another thread: "I agree with this. I never said people should use C by default; absolutely not. They should use Rust by default."

I'm not so sure because I used to be one of the people that thought C should die, as in, have everything rewritten into another language. People like that do exist.

But again, they may just be personas. Whoops.

> Being taken away is very, very different from other people not being sufficiently motivated to build something you want to exist. Nobody has "taken away" COBOL from folks by not porting compilers to arm64 (I have no idea if arm64 COBOL compilers exist, feel free to substitute a suitable arch/lang).

This is a fair counterargument. I guess I should correct myself by saying that it would feel like my C compiler would be taken away.

> I'd be willing to bet substantial sums of money that "eventually" will not include either of our lifetimes.

I'm not so sure, but no one can predict the future. :) I wouldn't have thought Rust would end up in the Linux kernel when I first heard of it. Now it's all but certain to happen.

> And my guess is it would be far less work to port C to (or write an LLVM backend for) x86-128 or aarch256 or RISC IX than to maintain your own language and keep it running on all the major platforms. But good luck to you nonetheless.

That's true. It's why my language will bootstrap itself to C99! For every platform that is too much work to support directly, I'll support it by having a C99 compiler for that platform.

C99 should not need much maintenance. I hope. Or someone else will care enough to do it. I hope.

> I'm pleased you pointed this out! I'd debated saying this but opted not to, it's great that we agree on that extra detail.

Good! And you're welcome.

Yeah, I'm picky. I've rewritten code given to me by a FreeBSD kernel developer who is twice my age and much better than me.


> If we expand that to Reddit and Hacker News, I have to agree, so it might have just been personas I went up against.

"Twitter personas" is just my pet name for the phenomenon because it seems strongest (or maybe I just first noticed it) there.

> But I've never been to RustConf, so I can't dispute this. I'll simply have to trust you on this one.

Hah! I've never actually been either. I just meant this as a standin for putting you in front of actual, real, physical people who also are likely closer to the evangelist end of the spectrum.


Lest you lump me in with the RESF, I just want to make it clear that all I did was point out your overconfidence in your C abilities. I don't care what language you use or anyone uses. I think Rust has lots of problems that it's stuck with, and I've said on multiple other occasions that the ideal language would be a mix of Rust (borrow checker, typesystem) and Zig (comptime, generics, explicit allocation).


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.


I am also wondering about your confidence given the fact that there are obviously memory bugs during development... what gives you so much confidence that no memory bugs exist in released versions? Do you have an extensive test suite with valgrind /etc to ensure that there aren't any memory bugs before a release but some can slip in in intermediate versions? Or what do you do that gives you the confidence to say that there aren't any memory bugs in the released versions? Or what is involved in all the extra effort it takes to make a memory bug free c program?


Rust is perfectly happy to leak. But as in C++, there is no temptation to.


Yes, let’s ignore the quite literally millions of examples of prior art for memory bugs in C/C++ programs because you believe you’ve managed to build a single-threaded CLI calculator that doesn’t have any.

I know how to call `strcpy` safely. Are we clear to put that back into the Linux kernel then?


Notice that in my first post, I said to use Rust where practical. I never said that everyone should use C.

I'm only claiming that I personally can do just as well in C as I could do in Rust, and that I prefer to work in C, so I might as well.

Break my `bc`.


I honestly don't understand the point of your post then. Are you looking for a pat on the back?


You misunderstood what I mean by "gap" here. The gap isn't that it is impossible to produce security/reliability equivalent program using C vs Rust, which you seem to be arguing. The gap is that statistically for many code written in real world for various degree of efforts, Rust code will have much less of security/reliability issues, and it will take much less effort to get to the similar quality point compared to C or C++, and that gap is NOT bridgeable by making C programmer better or by inventing better conventions and styles.




Join us for AI Startup School this June 16-17 in San Francisco!

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

Search: