Souběžné načtení dvou vektorů promptu obešlo ve vLLM pojistku proti loňské díře
Záznam CVE-2026-73557 popisuje, že pojistka, kterou vLLM přidal v prosinci 2025 proti chybě CVE-2025-62164, stála na přepínači společném pro celý proces. Když server načítal dva vektory promptu z jednoho požadavku naráz, první z nich přepínač po sobě vypnul dřív, než druhý dočetl. Oprava je ve vLLM 0.26.0 z 25. července, číslo CVE dostala 13. srpna a v databázi GitHubu se objevila až 4. září spolu se třemi dalšími.

Server vLLM dostal loni v listopadu záznam CVE-2025-62164 za chybu v načítání vektorů promptu. Klient může místo textu poslat rovnou vstupní vektory (prompt_embeds) serializované PyTorchem a server je načte funkcí torch.load. PyTorch od verze 2.8.0 u řídkých tenzorů nekontroluje, jestli indexy sedí do udaného tvaru, takže podstrčený tenzor mohl při převodu na hustý zapsat mimo přidělenou paměť. Hlášení dostalo známku 8,8 z deseti a mluví o pádu serveru a „potenciálně“ o spuštění cizího kódu.
Oprava přišla ve dvou krocích. Návrh 27204 z 22. října 2025 schoval celou funkci za přepínač --enable-prompt-embeds, který je vypnutý; vyšel ve verzi 0.11.1 z 19. listopadu. Commit additional protection for CVE-2025-62164 z 15. prosince pak obalil načtení tenzoru kontextem torch.sparse.check_sparse_tensor_invariants(), který kontrolu indexů zapíná. Ten vyšel ve vLLM 0.13.0 z 19. prosince 2025. O téhle druhé pojistce je nový záznam.
Přepínač platí pro celý proces, ne pro jeden požadavek
Dokumentace PyTorche popisuje kontrolu invariantů jako jeden přepínač s metodami is_enabled(), enable() a disable(). Kontextový manažer si při vstupu uloží dosavadní hodnotu, kontrolu zapne a při výstupu uloženou hodnotu vrátí. Nic z toho není vázané na vlákno. Věděl to i sám vLLM: test tests/renderers/test_sparse_tensor_validation.py v dotčené revizi nese poznámku, že globální příznak není vláknově lokální a souběžní uživatelé kontextu si stav „zapnuto“ můžou prosakovat mezi testy.
Dokud se vektory promptu posílaly jen na /v1/completions, na tom nesešlo. Návrh 40720 z 1. května 2026 ale dovolil posílat prompt_embeds i jako části zprávy na /v1/chat/completions, a to víc částí v jednom požadavku. Server je načítá souběžně: sesbírá je přes asyncio.gather a každou pošle do výchozího fondu vláken smyčky událostí. Změna vyšla ve vLLM 0.21.0 z 15. května 2026.
Záznam CVE-2026-73557 popisuje pořadí, ve kterém se pojistka ztratí. Neškodná část A vstoupí do kontextu, uloží si „vypnuto“ a kontrolu zapne. Podstrčená část B vstoupí hned za ní a uloží si „zapnuto“. Část A dočte svůj tenzor, vystoupí a vrátí uloženou hodnotu, tedy kontrolu vypne. Část B je pořád uvnitř svého kontextu, jenže torch.load už běží bez kontroly a neplatný řídký tenzor s indexy [[10], [10]] pro tvar 3 × 3 projde až k převodu na hustý tenzor.
Nahlásila to firma hexcraft-labs a její hlášení je na doložení opatrné. Popsané prokládání ověřila deterministickým během nad zdrojovým kódem dotčené revize s PyTorchem 2.11.0; volání to_dense() přitom zachytila dřív, než na neplatný tenzor sáhlo. Živý server, pád procesu, poškození paměti ani spuštění kódu nikdo nepouštěl. Hlášení k tomu píše, že spolehlivé spuštění kódu netestovalo a netvrdí. Známka je proto 6,3 podle CVSS 4.0, tedy střední, proti 8,8 u původní chyby. Podmínkou je zapnutý přepínač --enable-prompt-embeds; multimodální model ani víc vláken vykreslovače potřeba nejsou a klíč k API vLLM ve výchozím nastavení nevyžaduje.
Třetí oprava je zámek kolem celého načtení
Commit 793cf79 ze 14. července 2026 (návrh 48583) přidal soubor vllm/utils/sparse_utils.py s jediným zámkem threading.Lock a obalem check_sparse_tensor_invariants_threadsafe(). Komentář v něm říká, že všechna volací místa MUSÍ používat tenhle obal. Vyměnily ho tři: načítání vektorů promptu, vektorů obrázků a vektorů zvuku. K tomu přibyl test, který popsané pořadí vstupů a výstupů vynutí bariérou mezi dvěma vlákny a ověří, že neplatný tenzor pořád spadne.
Oprava vyšla ve vLLM 0.26.0, na PyPI 25. července a jako vydání na GitHubu 27. července 2026. Poznámky k vydání mají oddíl Security a v něm větu o opravě souběhu, který obešel nápravu CVE, bez čísla záznamu. Hlášení na repozitáři projekt zveřejnil týž den v 9.21 UTC. Číslo CVE si GitHub jako přidělující autorita vyhradil 12. srpna a záznam na cve.org vydal 13. srpna; NVD ho k 5. září vede ve stavu „Received“, tedy bez vlastního ohodnocení. Do globální databáze hlášení GitHubu, ze které čte Dependabot, se záznam dostal až 4. září ve 21.39 UTC. Od commitu s opravou k tomu uběhlo sedm týdnů.
Rozsah dotčených verzí se mezi záznamy liší. Hlášení na repozitáři i databáze GitHubu píšou „od 0.21.0 do 0.26.0“, záznam na cve.org „od 0.20.2rc0“. Porovnání větví přes API GitHubu říká, že commit s chatovými částmi ve značce v0.20.2 není a ve v0.21.0 je; proč cve.org uvádí o jedno vydání starší hranici, z veřejných záznamů nevyčteme.
Tři další záznamy z téhož večera
GitHub zveřejnil 4. září během osmi minut čtyři záznamy k vLLM a všechny čtyři opravuje verze 0.26.0. Druhý z nich je stejného rodu jako ten první. Záznam CVE-2026-73556 (známka 5,3) popisuje, že oprava starší chyby CVE-2026-55574 z verze 0.24.0 přidala časový limit na překlad regulárního výrazu pro strukturovaný výstup jen u backendů xgrammar a outlines. Třetí backend, lm-format-enforcer, zůstal bez limitu a překlad výrazu (a{1,300}){300} podle hlášení nedoběhl ani za dvacet sekund, po které držel jedno jádro procesoru na plném výkonu. V titulku hlášení stojí „missed sibling“, tedy opomenutý sourozenec. Backend si musí správce zvolit výslovně, výchozí auto vede na xgrammar.
Záznam CVE-2026-73555 (5,3) se týká chybových hlášek: na vadný JSON odpověděl server textem výjimky včetně cesty k souboru a čísla řádku, z čehož nepřihlášený klient vyčetl uživatelské jméno, domovský adresář, verzi Pythonu i přesnou verzi vLLM. Záznam CVE-2026-71486 (4,3) míří na koncové body /derender, které dekódují seznamy identifikátorů tokenů dodané klientem bez horní meze; podle nálezce ty koncové body vydaná verze 0.23.0 ještě neměla, databáze GitHubu přesto uvádí rozsah „do 0.26.0“ bez dolní hranice.
Nejnovější vLLM je 0.28.0 z 26. srpna 2026, takže kdo aktualizuje průběžně, má všechny čtyři opravy měsíc. Vektory promptu projekt v dokumentaci určuje důvěryhodným uživatelům a přepínač nechává vypnutý. Hlášení k CVE-2026-73557 k tomu dodává větu, kterou v překladu uvádíme celou: „Není to hranice paměťové bezpečnosti: uživatel oprávněný posílat vektorové vstupy by neměl mít možnost vypnout procesní invariant pro souběžnou práci.“
Zdroje
- GHSA-pr7f-p5mw-fc87 / CVE-2026-73557 – hlášení o souběhu v databázi GitHubu, včetně časové osy oprav a tabulky, co bylo ověřeno během
- GHSA-mrw7-hf4f-83pf / CVE-2025-62164 – původní hlášení z listopadu 2025
- Návrh 48583 a soubor sparse_utils.py ve značce v0.26.0 – zámek a jeho komentář
- Poznámky k vydání vLLM 0.26.0 – oddíl Security
- Záznam CVE-2026-73557 na cve.org – data vyhrazení a zveřejnění, rozsah verzí
- Dokumentace torch.sparse.check_sparse_tensor_invariants
- GHSA-48jh-3gj7-fg8v, GHSA-hwrm-c4cx-rf4j a GHSA-8737-qx52-hjff – tři další záznamy ze 4. září