The Hidden Paths Between a Model File and the Loader
A model-loading API often looks like one operation—give it a path and receive a model—but the security-relevant work may pass through several representations first. This chapter maps that loading surface: the user-facing entry point, archive or compression layers, compatibility branches, and the eventual pickle sink. The practical value is a way to inspect a model workflow by its transformations and interfaces rather than trusting the filename or the most common path.
The previous chapter established that pickle is an interpreter for object reconstruction. The question now is how an artifact reaches that interpreter. A scanner and a framework may both claim to handle “PyTorch files” or “model checkpoints” while following different routes through the artifact. The difference is not a technical footnote. It determines whether the scanner sees the same representation that the consumer’s framework will execute.
A loading API is a pipeline, not a single door
A direct call to pickle.load is easy to imagine: open a stream, pass it to an unpickler, and receive an object. AI/ML frameworks usually need to do more. They support model files that package weights with configuration, compress large payloads for transport, preserve older formats, or combine several containers for a deployment workflow. Their public API therefore acts as the front of a pipeline.
A useful decomposition has four parts. First is the loading entry point, the function or method a consumer calls. Second are intermediate transformations, such as opening an archive, decompressing a stream, decoding a representation, or selecting a member file. Third is the compatibility logic, which chooses an older or alternate path when the artifact does not match the preferred format. Fourth is the pickle sink, the point at which the resulting bytes are handed to a pickle interpreter or an equivalent low-level loader.
Each part owns a different decision. The entry point decides what kind of artifact it believes it has received. An archive handler decides which member becomes the next input. A decompressor decides whether the resulting stream is valid and what bytes are exposed downstream. Compatibility code decides whether to preserve a legacy behavior rather than reject the file. The pickle sink interprets the final representation. A scanner that models only the first decision can miss the security significance of the later ones.
The USENIX study identified five foundational frameworks with pickle-loading call chains: NumPy, Joblib, PyTorch, TensorFlow/Keras, and NeMo. Its audit found 22 distinct exploitable pickle-based loading paths, organized into seven categories. The number is important as an empirical signal, but its scope is equally important. It is the result of the authors’ framework selection, static analysis, manual auditing, and exploit verification; it is not a claim that 22 is the total number of paths in the AI/ML ecosystem.
The result nevertheless changes the mental model. If a scanner recognizes only the best-known direct or standard archive path, it is not scanning “the model” in the broad sense. It is scanning one interpretation of the model. A compressed or nested representation may still be fully supported by the consumer’s framework even when the scanner does not know how to reach the embedded pickle. The artifact’s outer appearance remains familiar while the route through the system changes.
The diagram is not an inventory of every format. It shows why coverage is a control-flow problem. Several routes can converge on the same sink, but a scanner may implement only one route or may make a different choice at a branch. The framework’s state is the artifact as transformed so far; the scanner’s state is its own partial representation. Those states do not automatically stay synchronized.
Packaging and compatibility create additional trust transitions
Archives and compression are often introduced for legitimate engineering reasons. A model may need to travel with configuration or tokenizer data. Large tensor files benefit from compact transport. A deployment package may need a predictable bundle for a serving system. Legacy support can be necessary because users have years of existing checkpoints. These requirements are not suspicious by themselves. They explain why the loading surface is broad enough to matter.
They also add transitions that must be validated separately. When an archive is opened, the loader must decide which entry to read and how to interpret it. When compressed data is expanded, the loader must accept a decompressor and its failure behavior. When containers are nested, the output of one parser becomes the input of another. When legacy support is invoked, old assumptions about layout and object reconstruction become active again. Each transition is a place where framework behavior and scanner behavior can diverge.
The study’s examples make the pattern concrete without requiring a catalogue. Joblib supports multiple compression routes before pickle deserialization. PyTorch can process its standard archive representation as well as raw or deprecated tar-based model forms. Other frameworks can package models in archives that lead through another loader before reaching pickle. A consumer may therefore call one high-level API while the framework selects a path based on headers, internal members, options, or compatibility rules that are invisible in the filename.
This is the meaning of a polyglot or multi-representation artifact in the security discussion. The same extension can be associated with different internal structures, and different structures can activate different code. A suffix is a hint for selecting a loader, not a proof of which loader branch will run. Even the absence of an unfamiliar suffix does not show that the artifact has only one representation.
PyTorch’s current documentation offers a useful example of the boundary. A typical checkpoint is a ZIP64 archive, and its data.pkl entry contains the pickled object metadata while tensor storages occupy separate entries. A tool that opens the archive but never analyzes data.pkl has not reached the part that describes Python-side reconstruction. A tool that extracts data.pkl but assumes every checkpoint has the same surrounding layout may miss legacy or wrapper behavior. The components are individually understandable; the security property emerges from how they are connected.
That connection also determines who owns state. The publisher chooses the structure and may intentionally package several components. The framework loader owns the transitions that turn the package into a model object. The scanner owns a separate interpretation, with its own parser and error handling. The consumer owns the environment in which the final loader runs. A scanner’s knowledge does not automatically transfer to the framework, and a framework’s ability to load does not mean a platform has successfully inspected the path.
Consider how the same logical payload can acquire different security properties as it moves through these interfaces. In a direct path, the bytes presented to the loader may already have the shape expected by the pickle interpreter. In a compressed path, the consumer supplies an artifact whose first meaningful operation is decompression; only the resulting stream resembles the serialized object. A scanner that recognizes ordinary pickle but does not reproduce that decompression step may classify the outer file as an unknown or harmless format without ever seeing the relevant instructions. The framework, however, may perform the transformation automatically because compression is a supported feature rather than an exceptional condition.
An archive path adds a selection decision. The package can contain tensor storage, metadata, configuration, or several candidate members. The loader must choose which member carries the object representation and which members are treated as supporting data. A nested path repeats that decision: one container yields another, and the inner archive yields the pickle-bearing content. Each layer may be valid on its own, yet the combination creates a route that a scanner must model end to end. Looking only at the outer archive answers whether the package has a familiar shape, not whether the eventual member is safe to interpret.
Legacy support changes the starting assumptions rather than removing the boundary. A current framework can retain code for older model layouts because users still need to open existing files. That fallback may use a different archive reader, a different set of expected entries, or a different route to object reconstruction. The result is a practical asymmetry: a format can be deprecated for saving while remaining accepted for loading. A publisher may not be able to create it through the current interface, but a consumer’s loader may still process it when it arrives from elsewhere.
These examples are not claims that compression, archives, or old formats are malicious. They are examples of hidden state transitions. Security review has to ask what each transition accepts, what it produces, and whether the next component receives the same bytes and metadata that the scanner analyzed. A scanner that rejects an unsupported path may be behaving correctly as a narrow tool; the unsafe outcome appears when the surrounding platform treats “unsupported” as equivalent to “clean” or allows the consumer to load the artifact without an additional control.
The study’s path count also illustrates why this problem cannot be solved by adding one more filename rule. A path is defined by the chain of operations between a public loader and a pickle sink. Two chains can share an extension but differ in their intermediate transformations. Two chains can use different extensions but converge on the same pickle behavior. The meaningful unit for security coverage is therefore the chain, including its branches and failure conditions. That is harder to maintain than a suffix list, but it is closer to the behavior that creates risk.
A framework audit can make this chain visible by starting at documented loading APIs and tracing where their inputs go. Static analysis can identify candidate routes, while manual inspection is needed to understand archives, compression, compatibility options, and the conditions under which a route is actually reachable. The USENIX study used that combination and then verified the exploitability of the paths it reported. Its methodology does not guarantee completeness, but it shows why a purely surface-level scan is unlikely to discover every route.
For a platform or application owner, the practical questions are concrete. Which high-level loading calls are used in production? Which archive and decompression handlers can they invoke? Are legacy paths still enabled? Does the scanner recognize nested or compressed representations, and does it record an explicit unsupported state when it does not? If the scanner and framework use different readers, do their acceptance and error conditions differ? These questions preserve the system’s actual interfaces instead of replacing them with a generic statement that “the model was scanned.”
Why the filename and common path are insufficient
A common-path scan is useful when it matches the path the consumer will run. It can identify ordinary pickle instructions, known imports, or a recognized archive layout without executing the model. The problem begins when “common” is treated as “complete.” An artifact may enter through a compatibility branch, carry compressed content, or place the relevant pickle inside a nested container. The scanner may accept the outer file, classify it as an unsupported variant, or stop before the embedded representation is visible.
The same issue appears in reverse. A scanner may know that an outer archive is present but not model how the target framework interprets a malformed or unusual member. It may apply generic archive rules while the framework uses custom extraction logic. A scanner verdict then describes the scanner’s parser, not necessarily the consumer’s execution path. That distinction will matter in the next chapter, where parser exceptions become a separate failure surface.
The study’s 22-path result should therefore be read as an architectural warning rather than a memorization exercise. It demonstrates that loading paths are numerous enough to be security-relevant and that hidden paths can be found by following data flow from official loading APIs to pickle sinks. It does not establish that every path is reachable in every installation, that every framework supports every listed representation, or that all current scanner versions have the same coverage. Vendor patches and version changes can alter the map.
A defensive design should follow the transformations in the order they occur. Begin with the exact loading API used by the application. Identify which representations it accepts and which branch conditions select them. Track what bytes cross each interface: archive contents, decompressed streams, decoded objects, and finally pickle data. Then ask whether the scanner analyzes the same stages, handles failures without silently returning a clean result, and records when it cannot establish coverage.
This approach is more durable than a list of extensions because it survives format naming changes. It also exposes a trade-off. Exhaustive path modeling requires maintenance as frameworks add features and preserve old ones. A narrow scanner is easier to operate but leaves more unexamined behavior. A broad scanner has a larger compatibility and false-positive burden. The right design is not to pretend that one side of the trade-off disappears; it is to make unsupported or failed analysis visible and route it to a stronger control such as restricted loading or isolation.
The loading surface is therefore the first layer of the trust problem. Before asking whether a suspicious function appears in a pickle stream, a defender must know whether the stream is the one the framework will actually consume. Once that question is made explicit, scanner failures can be separated into two categories: the scanner never reached the relevant path, or it reached the path but could not reason about what the loaded behavior could do. The next chapter examines both cases, beginning with what happens when the scanner fails before producing a verdict.