Cognithesis — an instrument for curiosity

cognithesis.← Return to the engine
← Field notes

OBSERVATION / 03

Who Cut the Cheese

The JIT compilation time bomb in modern ML frameworks: machine learning's whole stack generates executable code at runtime from user-controllable inputs, and the data-governance story built on top of it is a compliance fiction.

In this entry

Executive Summary

Modern machine learning frameworks harbor a fundamental architectural flaw that would make any security engineer’s hair stand on end: they’re all running JIT compilation engines that generate executable code at runtime based on user-controllable inputs. This architectural smell—the “cheese” in our title—permeates every layer of the modern ML stack. But the real crisis isn’t just security—it’s data governance. Organizations deploying these frameworks to process sensitive data have no meaningful way to audit actual data access, guarantee deletion, or provide multi-tenancy isolation. They’re checking compliance boxes based on API call logs while the actual data operations happen in dynamically generated machine code that leaves no audit trail and respects no boundaries.

To understand how we got here, we need to talk about Python—the Trojan horse that normalized terrible security practices and convinced an entire generation of developers that “easy” meant “no guardrails.” And we need to talk about what this means for anyone who actually cares about data governance, because spoiler alert: it’s a disaster.

The Data Governance Apocalypse

Let’s start with what matters most: if you’re running modern ML frameworks in production with sensitive data, you need to understand that you have no data governance. You have data suggestions. Let’s be precise about what this means.

The Compliance Nightmare

Every major data protection regulation—GDPR, CCPA, HIPAA, SOC 2, ISO 27001—requires you to demonstrate control over your data. They want answers to questions like:

  • Who accessed this data?
  • When was it accessed?
  • What operations were performed on it?
  • How do you prevent unauthorized access?
  • How do you ensure data is deleted when required?
  • What’s your incident response plan when something goes wrong?

Now try answering those questions when your ML stack looks like this:

Question: “How do you prevent unauthorized access to sensitive data during model training?”

Honest Answer: “We run Python code that uses JIT compilation to dynamically generate and execute machine code based on model architecture and weights. This generated code directly accesses training data in memory with no verification or audit trail. The code doesn’t exist until runtime, varies based on user-controllable inputs, and leaves no trace of what data was actually accessed. An attacker who corrupts the right data structure during JIT compilation can influence what code gets generated and executed. But we logged the API calls, so we’re good, right?”

The Audit Trail Illusion

Here’s what kills me: organizations think they have data governance because they can see API calls in their logs. “Look, we tracked who called model.fit() with this dataset!”

Congratulations, you’ve logged the front door. Meanwhile:

  • The JIT compiler is generating thousands of lines of executable code that never touches your audit log
  • That generated code is directly accessing your training data in memory
  • The code generation process itself is influenced by model architecture and weights—user-controllable inputs
  • Any corruption of the JIT compilation process can redirect data access
  • The Python interpreter provides zero boundaries between different security contexts
  • Model weights themselves can influence code generation, creating side channels

You’re not auditing the actual data access—you’re auditing a Python function call that triggers a Rube Goldberg machine of unverifiable code generation that eventually accesses your data in ways you can’t observe, control, or audit.

Technical Reality: JIT-generated code doesn’t go through instrumentation hooks. There’s no way to intercept memory accesses in generated machine code without hardware support (Intel Processor Trace, ARM CoreSight). Your audit infrastructure sees the API boundary, not the actual operations. It’s like having security cameras in the lobby while the vault is being emptied through a tunnel.

The “Right to be Forgotten” Problem

GDPR gives users the right to have their data deleted. Simple request, catastrophic implementation in modern ML:

  1. User requests deletion of their data
  2. You delete it from your database (great!)
  3. But wait—was it used to train a model?
  4. Is it embedded in model weights somewhere?
  5. Did the JIT compiler cache code that was optimized for that specific data?
  6. Is it in some intermediate tensor that got swapped to disk during training?
  7. Is it in Python object references that the GC hasn’t collected?
  8. Did it leak into shared memory that other processes can access?
  9. Nobody knows.

You can’t prove deletion because you never had control in the first place. You have no memory safety guarantees, no isolation boundaries, and no way to verify that sensitive data hasn’t leaked into some JIT code cache, been serialized into a checkpoint file, or been copied by Python’s promiscuous memory model.

Technical Reality: Python’s lack of memory safety means data can be copied arbitrarily. The del statement doesn’t zero memory—it just decrements reference counts. Model weights may encode training data (membership inference attacks prove this). JIT caches persist in unpredictable ways. Garbage collection is non-deterministic. There is no “secure delete” in Python’s memory model.

You cannot comply with deletion requirements. Full stop. You can delete database rows, but you cannot guarantee that data isn’t lingering in model weights, JIT caches, checkpoint files, swap space, or any of the thousand places Python’s lack of memory safety lets it propagate.

The Multi-Tenancy Disaster

Modern ML platforms often run multiple customers’ workloads on shared infrastructure. From a data governance perspective, this requires strong isolation:

  • Customer A’s data must never leak to Customer B
  • Each customer must have separate, verifiable security boundaries
  • Resource exhaustion attacks must be impossible
  • Side-channel attacks must be mitigated

Now implement this with Python and JIT compilation:

Memory Isolation? Python doesn’t have it. Everything’s in the same address space, accessible via reflection and introspection. The GC can touch anything. There are no security boundaries. One customer’s objects can reference another’s through any number of Python’s dynamic features.

JIT Code Isolation? The generated code runs in the same process, with the same permissions, accessing the same memory. If Customer A can influence the JIT compiler (hint: they can, via model architecture and weights), they can potentially affect Customer B’s execution or probe for Customer B’s presence.

Timing Side Channels? JIT compilation timing depends on what code paths were previously compiled. Customer A can probe for the presence of Customer B’s models by observing JIT timing. This isn’t theoretical—it’s been demonstrated in academic literature on JIT side-channels (Spectre, Meltdown, and numerous browser JIT exploits).

Resource Limits? Python’s GIL (Global Interpreter Lock) makes proper resource isolation nearly impossible. The JIT compiler can consume unbounded memory during code generation. There’s no way to guarantee fair scheduling or prevent resource exhaustion attacks. One customer’s computationally expensive model can starve other customers’ workloads.

Technical Reality: True multi-tenancy requires process isolation at minimum, ideally with hardware-enforced boundaries (containers, VMs, separate cores). Python + JIT frameworks provide none of this. Your “isolation” is hoping that customers don’t figure out how to exploit shared state.

The Supply Chain Problem

Here’s a fun one: your ML model is trained on proprietary data. You want to deploy it. How do you ensure the model doesn’t leak training data?

Traditional software: You compile it, review the binary, perform static analysis, verify there are no data leaks. Done.

Python ML framework:

  • The model weights themselves might encode training data (membership inference, model inversion attacks demonstrate this)
  • The JIT compiler generates new code at inference time based on those weights
  • That generated code is different for every model architecture and parameterization
  • There’s no static analysis possible because the code doesn’t exist until runtime
  • The code generation process itself might leak information via timing or memory allocation patterns
  • Pre-trained models from model zoos might contain backdoors that influence JIT behavior

You literally cannot verify that your model isn’t leaking data because the code that runs at inference time doesn’t exist until inference time, and it’s generated by a process you don’t control based on weights you can’t fully analyze.

Real-world implications: Organizations download pre-trained models from HuggingFace, fine-tune them on proprietary data, and deploy them. They have no way to verify what that model actually does at inference time. The JIT compiler generates code based on model architecture (from untrusted source) and weights (encoding proprietary training data). This is a supply chain nightmare.

The Incident Response Black Hole

When (not if) a breach happens, you need to answer:

  • What data was compromised?
  • How did the attacker gain access?
  • What was the scope of the breach?
  • How do we prevent it from happening again?

With traditional compiled systems, you have:

  • Core dumps that show execution state
  • Binary analysis of what code was running
  • Memory protections that limit exploit scope
  • Clear boundaries between code and data
  • Static analysis tools that can verify properties
  • Reproducible builds for verification

With Python + JIT ML frameworks, you have:

  • A pile of dynamically generated machine code that’s already been freed
  • No separation between code and data
  • No way to reconstruct what code was actually executing
  • No boundaries to limit exploit propagation
  • An interpreter that lets you introspect and modify literally everything
  • Non-deterministic behavior (JIT decisions depend on runtime state)
  • No reproducible execution (different runs generate different code)

Your incident response plan is basically “burn it down and hope we got everything.” You can’t perform forensics on code that no longer exists. You can’t reproduce the breach because the JIT might generate different code. You can’t verify containment because there are no boundaries.

The Regulatory Reckoning

Here’s what’s coming: regulators are going to catch up. Eventually, some organization is going to suffer a major breach because of JIT compilation vulnerabilities or data governance failures in their ML stack. The forensics will reveal that:

  1. They had no way to verify what code was executing
  2. They had no memory safety boundaries
  3. They had no audit trail of actual data access
  4. They had no way to enforce data deletion
  5. They had no isolation between security contexts
  6. They couldn’t reconstruct what happened during the breach

And the regulators are going to ask: “Why did you choose this architecture?”

The answer—“everyone else was doing it”—is not going to fly. Neither is “but it made our benchmarks fast.” When you’re processing healthcare data, financial records, or personal information, you have a duty of care. You don’t get to choose convenience over security.

Current compliance gap: Existing regulations focus on data encryption at rest/transit, access logs, and audit trails—not code execution guarantees or memory safety. Organizations are compliant with current regulations while being vulnerable to risks those regulations don’t yet address. It’s like complying with seatbelt laws when the regulation was written before airbags existed. You’re compliant, but you’re missing modern safety features.

The regulations will catch up. The question is whether your organization gets caught on the wrong side of history.

What This Means for Organizations Right Now

If you’re running modern ML frameworks in production with sensitive data, here’s your reality check:

You have no data governance. You’re hoping that data stays where you think it is, but you have no technical guarantees. Your audit logs show API calls, not actual data access. Your security boundaries are social constructs, not technical enforcement.

You cannot comply with deletion requirements. You can delete database rows, but you cannot guarantee that data isn’t lingering in model weights, JIT caches, checkpoint files, or any of the thousand places Python’s lack of memory safety lets it propagate.

You cannot provide multi-tenancy guarantees. Your isolation is based on Python’s honor system and hoping that JIT compilation doesn’t create side channels. That’s not a security model—it’s a prayer.

You cannot perform effective incident response. When something goes wrong, you won’t be able to determine scope, reconstruct events, or prove containment. Your forensics capability is approximately zero.

You are accepting liability you don’t understand. Every day these systems run in production is another day of regulatory, legal, and reputational risk that you probably haven’t properly quantified.

This isn’t fear-mongering. This is the technical reality of the architecture you’re running. The absence of public exploits reflects the current maturity of ML deployment, not the security of the architecture. As ML moves to production and handles sensitive data, this attack surface will be explored. The question is whether we fix it proactively or reactively after incidents.

The Python Problem: Understanding the Architectural Smell

To understand how we got into this data governance nightmare, we need to understand Python—the source of the architectural smell that permeates modern ML. Python’s rise in ML wasn’t an accident—it was the path of least resistance. But let’s be honest about what Python actually is: a query language masquerading as a programming language, held together by C/C++ libraries doing all the actual work.

The Syntax Shell Game

Every professional language—C, C++, Java, C#, JavaScript—shares common syntactic DNA. Curly braces. Explicit typing (or at least clear type semantics). Semicolons. A consistent object model. When you learn one, you’re learning transferable patterns that apply across the ecosystem.

Python? Python decided it was special. Whitespace-significant syntax that breaks copy-paste. Duck typing that turns runtime errors into exciting production surprises. An object model bolted on as an afterthought after the language was already in the wild. It’s not “easy to learn”—it’s “easy to write code that runs,” which is emphatically not the same thing.

The dirty secret: Python doesn’t teach you programming. It teaches you how to glue together C++ libraries while avoiding all the discipline that makes those libraries work. It’s the programming equivalent of learning to drive in a bumper car—sure, you’re moving around, but you’re not learning anything that translates to a real vehicle.

The Bad Habits Factory

Python actively reinforces anti-patterns:

  • No compile-time checking: “It’ll probably work” becomes your deployment strategy
  • Implicit everything: Variable scope? Who knows! Type errors? Runtime problem!
  • Performance nihilism: “Just call NumPy” becomes the answer to everything
  • Security? What security?: When your entire execution model is “eval() but with extra steps”

You know what Python developers don’t learn? Memory management. Type safety. Compilation pipelines. Security boundaries. All the foundational concepts that matter when you’re building actual systems. They learn to be consumers of C++ libraries, not programmers.

Object-Oriented as Afterthought

Let’s talk about Python’s object model, or as I like to call it, “what happens when you add classes to a scripting language because everyone else has them.”

Historical fact: Python 0.9.0 (1991) had no classes. Python 1.0 (1994) added them. The object model was retrofitted onto an existing procedural language, and it shows.

Python’s OOP is like a toupee on a bald man—technically present, but fooling nobody. Everything’s public (underscore conventions are social, not technical). Multiple inheritance creates method resolution orders that require a PhD to understand. self has to be explicit everywhere because the language can’t figure it out. Metaclasses exist, but they’re basically a cry for help.

Compare this to Java, C++, or C#, where object-oriented principles are baked into the language design from day one. Those languages have actual access controls, coherent inheritance models, and type systems that catch your mistakes before runtime. Python has __init__ and hopes for the best.

Why Python Won ML (And Why That’s Terrifying)

So how did this Frankenstein’s monster of a language become the de facto standard for machine learning? Simple: it avoided the cops.

Python succeeded in ML precisely because it had no adult supervision:

  • No VM security model: Unlike Java, there’s no sandbox, no bytecode verification, no security manager
  • No platform restrictions: Unlike iOS/macOS, there’s no code signing, no executable memory restrictions
  • No compilation boundaries: Unlike compiled languages, there’s no separation between code and data
  • No type safety: Unlike literally every other serious language, runtime is when you find out you messed up
  • No memory safety: Everything is mutable, reflectable, accessible. The pickle module is literally arbitrary code execution as a feature.

This wasn’t a feature set—it was the absence of features. The ML community chose Python because it would let them do whatever they wanted, including fundamentally unsafe things.

But let’s be fair about the real reason: Python wasn’t chosen for security properties—it was chosen for developer velocity and library ecosystem. The problem is that “good for research prototypes” became “production deployment standard” without anyone asking whether that transition made sense.

The C/C++ Reality

Here’s the uncomfortable truth: everything actually good about Python for ML is written in C or C++:

  • NumPy: Core is C, linear algebra is Fortran (BLAS/LAPACK)
  • PyTorch: ~45% C++, ~15% CUDA, only ~25% Python
  • TensorFlow: ~50% C++, ~30% Python, ~10% Go
  • Pandas: Uses Cython (compiles to C) for all performance-critical paths
  • SciPy: C, C++, and Fortran wrappers

Python is a query language. You’re not programming in Python—you’re writing database-style queries that dispatch to precompiled C++ kernels. The Python is just the glue, and it’s not even good glue. It’s the duct tape holding together a high-performance engine, and everyone pretends this is normal.

This matters for data governance because Python’s role as “glue code” means there’s no single point of control. Data flows through Python into C++ kernels, back to Python, into JIT-generated code, back to Python, into GPU memory, back to Python. There’s no coherent data flow model, no single audit point, no way to track where data actually goes.

The JIT Security Nightmare: The Core of the Smell

Now we get to the mechanism that makes data governance impossible: JIT compilation. This is the heart of the architectural smell—the “cheese” that’s been cut. Because Python has no guardrails, ML frameworks built on it inherited that “move fast and break things” mentality—including breaking fundamental security principles.

The Modern ML Framework Architecture

Let’s be precise about what’s actually happening in PyTorch/TensorFlow/JAX:

PyTorch: Uses TorchScript JIT compiler. When you call torch.jit.trace() or torch.jit.script(), it:

  1. Analyzes your Python code and model architecture
  2. Generates an intermediate representation
  3. Optimizes that IR based on model structure and weights
  4. Allocates writable memory pages
  5. Generates native machine code into those pages
  6. Uses mprotect() to mark pages as executable
  7. Jumps into the generated code

TensorFlow: XLA (Accelerated Linear Algebra) JIT compiler:

  1. Captures your computation graph
  2. Generates LLVM IR based on graph structure
  3. LLVM compiles to native code
  4. Code is loaded into executable memory
  5. Executes with direct memory access to your data

JAX: Heavily JIT-based via XLA. The @jit decorator is fundamental:

  1. Traces your Python function
  2. Generates XLA HLO (High-Level Optimizer) representation
  3. Compiles to machine code
  4. Caches compiled code for reuse
  5. Generated code directly accesses array data

Critical point: In all cases, model architecture and weights—user-controllable inputs—directly influence what code gets generated. This is fundamentally different from browser JITs or the JVM.

The Attack Surface (Technical Deep-Dive)

Let’s be specific about the attack surface we’ve created:

1. User-Controlled Code Generation: Model architecture and weights directly influence JIT output. An attacker who can craft malicious model weights can influence what machine code gets generated. This is not theoretical—it’s the design.

2. Predictable Memory Layouts: JIT buffers need to be findable and executable. They follow predictable allocation patterns. An attacker with memory disclosure can locate them.

3. Known Compilation Patterns: The JIT compiler backends are open source. An attacker can study exactly how model architectures map to generated code and craft inputs accordingly.

4. No Code Verification: Generated code executes without any integrity checks, signing, or verification. There’s no “this code came from a trusted source” guarantee.

5. Persistent Code Caches: JIT code sticks around in memory, creating stable attack targets. The caches are indexed by model architecture hash, which an attacker can predict.

6. Python’s Dynamic Nature: Reflection, introspection, __dict__ manipulation, dynamic imports—Python provides numerous ways to influence JIT compiler behavior before code generation.

A novice hacker who understands this architecture doesn’t need to find a traditional ROP chain—they just need to corrupt the right data structure during JIT compilation and the framework will helpfully generate and execute their payload for them.

Technical nuance: Modern systems use W^X (write XOR execute) protection, so JIT compilers allocate pages as writable, generate code, then use mprotect() to mark them executable (not both simultaneously). However, this doesn’t eliminate the attack surface—it just moves it. If you can corrupt JIT compiler state before the W^X transition, you control what gets written and subsequently executed. The JIT pipeline itself becomes the vulnerability.

Why Nobody Called the Cops: Java and Apple

ML frameworks systematically avoid platforms with mature security models. This isn’t conspiracy—it’s architectural incompatibility.

Java and the JVM:

Counter-argument you’ll hear: “But the JVM itself uses JIT! HotSpot, GraalVM—they all JIT compile!”

The critical difference: The JVM’s JIT compiler is part of the trusted computing base. It’s a signed, verified system component. User code doesn’t control JIT behavior. You can’t upload a JAR file that manipulates what machine code HotSpot generates.

In ML frameworks, model architecture and weights—user-provided, potentially untrusted data—directly influence JIT code generation. This is architecturally different. To run in Java’s security model, user-controlled JIT would need to be part of the trusted computing base, which is unsound.

Additionally, the JVM has three decades of security infrastructure: SecurityManager, bytecode verification, sandbox boundaries, access controls. ML frameworks have none of this. They can’t run under Java’s security model because their entire architecture violates it.

Apple Platforms:

iOS: Prohibits executable heap entirely (since iOS 4.3). Apps must use pre-compiled code or Apple’s JavaScriptCore (which has special entitlements as part of the OS). ML frameworks that rely on JIT compilation cannot run on iOS. Period.

macOS: Technically permits executable heap with proper entitlements, but sandboxed apps can’t do it. Unsigned code can’t mark heap executable without security warnings. ML frameworks work on development machines (which are more permissive) but can’t be properly deployed in secure, sandboxed environments.

Core ML: Notice that Apple’s own ML framework uses ahead-of-time compilation exclusively. Models are compiled during export, not at runtime. This is not a performance choice—it’s a security requirement.

The pattern: ML frameworks work on permissive development platforms but fail when you try to deploy them in secure, production-grade environments. This isn’t accidental—their architecture is fundamentally incompatible with strong security models. This is the architectural smell we keep detecting—the refusal to work in environments with proper oversight.

Comparison: Legitimate vs. User-Controlled JIT

Let’s be clear about the distinction:

Browser JITs (V8, SpiderMonkey, JavaScriptCore):

  • Part of trusted computing base
  • Extensive security research and fuzzing
  • Sandboxed execution environment
  • Site isolation, process-per-tab
  • Can’t be influenced by user data to generate arbitrary code
  • Decades of hardening

JVM/CLR JITs:

  • Part of trusted computing base
  • Bytecode verification before JIT
  • SecurityManager/Code Access Security
  • User code can’t manipulate JIT behavior
  • Formal verification efforts

ML Framework JITs:

  • User-controllable via model architecture
  • Influenced by model weights (untrusted data)
  • No sandboxing whatsoever
  • No verification of generated code
  • Minimal security research
  • Python provides no boundaries

The problem isn’t JIT per se—it’s user-controllable JIT without sandboxing. When model weights can influence code generation and there’s no security boundary, you’ve created an attack surface that doesn’t exist in mature JIT systems.

Counter-Arguments and Responses

Let’s address the objections we’ll hear:

“But there are no public exploits!”

Response: Fair challenge. Let’s be honest about this.

There are no known public exploits specifically targeting ML framework JIT pipelines for code injection. This doesn’t mean safe—it means:

  1. Attackers haven’t focused here yet: They’re busy with web/mobile where the ROI is higher
  2. ML isn’t ubiquitous enough: Not enough high-value targets to make developing exploits worthwhile
  3. Requires sophistication: Need to understand both JIT internals and ML frameworks
  4. Limited deployment: Most ML still runs in research environments, not production

But we have precedent:

  • Spectre/Meltdown: JIT timing side-channels
  • Numerous browser JIT exploits (hence massive security investment)
  • Academic literature on JIT side-channels
  • Model backdoors and poisoning attacks (demonstrated)

The trajectory is concerning:

  • ML is moving to production at scale
  • Models increasingly come from untrusted sources (HuggingFace, model zoos)
  • Cloud ML services handle multi-tenant workloads
  • Pre-trained models are supply chain attack vectors
  • Financial incentives for exploitation are growing

The absence of public exploits reflects the current maturity of ML deployment, not the security of the architecture. As ML handles sensitive data, this attack surface will be explored. The question is whether we fix it proactively or reactively after incidents.

“AOT compilation isn’t practical for ML”

Response: Actually, it’s increasingly common and proven at scale.

Already in production:

  • TensorFlow Lite: AOT compilation for mobile
  • PyTorch Mobile: Pre-compiled models
  • ONNX Runtime: AOT optimization paths
  • Apple’s Core ML: Fully AOT (required by iOS)
  • NVIDIA TensorRT: Ahead-of-time optimization
  • AWS Inferentia: AOT compilation required

“But we need dynamic shapes!”: You can AOT compile with dynamic dimensions. You generate code that handles variable sizes (parameterized loops, dynamic dispatch), not JIT compile different code for each size. This is exactly what Core ML and TensorRT do.

Performance: AOT compilation often outperforms JIT because you can spend more time optimizing without runtime pressure. TensorRT’s AOT optimization produces faster code than PyTorch’s JIT in most benchmarks.

Revised position: AOT compilation is not only practical—it’s already the deployment model for every mobile ML framework, every embedded ML system, and increasingly for cloud deployments. The JIT-everywhere approach is a development convenience, not a technical necessity.

“Large companies use this safely”

Response: True, and instructive about what “safely” requires.

Google, Meta, Microsoft—they all run ML in production using these frameworks. How?

  1. Massive infrastructure investment: Container isolation, network segmentation, hardware security modules, extensive monitoring, dedicated security teams
  2. Risk acceptance: Large organizations have risk management—they know the issues and accept them as business tradeoffs
  3. Compensation through process: Manual security reviews, restricted model sources, air-gapped training environments
  4. They’re moving away: Notice Google’s push for Go and Rust. Meta’s investments in Rust. They’re not doubling down on Python for critical infrastructure.

The key point: What works for Google with 1000-person security teams doesn’t work for mid-size companies deploying ML. Large tech companies can compensate for Python’s security limitations through extraordinary infrastructure investment. This doesn’t validate the security model—it demonstrates that Python requires extraordinary measures to deploy safely.

Most organizations don’t have Google’s resources. They’re deploying ML frameworks in environments that can’t compensate for these architectural flaws. That’s the problem.

“Current regulations don’t require this level of security”

Response: Current regulations don’t address ML-specific risks because they predate widespread ML deployment.

HIPAA, GDPR, CCPA—these were written when ML was primarily academic. They focus on:

  • Data encryption at rest/transit
  • Access logging
  • Audit trails
  • Breach notification

They don’t address:

  • Code execution guarantees
  • Memory safety requirements
  • JIT compilation risks
  • Model weight data encoding
  • Side-channel attacks

Organizations are compliant with current regulations while exposed to risks those regulations don’t yet address. This is temporary. Regulators will catch up—probably after high-profile incidents.

The question: Do you want to be proactive or reactive? Do you want to fix this before regulations require it, or after a breach forces you to?

The Performance Theater

The defense you’ll hear: “But JIT is fast! And Python is easy! And we need to move quickly in ML!”

Let’s address each:

The JIT Performance Myth

The performance gains from JIT compilation in ML workloads are real, but they’re not magical—they come from avoiding interpreter overhead and generating optimized assembly. You know what else does that? Ahead-of-time compilation.

The dirty secret: JIT makes development fast for framework authors. They can iterate quickly, patch performance problems dynamically, avoid the complexities of proper compiler toolchains, and ship features faster. They’ve prioritized their own convenience over their users’ security.

Performance comparison: In benchmarks where both are properly optimized, AOT often wins:

  • TensorRT (AOT) typically beats PyTorch (JIT) on inference
  • Core ML (AOT) outperforms TensorFlow Lite (mixed) on iOS
  • ONNX Runtime (AOT paths) competitive with native frameworks

JIT’s advantage is flexibility during development, not raw performance in production.

The Python “Ease” Myth

Python isn’t easy—it’s permissive. It’s easy to write code that runs. It’s easy to avoid thinking about types, memory, or compilation. It’s easy to never learn the fundamentals that every other language forces you to understand.

But “easy to write” and “easy to maintain,” “easy to secure,” or “easy to govern” are completely different properties. Python optimizes for the first five minutes of development at the expense of everything that comes after.

The “Move Fast” Fallacy

“We need to move fast” is not an excuse for abandoning data governance. You know what’s slow?

  • Breach disclosure processes
  • Regulatory fines and investigations
  • Class action lawsuits
  • Rebuilding customer trust
  • Rewriting your entire ML pipeline when regulations catch up

Moving fast is great. Moving fast while processing sensitive data without proper controls is negligence.

The Alternative Universe

This isn’t unsolvable. You can have your cake and eat it too:

1. AOT Compilation

Generate code during model training/export, not inference. Now you can:

  • Analyze the actual code that will run
  • Audit data access patterns
  • Verify no data leaks
  • Sign and seal the compiled artifact
  • Track provenance from training to deployment

Already proven at scale: Core ML, TensorRT, TensorFlow Lite, ONNX Runtime.

2. Verified Execution Environments

Run on platforms with security guarantees:

  • JVM: Sandbox, bytecode verification, security manager, mature tooling
  • WebAssembly: Memory isolation, capability-based security, growing ML support
  • Restricted Python: Run under seccomp, AppArmor, SELinux with proper confinement

3. Hardware Acceleration Without JIT

  • Metal: Apple’s GPU framework, no executable heap required
  • Neural Engine: Dedicated ML hardware, AOT compiled models
  • TPUs: Google’s ML accelerators, fixed compilation pipeline
  • CUDA: While supporting JIT, also supports pre-compiled PTX

4. Data-Centric Security Architecture

Design ML pipelines with data governance as first-class:

  • Data lineage tracking: Cryptographic provenance from source to model
  • Access controls at data layer: Not application layer
  • Verifiable deletion: Memory-safe languages, secure erasure guarantees
  • Audit at data access: Not API boundaries
  • Isolation by design: Process-level minimum, hardware-level preferred

5. Memory-Safe Languages

Use languages designed for production systems:

  • Rust: Memory safety without GC, zero-cost abstractions, growing ML ecosystem
  • C++ with modern practices: RAII, smart pointers, sanitizers, static analysis
  • Swift: Memory safety, performance, Apple platform integration
  • Even Java/C#: Better than Python for production data governance

The ManifolDB Approach

We don’t do this because we’re paranoid—we do it because we understand the requirements of production data systems. When you’re building systems that will run in production environments handling valuable data, you don’t get to handwave away fundamental security principles because “everyone else is doing it.”

Our architecture:

  • AOT compilation for all production code paths
  • Memory-safe core (C++20 with strict modern practices, Rust for critical paths)
  • Hardware acceleration through Metal/Neural Engine (no executable heap)
  • Cryptographic data lineage tracking
  • Process-level isolation minimum
  • Comprehensive audit logging at data access layer
  • Verifiable deletion guarantees

We can answer the compliance questions because we designed the system to make those answers possible.

Conclusion: Identifying the Smell

The title of this memo asks “Who Cut the Cheese?” The answer is clear: the entire modern ML stack has an architectural smell that’s been present from the beginning. This smell—this fundamental design flaw—comes from two compounding choices:

  1. Python as lingua franca: A language with no security model, no type safety, no compilation boundaries, no memory safety
  2. JIT everywhere: Dynamic code generation with user-controlled inputs influencing executable code

The result? Frameworks that violate basic security principles, running in an interpreter that provides zero protection, processing untrusted data and generating executable code based on it.

But the real crisis isn’t just security—it’s data governance. Organizations are deploying these frameworks to process sensitive data while having:

  • No audit trail of actual data access (just API calls)
  • No way to guarantee deletion (data propagates uncontrollably)
  • No multi-tenancy isolation (Python has no boundaries)
  • No incident response capability (can’t reconstruct what happened)
  • No supply chain verification (can’t analyze what models actually do)

They’re checking compliance boxes based on API logs while the actual data operations happen in dynamically generated machine code that leaves no trace and respects no boundaries.

These frameworks run in production. They process data protected by GDPR, HIPAA, CCPA. They make decisions affecting people’s lives. They handle financial records, healthcare data, personal information. And they’re doing it with an architecture that makes data governance impossible.

The emperor has no clothes, and he’s writing Python while processing your medical records.

When the regulatory reckoning comes—and it will—“but everyone else was doing it” won’t be an acceptable answer. Neither will “we didn’t know.”

Because now you do.

The only question is whether you’ll fix it before or after the breach. The architectural smell has been identified. The question is: who’s going to clean it up?


“If your threat model doesn’t include ‘attacker with a novice understanding of JIT internals,’ you don’t have a threat model. If your language doesn’t have a type system, you don’t have a language. If you can’t prove data deletion, you don’t have data governance.”

← Back to field notes