Full Report
Memory safety is a common topic when discussing programming languages. However, it's not well-defined what is being talked about. Compiler-based static analysis. Compiler-based code creation that is expected to be memory-safe. Runtime mechanisms like garbage collection and array bounds checking. Security hardening. Aka, just trying to make the system safe against attackers. Lisp, traditionally memory safe language, has features that may allow for memory corruption. C/C++, traditionally considered a memory unsafe language, can also be done safely. Static analysis tools, strict code review and good runtime detectors do well. This demonstrates that memory safety isn't the sole responsibility of the compiler or the runtime - it's a coordinated effort. The author of this post comes from a project called Hardened Linux that's goal is to create a version of Linux that is resistant to compromise. The lifecycle of vulnerability has a few stages: Identifying a bug and assessing whether it can be exploited. Writing a PoC. Adapting the PoC into a stable exploit. Digital arms dealers integrating it into a weaponizing framework. Most of the effort in preventing vulnerabilities is around a bug not existing in the first place. However, there are other ways to keep users safe besides removing bugs - we could just make them unexploitable to get RCE. Fil-C is a customization on Clang/LLVM compiler that catches many memory safety vulnerabilities by doing a combination of garbage collection and capability checking on pointer accesses made by Epic Games. Buffer overflows, type confusions, use after frees and many classes of vulnerabilities can be prevented using this. Another strategy is around mitigation techniques at the hardware or software level. NX, CET, etc. are good examples of this. Many vulnerabilities would have been harder to exploit with some of these protections, if not outright impossible. Every protection is another roadblock that makes it less likely that exploitation will occur. Practically speaking, I like this take on simply rewriting software: "Rewriting software and libraries using memory-safe languages is an expensive endeavor. If you have thoroughly considered this approach and decide to proceed, please consider rewriting them in Lisp/Scheme." Great post on the practically on exploiting systems!
Analysis Summary
# Best Practices: Multi-Layered Memory Safety
## Overview
These practices address the mitigation and prevention of memory corruption vulnerabilities (e.g., buffer overflows, use-after-free, type confusion). Instead of viewing memory safety as a binary choice between "safe" and "unsafe" languages, these guidelines promote a coordinated effort across the software development lifecycle (SDLC), utilizing compiler tools, runtime detectors, and hardware mitigations to create systems resistant to compromise.
## Key Recommendations
### Immediate Actions
1. **Enable Sanitizers:** Integrate AddressSanitizer (ASan), UndefinedBehaviorSanitizer (UBSan), and LeakSanitizer (LSan) in all debug and test builds for C/C++ projects.
2. **Activate Hardware Protections:** Ensure existing hardware-level mitigations like NX (No-Execute), CET (Control-flow Enforcement Technology), and ASLR are enabled at the OS level.
3. **Audit Dependencies:** Identify critical libraries (e.g., crypto or networking) that lack sanitization or automated testing.
### Short-term Improvements (1-3 months)
1. **Implement Continuous Fuzzing:** Integrate fuzz testing (e.g., Syzkaller for kernels or libFuzzer for libraries) into the CI/CD pipeline.
2. **Adopt Secure Distribution Standards:** Migrate production systems to community-driven distributions with professional maintenance records (e.g., Debian) that prioritize stable security backports.
3. **Static Analysis Integration:** Deploy compiler-based static analysis (Clang Static Analyzer) to catch potential issues before they reach the runtime.
### Long-term Strategy (3+ months)
1. **Compiler Customization:** Evaluate advanced compiler-based safety mechanisms like **Fil-C** (LLVM/Clang customization) which uses garbage collection and capability checking on pointer accesses to prevent RCE.
2. **Targeted Rewriting:** For critical, high-risk components, evaluate rewriting in memory-safe languages. (Author suggests Lisp/Scheme for those seeking robust, non-mainstream alternatives).
3. **Vulnerability Lifecycle Hardening:** Implement a defense-in-depth strategy that focuses on making bugs "unexploitable" rather than just "non-existent."
## Implementation Guidance
### For Small Organizations
- Focus on the "lowest cost approach": Always enable sanitizers in test builds.
- Rely on upstream security patches from stable, well-maintained Linux distributions.
### For Medium Organizations
- Implement automated regression testing combined with fuzzing for all proprietary code.
- Adopt a strict code review process specifically focused on pointer arithmetic and memory management in C/C++.
### For Large Enterprises
- Develop or integrate custom compiler toolchains (like Fil-C) to enforce memory safety at the binary level.
- Contribute to open-source fuzzer coverage (e.g., Syzkaller filters) for the specific kernel subsystems used in your infrastructure.
## Configuration Examples
While specific code snippets depend on the environment, the following compiler flags and tools are recommended:
- **GCC/Clang Sanitizers:**
`CFLAGS="-fsanitize=address,undefined -g -O1" LDFLAGS="-fsanitize=address"`
- **Hardware Mitigations:**
Enable `CONFIG_X86_CET` and `CONFIG_SHADOW_CALL_STACK` in the Linux kernel configuration where supported.
- **Pointer Safety:**
Explore the **Fil-C** project for LLVM to automate capability checking and garbage collection for C code.
## Compliance Alignment
- **NIST SSDF (Secure Software Development Framework):** Aligns with "Produce Secure Software" by automating vulnerability detection.
- **ISO/IEC 27001:** Supports technical compliance and vulnerability management requirements.
- **CIS Benchmarks:** Aligns with system hardening and compiler-level security settings.
## Common Pitfalls to Avoid
- **The "Safe Language" Fallacy:** Assuming a language is 100% safe. Even Lisp or Java can have memory corruption vulnerabilities if the interpreter or runtime is poorly implemented.
- **Testing Without Sanitizers:** Running regression tests without sanitizers enabled often fails to catch latent memory bugs that only trigger under specific exploit conditions.
- **Over-reliance on Rewriting:** Abandoning existing stable systems to "rewrite everything" is often too expensive and introduces new logic bugs; hardening existing code is frequently more practical.
## Resources
- **HardenedLinux Project:** hxxps://hardenedlinux[.]org/
- **Fil-C Manifesto:** hxxps://github[.]com/pizlonator/llvm-project-deluge (Memory safety for C)
- **Syzkaller (Kernel Fuzzer):** hxxps://github[.]com/google/syzkaller
- **Clang Static Analyzer:** hxxps://clang-analyzer.llvm[.]org/