{"schema":"threatline.record/2","record":{"id":"GHSA-w2v2-5662-8v5q","aliases":["GHSA-w2v2-5662-8v5q"],"summary":"Rust gRPC Generate accepts out-of-vocabulary prompt token IDs and can terminate EngineCore","details":"## Summary\n\nThe Rust gRPC `Generate` and `GenerateStream` service accepts pre-tokenized prompt IDs from the protobuf `TokenIds` field and forwards them to `TextLlm`/`EngineCore` without checking them against the served model or tokenizer vocabulary.\n\nThis is the gRPC sibling of the public Rust HTTP `/inference/v1/generate` issue fixed in PR `#45441`: a single prompt token ID such as `9000000` can reach the engine embedding lookup, trigger a fatal EngineCore failure, and leave the server unhealthy. PR `#45441` adds validation for the HTTP `/inference/v1/generate` route, but it does not touch `rust/src/server/src/grpc/*`.\n\n## Technical Details\n\n`rust/proto/vllm_grpc.proto` exposes pre-tokenized prompts:\n\n```proto\noneof prompt {\n  string text = 3;\n  TokenIds token_ids = 4;\n}\n```\n\n`rust/src/server/src/grpc/convert.rs` maps those IDs directly:\n\n```rust\nSome(pb::generate_request::Prompt::TokenIds(ids)) => Prompt::TokenIds(ids.ids),\n```\n\nThe gRPC handlers then submit the converted `TextRequest`:\n\n```rust\nlet text_request =\n    convert::to_text_request(proto_req, false, self.state.served_model_names())?;\nlet stream = self.state.chat.text().generate(text_request).await;\n```\n\nFor pre-tokenized prompts, `rust/src/text/src/lib.rs` intentionally bypasses tokenization:\n\n```rust\nPrompt::TokenIds(token_ids) => token_ids,\n```\n\n`rust/src/model/src/request.rs` forwards the IDs into the engine request:\n\n```rust\nprompt_token_ids: Some(prompt_token_ids),\n```\n\nThere is no gRPC-side equivalent of the token-id range validators used by the Rust OpenAI routes.\n\nThe Rust OpenAI validation layer already treats out-of-vocabulary token IDs as invalid client input. `rust/src/server/src/routes/openai/utils/token_ids.rs` rejects prompt token IDs, `allowed_token_ids`, and `logit_bias` keys outside the relevant vocabulary bounds.\n\nPR `#44680` added that validator family to `/v1/completions` and `/v1/chat/completions`. PR `#45441` extends the same validator family to the Rust HTTP `/inference/v1/generate` route after demonstrating that an OOV `token_ids` prompt kills EngineCore. The gRPC service still lacks the same pre-submit guard.\n\n## Impact\n\nAn attacker who can reach a vLLM Rust gRPC endpoint can submit a `Generate` or `GenerateStream` request with an out-of-vocabulary `prompt.token_ids` value. The request is accepted by the gRPC frontend and submitted to the shared text generation path. The same invalid token-id sink is documented publicly in PR `#45441` as aborting EngineCore for the Rust HTTP sibling route.\n\nExpected impact is a single-request denial of service for deployments exposing the Rust gRPC service. Fixed behavior should be an `INVALID_ARGUMENT` gRPC status before the request is submitted to the engine.\n\nSuggested severity: High if the gRPC endpoint is network-reachable without an additional deployment-layer credential requirement.\n\nSuggested CVSS v3.1:\n\n`CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H` = 7.5\n\nIf maintainers model the gRPC surface as reachable only to already-authorized clients, the same issue becomes:\n\n`CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H` = 6.5\n\nSuggested CWEs:\n\n- `CWE-20`: Improper Input Validation\n- `CWE-400`: Uncontrolled Resource Consumption\n\n## Suggested Fix\n\nApply the same token-id range validation used by the Rust OpenAI and HTTP generate routes before either gRPC handler calls `state.chat.text().generate()`.\n\nThe gRPC fix should validate:\n\n- `GenerateRequest.prompt.token_ids` against the same prompt bound used by PR `#45441`, namely the union of tokenizer and model vocabulary bounds.\n- `DecodingParameters.allowed_token_ids` against tokenizer vocabulary.\n- `DecodingParameters.logit_bias` keys against model vocabulary.\n- `ResponseOptions.prompt_candidates.token_ids` and `ResponseOptions.output_candidates.token_ids`, if they are lowered into `logprob_token_ids`, against the same model/tokenizer bounds used by the corresponding HTTP route.\n\nAdd tests for both unary `Generate` and streaming `GenerateStream` that assert OOV token IDs return `INVALID_ARGUMENT` before any engine request is submitted.\n\n## Appendix: Duplicate Research\n\nI checked:\n\n- Public PR `#44680`: validates Rust `/v1/completions` and `/v1/chat/completions`; changed files do not touch gRPC.\n- Public PR `#45441`: validates Rust HTTP `/inference/v1/generate`; changed files do not touch gRPC.\n- GitHub issue/PR searches for `grpc token_ids out-of-vocab`, `grpc Generate token_ids EngineCore`, and `grpc token_ids validation`: no public gRPC duplicate found.\n- vLLM security advisories matching `grpc`, `token_ids`, `out-of-vocab`, `EngineCore`, or `Generate`: no gRPC duplicate found.\n- Local prior vLLM submissions: none cover Rust gRPC prompt token-id range validation.\n\n## Affected versions\n\n- Package: `vllm` (PyPI).\n- Affected versions: `>= 0.22.0, < 0.24.0`.\n- First stable patched version: `0.24.0`.\n\nAdvisory fixed in [#45685](https://github.com/vllm-project/vllm/pull/45685).\n","categories":["Unclassified"],"product":"vllm","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/vllm-project/vllm/security/advisories/GHSA-w2v2-5662-8v5q","source_modified_at":"2026-10-27T22:09:23Z","fetched_at":"2026-10-28T01:10:53.534015Z","package":{"ecosystem":"pip","name":"vllm"},"range":">= 0.22.0, < 0.24.0"}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/vllm-project/vllm/security/advisories/GHSA-w2v2-5662-8v5q","source_modified_at":"2026-10-27T22:09:23Z","fetched_at":"2026-10-28T01:10:53.534015Z","package":{"ecosystem":"pip","name":"vllm"},"version":"0.24.0"}],"cvss":5.9,"severity":"Medium","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-10-09T07:49:41Z","modified_at":"2026-10-27T22:09:23Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/vllm-project/vllm/security/advisories/GHSA-w2v2-5662-8v5q","fetched_at":"2026-10-28T01:10:53.534015Z","source_modified_at":"2026-10-27T22:09:23Z"}],"sources":["GitHub Advisory Database"],"references":[],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":null,"confidence":"unknown","reviewed":false},"change_timestamp":"2026-10-27T22:09:23Z","content_hash":"d1588380eb2e0037c05c83f4f9657fb11d5a3b9474b0171a134e27c0f090cc3d"}}
