The policy guidance is clear: adopt memory-safe languages. The reality is messier.
Many organizations have C/C++ code that can’t be replaced any time soon:
- Legacy systems that are too large to rewrite
- Vendor dependencies you don’t control
- Performance requirements that require manual memory management
- Embedded/IoT constraints where alternatives don’t exist
- Regulatory mandates requiring certified codebases
So what do you do if you can’t just stop using C/C++?
Strategy 1: Harden What You Have
Compiler Mitigations:
- Stack canaries (
-fstack-protector-strong) - ASLR and PIE (
-fPIE) - Control-Flow Integrity where available
- Stack clash protection
Runtime Mitigations:
- Address Sanitizer (ASan) in testing
- Memory Sanitizer (MSan) for uninitialized reads
- Undefined Behavior Sanitizer (UBSan)
These don’t eliminate vulnerabilities, but they make exploitation harder.
Strategy 2: Isolate Dangerous Code
Process Isolation:
Run C/C++ components in separate processes with minimal privileges. A compromise in the sandboxed process can’t directly escalate.
Containerization:
Limit the blast radius through container security boundaries.
WebAssembly (Wasm) Sandboxing:
Compile C/C++ to Wasm and run in a sandboxed runtime. Growing adoption for plugins and extensions.
Hardware Isolation:
Trusted Execution Environments (TEEs), enclaves, or separate hardware for sensitive operations.
Strategy 3: Incremental Migration
You don’t have to rewrite everything at once.
New Code in Safe Languages:
Every new component is memory-safe. The unsafe codebase doesn’t grow.
FFI Wrappers:
Wrap C/C++ libraries in safe language bindings with careful boundary validation.
Modular Replacement:
Identify discrete components and replace them one at a time. The Strangler pattern applied to memory safety.
Strategy 4: Enhanced Testing
If you can’t prevent bugs, find them before attackers do.
Fuzzing:
libFuzzer, AFL, Honggfuzz - continuous fuzzing of C/C++ code.
Static Analysis:
CodeQL, Coverity, PVS-Studio - catch common patterns at build time.
Penetration Testing:
Regular security assessments focused on memory safety.
What to Put in Your Roadmap
If full migration isn’t feasible, your roadmap should document:
- Why C/C++ remains necessary (specific justification per component)
- What mitigations are in place
- What isolation boundaries exist
- What testing ensures ongoing safety
- What the long-term migration path looks like (even if it’s 10+ years)
A roadmap that says “we can’t migrate but here’s how we’re managing risk” is better than silence.