When a Model File Becomes More Than Data

A model file is usually described as a collection of learned weights: numbers that an application reads and uses for inference. That description is incomplete when the file is serialized with Python’s pickle format. This series examines pickle-based model loading as a supply-chain security boundary: what the file contains, which framework path interprets it, what a scanner can actually see, and how much damage a load can cause. The practical goal is not to make every model look suspicious. It is to give readers a sound way to decide when a model artifact can be trusted, when it needs a restricted loader, and when it should be opened only inside an isolated environment.

The key shift is simple but consequential: the security question is not only “What bytes did I download?” It is also “What code will interpret those bytes, and what can that interpretation do?” Once that question is visible, model hubs, scanners, framework loaders, and runtime sandboxes become parts of one system rather than separate conveniences.

A model file can carry behavior

Serialization turns an in-memory object into a representation that can be saved or shared. Deserialization reverses that process. With a passive data format, the reverse operation is intended to recover values: arrays, numbers, and perhaps a few structural labels. Pickle is more flexible. It represents object reconstruction as a sequence of instructions that an unpickler interprets. Those instructions can refer to Python functions and classes, and reconstruction can invoke behavior associated with them.

That flexibility explains pickle’s appeal. A developer can save a complicated Python object without designing a separate schema for every class, nested structure, or compatibility detail. The same convenience creates a security boundary. If an attacker changes the serialized instruction stream, a victim may trigger behavior simply by loading the object. The risk is not that every pickle file is malicious, nor that every instruction is dangerous. The risk is that the format gives deserialization capabilities that a consumer may mistake for ordinary data reading.

The distinction matters especially for machine-learning artifacts. A model may contain tensors, configuration, optimizer state, and other objects. The consumer often wants only the learned values, but a general-purpose loader may be capable of reconstructing much more. The file extension does not settle that question. A file ending in .pt, .pth, or another familiar suffix tells a consumer how an ecosystem commonly names the artifact; it does not, by itself, prove that loading will be an inert operation.

flowchart LR P["Model publisher"] --> H["Model-sharing platform"] H --> S["Scanner and provenance signals"] S --> C["Consumer application"] C --> L["Framework loader"] L --> B["Execution boundary"] B --> R["Runtime or host impact"]

The USENIX Security study describes this as a model-poisoning problem because a malicious publisher can place a crafted artifact where consumers expect useful models. The consumer does not need to run a visibly suspicious program. The loading step itself can be the trigger. That is why “download,” “scan,” and “load” are three different trust events. Downloading obtains bytes. Scanning attempts to characterize them. Loading asks a framework to interpret them, possibly with privileges and dependencies that the scanner did not reproduce.

Why a local format became a supply-chain problem

Pickle’s risk predates model hubs, but the surrounding ecosystem changes its reach. Public platforms make it easy for a publisher to distribute a model to people who did not write the model code and may not know which serialization path produced it. A consumer may select an artifact because it has a useful name, a familiar organization, or a high download count. The platform may display a scanner result. The application then fetches the artifact and passes it to a framework loader, often as part of an otherwise routine setup step.

Three parties therefore share one boundary. Publishers choose how an artifact is serialized and what dependencies it expects. Platforms host the artifact and may inspect it before or after publication. Consumers decide whether and where to load it. A weakness at one point can change the risk at the next: a publisher can exploit a loader behavior; a platform can fail to recognize that behavior; a consumer can treat a warning or a missing warning as a safety certificate.

The ecosystem did not adopt pickle because developers wanted an attack surface. It adopted it because the format handles complex Python objects compactly and fits established workflows. The PyTorch serialization documentation describes saving tensors and Python objects, and its current checkpoint format keeps pickled metadata alongside tensor storage. Compatibility with older checkpoints and framework-specific behavior also matters. Replacing a deeply embedded format is not a single code change: it affects publishers, libraries, tooling, model archives, and the consumers who depend on them.

That trade-off is real, but it should be described accurately. Convenience does not make the behavior safe. Nor does the existence of a safer alternative instantly remove every old artifact from circulation. The result is a transition problem: the ecosystem must keep functioning while moving trust decisions away from unconstrained object reconstruction wherever possible.

This is also why broad statements such as “the model is only weights” can mislead. Some workflows do use a state dictionary or another representation focused on parameter values. Others preserve a richer Python object graph, place pickle data inside an archive, or route an old format through compatibility code. Two files that both look like model checkpoints may therefore invoke different code paths. The security boundary belongs to the loader workflow, not to the label attached to the file.

What scanners can and cannot establish

The central subject of this series is not merely pickle’s weakness. It is the relationship between the code that loads a model and the systems that try to inspect it before loading. The USENIX study separates that relationship into two connected surfaces.

The model-loading surface is the set of routes by which a framework reaches pickle data. A route may pass through an archive, compression, a legacy representation, or several layers of packaging. If a scanner understands only the most common route, it may never inspect the representation that the framework will eventually deserialize. The study reports 22 exploitable pickle-based loading paths across five foundational AI/ML frameworks. That is a scoped research result, not a census of every framework, but it makes the architectural point concrete: “scan the model file” is not a complete instruction until the relevant loading path is known.

The risky-function surface is the behavior available once deserialization begins. A scanner may recognize obvious primitives such as direct code execution, process creation, or filesystem access. But a function that wraps or reaches an equivalent capability may not have a conspicuous name. The study reports 133 exploitable gadgets and found that even its best-performing evaluated scanner failed to detect a large majority of them. Again, the number belongs to the study’s tools and test conditions. The broader lesson is that a denylist of familiar names is not the same thing as a complete model of behavior.

Hugging Face’s official pickle-scanning guidance illustrates the right role for inspection. Its tooling can read pickle instructions and display imported objects without executing the file, which is valuable because a platform should not need to run an untrusted artifact merely to examine it. But the same guidance says the method is not foolproof and leaves users responsible for judging unfamiliar imports. Signed commits can help establish who supplied a file; they do not prove that the file is harmless. Provenance, static inspection, and runtime safety answer different questions.

The USENIX study also found scanner-side exceptions: a scanner or one of its parsing dependencies may fail on an input that a target framework can still load. Its reported evaluation identified nine such cases, seven of which bypassed all the scanners it tested. That result does not mean every scanner fails on every malformed artifact. It does mean that “the scanner did not flag it” can hide several states: the path may have been recognized and judged clean; the path may not have been supported; or analysis may have stopped before a verdict.

Those distinctions lead to the boundary-first question that will guide the rest of this series: What exactly is being interpreted, which loader path will interpret it, what evidence covers that path, and what happens if the evidence is wrong? A safer format can reduce executable reconstruction. A restricted loader can narrow what is permitted. A scanner can reveal suspicious structure. A signature can strengthen provenance. A sandbox can contain the consequences. None of these controls, considered alone, proves that an artifact is safe.

The evidence also calls for proportion. In a small, time-bounded measurement of the 100 most-downloaded Hugging Face models as of December 6, 2025, the study found no evidence that the newly described unscannable paths, exception-directed techniques, or gadget exploits were already present in those models. It did find benign uses of unfamiliar APIs and a false-positive case, while noting prior malicious model samples elsewhere. The defensible conclusion is neither complacency nor panic: scanner and loader gaps are demonstrated capabilities that deserve correction, but the cited measurement does not show that every popular model is compromised.

The next step is to look closely at the mechanism hidden behind the word “load.” Once readers can see how pickle reconstructs objects and how a PyTorch checkpoint separates metadata from tensor storage, the later questions about archives, scanner coverage, and safer loading boundaries become concrete rather than abstract.

References