CodeVetter · Repo Unpack · Risk map
pytorch/pytorch
The dominant risks are deserialization and the trust boundary of model artifacts: torch.load is a pickle surface (mitigated by the now-default weights_only mode), TorchScript/mobile artifacts are executable code, and distributed primitives have zero authentication or encryption by design. The project's own SECURITY.md explicitly scopes out crashes/OOB from invalid inputs and cache poisoning, meaning large parts of the C++ kernel surface are intentionally unhardened. Internally, torch/__init__.py is a fragile monolith with import-order constraints and environment mutation (PATH, dlopen flags), serialization depends on vendored miniz internals, and the sprawling CI matrix plus content-hashed Docker images create operational blast radius.
- torch.load with weights_only=False executes arbitrary pickle — documented as 'Never load data from an untrusted source'; weights_only=True became the default in 2.6 and uses the restricted _weights_only_unpickler with a safe-globals allowlist.
- torch.distributed primitives (c10d, RPC, TCPStore) include no authorization and send unencrypted messages: 'anybody with access to the network can execute arbitrary code with the privileges of the user running PyTorch.'
- Distributed checkpoints intentionally bypass weights_only protections — loading a checkpoint 'may execute arbitrary code'; the trust model assumes your own store produced the artifact.
- TorchScript and mobile .ptl models are treated as untrusted executable code; even inspection tools like torch.utils.model_dump can execute embedded code.
- PyTorch's stated policy is to not validate all inputs: wrong shapes/dtypes/devices can cause crashes or out-of-bounds memory access and are classified as bugs, not vulnerabilities — a deliberate, large unhardened C++ surface.
- Local caches (compiled kernels, Triton cache) are trusted by design; cache poisoning is explicitly out of scope, so any code path writing/reading these caches assumes a non-hostile filesystem.
- CI supply-chain mitigations are documented: sccache caching is susceptible to corruption (artifacts must not run near secrets), non-ephemeral runners risk reverse shells (hence no repo secrets), and release builds require ephemeral runners + cold-start builds + deployment environments.
- torch/__init__.py is a 3588-line module with hard ordering constraints (device modules must precede multiprocessing; quantized/ao imports must be last), global mutation (sys.setdlopenflags, os.environ['PATH'] on Windows, os.add_dll_directory), and a sorted-__all__ runtime assertion — edits here have global blast radius.
- _load's fast path hardcodes miniz internals — data descriptor sizes 24/16 bytes, 0xFFFFFFFF boundary, header-offset arithmetic that 'must be kept in sync with that of miniz' — coupling the Python loader to vendored zip-writer behavior.
- Windows loading mutates process-global state: prepends dll_paths to os.environ['PATH'] and LoadLibraryW's every *.dll in torch/lib — classic DLL search-order/preload surface.
- Generated-file traps: .pyi stubs are generated from .pyi.in templates (edit the template, not the output), and torchgen emits the ATen/op registration surface at build time — editing generated files or native_functions.yaml without understanding codegen silently changes public API.
- The .ci/docker directory is content-hashed: any file change — including the README — forces a full Docker image rebuild for CI; AGENTS.md warns against touching it (e.g., during upstream Ubuntu outages).
- sparse-tensor validation under weights_only is an O(nnz) scan per tensor — a documented performance cliff for large checkpoints that can look like a hang.
- Test selection depends on downloaded configs and target determination (options.enable_td runs 25% of tests), so a TD/config failure can silently reduce coverage; failures in the gating workflows (job-filter, llm-td) cascade into what actually runs. (inferred)