It's implicit state that's also untyped - it's just a String -> String map without any canonical single source of truth about what environment variables are consulted, when, why and in what form.
Such state should be strongly typed, have a canonical source of truth (which can then be also reused to document environment variables that the code supports, and eg. allow reading the same options from configs, flags, etc) and then explicitly passed to the functions that need it, eg. as function arguments or members of an associated instance.
This makes it easier to reason about the code (the caller will know that some module changes its functionality based on some state variable). It also makes it easier to test (both from the mechanical point of view of having to set environment variables which is gnarly, and from the point of view of once again knowing that the code changes its behaviour based on some state/option and both cases should probably be tested).
That's exactly why, access to global mutable state should be limited to as small a surface area as possible, so 99% of code can be locally deterministic and side-effect free, only using values that are passed into it. That makes testing easier too.
environment variables can change while the process is running and are not memory safe (though I suspect node tries to wrap it with a lock). Meaning if you check a variable at point A, enter a branch and check it again at point B ... it's not guaranteed that they will be the same value. This can cause you to enter "impossible conditions".
Wait, is it expected for them to be able to change? According to this SO answer [0] it's only really possible through GDB or "nasty hacks" as there's no API for it.
Rust cannot help you if race condition crosses API boundary. No matter what language you use, you have to think about system as a whole. Failure to do that results in bugs like this
The bigger problem here is it seems like the rust utilities were rushed to be released without extensive testing or security analysis because simply because they are written in rust. And this isn't the first serious flaw because of that.
Doesn't surprise me coming from Canonical though.
At least that's the vibe I'm getting from [1] and definitely [2]
> Performance is a frequently cited rationale for “Rewrite it in Rust” projects. While performance is high on my list of priorities, it’s not the primary driver behind this change. These utilities are at the heart of the distribution - and it’s the enhanced resilience and safety that is more easily achieved with Rust ports that are most attractive to me.
> The Rust language, its type system and its borrow checker (and its community!) work together to encourage developers to write safe, sound, resilient software. With added safety comes an increase in security guarantees, and with an increase in security comes an increase in overall resilience of the system - and where better to start than with the foundational tools that build the distribution?
So yes, it sounds like the primary official reason is "enhanced resilience and safety". Given that, I would be interested in seeing the number of security problems in each implementation over time. GNU coreutils does have problems from time to time, but... https://app.opencve.io/cve/?product=coreutils&vendor=gnu only seems to list 10 CVEs since 2005. Unfortunately I can't find an equivalent for uutils, but just from news coverage I'm pretty sure they have a worse track record thus far.
> Performance is a frequently cited rationale for “Rewrite it in Rust” projects.
Rewrite from what? Python/Perl? If the original code is in C there _might_ be a performance gain (particularly if it was poorly written to begin with), but I wouldn't expect wonders.
Could be. The thing is, it kinda doesn't matter; what matters is, what will result in the least bugs/vulnerabilities now? To which I argue the answer is, keeping GNU coreutils. I don't care that they have a head start, I care that they're ahead.
That's short sighted. The least number of bugs now isn't the only thing that matters. What about in 5 years from now? 10 years? That matters too.
To me it seems inarguable that eventually uutils will have fewer bugs than coreutils, and also making uutils the default will clearly accelerate that. So I don't think it's so easy to dismiss.
I think they were probably still a little premature, but not by much. I'd probably have waited one more release.
It's extremely early to say if things are rushed or not. It's unsurprising that newer software has an influx of vulnerabilities initially, it'll be a matter of retrospectively evaluating this after that time period has passed.
It's a little different with software since you don't usually have the code or silicon wearing out, but aging software does start to have a mismatch with the way people are trying to use it and the things it has to interact with, which leads to a similar rise of "failure" in the end.
It's not even about API boundaries, it's about logic and the language isn't really responsible for that.
Expecting it to prevent it would be as gullible as expecting it to prevent a toctou or any other type of non trivial vulnerability.
That's why even though I appreciate the role of these slightly safer languages I still have a bit of a knee-jerk reaction to the exagerated claims of their benefits and how much of a piece of crap C is.
Spoiler, crappy programmers write crappy code regardless of the language so maybe we should focus on teaching students to think of the code they're writing from a different perspective and focus safety and maintainability rather than "flashiness"
So I was saying that rust monolithicism is NOT based on ignorance and naivety.
Do you see what I mean by nuance? I think you just glanced at the comment, saw that there were negative words around rust, and you lossy compressed into "Rust bad".
You can bump /proc/$firefox_pid/oom_score_adj to make it likely target. The easiest way is to make wrapper script that bumps the score and then starts firefox. All children will inherit the score.
Haskell's GHC partially does it. LLVM can do it in principle, if your frontend gives enough information. Some JVMs can partially do some of it.
The above is about the optimiser figuring out whether to box or unbox by itself.
If you are willing to give the compiler a hand: Rust can do it just fine and it's the default when you define data structures. If you need boxing, you need to explicitly ask for it, eg via https://doc.rust-lang.org/std/boxed/struct.Box.html
Can you elaborate, what key assumptions about memory safety linked lists break? Sure, double linked lists may have non-trivial ownership, but that doesn't compromise safety.
Rust wants all memory to be modeled as an ownership tree: the same bit of memory can't be owned by more than one data structure. A doubly linked list breaks that requirement so it can't be modeled in safe Rust directly. The options are using unsafe, or using one of the pointer wrapper types that have runtime checks that ensure correct behavior and own the underlying memory as far Rust is concerned.
Right. So it is not that double-linked lists are inherently unsafe, it is (just) Rust ownership model cannot represent them (any other cyclic structures).
It's not that it cannot, it just doesn't want to :-) (but you're right). I guess that in this very case of DLL, it's a bit hard to swallow. To be honest, it's because the rest of rust really helps me in other areas of my projects that I have accepted that. Learning the ownership model of rust is really painful, it really forces you to code in its way and it was not pleasant to me.
I've been trying to convert to Rust an in-memory database and failed. It is strictly single-threaded and full of intrusive lists. I tried hard to please borrow-checker, but one have little choice when internal structures are full of cycles. The result was ugly mess of Rc all over the place. I guess it is just an example of a problem that doesn't fit Rust well.
This makes me wonder: what performance cost Rust code pay due to inability represent cyclic structures efficiently? It seems people tend to design their data in way to please Rust and not in way that would be otherwise more performance efficient.
You can do it by combining ghostcell/qcell along with some bespoke "static/compile-time reference counting" for the double links part. But ghostcell/qcell is quite difficult to use with Rust's current feature set (it has to use lifetime hacks to place a safe "brand" on type instantiations, a kind of quasi-capability construct), so it hasn't become a part of standard rust so far.
It is worth noting, that all three CVEs could be prevented by simple bounds checking at runtime. Preventing them does not require borrow checker or any other Rust fancy features.
It is also worth noting that theoreticals don't help such discussions either.
Yes, C programmers can do much more checks. The reality on the ground is -- they do not.
Forcing checks by the compiler seems to be the only historically proven method of making programmers pay more attention.
If you can go out there and make _all_ C code utilize best-in-class static checkers, by all means, go and do so. The world would be a much better place.
If your only criteria is "remove the buffer under- and over-flows", yes. IMO Rust helps with a few more things. Its strong static typing allows for certain gymnastics to make invalid states unrepresentable. Though in fairness, that is sometimes taken too far and makes the code hard to maintain.
Sometimes managed switch is the only way to find out faulty cable. I'm speaking about a bit bad cable, which corrupts some data, not all of it. Just by looking on interface error counters you can easily tell if something is off. Without it you either need somehow come up with very expensive cable tester or just pretend that slow network speed is due to some other popular blame destination (e.g. it's just bad macos update) ;)
"immutable" top-level folders won't cut it. In order to recursively delete a folder, rm has to delete leaves first. So, you will endup with empty top-level folders, which is no better.
I think they mean that rm could check the folder you're trying to delete and see if it has whatever attribute and if so don't delete it's contents. so "rm -rf /" would see that '/' has it and be a no-op, but '~/home/foo' doesn't so it would recurse and start deleting. It could go further do that check for each sub directory recursively, so say '~/home/foo/bar/baz' that 'bar' does have the flag so `rm -rf ~/home/foo/` would delete everything else from the foo folder but leave bar and it's descendants alone.
reply