There’s a misconception in some of the memory safety discussion that “memory-safe” equals “Rust.” It doesn’t.
The NSA’s list of memory-safe languages includes: Rust, C#, Go, Java, Ruby, Swift, Python, and JavaScript.
If you’re running a modern tech stack, you’re likely already using memory-safe languages for most of your code. The question is: what about the parts that aren’t?
The Language Decision Matrix
Already Using Java/C#/.NET?
You’re already memory-safe for that code. Focus on identifying any C/C++ dependencies or native extensions.
Already Using Go?
Same story. Go’s garbage collector handles memory management. Your roadmap might already be mostly complete.
Already Using Python/JavaScript?
The language itself is memory-safe. Watch out for native extensions (NumPy’s C core, Node.js addons, etc.).
Have Systems Code in C/C++?
This is where Rust becomes relevant - when you need manual memory management for performance but want safety guarantees.
When Rust vs Other Memory-Safe Languages
Choose Rust When:
- You need C/C++ level performance
- You’re replacing existing C/C++ code
- You need predictable latency (no GC pauses)
- You’re building systems software, embedded, or kernel code
Choose Go When:
- You’re building networked services
- Simplicity and fast development matter
- GC pauses are acceptable
- Team productivity is prioritized over micro-optimization
Keep Java/C# When:
- You have existing investment and expertise
- Your performance requirements are met
- The ecosystem and tooling serve your needs
The “Hidden C” Problem
Even if your application code is Python or Java, you might have C/C++ in:
- Native extensions - Image processing, ML libraries, crypto
- Database clients - Native protocol implementations
- FFI calls - Calling into system libraries
- Build tools - Compilers, bundlers, etc.
Your memory safety roadmap should identify these dependencies and assess their risk.
The Pragmatic Approach
- Inventory your codebase by language
- Identify C/C++ components and native dependencies
- Risk-assess based on attack surface exposure
- Plan migration or isolation for high-risk components
- Default to memory-safe languages for new code
For most organizations, this isn’t a massive Rust rewrite. It’s identifying the 5-10% of code that needs attention.
Alex’s point about “hidden C” is particularly relevant for data and ML teams.
The Python Paradox
Python is memory-safe. But almost everything that makes Python fast for data work is written in C:
- NumPy - The core is C and Fortran
- Pandas - Built on NumPy
- PyTorch/TensorFlow - C++ cores with Python bindings
- SciPy - C, C++, and Fortran
- OpenCV - C++ library
- scikit-learn - Cython and C
When you import these libraries, you’re running C/C++ code with all its memory safety implications.
Does This Matter for Compliance?
It depends on your threat model and regulatory context:
If you’re processing untrusted input:
A malformed image processed by OpenCV or a crafted tensor passed to PyTorch could trigger memory bugs in the underlying C/C++ code.
If you’re running in a sandbox:
Containerized ML inference with limited privileges has a smaller blast radius.
If you’re just doing internal analytics:
The risk profile is different than internet-facing services.
The Practical Assessment
For ML teams thinking about memory safety roadmaps:
- Identify your C/C++ dependencies -
pip show <package> often reveals the implementation language
- Assess input sources - Does untrusted data touch these libraries?
- Evaluate alternatives - Some Rust-based ML libraries exist (but the ecosystem is immature)
- Consider isolation - Sandboxing, process separation, capability restrictions
The Honest Answer
For most data teams, replacing the NumPy/PyTorch stack isn’t realistic in any timeline. The CISA guidance acknowledges this:
“A balanced approach acknowledges that MSLs are not a panacea and that transitioning involves significant challenges, particularly for organizations with large existing codebases.”
Document the dependencies, assess the risk, implement mitigations, and track upstream security patches. That’s a defensible position.
The enterprise Java perspective is relevant here because many large organizations already have significant Java investment.
Java’s Memory Safety Position
Java has been memory-safe since 1995. The JVM handles:
- Automatic memory allocation and garbage collection
- Array bounds checking
- Null pointer safety (exceptions, not undefined behavior)
- Type safety enforcement
For compliance purposes, Java applications are memory-safe by default.
The JNI Caveat
The exception is Java Native Interface (JNI) - when Java code calls into C/C++ libraries:
- Database drivers with native components
- Performance-critical operations
- Platform-specific integrations
- Legacy library bindings
These JNI boundaries need examination in your memory safety assessment.
What This Means for Compliance
If your stack is primarily Java:
- Audit JNI usage - Where does Java call native code?
- Assess risk - Are those native calls in security-sensitive paths?
- Consider alternatives - Pure Java implementations may exist
- Document justification - If native code is necessary, explain why
The Enterprise Java Advantage
Organizations with Java-heavy stacks actually have an easier compliance story:
- “Our business logic is written in memory-safe Java”
- “Native dependencies are limited to X, Y, Z with documented risk assessment”
- “New development defaults to Java/Kotlin with pure-Java libraries where possible”
That’s a credible memory safety roadmap without a major technology shift.
The Modernization Opportunity
For teams considering Kotlin - it’s also memory-safe (runs on JVM) and offers modern language features while maintaining Java interoperability. It’s another path forward within the existing ecosystem.
Alex’s framework is helpful. Let me add the security trade-off perspective across different memory-safe languages.
Memory Safety Is Not the Only Security Property
All the NSA-listed languages prevent memory corruption. But they differ in other security-relevant properties:
Type Safety Strength
- Rust, Java, C#: Strong static typing catches errors at compile time
- Python, JavaScript, Ruby: Dynamic typing - more runtime errors possible
- Go: Moderate - has some dynamic elements
Concurrency Safety
- Rust: Compile-time data race prevention
- Go: Runtime race detection, but races are possible
- Java: Thread-safe constructs, but requires discipline
- Python: GIL limits true concurrency anyway
Null Safety
- Rust: No null (uses Option type)
- Kotlin, Swift: Null-safe by default
- Java, Go: Nullable references, null pointer exceptions possible
The Security Hierarchy
From a security posture perspective, I’d roughly rank:
- Rust - Memory-safe + data race-safe + null-safe
- Java/C#/Go - Memory-safe, other guarantees vary
- Python/JavaScript/Ruby - Memory-safe but dynamic typing introduces different risks
This doesn’t mean “use Rust for everything.” It means understand what each language prevents and what it doesn’t.
The Audit Conversation
When security reviewers ask about memory safety:
- “We use Java” is a good answer for memory safety specifically
- But then they might ask about null pointer exceptions, thread safety, dependency management
Memory safety is one vulnerability class. A comprehensive security posture considers the full picture.
Practical Recommendation
For the CISA roadmap specifically, focus on memory safety - that’s what they’re asking about. But use this as an opportunity to assess your broader language security posture.
Different vulnerability classes, different mitigations. Memory safety is just the one getting government attention right now.