Skip to content

llama-index-embeddings-adapter: torch.load() without weights_only=True allows pickle deserialization #21465

@ryosuke-tokushima

Description

@ryosuke-tokushima

Issue Title

llama-index-embeddings-adapter: torch.load() without weights_only=True allows arbitrary code execution via pickle

Summary

BaseAdapter.load() in llama-index-embeddings-adapter calls torch.load() without weights_only=True. Since PyTorch 1.13+, omitting this parameter is deprecated and triggers a FutureWarning; in future PyTorch versions it will default to True. More importantly, loading an untrusted .bin file with the current code allows arbitrary code execution via Python's pickle deserialization.

Affected File

llama-index-integrations/embeddings/llama-index-embeddings-adapter/llama_index/embeddings/adapter/utils.py
(confirmed on llama-index-core==0.14.21, commit a3aeb31)

Vulnerable Code

@classmethod
def load(cls, input_path: str) -> "BaseAdapter":
    """Load model."""
    with open(os.path.join(input_path, "config.json")) as fIn:
        config = json.load(fIn)
    model = cls(**config)
    model.load_state_dict(
        torch.load(                                      # ← no weights_only=True
            os.path.join(input_path, "pytorch_model.bin"),
            map_location=torch.device("cpu"),
        )
    )
    return model

input_path is passed directly from the caller (AdapterEmbeddingModel.__init__ via adapter_path argument), making it user-controlled.

Attack Scenario

  1. Attacker publishes a HuggingFace Hub repository containing a malicious pytorch_model.bin (a pickled object with __reduce__ that executes a shell command).
  2. A user who fine-tuned an adapter model downloads it and loads it via AdapterEmbeddingModel(adapter_path="attacker/malicious-adapter", ...).
  3. torch.load("pytorch_model.bin") deserializes the pickle payload → arbitrary code execution on the user's machine.

Impact

Arbitrary code execution at model-load time for any user who loads an adapter from an untrusted path.

Suggested Fix

model.load_state_dict(
    torch.load(
        os.path.join(input_path, "pytorch_model.bin"),
        map_location=torch.device("cpu"),
        weights_only=True,   # ← safe: only loads tensor weights, no pickle execution
    )
)

If the adapter structure requires non-tensor objects that weights_only=True cannot handle, consider using safetensors format instead:

from safetensors.torch import load_file
state_dict = load_file(os.path.join(input_path, "model.safetensors"))
model.load_state_dict(state_dict)

Reference

Discovery

Found via static analysis of the llama_index codebase.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions