Fifty patches in six months: starting out in the Linux kernel
How the first netfilter patch became fifty merged fixes and 26 CVEs, and what the process looks like from the outside.
I sent my first upstream Linux kernel patch on 19 May 2026. By November I had roughly fifty patches merged and 26+ CVEs assigned to fixes I wrote. This post is the honest version of how that started, what the process actually looks like from the outside, and what I wish someone had told me on day one.
The first patch
The bug was small and old. nf_conntrack_irc, the IRC connection tracking helper, had a newline search loop in parse_dcc() that treated its end pointer as inclusive while the caller passed an exclusive one. A DCC message without a trailing newline made the parser read exactly one byte past the packet data. The bug had been there since the start of the git history, commit 1da177e4c3f4, April 2005.
I found it by reading the helper, not by fuzzing. The kernel teaches you quickly that "pointer plus length" means the first invalid address, and this loop used a less-than-or-equal against exactly that value. The fix was two comparison operators and a comment update. Florian Westphal took it through the netfilter tree, and it landed in 7.2-rc1 as ef6400ca25a1.
The kernel CVE team later assigned CVE-2026-80603 to it, and the scoring discussion that followed taught me more about the industry than the bug itself did. The kernel CNA scored it 9.1 Critical. Red Hat independently scored it 5.3 Moderate. Same bug, same code, same fix. The disagreement is about threat model: the CNA scores what a wrong conntrack expectation can permit through a firewall, Red Hat scores what realistically leaks. When people ask me what a CVE score means, my answer now is "read the vector, then read the code, then decide whose assumptions you share."
Where the bugs actually came from
After netfilter I went looking in three places, in this order.
Staging drivers first, because rtl8723bs is a goldmine of parser code that has never seen a fuzzer. Five out-of-bounds reads in that one wifi driver, all reachable from crafted frames, including a WPS attribute parser that used a wire supplied 16 bit length as a memcpy size into a one byte stack variable. That one is CVE-2026-90018, scored 8.8, and it fires while the victim is simply scanning.
Then one driver audited end to end: hp-bioscfg, the HP BIOS configuration interface. Nine patches, four CVEs, and a follow-up series still going. When you read one driver completely instead of skimming for patterns, you find the bugs that pattern matching misses, like a package count read through a union from a field that is actually a string length. I wrote that series up separately in the hp-bioscfg field report.
Then new accelerators: the Arm Ethos-U NPU driver and the Rocket driver, both fresh in the tree, both parsing userspace command streams. Five CVEs in Ethos-U alone, including two genuine heap write primitives and a U64_MAX sentinel that could hand stale physical addresses to the DMA engine.
The process, for anyone about to start
The flow is smaller than it looks from outside. You read code, you write a fix, you run get_maintainer.pl to find the subsystem list and reviewers, you send with git send-email, you answer review comments, and eventually the subsystem maintainer sends a pull request to Linus. My first patch had four revisions. The reviewers were right every time.
What nobody tells you: the stable backports happen without you. Your fix lands in mainline, the stable team picks it up into every supported branch, and a few weeks later Greg Kroah-Hartman's CVE team sends mail saying your commit now has a CVE number. Twenty-six of those emails later, I stopped keeping count by hand and built the CVE index this site runs on, with a research note per CVE: the bug, the trigger, the exploit, the scoring disputes, every backport.
What I would do differently
Three things. I would start with one subsystem instead of three, because the reviewer relationships matter more than the bug count. I would set up KASAN and syzkaller reproducers before writing the first line of a fix, because a bug you cannot reproduce is a bug you cannot defend in review. And I would read the previous fixes in the file before touching it, every time. Two of my CVEs existed only because an earlier fix bounded half a loop and left the other half open.
The kernel is enormous, but it is also a machine that accepts good work from strangers. Fifty patches in, my recommendation is unchanged from patch one: pick a driver nobody loves, read all of it, and fix what you find.
References
- My first upstream patch, the nf_conntrack_irc fix: ef6400ca25a1 on kernel.org
- Full research note for CVE-2026-80603, the parse_dcc() off-by-one that started all of this
- The wifi parser cluster, headlined by the WPS stack overflow: CVE-2026-90018
- The hp-bioscfg audit, written up as a field report: The hp-bioscfg field report, covering CVE-2026-81011 through CVE-2026-81014
- The Ethos-U command stream series, CVE-2026-53169 through CVE-2026-53173
- The Bluetooth lifetime bug series, CVE-2026-63945 through CVE-2026-63947
- All 26+ CVE research notes: the CVE index
- Everything merged under my name: Muhammad Bilal on kernel.org