03 — Secure C and CVE Literacy

Every serious C engineer can read a CVE writeup, find the vulnerable line, and describe the fix in one paragraph. This is not “security researcher” territory; it is baseline literacy for anyone who ships C. study partners at Cloudflare, Nvidia, and the Valkey team will ask you about CWE-120 by name. You should be able to answer without googling.

This file covers (1) the CWE top-25 filtered to what applies to C, (2) five concrete 2024–2026 CVEs with the vulnerable pattern named, and (3) how bugs actually become CVEs — the pipeline from fuzzer crash to public disclosure.

The C-specific CWE cheat sheet

MITRE publishes an annual “Top 25 Most Dangerous Software Weaknesses.” Roughly half apply anywhere; the ones below are the C-and-C++-only class. Memorize these codes; they show up in every serious postmortem.

CWE

Name

One-line pattern

Typical fix

CWE-119

Improper restriction of ops within buffer

buf[i] with i from input

Bounds check + size_t

CWE-120

Classic buffer overflow (“strcpy without check”)

strcpy(dst, user_input)

strncpy + explicit NUL, or snprintf, or a length-prefixed string type

CWE-121

Stack-based buffer overflow

char buf[64]; strcpy(buf, argv[1]);

Same, plus -fstack-protector-strong

CWE-122

Heap-based buffer overflow

malloc(n); memcpy(p, src, m>n)

Recompute size after validation

CWE-125

Out-of-bounds read

buf[len] when len == length

Off-by-one hunt

CWE-190

Integer overflow / wraparound

malloc(count * sizeof(x)) where count is untrusted

__builtin_mul_overflow or <stdckdint.h> (C23)

CWE-191

Integer underflow

size_t n = a - b; where b > a

Signed pre-check

CWE-401

Missing free (memory leak)

Any malloc without matching free on error paths

goto cleanup: pattern

CWE-415

Double free

free(p); ...; free(p);

Set p = NULL after free

CWE-416

Use-after-free

free(p); return p->x;

Same; run ASan

CWE-457

Use of uninitialized variable

int x; use(x);

-Wuninitialized + MSan

CWE-476

NULL pointer dereference

p = malloc(...); *p = ...; without check

Check every malloc return

CWE-590

Free of non-heap memory

free(&stack_var)

Only free what malloc gave you

CWE-134

Uncontrolled format string

printf(user_input)

printf("%s", user_input)

CWE-787

Out-of-bounds write

buf[i] = v with i >= n

Bounds check every index

One rule that eliminates 60% of these: never call a str* function that does not take a length. strcpy, strcat, sprintf, gets — banned. Use strncpy + explicit NUL termination, strlcpy where available (glibc 2.38+), snprintf, fgets. Modern C23 also gives you <stdckdint.h> for checked integer math.

Five recent CVEs to actually read

Do not just note the CVE number. Open each writeup, find the vulnerable commit, read it, and write two sentences in your notes about what would have prevented it. The pattern-recognition builds fast.

CVE-<phone_number_or_numberic_id_or_random_id_24> — Palo Alto PAN-OS pre-auth stack overflow (May 2026)

Pre-authentication stack buffer overflow in the User-ID Auth Portal (captive-portal parsing) leading to remote code execution as root. Textbook CWE-121: a fixed-size stack buffer, an unchecked memcpy sized from a client-controlled header field. The fix was a length check before the copy plus stack canaries getting exercised on the crash path. This is why -fstack-protector-strong and -D_FORTIFY_SOURCE=3 are not optional.

Primary source: NVD entry at <https://nvd.nist.gov/vuln/detail/CVE-<phone_number_or_numberic_id_or_random_id_25>>. Palo Alto advisory at security.paloaltonetworks.com.

CVE-<phone_number_or_numberic_id_or_random_id_26> — Samsung Exynos Wi-Fi driver NL80211 overflow (April 2026)

Buffer overflow in the Samsung Exynos kernel Wi-Fi driver via the NL80211 netlink interface. Affects Exynos 850, 980, 1280, 1330, 1380, 1480, 1580, and W-series 920–1000. Classic CWE-120: a driver copies attribute data into a fixed-size buffer without validating the netlink attribute’s length field. No authentication required beyond “be on the same Wi-Fi network.” This is the pattern in every kernel driver CVE from the last decade.

Lesson: driver code that trusts a length field from userland or from a peer device is CVE-inevitable. Always cap with a min(user_len, sizeof(buf)) and then treat the excess as an error, not a truncation.

CVE-<phone_number_or_numberic_id_or_random_id_27> — SolarWinds Web Help Desk RCE (2025–2026 exploitation)

Disclosed 2025, still under active exploitation in 2026 (per CISA KEV additions). Java-family root cause but the C lesson is a meta-lesson: the fix ships and users don’t apply it. This is why long-term-support branches and backport discipline (see 04_portability_and_abi.md) matter as much as the initial patch. A CVE is not resolved when the maintainer commits the fix; it is resolved when the last customer upgrades.

CVE-<phone_number_or_numberic_id_or_random_id_28> — Windows exFAT heap-based buffer overflow (June 2024)

Crafted USB stick triggers a heap overflow in the Windows exFAT driver on mount. CWE-122. The interesting angle is the attack surface: the physical layer. File systems, USB stacks, and Bluetooth stacks are attack surface even before login. This is why kernel-mode C is a different discipline: your input is not “a user”; it is “anyone within physical reach.”

CVE-<phone_number_or_numberic_id_or_random_id_29> — Windows RRAS unauth network RCE (CVSS 8.8)

Heap-based buffer overflow in Windows Routing and Remote Access Service, remotely triggerable, no auth. CWE-122. Same shape as the Exynos CVE at a different layer of the stack. Read the Microsoft advisory then look at the CVSS decomposition (AV:N/AC:L/PR:N) — network vector, low complexity, no privileges required. That decomposition is why unauth network C code is scored so harshly and why fuzzing network parsers is where security teams spend most of their budget.

The bonus curl CVE reading list

curl publishes every one of its CVEs at https://curl.se/docs/vuln.html with the vulnerable commit hash, the fixing commit, and Daniel Stenberg’s own postmortem. This is the single best CVE-education resource in open source. Read three of them; you will spot the same three patterns.

Specifically look for the ones with subtitles containing “buffer overflow,” “double free,” or “integer overflow” — those are your textbook cases.

From fuzzer crash to public CVE — the pipeline

Understanding this pipeline demystifies security work. It is not magic; it is a workflow.

  1. Fuzzer finds crash. OSS-Fuzz or a private harness produces a minimized input that segfaults or triggers ASan.

  2. Triage. Is it exploitable (memory corruption) or a plain DoS (assert failure)? Exploitable → CVE track.

  3. Private disclosure. Reporter emails the project’s security contact (security@curl.se, security@valkey.io). PGP if the project asks for it.

  4. Fix developed in private branch. Maintainer + reporter iterate.

  5. CVE ID requested from CNA. MITRE for most projects; some (Red Hat, Google, GitHub) are their own CNAs.

  6. Coordinated release. Fix commit + advisory + CVSS score + credit — published together, usually on a scheduled day.

  7. Public disclosure. oss-security mailing list, NVD entry, distro backports.

Curl’s typical timeline from private disclosure to public CVE is 2–4 weeks; Google’s Project Zero uses a hard 90-day deadline; Linux kernel varies from days to months. Read one full timeline (curl publishes them) and you will never treat a security bug the same way.

The oss-fuzz-gen story — why LLMs matter here

In November 2024, Google’s oss-fuzz-gen project used LLMs to generate fuzz harnesses and found 26 new vulnerabilities across mature projects, including a 20-year-old bug in OpenSSL. The lesson is not “AI is magic”; the lesson is that the harness is often the bottleneck — the fuzzer had the muscle to find these bugs years ago, but no human had written a harness that exercised the vulnerable API. Writing harnesses is now high-leverage security work, and something you can do without being a security researcher.

What most people get wrong about this

They think “secure C” is about knowing arcane exploitation techniques (ROP chains, heap grooming). It isn’t. Secure C is about knowing the fifteen patterns above and never writing them. 99% of CVEs in C are one of those fifteen patterns, and 99% of those are caught by -Wall -Wextra -D_FORTIFY_SOURCE=3 + ASan + a fuzz harness. The exploitation lore is fun; the boring hygiene is what actually keeps you off the CVE list.

The second thing they get wrong: they think “if it’s a memory-safe language, we’re fine.” You still ship a C dependency somewhere — SQLite, zlib, libpng, OpenSSL, libc itself. Knowing how those fail is part of the job even if your surface code is Rust or Go.


Return to README.md · Next: 04_portability_and_abi.md