La couche de service est la surface d'attaque : bugs de concurrence dans vLLM et SGLang
Un fuzzer de mai 2026, GRIEF, traite des traces de requêtes concurrentes comme entrées et trouve 15 bugs (2 CVE) dans vLLM et SGLang : contamination de sortie entre requêtes, déni de service « voisin bruyant » et crashs différés — sans entrée malformée.
En bref L’essentiel des tests de sécurité LLM vise le modèle : jailbreaks, injection de prompt, hallucination. Or un corpus de travaux croissant soutient que le framework de service — le moteur qui regroupe les requêtes en batch, partage un cache KV entre locataires et ordonnance le temps GPU — constitue sa propre frontière de sécurité, plus poreuse. Un papier publié en mai 2026, Continuous Discovery of Vulnerabilities in LLM Serving Systems with Fuzzing (arXiv 2605.11202), présente GRIEF, un fuzzer greybox qui mute des traces de requêtes concurrentes datées. En campagnes initiales de 8 heures contre vLLM et SGLang, il a révélé 15 vulnérabilités, 10 confirmées par les mainteneurs, dont 2 CVE attribuées — toutes déclenchées par des requêtes parfaitement valides qui ne deviennent dangereuses que lorsqu’elles se chevauchent.
De quoi s’agit-il ?
Un moteur d’inférence moderne comme vLLM ou SGLang n’est pas une fine surcouche autour d’un modèle. Pour atteindre ses cibles d’utilisation GPU, il exécute du batching continu, un cache KV paginé partagé, du partage de préfixe, du décodage spéculatif, du multiplexage d’adaptateurs LoRA et un ordonnanceur multi-locataires. Tout cela crée un état mutable partagé entre des requêtes appartenant à des utilisateurs différents. Les tests standards ne l’exercent jamais : les évaluations de sûreté du modèle vérifient si une réponse isolée est sûre, les tests d’API si une requête isolée est correctement traitée, et les fuzzers classiques cherchent des crashs provoqués par des octets malformés. Aucun ne sonde ce qui se passe quand des dizaines de requêtes individuellement valides se disputent le même cache et le même ordonnanceur sous une concurrence réaliste.
GRIEF (arXiv 2605.11202) est la première tentative systématique de fuzzer cette surface. Son modèle de menace est délibérément faible : un client d’API non privilégié envoyant des requêtes bien formées via des points d’accès documentés — pas de trafic malformé, pas d’accès privilégié, pas de colocalisation hôte. Il en résulte la première démonstration empirique que la pile de service est « une surface de vulnérabilité distincte et sous-testée ».
Comment ça marche
L’idée clé tient dans l’abstraction d’entrée. Au lieu de muter une chaîne d’octets ou un prompt isolé, GRIEF traite une trace de requêtes datée comme unité de fuzzing : une séquence horodatée d’événements client qui capture quand les requêtes se chevauchent, quelle forme elles portent et quelles requêtes doivent partager une identité de prompt sémantique. Le fuzzer mute le co-batching, la réutilisation de cache de préfixe, le co-ordonnancement d’adaptateurs, le timing d’annulation et la pression sur l’ordonnanceur, puis utilise des oracles légers — anomalies de latence, télémétrie du cache KV, comparaison de sorties entre exécutions — pour signaler les runs suspects. La diffusion étant bruitée et non déterministe, tout cas suspect passe par une étape de relecture contrôlée avec vote majoritaire et vérification des log-probabilités de tokens, séparant les vrais bugs du jitter d’ordonnancement.
Les résultats se regroupent en trois classes d’impact (Table 1 du papier) :
Classe de défaillance Total vLLM SGLang Confirmées Impact représentatif
Corruption d'état / isolation 7 5 2 7 Contamination de sortie entre requêtes
Pathologie de performance 4 2 2 2 Déni de service « voisin bruyant »
Crash / vivacité 2 1 1 1 Perte de disponibilité (processus)
Dans la classe isolation, une charge concurrente a fait réutiliser à une requête l’état de cache KV obsolète d’une autre requête, polluant la sortie de la victime — une rupture d’isolation sans crash ni erreur dans les logs. Dans la classe performance, une requête valide mais façonnée par l’attaquant a externalisé son coût sur les utilisateurs co-ordonnancés, poussant la latence du premier token à la hausse et le débit utile à près de zéro. Dans la classe vivacité, un lot de requêtes valides à adaptateur LoRA a violé un invariant de l’ordonnanceur et interrompu le processus de service — et comme le crash était différé par rapport au déclencheur, les logs montraient un prefill normal et des réponses réussies jusqu’à la panne.
Cette classe de pathologie de performance est précisément ce qu’un autre papier de février 2026, Rethinking Latency Denial-of-Service: Attacking the LLM Serving Framework, Not the Model (arXiv 2602.07878), formalise sous le nom de stratégie « Fill and Squeeze » : Fill épuise le cache KV global pour induire un blocage de tête de file, Squeeze force l’ordonnanceur à des préemptions répétées. Les auteurs rapportent jusqu’à 20 à 280× de ralentissement sur le temps jusqu’au premier token à un coût d’attaque inférieur de 30 à 40 % aux attaques de ralentissement antérieures au niveau modèle — en ciblant l’état de l’ordonnanceur FCFS, pas le modèle.
Pourquoi c’est important
Deux propriétés aggravent le tableau. Premièrement, aucune de ces défaillances ne nécessite d’entrée malformée ni d’erreur explicite — elles sont silencieuses. Un locataire dont la sortie a été contaminée par l’état de cache KV d’un autre utilisateur n’a aucun signal que quelque chose a déraillé ; un opérateur qui surveille des tableaux de bord « crash seulement » voit un serveur en bonne santé. Les travaux empiriques cités par les auteurs de GRIEF estiment que plus de 35 % des bugs de moteurs d’inférence se manifestent en anomalies sans crash. Deuxièmement, le seuil pour l’attaquant est bas : un utilisateur d’API payant ordinaire sur un point d’accès multi-locataires partagé peut, en cadençant ses requêtes, dégrader ou contaminer d’autres locataires. Pour qui exploite une flotte vLLM ou SGLang partagée — équipes plateforme internes, fournisseurs d’inférence à la demande — c’est un problème de confidentialité et de disponibilité inter-locataires, pas une note de bas de page sur la qualité du modèle. Cela correspond directement à Unbounded Consumption de l’OWASP LLM Top 10 et aux exigences classiques d’isolation des locataires.
Défenses
Agir ne demande aucun payload ; les mitigations sont architecturales.
- Tester la concurrence, pas seulement les requêtes. Ajoutez du fuzzing de couche de service ou de la relecture de traces concurrentes (l’approche de GRIEF) à la CI de tout moteur auto-hébergé. Les tests de correction par requête ne captureront pas ces bugs.
- Imposer l’isolation des locataires dans le cache. Partitionnez ou clavez le cache KV et les pools de partage de préfixe par locataire afin que la réutilisation entre requêtes ne franchisse pas une frontière de confiance ; désactivez le partage de préfixe entre locataires là où la confidentialité compte.
- Rendre l’ordonnanceur équitable et borné. Remplacez le FCFS naïf par un ordonnancement à part équitable ou par priorité, plafonnez l’occupation du cache KV et les tokens en vol par locataire, et appliquez un contrôle d’admission pour qu’un client ne puisse monopoliser la mémoire GPU. Les travaux récents d’ordonnancement (p. ex. atténuation du blocage de tête de file) visent directement le schéma « Fill and Squeeze ».
- Quotas et limites de débit comme plancher financier. Les limites de débit (tokens et requêtes) par locataire sont la première défense contre les effets « voisin bruyant » et de déni de portefeuille.
- Observer les bons signaux. Une surveillance « crash seulement » est aveugle ici. Suivez les distributions de TTFT/TPOT par locataire, la télémétrie de hits/évictions du cache KV et le nombre de préemptions ; traitez une divergence de latence inter-locataires soutenue comme un événement de sécurité.
- Patcher rapidement. Les deux CVE de GRIEF ont été confirmées par les mainteneurs de vLLM/SGLang — gardez les moteurs à jour et surveillez leurs advisories.
Statut
GRIEF a été publié en mai 2026 (arXiv 2605.11202) ; il rapporte 15 découvertes, 10 confirmées par les développeurs et 2 avec CVE attribuées, d’autres demandes de CVE en attente, sur vLLM et SGLang testés avec Qwen-2.5-0.5B-Instruct. L’analyse complémentaire de déni de service par latence (arXiv 2602.07878) date de février 2026. Les auteurs de GRIEF ont suivi une divulgation responsable et retiennent délibérément un inventaire d’exploits complet ; cet article décrit de même les classes de défaillance et leurs défenses, pas les étapes de reproduction. La leçon de fond est durable, quel que soit le correctif : pour les LLM auto-hébergés, la logique de concurrence, de cache et d’ordonnancement du moteur d’inférence est une frontière de sécurité de premier rang qui exige ses propres tests.