Buffer overflow when pwfeedback is set in sudoers
sudo.ws
sudo.ws
> Exploiting the bug does not require sudo permissions, merely that pwfeedback be enabled.
> the stack overflow may allow unprivileged users to escalate to the root account. Because the attacker has complete control of the data used to overflow the buffer, there is a high likelihood of exploitability.
https://github.com/sudo-project/sudo/commit/b5d2010b6514ff45...
Time to delete sudo.
We should have standardized a C API to safely read/write buffers from files, networks, etc. decades ago. But here we are in 2020 manually moving pointers around - and what a surprise that we're finding "whoops, another off-by-one error, you know how it goes."
No one should be writing new code to read/write bytes. This should be settled. It's like a carpenter building their own nail gun each time they want to build a house.
Maybe I'm missing something, but wasn't that a pointless change? The value of the expression was unused, so post- and pre- are equivalent.
When I was a pentester, out of more than 50 projects I was on, I always found at least a medium-severity bug (a bug which could allow an attacker to pwn the app using nothing but a web browser). Guess how many of those codebases were written in C? Zero.
The smugness is endemic to people outside of security. I was smug too. There are comments I wrote that I cringe at, looking back. Pentesting changes your perspective on things.
C isn't going away. Yes, let's make it safer (by pentesting). No, it's not a mortal sin to write C. I'll find ways to exploit your webapp regardless of the language.
A big part of why people make a big deal of C is that there are lots of code bases that have been under a lot of scrutiny from security minded code reviewers and pentesters and still keep on yielding more vulnerabilities. Ie it's hard to make C safe even when you really try and are a good programmer.
In contrast IME most pentesters in the biz work on code bases that have seen much less scrutiny and are often in-house and/or "enterprise" apps whose code was written by people who are probably more domain experts than generally good programmers or security-aware developers...
Don't write code in unsafe languages unless you have to (almost never for new code). Period. You will have some problems either way, but you will have fewer problems in a memory safe language.
Pen testing is not even remotely a good mitigation to depend on against the dangers of a language like C. Its fine to do for defense in depth and to find problems not related to memory safety, but it's no substitute for making a more sensible language choice.
Is it so wrong to recommend a solution that will kill a good chunk of the problems right at the door?
And FYI, you are being pretty smug here as well. The fact that people criticised unrelated parts of the PR in addition to the relevant parts shouldn't give you a free license to dismiss the bigger criticism.
So, based on those two comments, you have zero data (anecdotal or other) comparing the rate of bugs in C programs versus other languages since you didn't test any C programs.
Code security is not about eliminating bugs but about lowering the rate they show up at since things always get through and money/time is finite. Pen testing doesn't catch everything and many projects that have been tested still yield bugs. There's also a limit to how much money can be spent on testing so using it find (and document, review, etc.) low hanging bugs due to using C means you're more likely to miss a deeper bug.
Or in other words, spending the same effort to pen test a safe language will leave fewer bugs left over than spending it to pen test an unsafe language. So using an unsafe language is still worse.
"An API should be easy to use correctly and hard to use incorrectly"
The dumbest link in the chain is always the guy writing the code, not the programming language, and not the machine executing it. So things should be designed in a way that makes it as hard as possible for programmers to make errors.
e: video is here for those who are interested https://www.youtube.com/watch?v=5tg1ONG18H8
Think about how often you blindly inserted a USB A or B (mini-B, micro-B) plug the wrong way first and had to try again. Why is the wrong way even possible? USB C's plug fixes this.
A huge improvement to railway safety was the invention of Mechanical Interlocking. It was made essentially impossible to set signals in some inherently unsafe ways. When you tried to give a Clear signal to the Express, through a junction a late running Coal Train is already crossing, the huge metal lever you're trying to move to do that won't budge because all your controls are connected via elaborate mechanical interlocking to prevent this mistake. The signal remains at Danger, the Express stops, tragedy is averted.
To be honest this isn't really an issue once you notice that the logo side of the USB connector indicates up. I suspect this is mostly an issue because USB connectors have only a tiny self-alignment range (<1 mm), which leads to the "USB exists in a 4D state" meme.
Perhaps you were a good pentester. More likely the code you attacked was rubbish.
I worked on a relatively important Internet-facing web application for about a decade, it was pen tested by a variety of outfits both when it was owned by a small start-up and when it was owned by a huge corporation.
Good design meant that we never saw any pen tester come anywhere close to "pwn the app" in either its public UI or the APIs which were (by my insistence) publicly accessible rather than wasting our time chasing endless "please change the allowed IP ranges" requests through layers of bureaucracy.
My experience was pen testers would bang on the predictable places, try obvious things, maybe trigger some alarms which I then had to pause - and get nowhere. We'd get back a report that said things like if I put in this nonsense email address I get a different error message than this other nonsense email address. OK, that's nice we'll put it on the backlog, did you find any actual security problems? No.
I'd say that using a memory safe language (Java) helped make that happen, IMNSHO as a very good C programmer for whom writing it in Java was not my first choice (nor was C).
In comparison, so much modern javascript is written by junior developers - often without a lot of oversight. Junior developers can't "see" security vulnerabilities yet because they haven't learned to look for them. So I'm not surprised there's lots of critical vulnerabilities in modern software. Many coding bootcamps don't bother to teach any security engineering or best practices, and doing consulting work I've (often by chance) caught a disastrous amount of awful code people have tried to push to production simply because they didn't know any better. A lot of it is really simple stuff - guessable database IDs used as session tokens. JSON objects passed from the browser directly into mongodb. Authentication checks accidentally missing from certain admin dashboard APIs. Passwords and credit card information stored in plaintext, handrolled crypto, and so on.
Given the choice between C code and Rust code written by someone who's been programming for 30 years I expect the rust code would be safer. But if I'm asked to choose between C code written by an experienced engineer and javascript code written by a junior engineer, it wouldn't surprise me if it turned out that the C code was on average still more secure.
(Not that 20 years of experience makes you a decent secure C programmer, but that's a different argument)
What experiences have given you that opinion?
I don't remember too many specifics (it was a while ago) other than being brought onboard and having to introduce the tech lead to the "static" keyword.
Perhaps it's meant to match a style guide that recommends preferring postfix operators? Or perhaps it's editing shrapnel from an earlier version of the changelist, that maybe used the result of that statement?
AFAICT, the only real changes are in (new) line 411, resetting the cp variable after the loop, and line 416, ignoring write errors. The former is probably the relevant one.
This is a small enough diff that I wouldn't complain if given this to review - but I probably also would've reverted the style changes (and would give myself at least 50/50 odds of splitting the changelist in two)
Perhaps I've just been too broken by 1000+ LOC diffs that intersperse multiple behavior changes and style and refactoring and ...
The prefix/postfix change does something to the reader. It's a distraction trying to figure out if that change was intended, whether the author knows it has no effect, or whether I'm wrong about the change having no effect.
I appreciate the author doing the hard work of fixing this, especially with the code in full view of the public, but if I were an official reviewer I'd ask to get all the unnecessary changes removed.
https://github.com/sudo-project/sudo is just a mirror.
The amount of anti-education authoritarian fearmongering in these discussions is disturbing. Then again, given how much everything seems to be rapidly moving in the direction of dystopian corporatocracy, perhaps that's not so surprising.
There's already a few comments about how "safer" languages don't really solve anything. They just push the problems up higher in the stack of abstractions... and when that time comes, what are the chances the "security" fearmongerers will just start blaming something else? It's fundamentally a problem of competence, and doing everything you can to make the world a prison in pursuit of that "perfect security" is really not a good idea. There's no replacement for intelligence.
Also let's not forget that C exploits go back to the 80's, when most of us where doing full application in Assembly.
Supposedly we knew our pointers pretty well.
I'm not sure this statement adds anything to your argument. "Exploits go back as far as the language was in common use" doesn't really mean anything.
I'm sure Ada has had exploits, especially around its early lifetime. But its a language that's supposed to be safe.
Yes. Exactly. Exploits having existed for a long time isn't interesting or relevant.
I offered no defence of C. Just that the point wasn't relevant to the argument, one way or the other.
>People who understand these essential basics, e.g. by starting with Asm, are going to be far less likely to make these sorts of mistakes and write less buggy code in general.
Userbinator's post continues and seems to paint the ecosystem as getting worse over time, as if it were much better in this area in the past. Pjmlp's point was that even the original C users who came from assembly, who were presumably more familiar with the essential basics of assembly and memory management, still pumped out vulnerable code as we see today. It doesn't seem like having a userbase made up of experienced assembly programmers is enough for C to be predictably safe.
Some pre-89 code won't even compile today without putting in considerable effort.
Morris worm was the first widely know C exploit, which still gives us about 30 years of exploits to look into.
Regarding how many exploits other languages have, for starters not memory corruption due to out of bounds access, integer overflow or implicit casts, as Algol derived system languages, since 1961 (8 years before C was even an idea), had checks for those kind of errors.
C only made it to fame, because original UNIX could not be sold and its source code tapes went from university to university.
Had it not been so, probably we would be using Bliss, PL/I, Mesa, PL/S, and not having arguments how good one needs to be to write code that doesn't allow for memory corruption errors.
Exploits being consistently discovered over a long time is, and does help your argument.
Here's one for how safe that was: alloca could not guarantee the pointer it handed you is either valid, or the size you requested.
POSIX.1 only came along in '88 (with our first definition of stdlib). Whilst IO had mostly stabilised by that point, there were still plenty of platforms that didn't use the Bells header (stdio.h).
Files, signals, floating point behaviour, process creation - none of that was standardised yet.
Most of what was defined by POSIX already existed on common platforms, like SunOS, before it was formally standardized. SunOS 4.0 even had mmap in early 1988! (See https://archive.org/stream/bitsavers_sunsunos4.erenceManual1... )
I used to have the attitude around vulnerable code of "The problem would be solved if everyone else was just better [like me]". I was desperately trying to prove myself. I'm good at finding and solving tricky bugs, including exploitable memory corruption issues. It felt nice having a thing that I was clearly superior to others in. I loved languages like C where I had a great knowledge of the footguns and I could save the day in. But then I got a good job, I no longer felt undervalued and needing to prove myself at all costs, and my attitude shifted.
I can't write the whole codebase myself or double-check everyone's work. This didn't really matter if I was just concerned with making myself look good, but if the thing I cared about was the product itself and stopping bugs (especially exploitable ones) from getting in to begin with, then it wasn't enough to drop in occasionally to save the day and chide others for not knowing enough about the specific footguns lying around. If there were a bunch of tools that were misused 2/3 of the times they were used in the company, then I could accomplish far more by finding and advocating for safer replacement tools than I could accomplish if I tried to double-check every time the old tool was used and endlessly reminded people that the flesh-magnetic hammer will seek out your thumb unless you know to use some specific swinging strategy.
>There's already a few comments about how "safer" languages don't really solve anything. They just push the problems up higher in the stack of abstractions
A program written in any general purpose language can have high-level bugs like forgetting to check the user's password when they sign in, but only languages with manual memory management make it easy to also have an exploitable memory corruption bug when handling the memory containing the user's password. Solving some kinds of issues is valuable because that can help reduce the count, likelihood, and severity of issues that do happen.
>The amount of anti-education authoritarian fearmongering in these discussions is disturbing. Then again, given how much everything seems to be rapidly moving in the direction of dystopian corporatocracy, perhaps that's not so surprising.
>... and when that time comes, what are the chances the "security" fearmongerers will just start blaming something else?
What possible ulterior motive do you think people advocating safer languages have? I really can't tell if this is parody. Do you think Mozilla made Rust as part of some bigger political narrative, rather than to just make it easier for them to write bug-free code?
Understanding these concepts is not the problem. The problem is that even people who understand them very well regularly make mistakes when programming with them, and in C these mistakes have disastrous consequences.
> There's already a few comments about how "safer" languages don't really solve anything. They just push the problems up higher in the stack of abstractions...
The problems that exist higher in the stack of abstractions are present in C programs too. C just creates a lot of additional ways to subvert your program.
$40 billion says whoever wrote this knew what they were doing.
There is a Linux port, too
alias sudo='doas'
It's tough to remember to type 'doas' instead of 'sudo', especially when you use both Linux and OpenBSD all the time (which is why I also have a "doas" alias on my Linux hosts!).A 3rd party rewrite is a great time to assess what features are core features and which are extraneous. I haven't evaluated doas, but I'm definitely in favor of priviledged utilities having less code in general and having less complexity.
If your goal is to eliminate unsafe C code from critical paths, you want a drop-in sudo replacement. If your goal is to just be opinionated, sure, make a non-sudo thing with a selection of features you personally consider important --- but don't be surprised if people keep using sudo.
All of sudo's command line options and config options are part of its complexity.
See https://www.openbsd.org/innovations.html and OpenBSD src commit dated Fri Jul 3 21:51:53 2015 +0000.
But all over the place in C code, you see people open-coding all sorts of fiddly nonsense over and over again. You see people manually manipulate buffer positions and lengths; they'll laboriously type "!strcmp(a, b)" instead of defining a tiny little "streq" function. It's silly. However unsafe C-the-language's memory safety is, the problem is exacerbated by C-the-culture's resistance to using the abstraction features of the language. Why so little imagination?
Maybe it's a kind of self-selection thing: maybe if you're the kind of person comfortable with abstraction, you switch to C++, which is much better at it than C and just as efficient, leaving only the open-code-all-string-manipulation people writing raw C.
Even C++ community is aware of the flaws inherited from C, hence why standard library supports arrays, strings with bounds checking enabled in debug builds and eventually in release as well, if one so wishes.
Papers are going forward to reduce amount of UB in C++.
Meanwhile, on C's side Annex K was moved into optional in C11, instead of trying to fix whatever implementation issues it might have had on C99.
Source, and more info: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1967.htm
I do agree that Annex K doesn't provide a proper solution, as data and length keep being separated.
However what I disagree is the way it was left to rotten without adoption, instead of actually fixing it.
Static analysis is not a solution, given that all surveys point out that only a minority cares to actually make use of them, or works in environments where they are welcomed, with supportive toolchains.
Also they don't work across third party binary dependencies, heavily used in the corporate environments.
So unwillingness to improve C language security in the standard shows how little those driving WG 14 care about security.
At least C11 made VLAs optional, which never were a good idea to start with.
(The compiler is one form of static analysis that users can't really get away from, though. Users can turn all the warnings off, but only the extremely novice or foolhardy do so.)
I agree letting Annex K hang out optionally in the standard isn't especially useful; in its current form it should be removed. It isn't clear how it would be "fixed" without being something completely different from Annex K. Unfortunately, C hasn't had an actual language update since C11; C18 was just a clarification and errata incorporation update. The C2x committee is in progress but hasn't yet made any binding decisions on formal language changes, AFAIK.
I don't know that 3rd party binary dependencies are heavily used in corporate environments. I find that a little difficult to believe. I work in an enterprise environment with a huge codebase and we have no 3rd party binary libraries. We have a number of 3rd party open source or NDA source libraries, but we can and do use static analysis on them. It would be impossible to get to the kind of reliability numbers essential to our business with unauditable, unfixable 3rd party code in our product.
WG 14 don't view bounded string copying as some core part of the language, I think. The C standard is about providing a minimal definition of a portable abstract machine; this leaves implementors or more concrete standards, like POSIX, free to provide their own safe abstractions. Quite a lot of the C standard library is sort of legacy accidents that might not be put in the standard language again if reinvented today. But there is obviously a lot of motivation to retain backwards compatibility.
Problem with POSIX is that not all environments support it, and when, not always the same version.
Agree with the rest of your points, though.
> So I think that after the lengthy discussion here, this issue can now be closed. In summary: the use-after-free potential with string_view is accounted for in the Lifetime profile, even if there may be bugs or limitations in preliminary implementations that mean it is not diagnosed.
This is the big difference across both language communities, being willing to improve instead of being stuck with what was born alongside UNIX.
> even if there may be bugs or limitations in preliminary implementations that mean it is not diagnosed.
The huge assumption there is that finding all or even most lifetime bugs involving string_view and other dangerous C++ idioms via static analysis is tractable, that it's just a matter of "overcoming bugs in preliminary implementations of the analyzer". There is no reason to believe that; in fact there are good reasons to disbelieve that, including:
* Languages with static lifetime checking, i.e. Rust, found it necessary to add lifetime annotations, which the C++ lifetime guidelines don't have.
* Just after that Github discussion, Herb Sutter released version 1.0 of the C++ Lifetime Guidelines with dramatically scaled-down ambitions. See https://robert.ocallahan.org/2018/09/more-realistic-goals-fo.... Anyone relying on the unrealistic promises of version 0.9, probably including the commenters in that Github discussion, would have had their hopes dashed.
Relying on the emergence of some crazy-powerful static analysis to save us reminds me of the Itanium fiasco, where poor performance was portrayed as a temporary problem while we wait for compilers to "catch up", which of course they never did.
At very least, not in the next couple of decades. Keep in mind that it took 30 years for C++ to get where it is, and there are still domains it keeps failing to take away from C.
So having C++ lifetime annotations is better than nothing, in what concerns improving the language's security.
Likewise, even if you're the best programmer in the world, if you write a large C program, sooner or later, you're going to introduce a security vulnerability. Switching to a safe language isn't an admission of some personal inadequacy: it just reflects how the world works. But pride gets in the way.
But perhaps the bigger problem is that consuming third-party libraries in C is so painful. It's not too bad if you stick to shipping on Linux distributions which you're sure package the libraries you depend on. Otherwise, you basically have to vendor the library manually. It is always more convenient to just write "strcpy" in a few (more) places.
Reference-counted heap-allocated strings are fine in practice for almost all programs, and it's easy to write an API that implements this model.
Refcounted strings are actually very nasty to use in C because you have no RAII or other automatic memory management. You have to manually deref every string value when it goes out of scope, or get leaks --- and if you accidentally ever double-deref along some path, you get probably-exploitable use-after-free.
Once you've picked some tradeoffs for your program, you have to live with the fact any foreign C you call isn't using your library so you'll be converting to/from their types (typically const char*) all over the place anyway, so the temptation to "just use chars" will be ever-present.
Given those issues, I'm not surprised I actually haven't seen any C programs use this model.
char buf[16]; sprintf(buf, "/proc/%d/mem", pid); int fd = open(buf, O_RDONLY);
If you accidentally double-free or have UAF along some error path, then it's not safer either (given compilers have strong mitigations for stack buffer overflows these days, but not double-free or UAF).
> char buf[16]; sprintf(buf, "/proc/%d/mem", pid); int fd = open(buf, O_RDONLY
The code you're using to demonstrate the simplicity of char arrays has a buffer overflow vulnerability: what happens when the input int is large? You're proving my point.
In practice, dynamic resource allocation doesn't lead to rampant use after free bugs, especially if you follow regular and simple rules for ownership. It certainly results in fewer bugs and fewer severe bugs than cowboy char arrays code.
I did that deliberately to show how tempting, but also dangerous, it is to use char buffers in C.
I'm not advocating char buffers. I'm advocating not using C, and not buying arguments of the form "C's fine if you just do this thing that no-one does".
A technical reason for GNU/Linux not to use Rust or Go is that C supports a lot more platforms so you can't replace core components at this moment, also I want to remind you that memory safe languages existed before Go and Rust and the only project I am aware to create a safe OS and utilities was Midori by Microsoft.
Firefox and it's dependencies is not yet 100% Rust so I honestly expect a new browser started from scratch in a safe language to be done before Firefox is "ported".
That is a gargantuan project considering all the standards you need to support to have a competitive web browser. Especially javascript engine, video codecs and webgl are attack surfaces that is difficult to replace with code written in a safe language.
I think is less work then pressuring existing projects to be rewritten in Rust. I would usggest Rust fans to pcik one of those dependencies like a codec and reimplementit in Rust or gather money to pay someone to do it, I think it would be faster and less hostile.
Where are you getting your information from?
Going forward, all Android devices on ARM are required to use ARM MTE, because the only way to keep C develoeprs on track is to have hardware control their pointer usage.
https://security.googleblog.com/2019/08/adopting-arm-memory-...
And as of NDK 21, Fortify is enabled by default, https://android-developers.googleblog.com/2019/10/introducin...
Given that Linux kernel downstream on Qubes , ChromeOS and Android are the only ones with all of the security counter measured turned on, that speaks a lot for the standard Linux kernel on random distribution X.
Nitpick: there's assembly in there. Not that that undermines your point at all:)
I think the real problem is
a) C and C++ need a divorce. Because the things that you would do to really fix C make it incompatible with C++.
b) The senior maintainers of the gcc compiler need to be replaced with people willing to take these issues seriously. Right now they are focused on how fast their micro benchmarks run at the expense of everything else.
C++ community mostly adopts ISO C libraries post C89 and everything that has safer alternatives in C++ has been left out.
C style coding is discouraged in modern teaching materials for C++.
Signed integer arithmetic has been partially fixed.
Core guidelines are being pushed and for C++23 there are a couple of papers regarding improving the whole UB stuff.
A lot less than 90% in the graphs at https://www.zdnet.com/article/microsoft-70-percent-of-all-se.... Use-after-free and type-confusion bugs definitely aren't addressed by bounds-checking or split-stack approaches; many heap corruption bugs aren't either.
alias sudo='sudo ./badscript; sudo'
Most Linux/Mac installations are single user anyway, so you can steal the user's private information without even gaining root. alias sudo='sudo ./badscript'
where badscript runs `exec $@`.It does require though that the user both is allowed to run an interactive shell with sudo, and actually does so, since you have to wait for him to run sudo in bash.
I should probably add a check for catching someone within the sudo timeout and give them a hard time if so.
Otherwise you're right, despite much cargo cult, in practice pwning an user that can interactively run as root, is the same as pwning root.
If you do development for servers, though, (1) multiuser systems do exist where no or most users do not have sudo access, and (2) privilege separation accounts (such as www for httpd) generally do not run interactive shells. If you can escalate from an untrusted user on a multiuser system or from a non-interactive shell account to root, that's a problem. Dropping this alias won't help you on either of these kinds of system, which are important real world scenarios (but maybe not a scenario you need to care about personally).
Also there are userspace rewrites in Go and OCaml.
https://github.com/oreboot/oreboot
Userspace in Go for Core Boot/OreBoot, also started by Ron Minnich.
Similar effort for the PI,
F-Secure Go's bare metal implementation, https://github.com/f-secure-foundry/tamago
For OCaml, there is https://ocaml.github.io/ocamlunix/.
And then all the utilities that come out from MirageOS and Xen projects.
Argh. https://medium.com/elementaryos/loki-updates-for-the-new-yea...
>The code that erases the line of asterisks does not properly reset the buffer position if there is a write error, but it does reset the remaining buffer length...
Then the next paragraph: >Because the remaining buffer length is not reset correctly on write error when the line is erased, a buffer on the stack can be overflowed.
So which is it? Does it correctly reset the buffer remaining length or not?
These types of contradictions existing in bug descriptions do not fill me with confidence that someone has actually fixed the issue. Guess I'll have to dig into this one myself. Just what I wanted to have to do. Excuse me while I go and learn way more about the innards of sudo than I ever wanted to.
Or just disable pwfeedback, if you went out of your way to turn it on for some reason.
Seriously though, as others are saying sarcastically, I'll join them right up: "C is completely fine, you are just doing it wrong, it's a safe language when you know what you are doing".
Yeah, about that. <Glances the article again>
It's not a matter of "if", guys. It's only a matter of "when".
Oracle has SPARC ADI, ARM MTE (now adopted by iOS, in the future Android as well), because the only way to have those magical excellent C coders, is to have the hardware be their tiny wheels.
No, not yet. The Rust ecosystem is still not mature enough. One of the big problems is Cargo which sidesteps distro packaging and pulls dependencies from some random server. This doesn't fly if you actually want reproducible builds. The other problems are the complete disregard for dynamic linking and the amount of churn in the standard library and compiler. Give Rust a few more years for things to stabilize and for people to figure out a reasonable way to handle these problems. The work will probably fall on the distro maintainers.
My opinion is that if you're looking for a short-term investment that someone might want to make towards fixing these problems, try working on static analysis security tools for C and C++. It could not be easier to make these now with libclang. Alternatively, maybe get your company to fund a purchase of PVS-Studio or something like that.
So no, Cargo doesn't "sidestep" distribution packaging, it works with distribution packaging.
Also, neither the standard library nor the compiler have "churn"; we're very careful to make sure old code still compiles with current Rust.
>Also, neither the standard library nor the compiler have "churn"; we're very careful to make sure old code still compiles with current Rust.
When did this happen? Last I checked Rust still had no LTS policy and there were no stability guarantees around the syntax, ABI, API, compiler flags, Cargo file format, etc. If some policy has changed here, please link me to any info.
Just to reiterate: I wrote the initial prototype, and massive credit goes to the current maintainers who picked it up and ran with it into production.
> there were no stability guarantees around the syntax, ABI, API, compiler flags, Cargo file format, etc.
There are absolutely stability guarantees around syntax, around compiler flags, around API, and around the Cargo file format. Code from old Rust and old Cargo will build and run with new Rust and new Cargo. We take a great deal of care to preserve that stability guarantee. The policies here have remained essentially the same since 1.0 (https://blog.rust-lang.org/2014/10/30/Stability.html), though we've since taken further care to specify them more precisely and to stabilize further areas.
(Source: I'm one of the leads of the Rust language team, and I'm on the Rust cargo team. Other Rust teams, such as the Rust library team, follow the same principles.)
https://www.pietroalbini.org/blog/shipping-a-compiler-every-...
https://blog.ryanlevick.com/rust-2020/
Disclosure, I don't work on Rust or any other language, I just keep an eye on it out of interest. I give it another few years before these big regressions stop happening. Please keep at it and don't take any of my statements as being discouraging.
https://gcc.gnu.org/bugzilla/buglist.cgi?cf_known_to_fail_ty...
That query lists all regressions filed in the last 6 weeks, and there are nearly 150 of them.
There's active work on Rust specifications with varying degrees of formality, but I don't think that would address the problem you're describing. Specifications don't make bugs go away.
There are, occasionally, regressions in rustc. We catch the vast majority of those before they hit the stable branch, but bugs do happen. We try to minimize them, and we also put out stable point releases with fixes.
I also don't think that having multiple Rust compilers would affect the problem you're talking about. On the contrary, you'd then have multiple different sets of occasional bugs to deal with, and multiple implementations with differences that you'd have to cope with.
Speaking personally, (and not wearing my language team hat), I personally don't see value in having multiple compiler frontends for Rust, given that rustc is Open Source. (I absolutely see value in having multiple code generation backends, but not in having multiple frontends.)
Finally, regarding an "LTS" release: stable releases of Rust are supposed to be stable, not stagnant.
As well, pulling from a random server isn't the problem. Trusting a version to be a particular commit is, which crates.io handles as well as Cargo via cargo publish and the lock file. If you pull a dep, the lock file will guarantee that whatever you pull is going to be identical to what was published (assuming you didn't add a git field to the dependencies list or say version = "*").
As for churn in the std or dynamic linking, I've never had an issue using Rust in production and getting reproducible builds otherwise. Same is true for Clang or GCC/stdlib. APIs aren't being changed, binaries may shift, but unless you're on nightly you're probably not going to see anything break - just get better. But you can handle that, just don't update the compiler until you're ready.
I'm not really that concerned about C so I won't be able to fund the efforts, but I'm sure once you have a bootable operating system that works with existing software that people will be willing to donate to you on Patreon.