> Rust struggles with alloca-style code too, to the extent that users who want to do this kind of trickery usually resort to unsafe.
Sort of, but not really. I've done this sort of "if it fits in a small stack buffer, use that, else fall back to heap" in code too. It's possible in safe-ish Rust:
let mut s = SmallString::<[u8; 1024]>::new();
write!(s, "{} frobs for {} widgets.", var_a, var_b)
.expect("writes to a String are infallible");
Safe-ish, because there is an unsafe {} hidden behind the safe interface exposed by `SmallString`. (And that crate has had vulnerabilities against it.)
But that's sort of the point: we're here looking at logging code, and the logging code itself shouldn't be doing this sort of unsafe trickery, it should be using some interface/utility that handles that, and then there's only a single spot that needs safety analysis. (Here, that would be the smallvec crate.) While I don't think C outright prevents you from building an equivalent, certainly how people tend to approach C does, and we see that here. The pattern is unsafe at the instantiation site, not at the definition, leading to far more unsafe code. (Not to mention the fact that in C, we have no unsafe {} blocks, so one must assume all such code is unsafe, but even if we just consider the laughable and impossible-to-grep "just the actually unsafe parts", it's far, far more.)
In the C at hand here, the second attempt here fails due to a variable never truly getting initialized (in the sense of having a meaningful value), and the version prior to that is just laughably unsafe, allocating a 1 byte buffer but passing a size far larger.
(The second attempt (i.e., time of bug) also uses the cursed variable name `l`, and the site this code is hosted on uses a font where `l` and `1` are homoglyphs.)
That seems pretty unpersuasive. It seems like you're agreeing that rust is subject to the same bug, are even aware of situations where it's happened, and are hiding behind a switcheroo where you're blaming not the language but the fact that C didn't use a standard gadget for the trick.
But (1) that says nothing about the language, you could totally implement something like smallstr in C, (2) smallstr isn't very standard! I literally had to look it up, and importantly (3) this is glibc, not app code, and you absolutely can't be pulling random libraries into the core standard library in any case.
Basically this seems like excusemaking. Rust in fact has the same problem, because it's a hard problem, and not subject to clean abstraction through the tooling. And it would be good for everyone to admit that fact rather than try to prestidigitize an explanation.
It does. Again, the area requiring audit is substantially smaller: just smallstr. And not just because we've extracted it there, but because we statically know that unsafe behaviors are limited to there. (By way of unsafe {} blocks.) I.e., we only have to audit the unsafe code in Rust, vs. all of the code in C.
> (2) smallstr isn't very standard!
Well, the same could be said for C? Rust's stdlib, like Cs, is somewhat purposefully kept small. It could be that someday it'd get added, but there is enough variation in implementation here that I'm not sure it would.
But pulling in a package in Rust is far easier than it is in C.
It might not be "standard", but I do think there's a set of crates in Rust that are what I'd call "well-known". Like Boost in C++ or requests in Python.
> (3) this is glibc, not app code, and you absolutely can't be pulling random libraries into the core standard library in any case.
That seems NIH. I see no reason glibc couldn't pull in static code, if it does the thing that needs doing, and correctly so.
> Basically this seems like excusemaking. Rust in fact has the same problem, because it's a hard problem, and not subject to clean abstraction through the tooling. And it would be good for everyone to admit that fact rather than try to prestidigitize an explanation.
No, Rust provides better tooling to solve the problem. (Even if it must still be solved, and even if you had to manually reimplement smallstr yourself.)
You're just going back to saying "Rust is better than C!" which is true, but uninteresting. The question at hand was whether this bug would have happened in a memory-safe language. And in fact it could have, because it's implementing an optimization Rust can only emulate using unsafe.
This is what drives me bananas about the rust community. The religion around memory safety persists even in circumstances where it doesn't exist. That's a cult, not an engineering effort.
Sort of, but not really. I've done this sort of "if it fits in a small stack buffer, use that, else fall back to heap" in code too. It's possible in safe-ish Rust:
Safe-ish, because there is an unsafe {} hidden behind the safe interface exposed by `SmallString`. (And that crate has had vulnerabilities against it.)But that's sort of the point: we're here looking at logging code, and the logging code itself shouldn't be doing this sort of unsafe trickery, it should be using some interface/utility that handles that, and then there's only a single spot that needs safety analysis. (Here, that would be the smallvec crate.) While I don't think C outright prevents you from building an equivalent, certainly how people tend to approach C does, and we see that here. The pattern is unsafe at the instantiation site, not at the definition, leading to far more unsafe code. (Not to mention the fact that in C, we have no unsafe {} blocks, so one must assume all such code is unsafe, but even if we just consider the laughable and impossible-to-grep "just the actually unsafe parts", it's far, far more.)
In the C at hand here, the second attempt here fails due to a variable never truly getting initialized (in the sense of having a meaningful value), and the version prior to that is just laughably unsafe, allocating a 1 byte buffer but passing a size far larger.
(The second attempt (i.e., time of bug) also uses the cursed variable name `l`, and the site this code is hosted on uses a font where `l` and `1` are homoglyphs.)