vLLM: リクエスト制御された `mm_processor_kwargs.code_revision` により、リモートコード実行が可能になる

## 概要

信頼できないマルチモーダルなリクエストが、`mm_processor_kwargs.code_revision` を設定できる可能性があります。vLLM が、Hugging Face のプロセッサが動的な `auto_map` コードを使用し、オペレーターが `--trust-remote-code` を有効にしたモデルを提供する場合、このリクエストの値が `AutoProcessor.from_pretrained` に到達し、Transformers によってインポートされる Python のバージョンを選択します。

これにより、クライアントはオペレーターが検証したプロセッサモジュールを、同じモデルリポジトリの別のバージョンのコードに置き換えることができます。オペレーターが選択したモデル/データの `revision` は固定されたままになり、実行されるプロセッサコードのみが変更されます。

Python モジュールのインポートは、vLLM の API/レンダラープロセスにおける任意のコード実行に相当します。以下に示す PoC (Proof of Concept) は、インポート時のファイルシステムへの書き込みによってこれを実証しています。これは完全にオフラインで実行でき、GPU、モデルの重み、または実際の Hub リポジトリへの変更は必要ありません。

## 影響を受けるバージョン

- リポジトリ: [`vllm-project/vllm`](https://github.com/vllm-project/vllm)
- 検証済みのコミット: [`570735520942b6428ae98f1eb442c6d88adae55e`](https://github.com/vllm-project/vllm/commit/570735520942b6428ae98f1eb442c6d88adae55e)
- Transformers 5.15.0 および 5.16.1 で検証済み
- 影響を受けるリリース範囲は、バイセクションによって特定されていません

## 影響を受ける設定

脆弱な状態は、以下のすべての条件が満たされている場合に発生します。

1. 提供されるモデルのプロセッサが、動的な `auto_map.AutoProcessor` コードを使用している。
2. オペレーターが `trust_remote_code` を有効にした。
3. サーバーが、リクエストで選択されたバージョンを取得できるか、または既にキャッシュされている。
4. 攻撃者が、提供されるモデルリポジトリにバージョンを作成したり、それらを制御したりできる。例えば、[Hugging Face community pull-request ref](https://huggingface.co/docs/hub/en/repositories-pull-requests-discussions#where-in-the-git-repo-are-changes-stored) が、そのリポジトリで許可されている場合などです。
5. マルチモーダルなリクエストが、プロセッサの処理パスに到達する。

デフォルトの `trust_remote_code=False` の設定では、この問題は発生しません。

## 攻撃対象領域

`mm_processor_kwargs` は、公開されているリクエストモデルで直接受け入れられます。

- [`TokenizeChatRequest.mm_processor_kwargs`](https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/entrypoints/serve/tokenize/protocol.py#L111-L114)
- [`ChatCompletionRequest.mm_processor_kwargs`](https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/entrypoints/openai/chat_completion/protocol.py#L372-L375)

`/tokenize` は特に重要です。なぜなら、vLLM の認証ミドルウェアは、`/v1`、`/v2`、`/inference`、および `/cohere` のプレフィックスのみを保護しているからです。

- [`AuthenticationMiddleware`](https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/entrypoints/serve/middleware/authenticate.py#L11-L62)

その結果、`/tokenize` は、`--api-key` が設定されている場合でも、外部プロキシによって保護されない限り、認証されません。vLLM の [セキュリティガイド](https://docs.vllm.ai/en/latest/usage/security/#unprotected-endpoints-no-api-key-required) では、`/tokenize` が保護されていないエンドポイントとして記述されています。

## 根本原因

リクエストの値は、以下のパスをたどります。

1. `OnlineRenderer.preprocess_chat` 関数([https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/renderers/online_renderer.py#L406-L435])は、リクエストの `mm_processor_kwargs` をプロンプトの追加情報として設定します。
2. マルチモーダルレンダリングは、`MMProcessorInputs` クラス([https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/renderers/base.py#L751-L760])と `_process_multimodal_async` 関数([https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/renderers/base.py#L831-L847])を通じて、これらのキーワード引数をプロセッサの構築に渡します。
3. `MultiModalConfig.merge_mm_processor_kwargs` 関数([https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/config/multimodal.py#L506-L518])は、初期設定値を上書きして推論時の値を適用するため、リクエストの値が優先されます。
4. `cached_get_processor_without_dynamic_kwargs` 関数([https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/transformers_utils/processor.py#L344-L375])は、認識されているプロセッサ呼び出しのキーのみをフィルタリングします。`code_revision` は、プロセッサ構築のキーワード引数として保持されます。
5. `cached_processor_from_config` 関数([https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/transformers_utils/processor.py#L380-L390])は、`revision` と `trust_remote_code` を固定しますが、残りのキーワード引数を予約せずにそのまま渡します。
6. `get_processor` 関数([https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/transformers_utils/processor.py#L205-L245])は、`AutoProcessor.from_pretrained(..., revision=revision, trust_remote_code=trust_remote_code, **kwargs)` を呼び出します。
7. Transformers の `AutoProcessor.from_pretrained` 関数([https://github.com/huggingface/transformers/blob/v5.15.0/src/transformers/models/auto/processing_auto.py#L294-L302])は、`code_revision` を使用して、動的モジュールを取得し、インポートします([https://github.com/huggingface/transformers/blob/v5.15.0/src/transform

攻撃者が改ざんしたコードが使用され、設定やデータは管理者が指定したものを使用しているという状況です。

リクエスト時に渡されるプロセッサの引数と、プロセッサの構築時に渡される引数が分離されておらず、`code_revision`というパラメータが予約されていません。

## 推奨される対策

1. プロセッサの構築時に渡される引数と、信頼できないリクエスト時に渡される引数を分離する。サポートされているリクエスト時の前処理機能の許可リストを作成することが最も安全です。
2. リクエストの`mm_processor_kwargs`に含まれる`code_revision`やその他のローダー/ソースに関する設定を拒否し、`model_config.code_revision`を明示的に、許可されたプロセッサのコードのバージョンとしてのみ受け入れるようにする。
3. リクエストによってロードされたモジュールのバージョンが、管理者が指定した設定が有効なままで変更されないことを証明する回帰テストを追加する。
4. `/tokenize`へのアクセスを、設定された認証ポリシーで保護し、多層防御を施す。

期待される安全な動作は、予約されたリクエストキーが使用された場合に4xxエラーを返すか、プロセッサの構築前にそれらを無視することです。拒否する方が望ましいのは、不正な試みが明確になるためです。

この問題に対する修正は、こちらにマージされました: https://github.com/vllm-project/vllm/pull/58830

影響を受けるパッケージ:
- pip (Python) vllm: 影響を受けるバージョン >= 0.7.3, < 0.31.0、修正されたバージョン 0.31.0