How to fix memory corruption in embedded firmware

Last updated:

Memory corruption covers a family of weaknesses — out-of-bounds writes and reads (CWE-787, CWE-119/121/122), classic buffer overflow (CWE-120), use-after-free (CWE-416), integer overflow feeding an overflow (CWE-190), NULL pointer dereference (CWE-476) and type confusion (CWE-843) — where a program accesses memory outside what it was actually allocated. MITRE's CWE-787 page (read 2026-10-29) defines the core case plainly: the product "writes data past the end, or before the beginning, of the intended buffer." These are the bugs most likely to turn a crash into remote code execution, and CISA's joint statement with the FBI, NSA and international partners puts them at roughly 70% of the vulnerabilities Microsoft and the Chromium project assign a CVE to each year — the single largest weakness family most C/C++ embedded codebases carry.

1. Detect

  • Run the firmware's parsers and protocol handlers (the code paths that touch network input: HTTP, MQTT, proprietary device protocols, file-format parsers for configuration or firmware images) through a fuzzer with AddressSanitizer or an equivalent memory-safety sanitizer enabled — this catches out-of-bounds access and use-after-free even on inputs that don't visibly crash the unsanitized build.
  • Run static analysis with CWE-787/120/416/190 rule coverage across the full C/C++ codebase, prioritising code that parses attacker-reachable input over internal-only utility code.
  • Audit every call to an unbounded copy or formatting function (strcpy, sprintf, gets, raw memcpy with an attacker-influenced length) for a missing or incorrect bounds check.
  • Check build flags: confirm stack canaries, FORTIFY_SOURCE//GS-equivalent hardening and ASLR/PIE are actually enabled in the shipped firmware image, not just the development build.

2. Fix

  1. Prefer a memory-safe language for new or rewritten code. MITRE's CWE-787 Requirements-phase mitigation is explicit: "use a language that does not allow this weakness to occur ... or provides constructs that make this weakness easier to avoid." For embedded teams, this usually means scoping new network-facing parsers in Rust or a similarly memory-safe language rather than C, even if the rest of the codebase stays C/C++.
  2. Publish — and work from — a memory-safety roadmap. CISA's joint statement asks manufacturers to set memory safety as a strategic priority and publish a roadmap with timelines for adopting memory-safe languages, including for open-source dependencies pulled into the firmware supply chain.
  3. Where C/C++ must stay, use vetted libraries and bounded functions. Replace strcpy() with strncpy() (checking NULL-termination behaviour when source and destination sizes match) or an equivalent bounded API; use a vetted safe-string library (for example SafeStr or Microsoft's Strsafe.h) instead of hand-rolled buffer arithmetic.
  4. Validate every buffer size and length against specification before use, following MITRE's "accept known good" input-validation strategy, rather than relying only on pattern-matching for known-bad input.
  5. Enable compiler and OS hardening in the shipped build: stack canaries, FORTIFY_SOURCE//GS, and ASLR/PIE. These do not fix the underlying bug, but they convert many exploitable overflows into a detected crash, raising the attacker's cost.
  6. For use-after-free specifically, null out freed pointers immediately after free()/delete, and consider a hardened allocator that detects use-after-free in test builds.

3. Verify

  • Re-run the fuzzer with sanitizer instrumentation against the patched code for a sustained period (not just the single original proof-of-concept input), since memory bugs often have multiple reachable variants in the same handler.
  • Confirm static-analysis findings for the relevant CWE(s) are resolved in the patched build, not merely suppressed by an inline annotation.
  • Keep every proof-of-concept and fuzzer-found crash input in a permanent regression corpus.

4. Ship an update

Memory-corruption fixes are usually self-contained but should still go through your full secure-update path — signed image, integrity verification, rollback protection — since these bugs are frequently remotely triggerable with no authentication. Confirm with your firmware SBOM exactly which shipped product lines and versions share the vulnerable parser or library before scoping the release.

5. Disclose via PSIRT

Memory-corruption CVEs are routinely assigned a high CVSS score because they so often lead to remote code execution; prioritise disclosure and patch timelines accordingly, and check CISA KEV/FIRST EPSS exploitation signals for similar classes — see our KEV + EPSS prioritization guide. Run the fix through your coordinated vulnerability disclosure process (see CVD process explained and, if you don't have one yet, PSIRT setup for a hardware company), and check the EU Cyber Resilience Act's 24-/72-hour reporting duty if there is evidence of active exploitation (CRA 24h/72h reporting).

SoftWSP's firmware scanning identifies memory-unsafe code paths and unbounded copy patterns directly in a binary or source tree, mapped to the specific CWE: see device & firmware security.

This guide summarises public MITRE CWE and CISA guidance; it is not a substitute for fuzzing, sanitizer-instrumented testing, or a full source-code review, and does not constitute legal advice.

Frequently asked questions

What counts as a memory-corruption weakness?

This guide covers the family of CWEs where a program writes or reads outside the bounds of the memory it was given: CWE-787 (Out-of-bounds Write), CWE-120 (Buffer Copy without Checking Size of Input, i.e. classic buffer overflow), CWE-119/121/122 (restriction-of-buffer, stack and heap overflow variants), CWE-416 (Use After Free), CWE-190 (Integer Overflow, which frequently triggers a downstream overflow), CWE-476 (NULL Pointer Dereference) and CWE-843 (Type Confusion). MITRE defines CWE-787 simply as the product "writes data past the end, or before the beginning, of the intended buffer"; CWE-120 is "the product copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer." Both pages read as of 2026-10-29.

How common are memory-safety bugs, really?

CISA's joint statement with the FBI, NSA and international partners ("The Urgent Need for Memory Safety in Software Products," first published 20 September 2023, revised 6 December 2023) cites several vendors' own figures: Microsoft states that "approximately 70 percent of their vulnerabilities" are memory safety issues; the Chromium project reports "around 70%" of its serious security bugs are memory safety problems; Mozilla found 32 of 34 critical/high severity bugs in one analysis were memory-related; and Google Project Zero attributed 39 of 58 (67%) of the zero-days it tracked in one year to memory corruption.

What is MITRE's top recommended fix?

CWE-787's own Requirements-phase mitigation is language selection: "use a language that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid" — i.e. prefer a memory-safe language (Rust, Go, Java, C#) for new code, especially network-facing parsers, over C/C++. CISA's memory-safety statement asks manufacturers to go further and publish a memory-safety roadmap: a public timeline for transitioning to memory-safe languages and for memory-safety work across the supply chain, including open-source dependencies.

We cannot rewrite our C/C++ codebase overnight — what fixes exist short of that?

MITRE's CWE-787/120 mitigations, short of a language change, are: use a vetted library or framework that avoids the raw pointer-arithmetic pattern; replace unbounded copy functions with length-aware equivalents ("replace strcpy with strncpy", and double-check that `strncpy()` still NULL-terminates correctly, since it may not when the destination equals the source size); validate every buffer-allocation size against a specification before use (an "accept known good" strategy) rather than only hunting for malicious patterns; and compile with hardening flags — Microsoft's `/GS`, GCC/Fedora's `FORTIFY_SOURCE`, stack canaries (StackGuard/ProPolice) and ASLR/PIE — which do not remove the bug but raise the cost of exploiting it and can turn a silent corruption into a detected crash.

How do we verify a memory-safety fix actually holds?

Run the firmware's fuzzing harness (or add one, scoped to the fixed parser/handler) with sanitizer instrumentation (AddressSanitizer or an equivalent) enabled, which catches out-of-bounds and use-after-free access even when the program would otherwise appear to run correctly, then confirm no crash or sanitizer report on the original proof-of-concept input and keep that input in the fuzz corpus for regression.

Sources

  1. MITRE CWE-787: Out-of-bounds Write
  2. MITRE CWE-120: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')
  3. CISA, FBI, NSA and international partners: The Urgent Need for Memory Safety in Software Products (20 September 2023, revised 6 December 2023)