Would Rust Secure Curl?
timmmm.github.io
timmmm.github.io
I'm really happy to see the data behind the claim! I've been slowly desensitized to opinions masquerading as unsourced supporting graphs of dubious provenance. It's so simple, but kudos to the author for the transparency.
When Daniel wrote "C is not the primary reason for our past vulnerabilities", he may or may not have been right. But he sadly did not back that up with easily verifiable, refutable, data.
Data is like a ray of sunlight. Instead of arguing high-level opinions, let's argue the data
If you want to prove that RustUrl would be better then cUrl, you will have to implement it and prove that its actually better, by taking users away from cUrl. Decisions are made by those who make things not by those who have opinions, even if they are backed up by data.
Daniel doesn't have to defend his choices, because you can write your own if you don't like his.
That is true. And as the post mentions, Daniel did decide to allow some Rust into the tree, years after he wrote that post. Sometimes people change their mind. It's often a good thing!
I refer you to the Linux Kernel mailing list FAQ 15-6, that I think makes my point:
For example, the Linux kernel is considering accepting Rust in tree. While that is predicated on people writing the code and demonstrating its value, even getting to that point has required a lot of communication and convincing. It is often considered polite in open source projects to attempt to build some consensus before sending large patches upstream.
>Speaking at the BlueHat security conference in Israel last week, Microsoft security engineer Matt Miller said that over the last 12 years, around 70 percent of all Microsoft patches were fixes for memory safety bugs.
https://www.zdnet.com/article/microsoft-70-percent-of-all-se...
Of course, Rust isn't the only language that tries to address memory safety, but it is one of the few that does so while retaining the performance of C.
However the same Microsoft has backtracked in their rejection of C, adding support for C11 and C17, replaced the C++ userspace drivers framework with a C one, and despite all security sales pitch for Azure IoT, they ship Sphere OS and RTOS with a C only SDK.
An example of where I've recently done something like this is my sparse linear algebra code. I have distinct types for column and row vectors for a matrix, so that if I want to right-multiply A with a vector, the input has to be a column vector and the output has to be a row vector. Similarly, if I want to left-multiply A with a vector, the input has to be a row vector and the output has to be a column vector.
>there are also a decent number of other bugs that come from cURL doing ad-hoc inline character-by-character parsing of just about everything, whereas in Rust you would probably use a library to fully parse things.
cURL is the library to fully parse these things,
Also, I have it on my TODO to go back and redo the Capture the Flag challenges from like 10 years ago that dealt with things like stack overflows. I had a ton of fun working on those and it would be fun to revisit them. Anyone got a modern version of something like that?
It can cross-compile down to tiny tiny binaries with no dependencies that run on microcontrollers quite comfortably.
https://curl.se/docs/CVE-2018-1000007.html is a logic bug that may cause sensitive headers to be sent to the wrong host on redirects. It's slightly obscure, but I can imagine what an exploit looks like.
https://curl.se/docs/CVE-2018-1000120.html is a memory bug that lets you write a single null byte out of bounds. That's a security issue, but exploiting it seems less straightforward. (Though maybe it is more dangerous than the other one on net? I don't know enough about these issues to say.)
It could be that Rust would prevent half the vulnerabilities yet prevent much less than half the danger caused by vulnerabilities.
I'm a big fan of Rust, if it matters.
https://googleprojectzero.blogspot.com/2014/08/the-poisoned-...
Those kind of exploits are thankfully getting harder with the many layers of mitigations we have today, but trying to predict which memory bug will or will not be exploitable is a very unintuitive and inadvisable exercise.
[1] https://learn.adacore.com/courses/intro-to-spark/chapters/01...
Huh!?
The language was unnecessary and unprofessional, but those are that only ways it was wrong. If I were writing something in a personal context where such opinions were not out of place rather than a technical article, I would absolutely apply that characterization.
You don't have to. But Don't be so baffled that anyone else does.
These are stereotypes. There's far more variety within constituents of a political or religious group (including atheists) than between them. Arguing that one group is more "zealous" than another is almost certainly untrue for any meaningful kind of comparison (e.g., Muslims have more extremists than other groups, but we would rightly balk at generalizing Muslims as extreme). At best, these comments are meaningless; at worst, they incite hate and divisiveness (predictable whataboutism: "but Trump is divisive! Why can't we be divisive toward his supporters?!"--yes he is divisive, but he shouldn't be our moral standard).
Yes, that was a needless statement that distracts but you know what, there is no point getting too hung up on things like these from readers perspective too.
However, the rest of the content was interesting.
Everyone ignores their political mini-rants or whatnot. It’s completely irrelevant to the discussion; especially this one.
Evidently one can insult Rust evangelists, why do many consider the arbitrary classification of “religion” so special? Especially when it seems a common criterion to call something a religion is “at least part of the belief must be provably false.”. — wakarimasen lol.
And naturally, the moralist is quick to loose such a perspective that not the entire world lives in his own back yard, it seems. — perhaps if he were more aware of how vast and different the world is, he would have been more doubtful of his own moral convictions.
While, of course, you can find specific locations where they aren't the majority, you can also find specific locations, in the US, where white people aren't the majority.
That doesn't change the equation when talking about punching up vs punching down.
Neither is really relevant to the post; he's clearly using hyperbole to describe Rust zealots in other peoples' minds.
Somehow, I feel you simply speak of the plurality of your own back yard.
And perhaps H.N. would have, but your argument is with me, and not “Hacker News” and I make no such distinction, provided, of course, that by “Jew” you mean “Judaist” and not “person whose maternal parent is a Jew”.
Therefore, use Rust if you really have to. Otherwise, tools like automated garbage collection are a much better way to reduce the likelihood of bugs.
That's not really true: Rust's borrowing and ownership apply to most kinds of in-program resource ownership and aliasing, not just memory.
> The resulting code then creates an increased cognitive load which can make other bugs more likely.
I'm not saying you're wrong, but I'd like to see any evidence for this. Personally, I'd expect the opposite to be true: knowing whether any given object is non-exclusively borrowed, exclusively borrowed, or owned at any point in the program sounds like very useful documentation for people reading the code.
I'm not in a position to claim that this theory is actually true, and I won't take a stance which is "better", but personally the former is much more appealing to me, for purely aesthetic reasons.
Also, much of the tooling I used to add to projects in other languages is either built-in and commonly used (test, fmt), or easily added to cargo (edit, bench, web, mobile).
Enums in C are just ints with names. They're error prone and not type checked. You can always use 0 instead of a name and it will work. You can use TLSv2 instead of ExterminateHumans and it will also work.
> What I mean by it is significant "programming in type language" as opposed to "in code language".
I honestly don't understand what you mean by this.
Yes, they are integers. You can do arithmetic with them, you can use them as bitmasks, or use sentinels instead, like -1. I like that.
Them: I like sandwiches.
You: I get sandwiches from McDonalds, they're great.
Them: Yes, but those are burgers. (The implication being that there are also other kinds of sandwiches.)
You: Yes, burgers are good.
You're not wrong, but you're kind of missing what they're saying.
Enums in Rust can be like enums in C, but they can also be tagged unions. Furthermore, Rust won't automatically cast an enum to an int, so it is a bit harder to make mistakes, even if you're using the C-like ones. That's the advantages they're speaking about. Enums are good and useful in C, it's true. That's why Rust added stuff to make them even more useful.
In my experience it is in fact still a requirement to understand what's happening in order to write clean and fast Rust code. It is possible to satisfy the compiler using a lot of clone() calls and similar shortcuts, but once you understand what data structure is allocated when and where and how the whole ownership principle works in practice you can suddenly avoid a ton of unnecessary heap allocations and other pitfalls. I'd say my experience with Rust improved a lot once I started grasping what's happening in memory and why certain syntax elements exist, as this makes fixing compiler errors more intuitive and less a blind trial and error session with the borrow checker.
Some of Rust's syntax can look weirdly unfamiliar at the beginning, but everything is there for a good reason. Nothing is done implicitly, and much of the pattern matching and Option/Result related stuff is Rust's way of replacing the concept of `null` with thoroughly type-checked expressions.
If having memory errors being compile time errors instead of runtime error leads to more logical errors due to complexity. Wouldn't that imply that using other types of complicated checkers also leads to more logical errors, for example static analysis software or valgrind?
I've seen people learn Rust as their first programming language, and it fills me with joy that the next generation of programmers won't have to build up this vault of apocryphal knowledge I've had to over the years. Or, at least, not the same one. And hopefully not as large.
I've also found Rust to be a very pleasant application development language. As a former NodeJS developer, I've been able to replace Express with Warp and Electron and React with Iced. It's not ready for all things, but it's ready for a heck of a lot. And the question of "what's possible" has broadened so much due to work by the community in even just the past year, I really think the majority of all other applications, including web frontend (cargo web), servers, desktop apps (iced), and mobile apps (cargo mobile), can be reasonably implemented in most part entirely in Rust within only a few years.
A simple difference would be that, for instance, in Java every object is nullable, whereas in Rust, this is only so when it be declared as such, and the type system requires one to in some capacity first verify of a nullable type that it isn't null, before something meaningful can be done with it.
Can I do linked lists in Rust?
Can I define classes such that:
cInches in1, in2, in3; // basically a float
cCm cm; // basically a float
in1 = in2 + in3; // OK
in1 == 2.f * in2; // OK
in1 = in2 / in3; // compilation error
cm = in1; // compilation error
void foo(cInches in);
foo(cm); // compilation error2. Yes. One is even provided in the standard library, if that suits your needs. However, most people don’t use linked lists because other data structures are near-universally better.
3. Rust doesn’t have classes, but you can do something similar. The name is the “newtype pattern.”
For those who don't know, Traits are like a sort of "class interface" that can be automatically applied to things that have implementations for those traits, and conversion between, once that code is pulled in scope via `use`.
Rust doesn't have classes or inheritance, instead, it has more powerful tools, ones that aren't as limited.
Ah. Maybe it's because I explained stuff that's basically newtypes using language that might make it seem they're different? Ugh. HN comments always make me so neurotic. But the content is usually more interesting than dev.to. I just wish they'd get rid of the downvote feature.
I'm recovering from a headache (and a little ketted out) so I might be missing something too, but in the spirit of trying to explain confusing downvotes, here's the only two potential nitpicks I can see:
1. newtypes are not really 'in addition' to structs, enums, etc. they are a particular type of struct with a fancy name.
2. Calling composition less powerful than inheritance could be seen as subjective and debatable. I mostly like composition over inheritance, but Rust for example has problems with up-casting of trait objects (https://github.com/rust-lang/rfcs/issues/2765) that inheritance/vtable based languages (e.g. C++) do not have.
So it's not a bad comment, and I suspect you mostly just got piled on due to human error/bias. Or maybe some poor soul got confused and thought you were trying to explain Rust to the poster above you (who may not need much explaining).Don't take it personally, you seem to have a good grasp of the language. Please keep on learning :)
That was a thoughtful response, though, and I'm grateful.
I've got 500KLOC C++/Qt running under Windows and macOS. I wonder if Qt have any plans.
There is a crate called “cxx” that provides support for C++ interop in Rust: https://cxx.rs/
Linked Lists are easy. _Doubly_ linked lists can't happen without Rc or unsafe, but that's fine.
And yes you can create types for domain units, while controlling the combinations like that, and can go deeper in that sort of idea than C and C++.
I'm so tired of these "rewrite in rust" articles that have no code behind them. Spend the time and write a replacement for curl, ship it, and people will start using it instead of curl. In some years time, you can write a paper showing how many vulns your replacement had vs. rust. curl continues to be written in C because it has someone actually doing the work to write and update it. A theoretical rust replacement does not exist because no one is doing the hard work of writing and maintaining it.
It's like LibreSSL vs OpenSSL. Did LibreSSL end up being more secure? At least we can find out instead of just guessing about whether removing crappy code from OpenSSL would be a good idea.
A buffer over-run isn't a bug, its the symptom of a a bug in the arithmetic that computes how to access the buffer. The way to fix that is to communicate and address the bug during development, not to try to mitigate the symptom at run time.
I am a C programmer, and to me buffer over-runs are a rare class of bugs, and a class of bugs that are (at least on the heap, and often on the stack) trivial to find and fix with the tools i have.
Basically the point is, it's very easy to end up in a situation where you know you have a bug but you're 2+ levels of indirection from the actual logic error. Those sorts of issues are always the biggest time sink.
The system tells me very precisely what has gone wrong with buffer over-runs, and it also make memory leaks and double frees trivial to find.
Visual studios debug mode does something similar with buffer overruns on the stack, but I tend not to put very many buffers on the stack so that much more rare as an issue.
If you want to go even more hardcore, windows has "gflags" that you can turn on to find overruns using memory protection.
LLVM also have the "Checked C" compiler that also finds these issues.
My own system is so good that, I haven't used gflags in a few years and I also haven't tested Checked C so I cant vouch for it, but there are loads of possibilities in this space that i hope to explore further in the future.
What does your testing setup look like? Does it include regular fuzzing and security audits? Buffer overruns are so common because normally your mental model of how the code should work assumes they don't happen, so you forget to test for their presence (or prove their absence, like Rust nudges you to).
I don't do fuzzing, because I leave string parsing to tools like re2c. I tried it in the past with my mail message parser written in C/re2c and it was not very helpful.
I use Pascal, and I never have any buffer overruns in my code. If there were any, Pascal's automatic bound checking on array indices would raise an exception
The only issue is, should I keep the bound checking in the release builds for maximum safety, or disable them for maximum performance?
I dont know why others struggle with it, but I suspect:
-Most programmers are not used to thinking about memory the way you should when using C.
-Most developers are not aware of the tools available for debugging C.
That being said, a kernel is a huge undertaking. There is nothing else with the scope of Linux. And substantial parts of writing a kernel involve dealing with architecture nastiness, and, in the absence of formal architecture models, no language will help much here. The Linux x86 low level code is a terrifying mess, and it’s also the best and most capable implementation I’m aware of. (I’m obviously biased.) A language like Rust would help only a tiny bit.
(Something like NMI handling on x86 is fundamentally memory unsafe. If you get nested NMIs, your stack gets clobbered. Thanks AMD. Linux has code, mostly in assembly, that detects and recovers. Good luck writing it in any memory safe language.)
I once skimmed seL4, and I would not use its x86 code for serious work in its current form.
In other areas of kernel code, buffers common, but the data is completely opaque. You won’t find kernel code parsing a buffer written to a filesystem because there is nothing to parse. The buffer is literally just a pointer and a length.
That being said, I would love something like Rust for lifetime management and thread safety and for the occasional data structure.
(Large sections of Linux are indeed quite scary from a memory safety perspective. Anything related to /proc or /sys comes to mind.)
You'll write bugs and they'll make it past the development phase because bugs are inevitable. So now the choice is whether they're a crash or an rce.