Sécurité GPU : un « Spectre pour le graphisme » est‑il possible ?

Cet article vous a aidé ?

Vous avez acheté des GPU pour aller plus vite. Maintenant vous découvrez qu’ils peuvent aussi accélérer votre pire journée — exposition de données, voisins bruyants, et incidents « pourquoi la sortie de mon modèle est différente ?» inclus.

La vérité gênante : les GPU modernes ressemblent beaucoup aux CPU juste avant que Spectre/Meltdown ne deviennent des sujets récurrents dans les canaux d’incidents. Ils sont partagés, optimisés pour la performance, remplis de comportements micro‑architecturaux non documentés, et pilotés par un énorme code noyau privilégié.

À quoi ressemblerait un « Spectre pour le graphisme »

Spectre n’était pas juste « un bug CPU ». C’était une leçon systémique : si vous concevez des fonctions de performance qui dépendent d’un comportement dépendant de secrets, quelqu’un finira par transformer ce comportement en primitive de lecture.
Les GPU regorgent de comportements dépendant de secrets. Ils sont aussi pleins de raccourcis « performance d’abord » qui restent invisibles tant que vous n’exécutez pas de code non fiable à côté de charges sensibles.

Un moment « Spectre pour le graphisme » ne serait pas nécessairement un CVE unique qui casse tout du jour au lendemain. Plus probablement, c’est un schéma :

  • Un canal auxiliaire micro‑architectural (temporisation des caches, conflits de banques de mémoire partagée, effets d’occupation, artefacts d’ordonnancement d’instructions) qui fuit quelque chose de précieux — poids de modèles, prompts, embeddings, clés crypto, ou caractéristiques de données.
  • Fuite inter‑locataires dans le cloud ou dans des clusters partagés (nœuds GPU Kubernetes, partitions Slurm, fermes VDI ou plateformes MLOps) où deux clients partagent un GPU ou partagent des chemins mémoire hôtes.
  • La réalité « pilote comme extension du noyau » où un bug dans un énorme module noyau GPU devient le moyen le plus simple d’évasion de conteneur sur la machine.
  • Rayon d’impact opérationnel parce que les mitigations coûtent en performance réelle, et l’entreprise essaiera de négocier avec la physique.

Le meilleur modèle mental n’est pas « un hacker lit les registres GPU ». C’est « un voisin qui infère vos secrets en mesurant des ressources partagées », plus « un patch du fournisseur qui change les caractéristiques de performance et casse vos garanties de reproductibilité ».

Si vous exploitez aujourd’hui une infrastructure GPU multi‑locataires, supposez que trois choses sont vraies :

  1. Quelqu’un trouvera un chemin de fuite que vous ne connaissiez pas.
  2. Votre récit d’isolation sera en partie contractuel (« nous ne faisons pas ça ») et en partie technique (« nous ne pouvons pas le faire »).
  3. La plupart des mitigations seront laides : ordonnancement, partitionnement et désactivation des astuces.

Faits et historique : la route vers des ennuis en forme de GPU

Les faits comptent parce qu’ils nous empêchent de traiter la sécurité GPU comme du folklore. Voici des points de contexte concrets — courts, ennuyeux, et donc utiles.

  1. Les GPU ont évolué des pipelines à fonctions fixes vers le calcul général sur environ deux décennies, et les modèles de sécurité ont pris du retard par rapport à la nouvelle réalité « du code non fiable qui s’exécute ici ».
  2. CUDA (2007) a normalisé le calcul GPU dans les systèmes grand public, ce qui a aussi fait du stack de pilotes une cible à forte valeur — gros, privilégié, et exposé à des entrées contrôlées par l’utilisateur.
  3. Meltdown/Spectre (publiés en 2018) ont requalifié les “fonctionnalités de performance” en surface d’attaque et ont fait des canaux auxiliaires une préoccupation de sécurité de premier plan, pas un hobby académique.
  4. Les GPU modernes partagent fortement des ressources on‑chip (caches L2, contrôleurs mémoire, fabrics d’interconnexion) entre contextes ; le partage est excellent pour l’utilisation et terrible pour « ne laissez pas mon voisin apprendre des choses ».
  5. La mémoire GPU est souvent gérée par le pilote + runtime plutôt que par des tables de pages matériellement appliquées comme les ingénieurs CPU aiment se rassurer ; même lorsqu’il existe une traduction d’adresses, les détails diffèrent selon l’architecture et le mode.
  6. SR‑IOV et vGPU ont rendu le partage temporel des GPU courant dans les offres VDI et cloud d’entreprise, augmentant le nombre de chemins où des données peuvent fuir à travers des frontières virtuelles.
  7. Le partitionnement de type MIG est un grand pas en avant (tranches dédiées de calcul + mémoire), mais ce n’est pas un autocollant magique « rien de partagé » ; certains composants restent partagés et le firmware demeure firmware.
  8. Le DMA GPU est puissant par conception : le dispositif peut lire/écrire la mémoire hôte à haute vitesse. Sans configuration IOMMU correcte, vous avez essentiellement attaché un moteur très rapide de corruption mémoire au bus PCIe.
  9. Les pilotes GPU figurent parmi les plus gros modules noyau sur les hôtes Linux, ce qui augmente la surface de bugs et fait de la cadence de patchs un enjeu de fiabilité, pas seulement d’hygiène de sécurité.

Rien de tout ceci ne prouve qu’il y aura un « GPU Meltdown » catastrophique unique. Cela prouve cependant que les préconditions existent : ressources partagées, comportements opaques, code privilégié, et d’énormes incitations à optimiser.

Modèle de menace : ce qui casse réellement en production

Définissons « Spectre pour le graphisme » en termes opérationnels. Vous n’avez pas besoin d’un livre blanc. Vous devez savoir quoi craindre et quoi ignorer.

Modèle de menace A : nœud GPU multi‑locataire

Deux charges partagent le même GPU physique dans le temps (partage temporel), ou partagent le même nœud avec passthrough/vGPU, ou partagent le chemin mémoire CPU du nœud via mémoire épinglée et DMA.
L’attaquant contrôle une charge, la victime en est une autre.

Objectif : inférer des secrets (poids de modèle, propriétés des données d’entrée, ou clés) en utilisant des canaux auxiliaires ou l’état résiduel.

Modèle de menace B : code GPU non fiable à l’intérieur d’un conteneur

Le conteneur exécute CUDA, ROCm, Vulkan, ou OpenCL. Il a accès à /dev/nvidia* ou /dev/dri*.
Le pilote est dans le noyau de l’hôte.

Objectif : échapper au conteneur, obtenir root, ou lire les données des autres locataires en exploitant un bug du pilote.

Modèle de menace C : code d’entraînement « de confiance » qui ne l’est pas vraiment

Une bibliothèque fournisseur, une dépendance pip, un plugin de modèle, ou un patch de performance « utile » exécute des kernels GPU et du code hôte.
Personne n’est malveillant ; beaucoup sont simplement négligents.

Objectif : fuite accidentelle ou perte d’intégrité — résultats silencieusement erronés, ou données restant en mémoire GPU.

Modèle de menace D : chaîne d’approvisionnement et firmware

Le firmware GPU, les blobs signés, les contrôleurs de gestion, et les agents hôtes (DCGM, daemons de persistance, exporters de monitoring) font partie de votre TCB que ça vous plaise ou non.

Si vous exploitez des nœuds mono‑locataire (un client par hôte physique, sans exception), vous pouvez réduire fortement le risque.
Si vous partagez des GPU, vous faites de l’ingénierie de sécurité. Vous n’avez peut‑être pas budgété pour cela. Tant pis.

Où les fuites de données GPU surviennent : les vraies lignes de faille

1) Mémoire GPU résiduelle (la classe « oublier d’effacer »)

La fuite la plus simple est aussi la plus embarrassante : un job libère de la mémoire, un autre job alloue, et des octets obsolètes réapparaissent.
Sur les CPU, les allocateurs OS et les politiques de zéro‑page réduisent cela. Sur les GPU, le comportement varie selon le pilote et le mode.

En production : supposez que la mémoire résiduelle est possible à moins que vous n’ayez prouvé le contraire pour votre stack. « Le pilote efface probablement » n’est pas un contrôle. C’est de la pensée magique avec une demande de budget attachée.

2) Caches partagés, mémoire partagée et temporisation

Les canaux auxiliaires prospèrent sur les ressources partagées. Les GPU ont des caches L2, des banques de mémoire partagée, des caches de textures (en contextes graphiques), et des contrôleurs mémoire qui peuvent fuir des informations via des patterns de temps et de contention.

L’attaquant n’a pas besoin de lire directement votre mémoire. Il suffit qu’il sache combien de temps quelque chose a pris, quelles lignes de cache ont été expulsées, ou s’il y a eu un motif de conflit de banques corrélé à un accès dépendant d’un secret.

3) Addressage virtuel unifié et mémoire hôte épinglée

UVA et la mémoire épinglée sont des fonctionnalités de performance. Elles sont aussi un piège de sécurité si vous ne contrôlez pas qui peut allouer quoi et quand, car elles étendent la portée du GPU dans les chemins mémoire hôtes.

Le DMA sans IOMMU correctement configuré est l’histoire classique : le périphérique peut accéder à la mémoire physique hôte au‑delà de ce que vous vouliez. Parfois « l’attaquant » n’est qu’un bug.

4) Surface d’attaque du pilote et du runtime

Le stack pilote GPU est un musée de couches de compatibilité : runtime CUDA, module noyau, bibliothèques espace utilisateur, compilation JIT, compilateurs de shaders, ICD Vulkan, et plus.
C’est gros. Il parse des entrées non fiables (kernels, shaders, PTX, SPIR‑V). Historiquement, c’est là que résident les bugs de corruption mémoire.

Blague #1 (courte, pertinente) : les pilotes GPU sont comme des horloges antiques — bel ingénierie, beaucoup de pièces mobiles, et si vous les bousculez elles font tourner le temps de travers.

5) Le partitionnement n’est pas de l’isolation à moins que vous puissiez expliquer les restes

MIG, profils vGPU, et politiques d’ordonnancement peuvent réduire les interférences, mais vous devez savoir ce qui reste partagé :
moteurs de copie, partitions L2, contrôleurs mémoire, chemins d’interconnexion, et pools gérés par le firmware.

Votre posture de sécurité dépend des boutons que vous pouvez régler et de ceux que vous êtes forcé d’accepter. Si vous ne pouvez pas le décrire, vous ne pouvez pas le défendre lors d’un audit — ni dans un postmortem.

Trois mini‑histoires d’entreprise (anonymisées)

Mini‑histoire 1 : L’incident causé par une mauvaise hypothèse

Une entreprise SaaS de taille moyenne a déployé des nœuds d’inférence équipés de GPU pour prendre en charge des clients d’entreprise « apportez votre propre modèle ».
Ils ont fait des choses raisonnables : chaque client a obtenu un namespace Kubernetes, des policies réseau, un stockage d’objets par locataire, et un RBAC strict.
Ils ont aussi fait une chose déraisonnable : ils ont supposé que le GPU était « juste un autre périphérique » et que les conteneurs étaient une frontière suffisante.

Un client a signalé voir des artefacts étranges, faiblement structurés, dans les sorties qui ressemblaient à des morceaux des prompts d’autres clients.
Au début, cela ressemblait à une hallucination du modèle. L’équipe support l’a classé comme « confusion du client ». Le client a insisté, envoyé des exemples reproductibles, et soudain le ton a changé.

L’enquête interne a révélé que des jobs étaient ordonnancés sur le même GPU les uns après les autres. Le processus d’inférence utilisait un allocateur de pool mémoire sur le GPU pour réduire le surcoût malloc/free.
Dans certains chemins d’erreur (timeouts et sorties anticipées), des buffers étaient libérés mais pas explicitement écrasés.
Les premières allocations d’un nouveau locataire réutilisaient parfois ces pages, et un point de debug (destiné à l’introspection du modèle) retournait des tenseurs intermédiaires bruts.

Le problème n’était pas un canal auxiliaire sophistiqué. C’était une fuite de « rétention de données » ennuyeuse et classique avec un accent GPU.
La correction a été tout aussi ennuyeuse : supprimer le point de debug des builds destinés aux locataires, ajouter l’effacement obligatoire des buffers sensibles, et imposer la règle « un locataire par GPU physique » jusqu’à validation d’un modèle d’isolation plus fort.

La leçon principale du postmortem : ils avaient traité « la mémoire GPU » comme si elle se comportait comme la RAM privée d’un processus.
Ce n’était pas le cas. Et l’incident n’a pas été déclenché par un attaquant ; il a été déclenché par un client attentif.

Mini‑histoire 2 : L’optimisation qui s’est retournée contre eux

Une fintech exécutait des simulations de risque accélérées par GPU pendant la nuit. La charge était prévisible, bien contrôlée, et pas multi‑locataire.
Puis l’entreprise a demandé « un tour plus rapide » et « meilleure utilisation », et quelqu’un a suggéré de mélanger des jobs d’analytics ad‑hoc pendant la même fenêtre.

L’équipe a activé un partage temporel agressif des GPU et a entassé plus de conteneurs par nœud.
Ils ont aussi activé un mode performance qui gardait les contexts chauds et évitait les resets entre jobs.
L’utilisation s’est améliorée sur le dashboard. La latence a diminué. Tout le monde applaudissait.

Deux semaines plus tard, ils ont eu un incident de fiabilité : les résultats de simulation divergeaient parfois de façon subtile. Pas de crashs, juste des réponses erronées.
À 3 h du matin, c’est le pire genre d’erreur.

Cause racine : interférence par ressources partagées. Les jobs ad‑hoc ont modifié la résidence dans le cache et la disponibilité de la bande passante mémoire selon des motifs qui ont impacté la stabilité numérique.
Certains kernels étaient sensibles à l’ordre d’exécution non déterministe ; d’autres reposaient sur des réductions concurrentes « assez bonnes » quand le GPU était sinon inactif.
Quand tout était entassé et partagé temporellement, la gigue d’ordre d’exécution a augmenté, et les résultats ont dérivé au‑delà des tolérances acceptables.

Ils ont annulé l’optimisation, séparé les charges par pools de nœuds, et requis des flags mathématiques déterministes pour tout job produisant des sorties régulées.
Enseignement sécurité : la réflexion sur les canaux auxiliaires et la réflexion sur l’intégrité sont cousines. Les ressources partagées ne fuient pas seulement ; elles biaisent aussi.

Mini‑histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise

Une équipe plateforme d’entreprise exploitait un cluster GPU pour des équipes ML internes. Ils n’étaient pas célèbres, pas tape‑à‑l’œil, et n’étaient jamais invités aux conférences.
Ils avaient un super‑pouvoir : ils étaient allergiques aux « cas particuliers ».

Chaque nœud GPU démarrait avec IOMMU activé, Secure Boot appliqué, et une politique de verrouillage du noyau.
Les versions de pilote étaient figées par pool de nœuds. Les déploiements de patchs étaient canari‑d’abord, avec rollback automatique si les budgets d’erreur bougeaient.
Les profils MIG étaient standardisés, et l’ordonnanceur n’affectait aux GPU partagés que des workloads avec labels de sécurité compatibles.

Un trimestre, une mise à jour du pilote GPU a introduit une régression provoquant des hangs sporadiques sous une combinaison spécifique de mémoire hôte épinglée et de transferts peer‑to‑peer.
Les équipes ML étaient mécontentes parce que leurs runs d’entraînement ralentissaient lorsque l’équipe plateforme retenait la mise à jour.
Mais l’équipe plateforme disposait de télémétrie : les nœuds canaris montraient une hausse des erreurs Xid et une augmentation des erreurs correctables PCIe. Ils ont gelé le déploiement.

Deux semaines plus tard, un avis de sécurité est paru pour la même branche de pilote, impliquant un chemin d’entrée contrôlé par l’utilisateur pouvant mener à une élévation de privilèges.
Ils ne la faisaient déjà pas tourner. Ils ont patché vers une build corrigée, avec les mêmes portes canari.

La morale : les contrôles ennuyeux n’empêchent pas seulement les violations ; ils évitent les week‑ends frénétiques. La seule chose meilleure qu’une réponse rapide à un incident est de ne pas avoir l’incident.

Tâches pratiques : commandes, sorties et décisions (12+)

Ce ne sont pas des tâches « lancez un scanner et priez ». Ce sont des vérifications rugueuses qui vous disent si votre flotte GPU est réellement isolée, patchée, et se comporte bien.
Chaque tâche inclut : commande, sortie exemple, ce que ça signifie, et la décision que vous prenez.

Tâche 1 : Inventaire du modèle GPU, du pilote et du runtime

cr0x@server:~$ nvidia-smi
Wed Jan 17 10:21:32 2026
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.14    Driver Version: 550.54.14    CUDA Version: 12.4     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA A100-SXM4-40GB  On| 00000000:81:00.0 Off |                    0 |
| N/A   46C    P0   165W / 400W |   8200MiB / 40960MiB |     72%      Default |
|                               |                      |              Enabled |
+-------------------------------+----------------------+----------------------+

Ce que cela signifie : Vous confirmez la branche exacte du pilote, la compatibilité CUDA, le mode persistance, et si MIG est activé.

Décision : Verrouillez ce tuple (modèle GPU + version du pilote + version CUDA) dans votre CMDB et vos manifests de déploiement. Si ça change de façon inattendue, traitez‑le comme un changement de production.

Tâche 2 : Vérifier les instances MIG et confirmer l’absence de partage accidentel

cr0x@server:~$ nvidia-smi -L
GPU 0: NVIDIA A100-SXM4-40GB (UUID: GPU-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee)
  MIG 1g.5gb Device 0: (UUID: MIG-GPU-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/1/0)
  MIG 1g.5gb Device 1: (UUID: MIG-GPU-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/2/0)
  MIG 2g.10gb Device 2: (UUID: MIG-GPU-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/3/0)

Ce que cela signifie : Le GPU est partitionné. Les workloads peuvent être assignés aux UUIDs d’instances MIG.

Décision : Si vous faites de l’ordonnancement multi‑tenant, n’affectez des locataires qu’à des instances MIG dédiées. Si vous ne pouvez pas expliquer ce qui est partagé entre instances pour votre architecture, ne prétendez pas à une « isolation forte ».

Tâche 3 : Confirmer quels processus sont sur le GPU

cr0x@server:~$ nvidia-smi pmon -c 1
# gpu        pid  type    sm   mem   enc   dec   jpg   ofa   command
    0      19423     C     78    20     0     0     0     0   python
    0      20110     C      5     2     0     0     0     0   tritonserver

Ce que cela signifie : Vous voyez les processus compute actifs et une estimation de l’utilisation des ressources.

Décision : Si vous attendez un comportement mono‑locataire et voyez des processus inconnus, arrêtez et enquêtez. Si vous êtes multi‑tenant, vérifiez que les processus correspondent aux pods/jobs attendus.

Tâche 4 : Cartographier les fichiers de périphérique GPU et les permissions (surface d’évasion de conteneur)

cr0x@server:~$ ls -l /dev/nvidia* /dev/dri/*
crw-rw-rw- 1 root root 195,   0 Jan 17 10:15 /dev/nvidia0
crw-rw-rw- 1 root root 195, 255 Jan 17 10:15 /dev/nvidiactl
crw-rw-rw- 1 root root 195, 254 Jan 17 10:15 /dev/nvidia-modeset
crw-rw---- 1 root video 226,   0 Jan 17 10:15 /dev/dri/card0
crw-rw---- 1 root render 226, 128 Jan 17 10:15 /dev/dri/renderD128

Ce que cela signifie : Les nœuds de périphérique GPU world‑writable sont un signal d’alerte. Ils élargissent qui peut parler au pilote noyau.

Décision : Renforcez les permissions (groupes comme video/render), et assurez‑vous que les conteneurs n’obtiennent que les périphériques dont ils ont besoin. « chmod 666 » appartient aux labs, pas à la prod.

Tâche 5 : Vérifier que l’IOMMU est activé (confinement DMA)

cr0x@server:~$ dmesg | grep -E "IOMMU|DMAR" | head
[    0.812345] DMAR: IOMMU enabled
[    0.812900] DMAR: Host address width 46
[    0.813210] DMAR: DRHD base: 0x000000fed90000 flags: 0x0

Ce que cela signifie : La plateforme a le remappage DMA activé. C’est un contrôle fondamental contre le « le périphérique peut tout lire ».

Décision : Si vous ne voyez pas ceci, corrigez les paramètres de boot (Intel : intel_iommu=on ; AMD : amd_iommu=on) et validez en staging. Pas d’IOMMU + workloads non fiables = provocation.

Tâche 6 : Confirmer que le GPU est dans un groupe IOMMU (sanity passthrough)

cr0x@server:~$ for d in /sys/kernel/iommu_groups/*/devices/*; do echo "$(basename "$(dirname "$d")") $(basename "$d")"; done | grep -i nvidia | head
27 0000:81:00.0
27 0000:81:00.1

Ce que cela signifie : Le GPU et ses fonctions associées sont groupés pour l’isolation.

Décision : Si votre GPU partage un groupe IOMMU avec des périphériques aléatoires, le passthrough et l’isolation forte deviennent plus difficiles. Ajustez les paramètres BIOS/ACS ou choisissez d’autres emplacements/plateformes.

Tâche 7 : Vérifier l’impureté du pilote noyau et les versions de modules

cr0x@server:~$ uname -r
6.5.0-27-generic
cr0x@server:~$ modinfo nvidia | egrep "version:|srcversion|vermagic"
version:        550.54.14
srcversion:     1A2B3C4D5E6F7890ABCD123
vermagic:       6.5.0-27-generic SMP preempt mod_unload

Ce que cela signifie : Confirme la build exacte du module noyau et la compatibilité noyau. Utile lors du triage d’incident (« est‑ce le nœud sur le pilote bizarre ?»).

Décision : Si vous avez des versions de pilote mélangées dans le même pool, arrêtez. Ça transforme le debug en archéologie.

Tâche 8 : Surveiller les erreurs Xid GPU (signaux défauts hardware/driver)

cr0x@server:~$ journalctl -k -g "NVRM: Xid" -n 5
Jan 17 09:58:01 server kernel: NVRM: Xid (PCI:0000:81:00): 31, pid=19423, name=python, Ch 0000003a
Jan 17 09:58:01 server kernel: NVRM: Xid (PCI:0000:81:00): 13, Graphics Exception: ESR 0x404600=0x80000002

Ce que cela signifie : Les codes Xid indiquent des fautes GPU. Certaines sont des bugs applicatifs ; d’autres des régressions de pilote ; d’autres du hardware.

Décision : Si les Xid corrèlent avec une mise à jour du pilote, rollback canari. Si elles corrèlent avec des workloads spécifiques, isolez et reproduisez. Si elles corrèlent avec température/puissance, vérifiez refroidissement et alimentation.

Tâche 9 : Vérifier la santé PCIe (erreurs correctables peuvent indiquer instabilité)

cr0x@server:~$ journalctl -k -g "PCIe Bus Error" -n 5
Jan 17 09:57:49 server kernel: pcieport 0000:80:01.0: PCIe Bus Error: severity=Corrected, type=Physical Layer
Jan 17 09:57:49 server kernel: pcieport 0000:80:01.0: device [8086:2030] error status/mask=00000001/00002000

Ce que cela signifie : Les erreurs correctables ne sont pas « normales ». Elles sont un indicateur précoce de liens instables, risers défectueux, intégrité de signal marginale, ou problèmes d’alimentation.

Décision : Si les comptes augmentent, planifiez une maintenance avant d’avoir des erreurs non correctables et des runs d’entraînement morts. La fiabilité est la cousine ennuyeuse de la sécurité.

Tâche 10 : Valider l’isolation des périphériques cgroup sur un nœud Kubernetes

cr0x@server:~$ kubectl get pods -A -o wide | grep gpu
mlteam-a   infer-7d9c6f6d7d-9p2kq   1/1   Running   0   2d   10.42.3.19   gpu-node-03
mlteam-b   train-0                  1/1   Running   0   1d   10.42.3.20   gpu-node-03
cr0x@server:~$ kubectl exec -n mlteam-a infer-7d9c6f6d7d-9p2kq -- ls -l /dev/nvidia0
crw-rw---- 1 root video 195, 0 Jan 17 10:15 /dev/nvidia0

Ce que cela signifie : Deux pods partagent un nœud ; vous vérifiez si l’exposition des périphériques est contrôlée et pas world‑writable.

Décision : Si vous faites du multi‑tenant, imposez des node pools + taints/tolerations + politiques de device plugin afin que des locataires non liés ne co‑habitent pas sauf si vous acceptez explicitement ce risque.

Tâche 11 : Confirmer si le reset GPU est possible et utilisé entre locataires

cr0x@server:~$ nvidia-smi --gpu-reset -i 0
GPU 00000000:81:00.0 is currently in use by one or more processes.
Reset could not be performed.

Ce que cela signifie : Vous ne pouvez pas réinitialiser un GPU en cours d’utilisation ; les resets sont perturbateurs et nécessitent de l’orchestration.

Décision : Si vous comptez sur des resets comme « effacement », construisez un hook scheduler : drainer les workloads, reset, puis admettre le locataire suivant. Sinon, considérez « reset entre locataires » comme un contrôle fantaisiste.

Tâche 12 : Vérifier le mode persistance et décider s’il nuit à l’isolation

cr0x@server:~$ nvidia-smi -q | grep -A2 "Persistence Mode"
    Persistence Mode                    : Enabled
    Accounting Mode                     : Disabled

Ce que cela signifie : Le mode persistance garde l’état du pilote chaud pour un démarrage plus rapide. Il peut aussi conserver plus d’état entre les jobs.

Décision : Pour une isolation multi‑tenant stricte, envisagez de désactiver la persistance dans les pools partagés et mesurer l’impact sur la performance. Ne le faites pas aveuglément ; faites‑le intentionnellement.

Tâche 13 : Inspecter les hugepages et la pression mémoire épinglée (performance + effets secondaires)

cr0x@server:~$ grep -E "HugePages|Hugetlb" /proc/meminfo
HugePages_Total:       8192
HugePages_Free:        1024
HugePages_Rsvd:         512
Hugetlb:           16777216 kB

Ce que cela signifie : Les workloads GPU utilisent souvent de la mémoire épinglée et des hugepages indirectement. La pression ici peut provoquer des pics de latence et des échecs étranges ressemblant à « GPU lent ».

Décision : Si HugePages_Free s’effondre au démarrage des jobs, ajustez les allocations de hugepages par pool et arrêtez de sur‑souscrire la mémoire comme un hobby.

Tâche 14 : Repérer un mappage de périphériques suspect dans un conteneur

cr0x@server:~$ kubectl exec -n mlteam-b train-0 -- sh -lc 'mount | grep -E "nvidia|dri" || true; ls -l /dev | grep -E "nvidia|dri"'
tmpfs on /dev type tmpfs (rw,nosuid,strictatime,mode=755,size=65536k)
crw-rw---- 1 root video 195, 0 Jan 17 10:15 nvidia0
crw-rw---- 1 root video 195, 255 Jan 17 10:15 nvidiactl

Ce que cela signifie : Vous vérifiez si le conteneur voit plus de nœuds de périphérique que prévu. Certains runtimes montent accidentellement des contrôles supplémentaires.

Décision : Si les conteneurs voient /dev/nvidiactl et que vous ne l’attendiez pas, revisitez la configuration du runtime. Réduisez l’exposition des périphériques. La surface d’attaque croît avec les descripteurs de fichiers.

Tâche 15 : Confirmer l’état de kernel lockdown / secure boot (empêche certains altérations du noyau)

cr0x@server:~$ cat /sys/kernel/security/lockdown
integrity
cr0x@server:~$ mokutil --sb-state
SecureBoot enabled

Ce que cela signifie : Le mode lockdown et Secure Boot rendent plus difficile le chargement de modules noyau non signés ou la falsification du noyau — utiles quand votre pilote GPU est un blob privilégié.

Décision : Si Secure Boot est désactivé dans des environnements où vous exécutez des workloads non fiables, vous acceptez un plus grand rayon d’impact. Activez‑le, puis gérez correctement la signature des pilotes.

Tâche 16 : Vérifier ce que l’ordonnanceur vous fait (co‑localisez‑vous des locataires ?)

cr0x@server:~$ kubectl describe node gpu-node-03 | egrep -A3 "Taints|Labels"
Labels:             nodepool=gpu-shared
                    accelerator=nvidia
Taints:             dedicated=gpu-shared:NoSchedule

Ce que cela signifie : Les labels/taints de nœud indiquent si le cluster est prévu pour le partage.

Décision : Si des workloads sensibles atterrissent sur gpu-shared, c’est un échec de policy. Corrigez les contraintes d’ordonnancement et les contrôles d’admission, pas seulement « dites aux gens d’être prudents ».

Voilà plus d’une douzaine de vérifications. Exécutez‑les régulièrement. Automatisez celles que vous pouvez, et alertez sur la dérive. La sécurité GPU, c’est 30 % d’architecture et 70 % d’empêcher la flotte de changer silencieusement sous vous.

Mode d’emploi pour diagnostic rapide

Quand quelque chose sent mauvais — sorties inattendues, latence inexpliquée, corrélations inter‑locataires étranges — vous avez besoin d’un chemin rapide vers « est‑ce un goulot, un bug, ou une brèche de frontière ? »
Voici un playbook qui marche au milieu d’un incident.

Première étape : classer la défaillance (confidentialité vs intégrité vs disponibilité)

  • Confidentialité : prompts, embeddings, tenseurs, ou poids apparaissent là où ils ne devraient pas ; les logs montrent des accès inattendus ; des locataires rapportent « voir d’autres ».
  • Intégrité : les résultats dérivent, sorties non déterministes, baisse silencieuse de précision, sommes de contrôle d’artefacts qui ne correspondent pas.
  • Disponibilité : hangs GPU, resets, tempêtes Xid, chutes de performance, timeouts.

Deuxième étape : déterminer si du partage se produit

  • Le MIG est‑il activé et correctement assigné, ou faites‑vous du time‑slicing sur des GPU complets ?
  • Deux locataires sont‑ils sur le même nœud physique ? Le même GPU ? Back‑to‑back sur le même GPU ?
  • Le mode persistance garde‑t‑il des contexts chauds ?

Troisième étape : chercher les « signaux classiques »

  • Logs noyau : erreurs Xid, erreurs bus PCIe, fautes IOMMU.
  • Cartographie des processus : PIDs inattendus sur le GPU, contexts zombies.
  • Dérive de l’ordonnanceur : labels/taints modifiés, mauvaise configuration de node pool, nouvelle version du plugin GPU.

Quatrième étape : isoler par soustraction

  1. Déplacez la charge sur un nœud monosite connu et comparez le comportement.
  2. Désactivez l’optimisation « utile »: persistence mode, pools mémoire agressifs, partage temporel.
  3. Verrouillez versions pilote/runtime et reproduisez. Si vous ne pouvez pas reproduire de façon déterministe, vous ne pouvez pas prétendre avoir corrigé.

Cinquième étape : décider si vous êtes en « mode incident de sécurité »

S’il y a une exposition crédible de données inter‑locataires, cessez de traiter ça comme un bug de performance. Geler les changements d’ordonnancement, préserver les logs, snapshotter les configurations, et escalader.
C’est là que vous voulez l’idée paraphrasée de Gene Kranz : être dur et compétent — pas de drame, pas de déni, juste une réponse disciplinée.

Erreurs courantes : symptômes → cause racine → correction

Les échecs de sécurité GPU se déguisent souvent en « performance étrange » ou « ML non déterministe ». Certains le sont. D’autres non. Voici un guide de terrain.

Erreur 1 : « On est en sécurité parce que c’est un conteneur »

Symptômes : un pod voit des nœuds de périphérique GPU qu’il ne devrait pas voir ; crashes noyau inattendus ; l’équipe sécurité demande « quel module noyau parse les entrées des locataires ?» et tout le monde regarde en bas.

Cause racine : l’accès GPU fait le pont directement vers les pilotes noyau hôtes. Les conteneurs ne virtualisent pas le noyau.

Correction : restreindre l’exposition /dev, utiliser des node pools dédiés pour les locataires non fiables, et traiter les mises à jour de pilote GPU comme des mises à jour noyau — avec canaris et rollbacks.

Erreur 2 : « On n’a pas besoin d’IOMMU ; c’est plus lent »

Symptômes : corruption mémoire inexpliquée, panics hôtes rares, résultats d’audit effrayants, ou incapacité à défendre les frontières DMA.

Cause racine : remappage DMA désactivé. Le GPU peut accéder trop librement à la mémoire hôte.

Correction : activer IOMMU dans le BIOS et les paramètres noyau ; valider le groupement des dispositifs ; benchmarker l’impact réel au lieu de supposer le pire.

Erreur 3 : « Reset GPU = effacement sécurisé »

Symptômes : « on reset entre locataires » mais on ne peut pas réellement reset sous charge ; échecs intermittents après resets forcés ; comportement de contexte obsolète persistant.

Cause racine : les resets sont opérationnellement difficiles ; certains états peuvent persister ailleurs ; et vous ne pouvez pas reset ce que vous ne pouvez pas drainer.

Correction : implémenter drainage de job + orchestration de reset, ou passer à une isolation stricte (GPU dédiés/instances MIG) et effacement explicite des buffers dans le code.

Erreur 4 : « Le mode performance est inoffensif »

Symptômes : corrélations inter‑job accrues, comportement étrange au warm‑start, et dérive qui disparaît au reboot des nœuds.

Cause racine : mode persistance, allocateurs en cache, et contexts chauds conservent plus d’état que prévu.

Correction : définir des niveaux de sécurité. Pour les workloads très sensibles, désactiver les fonctionnalités de warm‑state ou isoler sur du hardware mono‑locataire.

Erreur 5 : « MIG signifie isolation parfaite »

Symptômes : les locataires s’influencent encore sur les performances ; les auditeurs demandent ce qui est partagé ; vous ne pouvez pas répondre sans une présentation du fournisseur.

Cause racine : MIG réduit le partage mais ne l’efface pas. Certains composants restent partagés et le firmware est une couche commune.

Correction : traiter MIG comme un outil de réduction du risque, pas une preuve complète. Ajouter des politiques d’ordonnancement, du monitoring, et des limites sur quels locataires peuvent co‑habiter.

Erreur 6 : « C’est juste de la non‑déterminisme »

Symptômes : sorties régulées ou critiques qui dérivent ; différents runs produisent des décisions différentes ; ça n’arrive qu’en concurrence.

Cause racine : contention sur ressources partagées change l’ordre d’exécution, le timing, et le comportement des réductions flottantes.

Correction : activer les modes déterministes quand disponibles, isoler les workloads critiques, et arrêter de mélanger jobs ad‑hoc avec des pipelines régulés sur des GPU partagés.

Erreur 7 : « On patchera plus tard ; les GPU sont fragiles »

Symptômes : branches de pilotes qui divergent ; upgrade effrayé ; avis de sécurité qui s’accumulent ; finalement vous restez bloqué sur un ancien stack incapable de faire tourner de nouveaux frameworks.

Cause racine : absence de discipline canari/rollback, plus couverture de tests insuffisante pour les workloads GPU.

Correction : construire un pipeline de patch GPU avec smoke tests automatisés (kernels simples, patterns d’allocation mémoire, collectives NCCL) et déploiement échelonné.

Blague #2 (courte, pertinente) : « On patchera le pilote GPU le trimestre prochain » est la version infra de « je commencerai les backups demain ».

Listes de contrôle / plan étape par étape

Étape par étape : durcir un pool de nœuds GPU multi‑locataire

  1. Décidez votre niveau de sécurité : nœuds mono‑locataire pour les workloads sensibles ; nœuds partagés uniquement pour des workloads internes de confiance, ou pour des locataires que vous acceptez d’isoler fortement.
  2. Activez IOMMU et vérifiez dans les logs. Confirmez des groupes IOMMU raisonnables. C’est le minimum pour la sécurité DMA.
  3. Standardisez la matrice pilote/runtime par pool. Un pool, une branche pilote, une cible CUDA/ROCm. La dérive est là où se cachent les incidents.
  4. Verrouillez les permissions des périphériques sur /dev/nvidia* et /dev/dri/*. Assurez‑vous que les conteneurs n’obtiennent que les périphériques nécessaires.
  5. Utilisez le partitionnement délibérément : profils MIG pour partage contrôlé ; évitez le time‑slicing ad‑hoc entre locataires non liés.
  6. Implémentez des contrôles d’admission pour que seules les namespaces approuvées puissent demander des GPU, et seulement sur des pools approuvés (via node selectors, taints, et runtime class).
  7. Désactivez les fonctionnalités « utiles » de persistance pour les pools haute sensibilité, ou prouvez qu’elles ne retiennent pas d’état sensible dans votre environnement.
  8. Effacez les buffers sensibles dans le code applicatif sur chemins d’erreur et sorties anticipées. Ne comptez pas sur le comportement de l’allocateur.
  9. Instrumentez les logs noyau pour Xid, fautes IOMMU, et erreurs PCIe. Alarmez sur les changements, pas seulement sur des seuils absolus.
  10. Canarisez chaque mise à jour de pilote avec des workloads représentatifs, puis déployez graduellement. Traitez les nœuds GPU comme une flotte noyau spéciale — parce qu’ils le sont.

Checklist : signes qu’il faut arrêter de partager les GPU immédiatement

  • Vous ne pouvez pas garantir quel locataire a tourné avant quel locataire sur le même GPU.
  • Vous n’avez pas IOMMU activé et vérifié.
  • Les nœuds de périphérique GPU sont world‑writable ou largement exposés aux pods.
  • Vous ne pouvez pas mapper rapidement les PIDs GPU aux pods/jobs durant la réponse à incident.
  • Vous n’avez pas de pipeline canari pour les mises à jour de pilote et vous craignez les patchs.
  • Vous traitez des données régulées ou des secrets contractuels et votre récit d’isolation est « faites‑nous confiance ».

Checklist : télémétrie minimale pour sécurité et fiabilité GPU

  • Version du pilote, version du firmware (quand disponible), et version du noyau par nœud.
  • Utilisation GPU, usage mémoire, événements ECC, et événements de reset.
  • Logs noyau : événements Xid, fautes IOMMU, erreurs PCIe.
  • Logs de placement de l’ordonnanceur : locataire → nœud → GPU/instance MIG mapping.
  • Cycle de vie des jobs : temps de démarrage/arrêt, classification du mode d’échec, et si le GPU a été drainé/reset entre locataires.

FAQ

1) Existe‑t‑il déjà un équivalent « Spectre pour les GPU » ?

Il y a eu des travaux de recherche et des avis sur des canaux auxiliaires et des problèmes d’isolation GPU, mais le point plus large est structurel : les GPU partagent des ressources et exécutent des pilotes privilégiés.
Les conditions pour une rupture de classe majeure existent même si le CVE à gros titre n’est pas encore apparu.

2) Les canaux auxiliaires sont‑ils pratiques dans le monde réel ?

Si l’attaquant peut exécuter du code sur le même GPU physique (ou le même hôte avec chemins partagés), la praticabilité augmente beaucoup.
La partie la plus difficile est généralement la colocation ; l’ordonnancement cloud et les clusters partagés la rendent plus facile que nous aimerions l’admettre.

3) MIG résout‑il la sécurité multi‑tenant ?

Cela aide—significativement—en partitionnant les ressources. Mais « résout » est trop fort.
Vous avez toujours le firmware, les pilotes, et certains chemins matériels partagés. Traitez MIG comme « réduction de risque + meilleurs primitives d’ordonnancement », pas comme un fossé magique.

4) Quel est le plus grand risque de sécurité GPU dans Kubernetes ?

La frontière, c’est le pilote noyau de l’hôte. Si vous donnez à un pod l’accès au GPU, vous lui donnez une surface d’attaque noyau complexe.
L’autre risque commun est la dérive politique : des pods atterrissant sur des nœuds partagés parce que labels/taints n’étaient pas appliqués.

5) Devons‑nous désactiver le mode persistance ?

Pour les pools multi‑tenant haute sensibilité, le désactiver est une valeur par défaut raisonnable — puis mesurez l’impact au démarrage et compensez avec une planification de capacité.
Pour les nœuds mono‑locataire, le mode persistance est généralement acceptable et améliore la fiabilité en réduisant le churn du pilote.

6) Comment empêcher les fuites de mémoire GPU résiduelle ?

Ne comptez pas sur « probablement effacé ». Ajoutez l’effacement explicite des buffers sensibles dans le code applicatif, surtout sur les chemins d’erreur.
Opérationnellement, isolez les locataires (GPU dédié ou instance MIG) et envisagez l’orchestration drain/reset quand c’est faisable.

7) Les mises à jour de pilotes GPU sont‑elles surtout un problème de sécurité ou de fiabilité ?

Les deux. Le pilote est du code privilégié. Les avis de sécurité comptent.
Mais en pratique, la plupart des équipes sont d’abord blessées par des régressions et des hangs. Construisez un pipeline canari pour pouvoir patcher sans parier.

8) Le confidential computing peut‑il protéger les workloads GPU ?

Le confidential computing côté CPU aide à protéger la mémoire hôte et les frontières VM, ce qui a de la valeur.
Les GPU ajoutent de la complexité : DMA dispositif, caches partagés, et modèles de confiance spécifiques au fournisseur. Considérez‑le comme « améliore l’histoire » plutôt que « résout le problème ».

9) Quelle est la réduction de risque la plus rapide si on ne peut pas tout repenser ?

Arrêtez de co‑localiser des locataires non liés sur le même GPU physique. Utilisez des GPU dédiés ou des instances MIG avec un ordonnancement strict.
Ensuite activez IOMMU et verrouillez l’accès aux périphériques. Ces deux changements éliminent beaucoup de modes d’échec stupides.

10) Que devons‑nous dire aux auditeurs et aux clients ?

Dites‑leur ce que vous faites réellement : si les GPU sont dédiés, partitionnés, ou partagés temporellement ; comment vous contrôlez l’accès aux périphériques ; votre cadence de patch ; et votre plan de réponse à incident.
N’exagérez pas l’« isolation » sauf si vous pouvez l’expliquer jusqu’au niveau du périphérique et du pilote.

Prochaines étapes à faire cette semaine

Si vous voulez un seul enseignement actionnable : arrêtez de traiter les GPU comme « juste des accélérateurs ». Ce sont des ordinateurs partagés avec une hiérarchie mémoire étrange et un pilote privilégié gros comme une petite ville.

  1. Inventoriez et verrouillez vos versions GPU/pilote/runtime par pool de nœuds. Rendez la dérive visible.
  2. Activez et vérifiez IOMMU. Puis confirmez des groupements IOMMU raisonnables.
  3. Auditez les permissions /dev et l’exposition des périphériques aux conteneurs. Supprimez les accès larges.
  4. Décidez votre politique de co‑localisation : mono‑locataire, partage MIG uniquement, ou partage temporel en dernier recours.
  5. Ajoutez un pipeline canari pour les mises à jour de pilotes GPU avec critères de rollback.
  6. Instrumentez les signaux : erreurs Xid, erreurs PCIe, fautes IOMMU, et mapping de placement de l’ordonnanceur.

Si un « Spectre pour le graphisme » frappe demain, vous ne gagnerez pas en ayant le meilleur communiqué de presse.
Vous gagnerez en ayant moins de frontières partagées, une meilleure télémétrie, et la discipline d’envoyer des mitigations sans tout casser en production.

← Précédent
Proxmox ZFS « cannot import pool » : causes, vérifications et options de récupération
Suivant →
L’ère du 14 nm : comment un nœud de procédé est devenu un drame commercial

Laisser un commentaire