{"id":"GHSA-q9v9-qpjp-fc24","kind":"vulnerability","aliases":["GHSA-q9v9-qpjp-fc24"],"related":[],"title":"Unauthenticated EngineCore crash via out-of-range prompt token id under skip_tokenizer_init (incomplete fix of GHSA-25q3-v2hm-8vpf)","description":"### Summary\n\nA single unauthenticated request with an out-of-range prompt token id crashes\nthe shared EngineCore (and the API-server process) when vLLM is served with\n`--skip-tokenizer-init`, denying service to all tenants until the process is\nrestarted.\n\nThe vocab-range validation added for GHSA-25q3-v2hm-8vpf lives in\n`InputProcessor._validate_model_input` but is gated on `tokenizer is not None`.\n`--skip-tokenizer-init` is the mode where the client is expected to send raw\ntoken ids (pre-tokenized / token-in-token-out / RL serving), and there is no\ntokenizer, so the check is skipped entirely. The OpenAI `/v1/completions`\n`prompt` field only constrains integer ids from below (`Field(ge=0)`), so a\nlarge positive id passes all validation, reaches the embedding as an index, and\nraises out of the model forward. EngineCore treats that as fatal and shuts down.\nThe same gap reopens the `/v1/embeddings` and `/pooling` paths that GHSA-25q3\noriginally covered, whenever the tokenizer is disabled.\n\nVerified against `main` at HEAD `dc89fdfb0e` (2026-09-15).\n\n### Details\n\n`vllm/v1/engine/input_processor.py`, `_validate_model_input`:\n\n```python\nif prompt_ids and tokenizer is not None:   # <- skipped when tokenizer is None\n    ...\n    if min_input_id < 0: raise VLLMValidationError(\"... out of vocabulary\")\n    if max_input_id > max(tokenizer.max_token_id, model_vocab_size - 1):\n        raise VLLMValidationError(\"... out of vocabulary\")\n```\n\nReachability (unauthenticated; the OpenAI server has no auth by default):\n\n```\n/v1/completions (prompt = list[int], Field(ge=0) only, no upper bound)\n  -> InputProcessor.process_inputs -> _validate_model_inputs\n  -> _validate_model_input  # tokenizer is None -> vocab check skipped\n  -> model forward -> embedding index out of range -> exception\nEngineCore._process_engine_step -> run_busy_loop (no per-step guard)\n  -> run_engine_core: \"EngineCore encountered a fatal error\" -> _send_engine_dead\n```\n\nThe busy loop has no per-request guard around the model step, so the exception\nis fatal for the whole engine, not scoped to the offending request.\n\n### Impact\n\nUnauthenticated remote denial of service (availability): one request\npermanently takes down the shared engine for all tenants, on any deployment run\nwith `--skip-tokenizer-init` (pre-tokenized / token-in-token-out / RL serving).\nSame class as GHSA-25q3-v2hm-8vpf (High), whose remediation did not cover the\ntokenizer-disabled path. CWE-20 / CWE-1284 / CWE-248.\n\n### Suggested fix\n\nRun the vocab-range check whenever prompt ids are present, not only when a\ntokenizer exists. When the tokenizer is disabled, use `model_config\n.get_vocab_size() - 1` as the upper bound; otherwise keep the existing\n`max(tokenizer.max_token_id, model_vocab_size - 1)` bound. This validation is implemented by the merged fix.\n\nReported by Matteo Atkinson (matteoatkinson@gmail.com), with AI assistance.\n\nA fix for this was introduced in https://github.com/vllm-project/vllm/pull/62911.","summary":"When vLLM runs with --skip-tokenizer-init, a single unauthenticated request with a large prompt token id makes the shared EngineCore crash. The crash takes the engine and API server down for every user until the process restarts.","severity":"high","cvss":7.5,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"vllm","fixed":["0.33.0"],"affected":">= 0.28.0, < 0.33.0","ecosystem":"pypi"}],"ecosystems":["pypi"],"tags":["ai-tooling","cwe-1284","cwe-20","llm-serving","python","self-hosted","vllm"],"not_affected_if":["auth-required","tokenizer-enabled","vllm-0.33.0-or-later","vllm-below-0.28.0"],"how_to_check":"- Check the installed version with `pip show vllm`; affected range is `>= 0.28.0` and `< 0.33.0`.\n- Check whether the server is started with `--skip-tokenizer-init`.","fix":"- Move `vllm` to `0.33.0` or later (the advisory's fixed version in the product data).","status":"open","published_at":"2026-11-02T19:50:12+00:00","first_seen_at":"2026-11-09T22:53:23+00:00","triaged_at":"2026-11-09T22:54:19+00:00","updated_at":"2026-11-09T22:54:19+00:00","category":"ai-tooling","confidence":0.8,"triage":{"at":"2026-11-09T22:54:19Z","model":"claude-haiku-5-5","output":{"fix":"- Move `vllm` to `0.33.0` or later (the advisory's fixed version in the product data).","reach":"network","title":"vLLM: out-of-range prompt token id crashes the shared engine without authentication","impact":"medium","summary":"When vLLM runs with --skip-tokenizer-init, a single unauthenticated request with a large prompt token id makes the shared EngineCore crash. The crash takes the engine and API server down for every user until the process restarts.","audience":"businesses","category":"ai-tooling","nicknames":[],"who_cares":["vllm","python","self-hosted","llm-serving"],"confidence":0.8,"fix_status":"available","needs_auth":"no","worst_case":"Unauthenticated remote request crashes the shared inference engine for all tenants","analyst_note":"","how_to_check":"- Check the installed version with `pip show vllm`; affected range is `>= 0.28.0` and `< 0.33.0`.\n- Check whether the server is started with `--skip-tokenizer-init`.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["vllm-below-0.28.0","vllm-0.33.0-or-later","tokenizer-enabled","auth-required"],"who_is_affected":"Servers running vLLM 0.28.0 up to but not including 0.33.0 with --skip-tokenizer-init, which is used for pre-tokenized, token-in-token-out and RL serving. Deployments with a tokenizer enabled are not covered by this advisory's description. The OpenAI-compatible server has no auth by default, so any reachable client can send the request.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"high"},"cost_usd":0.000869,"input_tokens":5756,"output_tokens":587,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-09':263C '-1284':39B,461C '-15':264C '-20':41B,459C '-248':463C '/pooling':243C '/v1/completions':195C,321C '/v1/embeddings':241C '/vllm-project/vllm/pull/62911.':537C '0':206C,288C,327C '1':305C,494C,510C '2026':262C '25q3':21A,141C,248C,444C '8vpf':23A,143C,446C 'add':137C 'ai':36B,526C 'ai-tool':35B 'api':79B,110C 'api-serv':109C 'around':383C 'assist':527C 'atkinson':523C 'auth':318C 'avail':408C 'bound':331C,498C,511C 'busi':356C,375C 'check':189C,344C,471C 'class':440C 'client':164C 'config':490C 'constrain':199C 'core':365C 'cover':250C,452C 'crash':3A,72B,74B,103C 'cwe':38B,40B,458C,460C,462C 'dc89fdfb0e':261C 'dead':373C 'default':320C 'deni':122C 'denial':405C 'deploy':422C 'detail':265C 'disabl':255C,456C,487C 'embed':217C,348C 'encount':367C 'engin':77B,364C,372C,395C,416C 'enginecor':2A,71B,106C,228C,366C 'enginecore._process_engine_step':354C 'entir':192C 'error':370C 'everi':83B 'except':353C,389C 'exist':482C,502C 'expect':166C 'fatal':232C,369C,391C 'fc24':28A,33A 'field':197C,204C,325C 'fix':17A,465C,519C,529C 'forward':227C,347C 'gap':238C 'gate':149C 'ge':205C,326C 'get':491C 'ghsa':20A,25A,30A,140C,247C,443C 'ghsa-25q3':246C 'ghsa-25q3-v2hm-8vpf':19A,139C,442C 'ghsa-q9v9-qpjp-fc24':24A,29A 'github.com':536C 'github.com/vllm-project/vllm/pull/62911.':535C 'guard':362C,382C 'head':260C 'high':447C 'host':48B 'id':11A,67B,102C,171C,201C,211C,273C,287C,297C,301C,474C,506C 'impact':402C 'implement':515C 'incomplet':16A 'index':220C,349C 'init':15A,57B,121C,158C,428C 'input':269C,286C,296C,333C,336C,339C 'inputprocessor._validate_model_input':146C 'inputprocessor.process':332C 'int':324C 'integ':200C 'introduc':533C 'keep':500C 'larg':64B,209C 'list':323C 'live':144C 'llm':43B 'llm-serv':42B 'loop':357C,376C 'main':258C 'make':68B 'matteo':522C 'matteoatkinson@gmail.com':524C 'max':295C,298C,503C 'merg':518C 'min':285C 'mode':161C 'model':226C,268C,302C,335C,338C,346C,385C,489C,507C 'none':154C,278C,283C,342C 'offend':400C 'one':409C 'openai':194C,314C 'origin':249C 'otherwis':499C 'out-of-rang':5A,96C 'pass':212C 'path':244C,457C 'per':360C,380C 'per-request':379C 'per-step':359C 'perman':411C 'posit':210C 'pre':173C,430C 'pre-token':172C,429C 'present':476C 'process':87B,112C,129C 'prompt':9A,65B,100C,196C,272C,322C,473C 'python':45B,270C 'q9v9':26A,31A 'qpjp':27A,32A 'rais':222C,289C,306C 'rang':8A,99C,135C,352C,470C 'raw':169C 'reach':215C 'reachabl':311C 'remedi':449C 'remot':404C 'reopen':239C 'report':520C 'request':61B,93C,381C,401C,410C 'restart':88B,131C 'rl':180C,437C 'run':52B,355C,363C,423C,466C 'scope':397C 'self':47B 'self-host':46B 'send':168C,371C 'serv':44B,116C,181C,438C 'server':80B,111C,315C 'servic':123C,407C 'share':70B,105C,415C 'shut':234C 'singl':59B,91C 'size':304C,493C,509C 'skip':13A,55B,119C,156C,191C,279C,345C,426C 'skip-tokenizer-init':54B,118C,155C,425C 'step':361C,386C 'suggest':464C 'summari':89C 'take':75B,412C 'tenant':126C,419C 'token':10A,14A,56B,66B,101C,120C,151C,157C,170C,174C,176C,178C,186C,253C,275C,281C,300C,340C,427C,431C,433C,435C,455C,481C,485C,505C 'token-in-token-out':175C,432C 'tokenizer-dis':454C 'tokenizer.max':299C,504C 'tool':37B 'treat':229C 'unauthent':1A,60B,92C,312C,403C 'upper':330C,497C 'use':488C 'user':84B 'v2hm':22A,142C,445C 'valid':136C,214C,267C,334C,337C,513C 'verifi':256C 'via':4A 'vllm':34A,49B,51B,114C 'vllm/v1/engine/input_processor.py':266C 'vllmvalidationerror':290C,307C 'vocab':134C,303C,343C,469C,492C,508C 'vocab-rang':133C,468C 'vocabulari':293C,310C 'whenev':251C,472C 'whole':394C 'whose':448C","nicknames":[],"mitigations":[],"who_affected":"Servers running vLLM 0.28.0 up to but not including 0.33.0 with --skip-tokenizer-init, which is used for pre-tokenized, token-in-token-out and RL serving. Deployments with a tokenizer enabled are not covered by this advisory's description. The OpenAI-compatible server has no auth by default, so any reachable client can send the request.","tier":"B","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"businesses","plain_action":null,"pkeys":["vllm"],"ptokens":["vllm"],"labels":["aitooling","cwe1284","cwe20","high","llmserving","pypi","python","selfhosted","vllm"],"campaign_type":null,"search_a":"'25q3':21A '8vpf':23A 'crash':3A 'enginecor':2A 'fc24':28A,33A 'fix':17A 'ghsa':20A,25A,30A 'ghsa-25q3-v2hm-8vpf':19A 'ghsa-q9v9-qpjp-fc24':24A,29A 'id':11A 'incomplet':16A 'init':15A 'out-of-rang':5A 'prompt':9A 'q9v9':26A,31A 'qpjp':27A,32A 'rang':8A 'skip':13A 'token':10A,14A 'unauthent':1A 'v2hm':22A 'via':4A 'vllm':34A","search_b":"'-1284':39B '-20':41B 'ai':36B 'ai-tool':35B 'api':79B 'crash':72B,74B 'cwe':38B,40B 'engin':77B 'enginecor':71B 'everi':83B 'host':48B 'id':67B 'init':57B 'larg':64B 'llm':43B 'llm-serv':42B 'make':68B 'process':87B 'prompt':65B 'python':45B 'request':61B 'restart':88B 'run':52B 'self':47B 'self-host':46B 'serv':44B 'server':80B 'share':70B 'singl':59B 'skip':55B 'skip-tokenizer-init':54B 'take':75B 'token':56B,66B 'tool':37B 'unauthent':60B 'user':84B 'vllm':49B,51B","search_c":"'-09':263C '-1284':461C '-15':264C '-20':459C '-248':463C '/pooling':243C '/v1/completions':195C,321C '/v1/embeddings':241C '/vllm-project/vllm/pull/62911.':537C '0':206C,288C,327C '1':305C,494C,510C '2026':262C '25q3':141C,248C,444C '8vpf':143C,446C 'add':137C 'ai':526C 'api':110C 'api-serv':109C 'around':383C 'assist':527C 'atkinson':523C 'auth':318C 'avail':408C 'bound':331C,498C,511C 'busi':356C,375C 'check':189C,344C,471C 'class':440C 'client':164C 'config':490C 'constrain':199C 'core':365C 'cover':250C,452C 'crash':103C 'cwe':458C,460C,462C 'dc89fdfb0e':261C 'dead':373C 'default':320C 'deni':122C 'denial':405C 'deploy':422C 'detail':265C 'disabl':255C,456C,487C 'embed':217C,348C 'encount':367C 'engin':364C,372C,395C,416C 'enginecor':106C,228C,366C 'enginecore._process_engine_step':354C 'entir':192C 'error':370C 'except':353C,389C 'exist':482C,502C 'expect':166C 'fatal':232C,369C,391C 'field':197C,204C,325C 'fix':465C,519C,529C 'forward':227C,347C 'gap':238C 'gate':149C 'ge':205C,326C 'get':491C 'ghsa':140C,247C,443C 'ghsa-25q3':246C 'ghsa-25q3-v2hm-8vpf':139C,442C 'github.com':536C 'github.com/vllm-project/vllm/pull/62911.':535C 'guard':362C,382C 'head':260C 'high':447C 'id':102C,171C,201C,211C,273C,287C,297C,301C,474C,506C 'impact':402C 'implement':515C 'index':220C,349C 'init':121C,158C,428C 'input':269C,286C,296C,333C,336C,339C 'inputprocessor._validate_model_input':146C 'inputprocessor.process':332C 'int':324C 'integ':200C 'introduc':533C 'keep':500C 'larg':209C 'list':323C 'live':144C 'loop':357C,376C 'main':258C 'matteo':522C 'matteoatkinson@gmail.com':524C 'max':295C,298C,503C 'merg':518C 'min':285C 'mode':161C 'model':226C,268C,302C,335C,338C,346C,385C,489C,507C 'none':154C,278C,283C,342C 'offend':400C 'one':409C 'openai':194C,314C 'origin':249C 'otherwis':499C 'out-of-rang':96C 'pass':212C 'path':244C,457C 'per':360C,380C 'per-request':379C 'per-step':359C 'perman':411C 'posit':210C 'pre':173C,430C 'pre-token':172C,429C 'present':476C 'process':112C,129C 'prompt':100C,196C,272C,322C,473C 'python':270C 'rais':222C,289C,306C 'rang':99C,135C,352C,470C 'raw':169C 'reach':215C 'reachabl':311C 'remedi':449C 'remot':404C 'reopen':239C 'report':520C 'request':93C,381C,401C,410C 'restart':131C 'rl':180C,437C 'run':355C,363C,423C,466C 'scope':397C 'send':168C,371C 'serv':116C,181C,438C 'server':111C,315C 'servic':123C,407C 'share':105C,415C 'shut':234C 'singl':91C 'size':304C,493C,509C 'skip':119C,156C,191C,279C,345C,426C 'skip-tokenizer-init':118C,155C,425C 'step':361C,386C 'suggest':464C 'summari':89C 'take':412C 'tenant':126C,419C 'token':101C,120C,151C,157C,170C,174C,176C,178C,186C,253C,275C,281C,300C,340C,427C,431C,433C,435C,455C,481C,485C,505C 'token-in-token-out':175C,432C 'tokenizer-dis':454C 'tokenizer.max':299C,504C 'treat':229C 'unauthent':92C,312C,403C 'upper':330C,497C 'use':488C 'v2hm':142C,445C 'valid':136C,214C,267C,334C,337C,513C 'verifi':256C 'vllm':114C 'vllm/v1/engine/input_processor.py':266C 'vllmvalidationerror':290C,307C 'vocab':134C,303C,343C,469C,492C,508C 'vocab-rang':133C,468C 'vocabulari':293C,310C 'whenev':251C,472C 'whole':394C 'whose':448C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-11-02T19:50:12+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-q9v9-qpjp-fc24","url":"https://github.com/vllm-project/vllm/security/advisories/GHSA-q9v9-qpjp-fc24","severity":"high","published_at":"2026-11-02T19:50:12+00:00","modified_at":"2026-11-02T19:50:13+00:00","first_seen_at":"2026-11-09T22:53:23+00:00","fetched_at":"2026-11-09T22:53:23+00:00","note":null}],"documents":[{"id":110579,"url":"https://github.com/vllm-project/vllm/security/advisories/GHSA-q9v9-qpjp-fc24","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa-repo","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}