As the person who wrote the fix for this issue (and not the original code), I will just mention that I find this paragraph makes the author sound incredibly entitled:
Shamefully, the inetutils project hasn’t actually released a fixed version of their software (at least at the time of publishing).
The bug was reported on a public mailing list, which is sadly common nowadays [1]. After my workday, during which I was not able to review the report, I wrote a script to confirm the bug was real, since I was seeing way too many slop reports at the time. Then I sent a patch before going to bed [2]. A third party then graciously shared the patch on oss-security [3], which all distributions follow. There is no need to make a new release, which is harder for the distributions than simply applying a small patch.
Perhaps I am just unlucky in my interactions, but I feel like this entitlement is too common among software security people. Note that I see zero return in spending time working on Inetutils, and I find other projects I work on more interesting.
The bug was reported on a public mailing list, which is sadly common nowadays
In defence of the reporter, your Readme only says "Send bug reports to bug-inetutils@gnu.org.", there is no distinction for vulnerabilities. [1]
There is a 3 months old pull request to advertise a private reporting email address but it's unmerged, maybe you could use this renewed interest as a nudge to set it up and merge: https://codeberg.org/inetutils/inetutils/pulls/26
One other thing the reporter could have done to make your life easier is to write a repro script rather than just explain the steps in prose.
The not-so-silent minority hanging out on HN know that the true heroes are those who take the time and put in the effort, and then put even more effort to reply and post about it here.
> The not-so-silent minority hanging out on HN know that the true heroes are those who take the time and put in the effort, and then put even more effort to reply and post about it here.
We know that the entirety of the AI movement is built on top of open-source projects like Linux and all the terminal and command line utilities. And runs inside projects doing god's work (say to contain the agents) like QEMU etc.
AI lives inside the work of our open-source heroes and would be absolutely nowhere without the work of all those people.
> Thanks to all of our heroes, op included.
Definitely, thanks GP and thanks to all our heroes.
Thank you for maintaining Telnet! I still keep it around mostly for retro computing purposes to interact with very old operating systems or with machines that aren’t capable of encrypting in any reasonable amount of time.
you are right they are entitled brats. usually they cant write good code themselves and they purposefully look for very old or low traffic code to find things that are 'old' etc. to make stupid uninteresting AI generated blog posts.
i have not seen a decent writeup in one of these clickbait things these ppl push out.
just ignore these types of ppl. its fine to fix the bugs ofc but what i mean is, ignore their attitudes. its a kids' attitude to life they keep
It's an unfortunate statement. And it does feel like it discredits you and the heroic efforts of those like you.
But this is probably forgiven as just sensationalism in writing, which is all too common. Not to excuse the author, but these types of writeups tend to drift into name calling and finger pointing a little too soon.
"Shamefully" is definitely the wrong word here, for sure.
As I have told the authors privately, I find it quite difficult to take their claim that they "don't care about licensing" seriously. 13/14 of the projects under uutils are relicensing GNU or GPL'd software. The other one, shadow-rs, is totally vibe-coded, and the README mistakenly says the original shadow is GNU software under the GPL, which is neither true. If 14/14 are relicensing, or in that one case, what is thought to be relicensing, the simple explanation is that it is an explicit goal of the project.
I think I saw the Rust based implementations sometimes used a separate thread for background tasks like statistics reporting. Not much additional in the way of "directly" using threads, but I wonder how many could actually be any faster with direct threading.
Its not that if they have advertised themselves as a drop-in replacement or not. What made me bummed that cannonical used it as drop-in replacement and it did break some of my installation scripts.
Most of them are TOCTOU races or improperly following symbolic links. For example, uutils mkfifo(1) would create a world-readable and writable FIFO before using chmod(2) to restrict its permissions. Another user could replace that file with a symbolic link between the mkfifo(3) call and the chmod(2) to change the permissions of arbitrary files [1].
Other ones I find concerning are that you could also bypass '-- no-preserve-root' with a symbolic link to root [2]. Or by using paths equivalent to "/", e.g., "/../" [3]. Historically, GNU coreutils has been pretty good with symbolic links and avoiding TOCTOU races. The only notable one I can remember is a chmod(1) bug [4].
I agree with your general point that the number of CVEs is a useless metric, though.
You can use equivs to create a dummy coreutils-from-uutils package, as mentioned in the responses to that report.
It is frustrating that Canonical has no interest in fixing it, though. It makes it hard to take their claims seriously that you can still use GNU coreutils if you want.
You're specifically frustrated about unbounded stack recursion exhausting the stack, triggered by multiple thousands of directories? It doesn't sound like this is about multi-thousand-deep directory structures, it sounds like it's about something else.
Because even diving into it, I would agree with a prioritisation decision that puts this bug down the bottom of a priority list.
Canonical is just rushing the switch because they want to get rid of software with GPLv3 license, not because there is any technical merit for doing so.
If they want the default to be non-GPLv3 Rust-based that's fine, but some of us don't care (and want the same behaviour everywhere, like on RH-based systems we may also have) and they should leave the GNU as an option. Potentially both could be installed at the same time (it's what update-alternatives is for after all).
That's a better thing to discuss, and I'd expect there to be better bugs to talk about to go with it. Stack exhaustion in an unrealistic environment should be fixed, but a "stop everything" bug it ain't.
FWIW, Canonical did not reach out to any of us who maintain GNU coreutils before, after, or during the transition. Had we known, we could have easily warned them about the incompatibilities.
I don't think this is a sarcasm, as a long standing Rust user myself, I don't think the community should preach out by "memory safety" -- rather the structural change in coding mindset and new ideas such as ADT and generic programming, while retaining mostly zero-cost like C++ without the bearing of C++ complexity (that includes a compiler that spits out 300 lines of compiler error just because I missed one <).
Memory safety is just what Rust inherited from C++ smart pointers with a stronger linear/affine type system twist, especially regarding move semantics, cloning and borrowing rules. What makes it powerful is the various language features it also incorporated from Cilk/C#/OCaml/Haskell.
Right now Rust and Zig is my two favourite middle ground. It is sad that Zig went completely sod-off-to-LLM mode but the most devastating aspect of Zig is that it is way too Linux focused. A lot of the code in Zig I cannot compile on Windows, plus the major changes to IO interface and the colorless function fiasco is really making me question the stability of Zig.
This recent event led me to displace Zig and replaced it with Nim, which emits C code instead and having a really powerful algebraic language system, while getting some benefits of Rust such as scoped smart pointer (I think they call it ORC), or using simple mark and sweep arena collector or even full-blown Boehm GC.
Right now I'm trying to create a distribution of Nim in a single binary (with musl and mingw packed together) and using tcc as the backend, all in a single binary with Rust and an internal project to convert wasm 3.0 and wasi proposal 1 modules into Rust code, which the Nim compiler and TCC are both compiled to WASIp1 first, then term-rewritten/transpiled to Rust (think w2c2 or wasm2c, but targets Rust, I found some nice structuralism of Rust and wasm lately)
There's very little incentive to switch. When people release for Linux they almost always first/only test it works on Ubuntu - hence it's going to be the least buggy. (ex: GOG only seemingly tested games on Ubuntu)
So the switching cost is buggy software. You'd need something radically different that brings enough new features to the table to make it worth it the switch and dealing with bad/non-existant support.
I think something like Nix/Guix but with stable library versions that matches Ubuntu/Debian LTS ones 1-to-1 could get traction
This is certainly not just affecting interim releases. Ubuntu 26.04.1 has been released but is currently held back from do-release-upgrade for the LTS channel (which IIRC is unusual for a LTS's .1 release) due to rust-coreutils issue:
> Users of Ubuntu 24.04 LTS will be offered an automatic upgrade to 26.04.1 LTS
via Update Manager a couple of weeks following this release after some planned
backports to address regressions in a recent version of rust-coreutils.
The apparently related bug in the origin project is https://github.com/uutils/coreutils/issues/2949, filed back in 2022 and which sat unmoved for several years until, probably not coincidentally, almost the same time yesterday that this appeared on Hacker News.
> Users of Ubuntu 24.04 LTS will be offered an automatic upgrade to 26.04.1 LTS
via Update Manager a couple of weeks following this release after some planned
backports to address regressions in a recent version of rust-coreutils.
This is what made me move away from Ubuntu. Use LTS and encounter issues with outdated packages? "Well duh, you're supposed to upgrade to interim releases if you need remotely up to date software". Use interim releases and encounter bugs? "Well duh, it's an interim release. Of course it's a buggy mess, nobody uses those"
Every Fedora release is intended to be solid and they come out twice a year.
I moved to Fedora from Ubuntu about a year ago for my laptop. My main motivation was not be defaulted to snap packages. I have had a great experience i.e it gets out of the way and it doesn't fall apart when I update stuff. I was worried about SELinux but find Fedoras defaults just fine and intuitive.
Reporting bugs before Ubuntu releases has never worked for me. They always land a bunch of major changes after the supposed "freeze" then they ignore all feedback because of the freeze. It's infuriating.
I've been a Ubuntu user for about 20 years, and I file bugs every now and then on launchpad. I don't recall any of them ever being fixed.
Maybe the bugs get traction if you have a service contract?
Best to file bugs directly to upstream, but that of course means you should try it on the latest upstream version and not whatever version ubuntu ships, so it's more friction.
It's entirely aisine and makes me avoid Ubuntu every time it is possible.
And it is repeated offence, Ubuntu always tried to push the envelope in worst place and way possible
Am I the only one cynical enough to feel physical pain when I see “blazing fast” in a README? It’s like a dog whistle for “hey everybody, I’m kinda annoying!”
I think ubuntu wants to kill desktop linux. No other explaination of why they push firefox inside snap, which then proceeds to constantly crash, when firefox used normally works completely fine.
I haven't tried chromium but I presume it's the same issue.
At work I'm forced to use ubuntu and I placed snapd on hold and added mozilla's own apt repository to my configuration to get firefox.
At least in the past few months the dbus crashes (been using systemd on debian for several years just fine, this never happened) that render the system unusable and un-rebootable have stopped… I guess when my company will decide to upgrade to 26.04 there will be more instability and problems.
Thanks to snap I've learned of Librewolf. Couldn't install non snap Firefox, so just got ff fork instead.
Also, after a system update all PPAs (including Librewolf) got turned off, so I went like a year without updating the browser. Fun stuff.
Also, snap is now partially fixed. It used to take all your drive space with old versions of snaps. You needed a special script to remove them automatically.
Now there's no more than 2(?) old versions of snaps anytime. Progress!
But not to complain too much, Ubuntu still kind of works. I moved my laptop to Fedora, and almost did it with the desktop, but learned of Librewolf. Should probably remove the fedora root and home at some point. Don't remember any issues that were not caused by me (other than snaps). Compared to Windows with it's auto updates when you need PC for sth critical, ads everywhere and this annoying way of installing software not from terminal and having to update it manually separate from system updates. Ubuntu did a lot for Linux, maybe more it's community than canonical, but it will always have a place in my heart.
Firefox installed as a snap feels sluggish to me. When I install a Ubuntu system, the first thing I do is to uninstall any snap and install the apt versions of things, possibly adding official PPAs or using Guix to install software.
Indeed. Thought not in all cases I can decide what OS to use. An employer might prescribe which one to use. And any distro will be better than being forced to use Windows.
sandboxing a browser makes total sense. my firefox and chromium running as snaps never crashed a single time for as long as I can remember, and I use both every day, at the same time.
Appimages are bloated and unreliable. You can't guarantee that your app will run on any machine because it might depend on different system libraries.
Flatpak uses shared stable runtimes that are the same everywhere and don't take up space more than once. It also comes with a native update system and sandboxing. Snap is the same thing but worse.
My problem with flatpak is that the underlying runtime has a limited lifespan. I used the Sublime Text 3 flatpak for many years, and it was deprecated a few months ago because the runtime it was using was no longer supported. This caused people to update the flatpak to Sublime 4, which is good, but I only have a license for 3. In the end I just downloaded the binary for 3 from their website, and it still just worked. Flatpak still doesn't save you from bitrot.
There's a project called "anylinux appimages" that aims to make the most compatible appimages. They also use DwarFS for faster opening and smaller file sizes.
yeah, this is a bug. And yes, it should be fixed. But I don't think it will affect many users, I mean who has a 32000 -evels deep directory on their system?
It's a stack overflow which means it's using recursion and for historical reasons that don't make sense any more, stacks are teeny tiny on 64-bit Linux - apparently only 8 MB on Linux! I'm not sure why they don't raise it to something reasonable like 4 GB. I guess because they want consistency with 32-bit? Maybe we can finally change it if/when they phase out support for 32-bit Linux. Apparently it might not be that far away:
8MB is the default per-thread stack size from glibc, also seems to be the default "ulimit" from pam or the kernel, I'm not sure. So for the main/default thread (or if not using threads) the process can use setrlimit() and for threads it can use pthread_attr_setstacksize() to get bigger stacks if it knows it may need them.
8MB is pretty huge though; musl libc is famous for defaulting to much smaller per-thread stack size of 128KB (to avoid over-committing lots of memory when there are many threads - the main dev is really principled/opinionated on this topic, but again there are a few ways for applications to explicitly size their stacks as large as they need). Linux kernel threads get a bit less than 16KB!
8MB is huge compared to the stacks we used to have when address spaces were 32-bit, but it's tiny compared to how much memory we can actually make use of now. The only real argument I can think of for having such a small stack is that large stack usage is often indicative of an infinite recursion bug and it catches them earlier.
But I don't really buy that for the same reason most programming languages don't limit loops to 8 million iterations (or whatever) by default - it would make catching infinite loop bugs easier!
I say most, because I know of at least one language that did do that - QuakeC! It made lots of sense in that context though.
For what it's worth there's reasonably active [1] work on implementing opt-in guaranteed tail calls - but it's not particularly fast going. LLVM (the backend rust uses) needs better support for musttail (e.g. some architectures just don't support it [2]).
By-default guaranteed tail calls really isn't rust's style, because it means subtle changes (introducing a destructor, re-ordering code, etc) can change semantics without you realizing it. If you want to guarantee that a call can't allocate a new stack frame you should have to say it.
Not so familiar with this area, but isn't the existing behavior of implicitly creating new stacks more of a problem than implicit tail-call elimination? Seems the latter is a kind of compiler-level optimization, of which there are already many (I think) that change the semantics internally but guarantee the outward behavior stays the same.
But I can understand the preference for an explicit opt-in, to make clear that it is enforced and not assumed.
I'd argue that it's explicit - that's what a function call does and you don't have implicit function calls in rust.
> Seems the latter is a kind of compiler-level optimization, of which there are already many (I think) that change the semantics internally but guarantee the outward behavior stays the same.
What you're asking for here already exists. Tail calls might be optimized into not allocating extra stack frames, the rust compiler just doesn't guarantee that it will perform that optimization (and almost certainly won't when code is compiled without optimizations... for instance).
What people want is the semantic guarantee that the stack frame won't be allocated. Not just a compiler that often performs the optimization. Otherwise you can't be sure that your code will keep working with new compiler flags/versions/architectures/... You could say "whenever the code is the right shape we'll guarantee the optimization" (C++ famously did this for things like copy elision)... but now the shape of code comes with non-obvious semantic guarantees and that's not rust's style. Hence the proposal for a keyword instead.
I see it, certain algorithms need guaranteed tail-call elimination, otherwise they are too inefficient and must be manually unrolled or rewritten to avoid blowing the stack. So a compiler optimization that is "nice to have" is not good enough.
I can see how it might require an explicit state machine (keep a mutable state number and switch over the inlined bodies of what could be functions), but I'm not seeing how it could require `goto`.
Are there more-complex relationships that might require it?
> because it means subtle changes (introducing a destructor, re-ordering code, etc) can change semantics without you realizing it.
No, it won't change semantics - if you say @musttail or similar, it will simply fail to compile if you, say, introduce a destructor - the semantics will not subtly change.
Uh, yes, if you guarantee the semantics only when the code explicitly opts in and not by default then semantics will not subtly change, that is the point of my comment
Guaranteeing an optimization that otherwise only might run is a change in semantics. The attribute doesn't allow (in any sensible language) the code to simply not compile because the optimizer doesn't feel like it today (or you compiled with -O0), it forces the compiler to not allocate a stack frame wherever the code fits the structure that makes that definitely possible and fails to compile wherever it doesn't (even if after other optimization passes it happens to fit a structure that makes it possible).
I don't think that's related? The bug alluded to looks something like
function rm(node) {
for (const child of ls(node))
rm(child);
unlink(node);
}
and no amount of tail call optimization will save you here, because this isn't tail recursion. Of course you could rewrite it using an explicit stack + tail recursion, but then you might as well be using a while loop.
So it's enumerating all the subdirectories first and unlinks the tree afterwards?
I get that this isn't transactional and inherently prone to race conditions, but if this is indeed the problem, it's rediculous. A single while loop could do the job correctly and use less RAM. It'd probably also be faster. But that wouldn't be a rusty thing to do?
I look at code from the heirloom project and, despite its warts, I think we've lost something in the past 45 years or so.
> A single while loop could do the job correctly and use less RAM. It'd
probably also be faster. But that wouldn't be a rusty thing to do?
No, the "single while loop" is just harder to implement than a naive
recursion, because recursion is a natural way to implement tree traversal.
With a while loop, you need an explicit stack, which is more complex.
(A stackless traversal seems unrealistic here, as getting the succeeding
node would be too expensive. Not that I've tried...)
> I look at code from the heirloom project and, despite its warts, I think
we've lost something in the past 45 years or so.
I've just tried and heirloom rm segfaults on the same test too. Which is
no wonder, seeing how that code also recurses.
(It's mentioned somewhere else in the thread, but this is exactly the
reason why GNU had to specify "no hard limits" as a policy. Unix used to
be full of such bugs.)
How interesting. I'd seen LISP(y) implementations like Scheme guarantee tail-call since recursion is a very common technique in that language family. But I didn't know Zig supported it.
They have an @call built-in that guarantees: always/never tail, as well as always/never inline. That's neat, I can see how that would be useful in various situations.
The point is that such bugs shouldn't exist in the first place.
Using recursion on unbounded inputs on a programming language that doesn't support that (which are most) is an extremely classical mistake that really should be known to all programmers, especially those of low level languages that care about safety.
Every time you call something recursively you should be thinking "how deep is this?".
By that logic, why even spend effort migrating from a known-working implementation to one which is known to have outstanding bugs that there isn't enough bandwidth to fix?
Hard to say, but there are legitimate reasons. One of which being they think Rust is growing while C is a shrinking language. Other could be safety (yes, yes the audit did surface bugs). Or even that having a single lib work seamlessly on Windows/Linux/MacOS/Redux/Web.
The problem there is that this is exactly the class of bug that does not exist in GNU coreutils because of philosophy of that project. Non-existence of such bugs proves that the impementation is not copied from AT&T code.
This isn't a port - it's a re-implementation without any use of the original source.
That's also not all that's happening. It's also making improvements like better internalization support, better error messages, and a small handful of other extensions.
I have had to tell them repeatedly to stop copying tests verbatim, including the original comments from GNU coreutils. So I doubt this is true, which is frustrating.
If they followed the license nothing. My uninformed knowledge is that the rust based rewrite is MIT, the originals are GPL, and you can't include GPL code in a MIT licensed project without making it GPL.
Ehh... Technically yes but when you don't own the copyright on the tests you need to be very careful against creating derivative works, and you need to preserve both licenses in the distributed source.
Nah, people should (and do) fix small issues as well as big issues. Lying about the scale of issues and calling them "big" when they aren't just leads to no ability to prioritize or evaluate.
Incidentally someone submitted a PR for this issue about 3 hours before the first comment about it in this thread - https://github.com/uutils/coreutils/pull/14554 (and 2 hours before this link was submitted to HN)
Their strategy is basically adopt now, hey now we’re forced to fix it! And it’s disgusting. Frankly, I don’t know how any enterprise users on Ubuntu will be able to forgive this. Well, many businesses are on RHEL and not Ubuntu anyways…
Wow! Memory safety and such... Reminds me when a friend of mine wrote in IRC long time ago: "Hmm, tail just segfaulted." When I asked "Are you on Hurd?" he just replied "Yes."
While I supported that idea of Rust coreutils, this your experiment showed me how bad it is. My results are totally different, but not what I expected (edited out long strings of "a/a/a" for brevity). I wouldn't call it stable...
root@71a8c5a6c5e3:/# mkdir -p $(yes a/ | head -n $((32 * 1024)) | tr -d '\n')
mkdir: File name too long
root@71a8c5a6c5e3:/# gnumkdir -p $(yes a/ | head -n $((32 * 1024)) | tr -d '\n')
gnumkdir: cannot create directory 'a/a/a/.../a/a': File name too long
root@71a8c5a6c5e3:/# gnumkdir -p $(yes a/ | head -n $((3 * 1024)) | tr -d '\n')
root@71a8c5a6c5e3:/# rm -rf a
rm: cannot remove 'a/a/a/.../a/a/a': Directory not empty
root@71a8c5a6c5e3:/# gnurm -rf a
root@71a8c5a6c5e3:/# rm -rf a
root@71a8c5a6c5e3:/#
And then they plan to move to a new Rust-based NTP. No comment...
Perhaps I am just unlucky in my interactions, but I feel like this entitlement is too common among software security people. Note that I see zero return in spending time working on Inetutils, and I find other projects I work on more interesting.
[1] https://lists.gnu.org/archive/html/bug-inetutils/2026-03/msg... [2] https://lists.gnu.org/archive/html/bug-inetutils/2026-03/msg... [3] https://www.openwall.com/lists/oss-security/2026/03/12/4
reply