Debugging a Linux network stack crash via a single register value
blog.cloudflare.com
blog.cloudflare.com
This is typically the kind of post which makes me want to actually apply, mostly because I can relate to it : it's not magical, doesn't require gazillions of tpus, or petabytes of storage : it's plain old, excellent engineering.
I wonder how often these issues creep up in practice, and how long it took the author to sort it out though! I've had my share of compiler bugs/kernel bugs, and they're usually quite expensive to understand, and it takes a long time to convince yourself that the bug is not in your code (granted, there's an oops here!)
./scripts/decodecode is what produces the disassembly of the code trace from the panic. (Seeing its output converted in Intel syntax in this post is...heresy)
For AMDGPU, the issue was that the x86 Linux kernel doesn't use 16B stack alignment (it uses 8B stack alignment), yet just the AMDGPU driver was forcing the stack alignment for itself back to 16B. The AMDGPU driver uses SSE2 instructions that require 16B stack alignment. From the trace, seeing RSP as a multiple of 8 and not 16 was the smoking gun (ie. a single register).
The fix was to use the same stack alignment (8B) consistently in the driver when using sse2 (except for old GCC versions), see this series: https://lore.kernel.org/lkml/20191016230209.39663-2-ndesauln.... Stack alignment has obvious ABI implications.
Yes, it is a big "come work here on interesting stuff with fantastic people" (and show off your own skills and learn something neat) - and it's done without bragging and I'm sure will help others to debug their next OOPS :)
Kudos to Jakub (and presumably reviewers)
Windows has been able to do this for decades. Why, in Unix-land, are we still reading text reports about crashes and puzzling over specific register values?
I'd love you to explain how hunting kernel bugs is easier on windows.
You shouldn't have to puzzle over the meaning of each register in the x86_64 to figure out what value a function parameter had at crash time
The answer is because "its linux" and crashdumps (except on a few distros) are not part of the culture. These days linux actually kexec's another kernel with reserved physical memory to handle doing the dumps. This was absurdly hard to setup the last time I needed to do linux kernel debugging (years ago).
The linux kernel is generally compiled without frame pointers, and with other optimizations that are hostile to debugging.
We have kernel and userspace crashdumps enabled on our fleet of FreeBSD CDN servers, and having a real crashdump and not just a stack has been super helpful.
I am sure we can always automate more things :)
In userland, there is ancient stuff like libunwind - last I tried it on ARM, it was too dumb to follow the instruction pointer on a NULL function call. Not to mention there is some sort of deadlock when you use an external crashdump handler - the crashed process hangs in the kernel trying to feed your handler the dump and that interferes with using ptrace to find out what happened. The only reliable way is to put the crash handling hooks directly into libc, like Android does it - but that is not a thing at all with musl/glibc.
Thank FSM for cleverer people than myself. Another case of feeling like an imposter...
When was this bug introduced? Does anyone maintain a list of when all the known Linux kernel bugs were introduced? I'd love to know how many bugs are added to the kernel each year, and if the rate is changing.
I'm not trying to troll. I think the Linux kernel is an amazing piece of software engineering. I just think this would be an interesting metric.
Fixes: bf296b125b21 ("tcp: Add GRO support")
Fixes: f993bc25e519 ("net: core: handle encapsulation offloads when computing segment lengths")
Fixes: e20cf8d3f1f7 ("udp: implement GRO for plain UDP sockets.")
That said, using this to track how many bugs are introduced each year is problematic. It's often the case that commit A introduces a bug, commit B aims to fix it and says "Fixes: A" but turns out to only be a partial fix, and then commit C completes the fix and says "Fixes: B". Naively, based on the se annotations it would make sense to say "B introduced a bug", but as in my example, this isn't always the case.Greg KH discusses this in his talk "CVEs are dead" (video: https://www.youtube.com/watch?v=HeeoTE9jLjM slides: https://github.com/gregkh/presentation-cve-is-dead/blob/mast... ).
Cloudflare is indeed a really cool place.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin...
Is cloudflare working on any formalization efforts like Microsoft's Project Everest?
1: https://www.microsoft.com/en-us/research/project/project-eve...