New Linux glibc flaw lets attackers get root on major distros
bleepingcomputer.com
bleepingcomputer.com
If we look at access beyond on a slice's boundary:
https://github.com/rust-lang/rust/blob/ea37e8091fe87ae0a7e20...
This bounds check is what enables Rust code to fail with a panic vs continuing (which is what triggers a lot of bugs).
Post about the impact on performance: https://blog.readyset.io/bounds-checks/
[1]: https://git.musl-libc.org/cgit/musl/tree/src/misc/syslog.c#n...
[2]: https://git.musl-libc.org/cgit/musl/tree/src/misc/syslog.c#n...
Maybe their implementation can't return negative, or only if the fmt string (constant, here) is invalid? I wouldn't count on it, especially of it being true forever. That's pretty sloppy code.
You can see the implementation here: https://git.musl-libc.org/cgit/musl/tree/src/stdio/vsnprintf... and https://git.musl-libc.org/cgit/musl/tree/src/stdio/vfprintf.... .
Not really a concern inside musl because those implementations are probably long gone and because it's calling its own snprintf() anyway.
> If you were trying to be portable and defensive you'd need to check for either error return.
Including defensive against future changes.
I'm sure thousands of bugs are being written every day because people don't check return values that "can't happen", because they know the code they call. Then 10-20 years later, someone changes that code they depend on, without violating the contract.
I encounter these kinds of bugs all the time. There's a simple way to avoid them: Check the damn return values, even if just with an assert.
The extra annoying ones are ones with a comment saying "Can't happen", that then does happen. The person who wrote that could have spent about the same number of characters simply handling the "can't happen".
We can't get away from Hyrum's Law, but we sure can try to minimize its impact.
I encounter bugs all the time that come from the fact that the caller knows the code they call, so they ignore the documented promises.
Let's all at least try to not invoke Hyrum's Law, even for code in the same project.
As usual, MUSL is secure at the cost of giving up usefulness. A 32-byte buffer might seem like a lot, but there are dozens of programs with a name longer than 31 bytes on my system. `dbus-update-activation-environment` is the most interesting (most others are gcc/binutils), and I know dbus in general uses syslog, though I haven't traced this particular entry point.
> As usual, MUSL is secure at the cost of giving up usefulness.
I would say 'Musl is deliberately simple at the cost of usefulness'. The simplicity lends itself to correctness and security.
Btw, since Musl does not implicitly construct log_ident from argv[0], program name is sort of irrelevant unless that program explicitly openlog()s with argv[0]. dbus-update-activation-environment does not appear to openlog or use syslog at all: https://gitlab.freedesktop.org/dbus/dbus/-/blob/master/tools... Instead it prints error messages to stderr.
See `dlmopen(3)`. Admittedly, multiple link-map namespaces are weird to use.
Note that libressl supposedly improved openssl compatibility dramatically after that.
Also note it would have been a very different scenario if Debian or Fedora actually made the switch as they should have, rather than a few smaller distributions.
Instead, incompetence was rewarded, with plenty of money thrown openssl's way. This has, in hindsight, proven to not have been a good decision; code quality is still bad, and libressl is still a world better.
There exist distros using libressl and musl, proving feasibility.
We have `l` and `1` being used in the same passage. Single letter variables are bad to start with, but `l` is particularly cursed since, if you happen to be using a font where `l` and `1` are homoglyphs, good luck reading that passage. But that's sadly the default in a number of OSes/environments, and indeed, the site this code is hosted on. One might presume `l` stands for length, … but there's also `vl`, `bufsize` and `sizeof bufs`. `bufs` and `buf`, although the former, `bufs`, is a singular buffer, leading one to wonder why the variable name is pluralized. I'm pretty such it's because it's stack-allocated (as opposed to the heap alloc'd `buf`, and thus `bufs` is "buf[fer], s[tack]". This is just nuts, and it should be no surprise there are multiple CVEs.
The OG code was had CVEs. The commit that "introduced" this — which I almost feel is wrong to say — attempted to use `bufsize` for the buffer size, which certainly feels more right that the OG, but alas, the variable is never meaningfully initialized. This CVE is on the patch to fix a CVE! And in the original CVE's bug report, the ominous words of "Doesn't seem too serious" were uttered.
I'm sure it's fixed now, though, right?
syslog is just a bad interface logging interface, to boot.
I see lots of ground for further research.
(from https://www.qualys.com/2024/01/30/cve-2023-6246/syslog.txt, the article from the duplicate HN thread)
I think by now everyone has accepted that the Unix/Linux account system is insecure by design and exists just to prevent accidental damage.
There are ways to restrict it but the default configuration simply exposes too much of an attack surface. I still give separate accounts to some services as defense in depth, but it mostly exists to slow down untargeted attacks.
Note that this exploit relies on being able to run as root (typically through setuid). If you don't fully trust a service, don't let it ever talk to code running as root in the first place. No opening sockets in /tmp, no listing processes in /proc, no dbus shenanigans, no sudo or su. One of this issues with this was that some programs require setuid for bad reasons (IIRC historically ping was setuid to be able to send ICMP packets). From a quick check (find -type f -perm -4000) most of these problems have been eliminated, via linux capabilities or otherwise.
These tactics successfully saved me from log4shell.
They have all had vulnerabilities, My preferred method is to not install stuff I don't need, and fix any dangerous configuration for the programs I do need. I prefer Podman over Docker because of rootless for example.
The way to protect against this is with an external supervisor. But then you have to care about privilege escalations attacks against the supervisor. Hopefully that one is much simpler than Linux so it has much fewer vulnerabilities.
Check out the Chromium docs on this topic: https://chromium.googlesource.com/chromium/src/+/HEAD/docs/l...
This is based on a buffer overflow in glibc itself. It is my understanding that rust still calls into libc for many lowlevel system functions. And if you call C code from rust, well, it's still C code with C code bugs.
Ultimately, if you want to get rid of those, you'd have to rewrite the libc in Rust.
in this case it was in a pam module
and dynamic modules loaded into setuid binaries (pam/nss/...) are also super-high risk and should be memory safe too
No, you can eliminate these bugs by getting better at writing code. The language is independent from the quality of any given code.
Rust is capable of introducing theses bugs, but the language is more explicit when you do. One languages isn't more secure than another. They each attempt to fill a niche, some trade language usability for correctness, some trade correctness for being easy to use.
No, the data doesn't show this. This is the "anger" response to being confronted with the reality of the inherent insecurity of C and C++. https://www.usenix.org/conference/enigma2021/presentation/ga...
Actually asking: Are there any studies that show high quality code is just as memory unsafe as low quality code? Are there any studies that show excluding memory overrun bugs, rust code has a lower risk of being exploitable? What about a memory unsafe language like C written to the MISRA C standards?
I'll happily admit I'm just an old dude angry on the Internet... doesn't mean I'm wrong :)
Rust eliminates these bugs and it results in fewer security issues in Rust code than C/C++. See https://security.googleblog.com/2022/12/memory-safe-language...
Additionally, even in brilliant soloist projects like curl, Linux, etc., you still get plenty of memory safety CVEs. The best still don't get it right.
> Are there any studies that show excluding memory overrun bugs...
Why exclude memory safety from the argument? That's the whole point -- to fix those significant fraction of vulnerabilities.
Because while it's a significant fraction of those vulnerabilities found. (key worksd found, because they're easier to find) It's not a significant fraction of the vulnerabilities leading to exploitation.
My original argument was, security issues are preventable by increasing skill. You tried to claim that's merely an instinctual reaction of anger. My refutation is that, yes rust eliminates some memory safety issues, when used correctly, and when you limit yourself to a smaller subset of the language.
The two arguments, 1 isn't that also true for MIRSA C, and 2 does programming is rust lead to a lower defect count in any other metric than memory safety?
Because if exploitation isn't due to memory safety, fixing that class of bug doesn't improve security.
And I'll actually make a 3rd argument, Does a large rust project have a lower defect density than a solo project written by an expert, like you e.g. curl?
So he says nothing about how to write better code, but how to convince developers to do it? It's known how to write better code at fairly low effort and is used in several popular projects, mostly networking for obvious reasons.
Apparently it also supports linux!
The user-facing APIs would be just as dangerous as ever, but the internal implementation-detail stuff - like this "__vsyslog_internal" - could presumably be made safer.
All those C users hatting on C++, while being forced to use tooling that has dropped C for C++.
Such haberdashery!
Anyway, something that I appreciate about the Rust community is their dedication to rewriting anything and everything in Rust. I'd love it if the Lisp community also had some of that drive. Seeing a library which uses a Python script to generate documentation is so disheartening, they don't need that horrendous, awful garbage, they could just do it in Lisp.
[0]: https://www.merriam-webster.com/dictionary/haberdashery [1]: https://www.merriam-webster.com/dictionary/balderdash
That's one nice thing about C, that the C stdlib is so bare bones that it could just as well not exist and not much would be lost (much harder to ignore the stdlib in C++, at least for some "modern C++" features).
Besides: MUSL is written in plain C ;P
C is the complete printout from ISO/IEC 9899.
> c-gull is a libc implementation. It is an implementation of the ABI described by the libc crate.
> Currently it only supports --linux-gnu ABIs, though other ABIs could be added in the future. And currently this mostly focused on features needed by Rust programs, so it doesn't have all the C-idiomatic things like qsort yet, but they could be added in the future.
E.g. Zig is getting a libc written in Zig:
https://github.com/ziglang/zig/issues/514
Rust would work too of course. MUSL is probably the only popular C stdlib actually written in C.
I know these are a lot more involved than the functions provided by the glibc, but the point is that libc means Library for C, not Library in C.
And of course on Unixes all software is expected to link to the glibc for basic functionality, no matter whether it's written in C or not. So the name is only historically accurate.
Windows has a similar structure, but the required kernel interface library is not the same one as the C runtime, so it’s less confusing language-wise.
Actually a large part of it is, and in Java's case there are bootstraped implementations, OpenJDK isn't the only one.
Java is like C and C++, where multiple implementations are available for a given specification.
So the answer is: Not necessarily. I agree though that the memory safety features in Rust would help reduce the risk. On the other hand, one could also write safer C by abstracting away buffer management. The world is not black and white.
Unsafe is for creating things that are beyond the understanding of the borrow checker. For example, using unsafe is mandatory for creating a mutex because the safety rules are upheld by the data structure itself. Unsafe is not "C mode" as most would assume - it is more unsafe than C because if you don't uphold the memory model things will break.
Rust's strict aliasing and mutability rules provide ample opportunity for compiler optimizations and zero cost abstractions. Turning to unsafe is generally a sign of incompetence/arrogance.
Basically: the code was an optimization trying to avoid a heap allocation by using a stack buffer instead, but its fallback for "it doesn't fit in 1k of stack memory" wasn't tested and didn't work right.
Rust struggles with alloca-style code too, to the extent that users who want to do this kind of trickery usually resort to unsafe. There's a link elsewhere in this topic to a Rust library vulnerability that looks similar.
The upshot is that if the code had been written idiomatically and just used the heap like it was supposed to, it probably would have worked fine. The glibc authors got fancy, loaded a footgun, and it went off, something that Rust is equally capable of.
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.)
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.)
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.
https://play.rust-lang.org/?version=stable&mode=release&edit...
EDIT: I wonder if you're thinking about how sometimes bounds checks are optimized away? It is true that that will happen in release mode more than debug mode, but those are only the checks that can be proven redundant, if there is any doubt, they will not be optimized out. Semantically, they are still there.
The problem is that Rust solves _a_ class of issues without even beginning to address other kinds of problems, and so this sort of thinking is evolving toward a mindset that "oh it's rust so no security issues", which is completely wrong.
Remeber that until AT&T was allowed to take commercial advantage of UNIX, its source code was available for a symbolic price, and it came with a C compiler toolchain, at least until Sun started the trend among UNIX vendors of spliting UNIX development tools into an additional purchase.
The UNIX vendors that had Ada compilers, that was an additional purchase on top of UNIX SDK (which already had C and C++), so unless there was a hard requirement to use Ada, no one bothered to pay extra.
And it's not like it has no effect on the final third either. The stronger type system and ownership system make non-memory related bugs less likely too.
Your comment makes it sound as if you have a better solution. But Rust is the best one we know so far, so this kind of naysaying does more harm than good.
Now as GP was saying, Rust inherently mitigates a great deal of generally serious software vulnerabilities, which is great, even if it doesn't mitigate anything else.