Comprendre Les failles Meltdown et Spectre Variant 1 & 2

Si vous êtes ici cela veut dire que vous avez forcément entendu parler des récentes failles de sécurité Spectre et Meltdown qui ont touché les différents grands fournisseurs de processeurs nouvelle génération (Intel, AMD, ARM etc.)

Chronologie des faits

Ces dernières semaines (enfin depuis le début de l'année) beaucoup d'encre a coulé concernant ces deux classes de vulnérabilité qui touchent tous les modèles de processeurs nouvelle génération. Il faut dire que l'alerte a été donnée par les chercheurs de Google Project qui au passage ont fait un boulot énorme de recherche sur ce sujet.

Le problème est que depuis, beaucoup d'acteurs s'en sont mêlés, surtout que le thème est plutôt racoleur et au niveau communication c'est une vrai bombe qui est lancée par les médias du numérique. A les croire c'est une énorme catastrophe : tous les systèmes sont vulnérables, les grands mots de la sécurité informatique sont donc vu employés (vulnérabilités sous embargo etc.) Puis on entend qu'un patch est divulgué mais qu’il mettrait à mal les performances des processeurs Intel (car au début apparemment seul Intel est touché ce qui est plus que suspect) qui pourrait aller entre 5% et 30% de perte. L'utilisateur lambda qui apprend ça se dit tout de suite, il est hors de question de faire une mise à jour qui me fera perdre jusqu'à 30% de performance.

Tout est mis en place pour instaurer la panique, des patchs sortent chez Intel puis des complications arrivent tout ça a été fait dans la précipitation afin de répondre au gros « bordel/bad buzz » qui a été lancé à ce sujet (des serveurs qui redémarrent sans raison, des problèmes de gestion de température etc.) Il est donc recommandé de ne pas faire certaines mises à jour. Donc je me suis dit : et si on redonnait à César ce qui appartient à César. Voyons ce qu'est vraiment Spectre et Meltdown et quels sont vraiment les problèmes de perf’ et surtout le plus important : qui sont touchés par ces problèmes de performances (car oui on n’est pas tous égaux à ce sujet).

Meltdown

Nous allons commencer ici par décortiquer la faille Meltdown qui elle pour le moment n'a qu'une seule variante et qui pour le coup a vite été patchée par les éditeurs d'OS. Au passage un gros big up aux équipes de développeur système (Linux car les autres on s'en tape) qui ont fait un travail énorme et qui ont dû passer des fêtes de fin d'année vraiment horrible pour que 99,9% du reste des utilisateurs puissent avoir des systèmes sûrs (tout ça gratuitement donc respect).

Bon une fois le décor planté revenons à nos affaires... Meltdown est une faille qui brise une hypothèse fondamentale dans le monde de la sécurité des systèmes d'exploitation qui dit qu'une application s'exécutant en espace utilisateur (user-pace) ne peut pas accéder à la mémoire du noyau. Ceci est extrêmement important car la mémoire du noyau contrairement à l'espace user-pace peut contenir des informations sensibles provenant d'autres applications telles que des mots de passe, voire des clés publique/privée.

Afin de gérer cette restriction d'accès, les systèmes d'exploitation utilisent ce qu'on appelle des tables de pages pour diviser la mémoire virtuelle en deux sections :

  1. Une table de page pour le noyau

  2. Une table de page pour les applications en mode utilisateur.

Le noyau dépend donc du processeur qui va permettre au noyau qui est plus privilégié d'accéder aux deux sections tout en limitant les applications à la table de page d'utilisateur.

Vous l'aurez compris l'idée est de lire à partir de la table utilisateur dans la table de page du noyau.

Et c'est là que Meltdown intervient et vient nous prouver que la façon dont cela est géré par certains processeurs (on ne balancera pas les noms) qui exécutent ces processus dans les tables de page dans le désordre peut laisser la mémoire du noyau passer en mode utilisateur assez longtemps pour qu'elle soit capturée par une attaque de cache par canal latéral. Compliqué tout ça n'est-ce pas ? Voyons ça en détail avec un exemple pour mieux comprendre ce qui se passe en supposant que nous ayons le pseudo code suivant :

  1. Invalide le cache pour toutes les adresses dans User_PS1.

  2. Lit un octet de la mémoire du noyau dans la variable KernelData.

  3. Lire à partir de UserPS1 au décalage de kernelData.

Toujours pas compris ?

Voyons, pourtant c'est limpide. Lorsqu'un processus est exécuté par une application utilisateur, l'accès à la mémoire du noyau dans la ligne 2 déclenchera une erreur de segmentation en raison des restrictions d'accès.

Toutefois Meltdown montre qu'un processeur va lire aveuglément la mémoire du noyau avant d'évaluer les permissions d'accès et exécutera en plus la ligne 3 avant la vérification des permissions. Vous vous dites sûrement pourquoi le processeur fait-il cela ? La réponse est simple cela se produit en raison d'une optimisation des performances par le processeur qu'on appelle out-of-order execution en français on peut dire exécution dans le désordre.

Conseil numéro 1 : ne jamais tronquer sa sécurité pour du confort (c'est pas moi qu'il le dit c'est Benjamin Frankelin ici).

Un peuple prêt à sacrifier un peu de liberté pour un peu de sécurité ne mérite ni l'une ni l'autre, et finit par perdre les deux.

Qu'on pourrai paraphraser ainsi :

Une technologie prêt à sacrifier un peu de sécurité pour un peu d'otpimisation ne mérite ni l'une ni l'autre, et finit par perdre les deux.

Bon je vais être gentil avec vous et vous dire ce qui se passe réellement.

Pendant que la ligne 3 est déjà à moitié exécutée dans le processeur, dès que la variable KernelData est lue, une course entre la fin de la ligne 3 et la réalisation par le processeur que la ligne 2 est défectueuse. Il s'avère qu'il arrive très souvent que la ligne 3 remporte cette course et soit donc évaluée par le processeur avant la fin de la ligne 2 qui sera donc évaluée en parallèle par le CPU. Une fois la réalisation par le CPU de la faute à venir par l'exécution de la ligne 2, les résultats erronés exécutés dans le désordre serons ensuite effacés du CPU mais gardés dans le cache du CPU.

Dans la logique ce que l'on pourrait penser de façon légitime est que le processeur exécute le code ligne par ligne, pour cela il doit attendre la fin de la ligne 2 qui est plus lente que les autres en raison de l'accès mémoire pour entamer la ligne 3. Ça c'est notre logique et c'est ce qui fait la différence entre un cerveau humain et un processeur (on fonctionne de manière séquentielle). Cependant le processeur-lui suppose que la ligne 2 va éventuellement fonctionner donc il commence son exécution en parallèle même si celle-ci dépend de la ligne 2 pour se terminer, il le calcule tout de même en attendant et annulera son résultat dans le cas contraire (au final soit il gagne du temps et au pire des cas c'est juste un calcul en plus qui aura été sur un temps d'attente quoi qu'il arrive car il faut attendre la fin de la ligne 2).

Les attaques de canal latéral exploitent donc ces effets de cache restant. Notre exemple ici nous en dit plus sur l'attaque qui est possible par la lecture, à première vue inoffensive de User_PS1 en ligne 3 qui créé une entrée de cache pour l'adresse User_PS1+KernelData.

Un attaquant peut donc déterminer à partir d'une application en mode utilisateur, d'un processus différent le temps nécessaire pour lire chaque adresse dans le cache afin de déterminer quelle adresse a été mise en cache.

La ligne 1 quant à elle permet de garantir qu'il n'y a qu'une seule adresse. Le décalage du cache de l'adresse lue le plus rapidement révèle la valeur de Kernel_Data.

Le code malveillant a donc réussi à lire un octet de la mémoire du kernel. En répétant cette attaque sur différentes adresses à plusieurs reprises, on démontre que la mémoire du kernel est lisible à 500 ko/s. Donc les données sensibles du kernel peuvent être lues en quelques heures.

Vous avez peur ? Pas de panique ; comme dit plus haut une solution de patch a vite été publiée concernant Meltdown. Le nom du sauveur est donc sans suspens si vous avez bien suivi jusqu'ici KPTI (Kernel Page Tabulation Isolation). Avec KPTI les données du kernel ne sont pas incluses dans les tables de pages utilisées par le mode utilisateur, ce qui empêche définitivement l'attaque Meltdown.

Résumons un peu tout ça:

Pour en revenir à Meltdown et mieux comprendre son comportement voici quelques précisions.

Si jusqu'ici on a bien suivi, avant Meltdown il a été supposé par la quasi-totalité des développeurs système ainsi que les constructeurs de CPU qu'il était clairement impossible pour un code malveillant exécuté en mode user-land d'accéder aux données sensibles du noyau. Cependant Meltdown vient de nous démontrer que cela n'était pas tout à fait exact à cause de l'exécution dans le désordre pour des raisons d'optimisation du temps d'exécution.

Pour mitiger Meltdown, les développeurs du kernel ont implémenté KPTI. KPTI remet en place l'isolation fondamentale de la mémoire noyau et la mémoire utilisateur. Il fournit donc à chaque application deux ensembles de tables pages au lieu d'une et passe d'une table à une autre lors de chaque transition kernel-utilisateur utilisateur-kernel. Les tables de pages en place lors de l'exécution en mode user-land n'incluent plus les mappages vers les données du noyau. Donc lorsque le code malveillant de la ligne 2 dans notre exemple sera exécuté (en environnement utilisateur bien sûr) les tables de pages dans les CPU n'auront plus de mappage vers l'adresse mémoire du kernel. Cela empêche le CPU de lire données sensible à l'aveugle.

Où est le problème de performance dans tout ça ?

Ben voyons, vous ne voyez vraiment pas pourtant ça saute aux yeux !

Non je vous rassure c'est normal car c'est assez compliqué en fait. Dans ces pertes de performances c'est un autre concept qui entre en jeu c'est celui du TLB (Translation Lookaside Buffer) qu'on peut appeler en "franglais" Mémoire tampon de translation (je vous avais dit que c'était compliqué les gars faut suivre :) ).

Donc l'impact potentiel sur les performances vient des deux échanges de tables de pages supplémentaires sur chaque appel système. Alors certes cela ne représente que quelques instructions de plus par transition, cependant ces instructions effacent le TLB, ce qui peut avoir un sérieux impact sur les performances. Car le TLB est un cache qui stocke les adresses virtuelles correspondantes aux adresses physiques.

Lorsqu'un accès à la mémoire virtuelle est effectué, le processeur convertit l'adresse virtuelle de la requête en une adresse physique et extrait les données de la RAM. Cette conversion d'adresse virtuelle en adresse physique est plutôt rapide si cette adresse virtuelle est stockée dans le TLB (+/- 1 cycle d'horloge CPU) contre entre +/- 10 à 100 cycle d'horloge CPU quand le processeur est forcé par KPTI de parcourir les tables de pages pour trouver l'adresse physique. On comprend donc assez aisément donc d'où viennent les pertes de performances mais on reviendra dessus plus tard.

Alors attention il ne faut surtout pas penser que le TLB a été retiré, NON ! Il a bien été conservé pendant les appels système mais il est maintenant effacé de toutes les entrées.

Mais les dévellopeurs kernel ne sont pas des guignols ils ont trouvé une solution à ça avec les constructeurs de CPU bien sûr (mais tout ça c'est à cause d'eux :) )

Les processeurs nouvelle génération ont une fonctionnalité d'identificateur de contexte de processus appelé PCID qui associe de manière unique chaque entrée TLB à un ensemble de tables de pages. Lorsque cette fonction est activée les échanges de table n'effacent pas le TLB.

Spectre

Passons maintenant à la vulnérabilité Spectre, qui, je vous le dis, est une attaque aussi sombre que son nom le laisse penser.

Donc spectre est une classe d'exploits, dont deux ont été découverts et publiés dans laquelle un attaquant amorce un cache prédicteur de branche afin de provoquer l'exécution spéculative d'un chemin de code malveillant par une application victime. L'exécution spéculative est un type spécial d'exécution hors ligne qui rend Spectre assez semblable à Meltdown.

L'exécution du chemin de code malveillant est annulée, mais il laisse les métadonnées dans un cache ouvert à une éventuelle attaque par canaux latéraux. Regardons maintenant de plus près les deux variantes de Spectre individuellement :

Bounds Check Bypass

Cette variante de Spectre profite des accès au tableau qui sont exécutés de manière spéculative en dépit d'une vérification des limites de l'index.

Alors Spectre tout comme Meltdown déconstruit une hypothèse fondamentale des développeurs de logiciel qui est que le code protégé par une instruction conditionnelle ne doit être exécuté que si la condition est vraie.

Quoi mais qu'est que tu nous raconte ? Tu déraille complètement mec va te coucher t'es fatigué !!!

Non pas du tout chers amis vous avez très bien lu, les processeurs n'attendent pas forcément qu'une instruction protégée par une condition soit vérifiée pour évaluer cette instruction.

Par exemple supposons que nous avons le pseudo programme suivant :

if (x < y) {
    z = array1[x]
}

Le comportement normal et attendu pour n'importe quel développeur est que l'instruction z = array1[x] ne s'exécute que si et seulement si x est inférieur à y.

Bien joué mais encore faux, Spectre viens de nous prouver que ceci ne se passe pas toujours ainsi. Ce qui ce passe est que le processeur va essayer de faire de l'optimisation en attendant que la condition if (x < y)\ soit vérifiée, le CPU devine quoi faire en se basant sur l'historique récent de cette ligne du programme. Donc on comprend bien que si cela a été le cas plus récemment, le processeur exécutera de manière spéculative z = array1[x]. Oui vous avez bien lu le CPU va spéculer que la condition sera vraie car elle a déjà été vraie auparavant et donc va exécuter l'instruction avant de vérifier l'exactitude de la condition. Il va ainsi gagner du temps et si jamais la condition est fausse il détruira le résultat.

Ici le travail d’un potentiel attaquant est d’entraîner le CPU à considérer que la condition if (x < y)\ va être juste, pour ensuite lui donner une valeur totalement arbitraire

Pour exploiter ce code vulnérable, un attaquant doit le trouver dans une application victime qu'il peut invoquer en continu à partir de sa propre application avec différentes valeurs de x. Si une telle configuration est trouvée, l'attaquant peut entraîner le prédicteur de branche en fournissant de manière répétée des valeurs de x qui sont inférieures à y. Cela convint donc le processeur que l'index fourni suivant sera également inférieur, ce qui l'oblige à exécuter de manière spéculative l'instruction z = array_1[x] avant de terminer la vérification que la condition est bien remplie, quelle que soit la valeur de x fournie.

Il suffit plus qu'à notre attaquant d'appeler ensuite l'application victime avec une valeur de x supérieure à y. Que se passe-t-il à la ligne z = array_1[x] maintenant ? Cette instruction va maintenant lire au-delà des limites de Array_1, en saisissant librement des données à partir de n'importe quel emplacement dans l'espace d'adressage de la victime. Cela ouvre donc un canal secondaire pour l'application attaquante afin de déterminer ce qui a été lu dans la mémoire de la victime en examinant le cache.

Résumer de l'attaque spectre version 1

Un attaquant fournit de façon répétée des données à la victime. Cela n'accède qu'aux données prévues, mais influence le comportement futur du prédicteur de branche. Lorsque l'attaquant fournit une mauvaise entrée, il utilise le comportement spéculatif du prédicteur de branche pour accéder aux données sensibles.

Spectre variant 2

Branch Target Injection

Cette variante de Spectre exploite l'exécution spéculative déclenchée par le prédicteur indirect de branche. Une branche indirecte est une instruction logicielle qui peut accéder à plusieurs emplacements possibles - les tables de fonctions et les fonctions virtuelles en sont de bons exemples. La recherche montre qu'un attaquant peut faire atterrir ces sauts à l'endroit de son choix.

Les CPU x86 permettent d’appeler une fonction dont l’adresse est stockée en mémoire. Comme aller chercher cette valeur en mémoire est une opération coûteuse, le CPU va essayer de deviner l’adresse où il doit aller et il va commencer à y exécuter les instructions. Lorsqu’il a terminé d’obtenir l’adresse, il vérifie s’il a vu juste ou s’il s’est trompé sur l’emplacement. Dans le premier cas, il a gagné du temps. Dans l’autre, il annule ce qu’il a commencé à exécuter.

Mais que se passe-t-il si nous parvenons à entraîner le CPU pour qu’il réalise une mauvaise prédiction ? Et qu’on arrive à ce que cette mauvaise prédiction soit sous notre contrôle et qu’on arrive donc à appeler une fonction qui va effectuer une lecture mémoire dépendante d’une valeur que l’on souhaite récupérer ? Comme pour les deux autres failles, la lecture mémoire va laisser une trace qu’il est ensuite possible de mesurer pour en extraire la valeur.

Résumer de la variant 2 de spectre

Un code malveillant entraîne le prédicteur de branche indirecte du processeur dans son propre contexte avec un faux hypercall qui ne prend que la branche injectée. Il invoque alors l'hypercall réel qui est programmé pour aller à la branche prévue, mais prend plutôt la branche injectée qui lui permet d'accéder aux données de la victime via une attaque par canal latéral.

Fixer Spectre version 2

Il existe actuellement deux techniques pour aider à se protéger contre les injections de branche indirecte de Spectre (Branch Target Injection). Le premier est un ensemble de mises à jour de microcode d'Intel qui permettent au logiciel de gérer l'état du prédicteur de branche. Ce patch fonctionne en ajoutant des barrières indirectes de prédiction de branchement (IBPB) à tous les points d'entrée du système. Ces barrières garantissent que le système n'exécutera pas de code de manière spéculative.

La seconde est une technique de compilation pour empêcher les branches indirectes d'être influencées par le prédicteur de branche et qui se nomme retpoline.

Pour retpoline, il faut au préalable mettre à jour GCC et ensuite compiler le nouveau kernel avec GCC. Plutôt que d'utiliser une instruction de saut pour les branches indirectes, il pousse l'adresse cible de la branche sur la pile et appelle les retours. La différence est que les branches de retour utilisent un cache de prédicteur spécial - le buffer de retour de pile (RSB) - dont l'état peut être facilement modifié juste avant le retour afin d'annuler tout amorçage malveillant effectué par un attaquant.

Ces deux atténuations affectent les performances de la même manière. Pour la plupart, ils font comme si le code corrigé fonctionnait sans prédiction de branchement indirect, ce qui signifie que le processeur doit attendre que l'adresse cible de la véritable branche soit lue de la mémoire jusqu'à ce qu'elle puisse effectuer une autre tâche.

La vérité concernant les pertes de perfomance

La vérité concernant les pertes de performances dans le cas de Meltdown et Spectre est qu’on n’est pas tous impactés. Pourquoi ? La raison est simple : les pertes de performances ne se font pas ressentir dans toutes les transactions effectuées par le système. Par exemple, on remarque que le calcul brut n'est pas impacté ou très peu par des pertes de performances (et on va le vérifier juste après) contrairement à la transaction sur les IO (Entré / Sortie) pour la lecture écriture sur les systèmes de fichier. Donc maintenant, on comprend mieux pourquoi on ne va pas tous perdre 5 à 30% de performances sur nos machines suite à une mise à jour d'un microcode de CPU ou une MAJ du kernel contrairement tous ce qui a pu être dit à ce sujet.

**Oui mais tu dis ça mais où sont les preuves de ce que tu avances ?**

Alors je dirais que la question de Mme/Mr « Michu » est tout à fait légitime c'est pourquoi nous allons voir tout ceci en détail très vite.

Donc, j'installe une Debian 9 avec 1G de RAM j'y installe git et récupère un script permettant de vérifier si je suis vulnérable à Spectre et Meltdown.

root@shado:~/spectre-meltdown-checker# ./spectre-meltdown-checker.sh 
Spectre and Meltdown mitigation detection tool v0.32

Checking for vulnerabilities on current system
Kernel is Linux 4.9.0-4-amd64 #1 SMP Debian 4.9.65-3+deb9u1 (2017-12-23) x86_64
CPU is Intel(R) Core(TM) i5-3330 CPU @ 3.00GHz

Hardware check
* Hardware support (CPU microcode) for mitigation techniques
  * Indirect Branch Restricted Speculation (IBRS)
    * SPEC_CTRL MSR is available:  YES 
    * CPU indicates IBRS capability:  NO 
  * Indirect Branch Prediction Barrier (IBPB)
    * PRED_CMD MSR is available:  YES 
    * CPU indicates IBPB capability:  NO 
  * Single Thread Indirect Branch Predictors (STIBP)
    * SPEC_CTRL MSR is available:  YES 
    * CPU indicates STIBP capability:  NO 
  * Enhanced IBRS (IBRS_ALL)
    * CPU indicates ARCH_CAPABILITIES MSR availability:  NO 
    * ARCH_CAPABILITIES MSR advertises IBRS_ALL capability:  NO 
  * CPU explicitly indicates not being vulnerable to Meltdown (RDCL_NO):  NO 
* CPU vulnerability to the three speculative execution attacks variants
  * Vulnerable to Variant 1:  YES 
  * Vulnerable to Variant 2:  YES 
  * Vulnerable to Variant 3:  YES 

CVE-2017-5753 [bounds check bypass] aka 'Spectre Variant 1'
* Checking count of LFENCE opcodes in kernel:  UNKNOWN 
> STATUS:  UNKNOWN  (couldn't check (missing 'readelf' tool, please install it, usually it's in the 'binutils' package))

CVE-2017-5715 [branch target injection] aka 'Spectre Variant 2'
* Mitigation 1
  * Kernel is compiled with IBRS/IBPB support:  NO 
  * Currently enabled features
    * IBRS enabled for Kernel space:  NO 
    * IBRS enabled for User space:  NO 
    * IBPB enabled:  NO 
* Mitigation 2
  * Kernel compiled with retpoline option:  NO 
  * Kernel compiled with a retpoline-aware compiler:  NO 
  * Retpoline enabled:  NO 
> STATUS:  VULNERABLE  (IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability)

CVE-2017-5754 [rogue data cache load] aka 'Meltdown' aka 'Variant 3'
* Kernel supports Page Table Isolation (PTI):  NO 
* PTI enabled and active:  NO 
* Running as a Xen PV DomU:  NO 
> STATUS:  VULNERABLE  (PTI is needed to mitigate the vulnerability)

A false sense of security is worse than no security at all, see --disclaimer

Donc on est vulnérable sur Meltdown et toutes les variantes de Spectre une véritable passoire mais ce n'est pas ça le plus important. Ce qu'on va faire maintenant c'est de mettre à l'épreuve notre système.

Testons en premier lieu notre puissance de calcul. Pour se faire c'est simple je vais demander à mon système de me calculer la valeur de PI à 10000 décimale près après la virgule.

root@shado:~/spectre-meltdown-checker# time echo "scale=10000; 4*a(1)" |bc -l
3.141592653589793238462643383279502884197169399375105820974944592307\
81640628620899862803482534211706798214808651328230664709384460955058\
...
...
real    2m5,317s
user    2m5,196s
sys     0m0,000s

Donc calculer la valeur de PI 10000 décimale près après la virgule me prend 2,5 minutes sans le patch KPTI pour corriger Meltdown.

Maintenant voyons au niveau IO ce qui l'en retourne.

On va donc cette fois-ci utiliser le programme Spew qui a été développer par HP et mis open source qui permet de stresser un système linux au niveau des entrée/sortie en lecture et écriture sur le disque.

root@shado:~/spectre-meltdown-checker# spew --read-after-write 5G /tmp/test
WTR:   317355.05 KiB/s   Transfer time: 00:00:16    IOPS:   634710.10
RTR:   916120.54 KiB/s   Transfer time: 00:00:05    IOPS:  1832241.09

Donc notre système écrit sur le système à une vitesse de 317355.05 KiB/s et lis à une vitesse de 916120.54 KiB/s ce qui me semble correcte.

Maintenant réalisons ces même test après avoir mis à jour notre système avec KPTI pour fixer Meltdown.

Vous êtes prêts, alors allons y.

Donc on vérifie si on a bien mis à KPTI :

root@shado:~/spectre-meltdown-checker# ./spectre-meltdown-checker.sh 
Spectre and Meltdown mitigation detection tool v0.32

Checking for vulnerabilities on current system
Kernel is Linux 4.9.0-5-amd64 #1 SMP Debian 4.9.65-3+deb9u2 (2018-01-04) x86_64
CPU is Intel(R) Core(TM) i5-3330 CPU @ 3.00GHz

Hardware check
* Hardware support (CPU microcode) for mitigation techniques
  * Indirect Branch Restricted Speculation (IBRS)
    * SPEC_CTRL MSR is available:  YES 
    * CPU indicates IBRS capability:  NO 
  * Indirect Branch Prediction Barrier (IBPB)
    * PRED_CMD MSR is available:  YES 
    * CPU indicates IBPB capability:  NO 
  * Single Thread Indirect Branch Predictors (STIBP)
    * SPEC_CTRL MSR is available:  YES 
    * CPU indicates STIBP capability:  NO 
  * Enhanced IBRS (IBRS_ALL)
    * CPU indicates ARCH_CAPABILITIES MSR availability:  NO 
    * ARCH_CAPABILITIES MSR advertises IBRS_ALL capability:  NO 
  * CPU explicitly indicates not being vulnerable to Meltdown (RDCL_NO):  NO 
* CPU vulnerability to the three speculative execution attacks variants
  * Vulnerable to Variant 1:  YES 
  * Vulnerable to Variant 2:  YES 
  * Vulnerable to Variant 3:  YES 

CVE-2017-5753 [bounds check bypass] aka 'Spectre Variant 1'
* Checking count of LFENCE opcodes in kernel:  UNKNOWN 
> STATUS:  UNKNOWN  (couldn't check (missing 'readelf' tool, please install it, usually it's in the 'binutils' package))

CVE-2017-5715 [branch target injection] aka 'Spectre Variant 2'
* Mitigation 1
  * Kernel is compiled with IBRS/IBPB support:  NO 
  * Currently enabled features
    * IBRS enabled for Kernel space:  NO 
    * IBRS enabled for User space:  NO 
    * IBPB enabled:  NO 
* Mitigation 2
  * Kernel compiled with retpoline option:  NO 
  * Kernel compiled with a retpoline-aware compiler:  NO 
  * Retpoline enabled:  NO 
> STATUS:  VULNERABLE  (IBRS hardware + kernel support OR kernel with retpoline are needed to mitigate the vulnerability)

CVE-2017-5754 [rogue data cache load] aka 'Meltdown' aka 'Variant 3'
* Kernel supports Page Table Isolation (PTI):  YES 
* PTI enabled and active:  YES 
* Running as a Xen PV DomU:  NO 
> STATUS:  NOT VULNERABLE  (PTI mitigates the vulnerability)

A false sense of security is worse than no security at all, see --disclaimer

On vois donc que PTI est donc maintenant activé.

Voyons si nos performances ont diminué.

Voyons ce que cela donne au niveau calcul brute :

root@shado:~/spectre-meltdown-checker# time echo "scale=10000; 4*a(1)" |bc -l
3.141592653589793238462643383279502884197169399375105820974944592307\
81640628620899862803482534211706798214808651328230664709384460955058\
...
...
real    2m6,320s
user    2m6,180s
sys 0m0,004s

Bon on observe une que les performances restent globalement les mêmes à une seconde près.

Voyons donc maintenant aux entrée/sortie ce qu'il en est :

root@shado:~/spectre-meltdown-checker# spew --read-after-write 5G /tmp/test
WTR:   242793.17 KiB/s   Transfer time: 00:00:21    IOPS:   485586.33
RTR:   547124.08 KiB/s   Transfer time: 00:00:09    IOPS:  1094248.17

On se rend bien compte que l'on perd un peu de performances au niveau des Entrées/Sorties sur la lecture/écriture sur le système de fichiers. Faisons donc un petit récap’ de la situation

Dans un premier temps avec ou KPTI on calcule la valeur de PI à la 10000ème unité près après la virgule et les résultats sont globalement les même à une seconde dans ce cas-là on ne peut parler de perte de performance.

Cependant sur de la lecture/écriture sur le système de fichiers on constate des pertes assez grandes.

Alors, avant le patch KPTI nous écrivions à une vitesse de 40,62144 MB et lisions à une vitesse de 117,26336 MB.

Après le patch nous écrivons à une vitesse de 31,077504 MB et nous lisons à une vitesse de 70,031872 MB.

Soit un écart de 23,4% en écriture et jusqu'à 40,2% en lecture. Donc le problème est surtout en lecture/écriture des IO plutôt qu'en calcul brut.

Pour finir sur le résultat des performances on peut donc en déduire qu'aux vues des niveaux de pertes (dans mon cas 23,4% en écriture, 40% en lecture et aucune en calcul brut) on peut affirmer sans trop de craintes que ces pertes de performance vont surtout dépendre du type d'applications utilisées. Par exemple les applications purement graphiques comme les jeux ou même les applications de calculs intensifs qui en définitive ne réalisent que très peu d'entrée/sortie ne seront pas vraiment impactées par ces problèmes de pertes de performances. Par contre, on ne peut pas affirmer la même chose pour applications communiquant avec une base de données, les serveurs web etc.

Tout n'est pas si sombre

A l'état actuel des choses, certains pourront tout de même se réjouir. Pour ce qui ont des processeurs assez récents qui disposent de l'extension PCID peuvent apparemment mieux s'en sortir que les autres car leur processeur serait moins impacté en termes de pertes par le patch KPTI. Cependant tout cela reste à vérifier.

Il y a aussi pour les puristes une autre chose à retenir : c'est que les développeurs système ont réalisé un travail monstrueux en un temps plutôt réduit et dans un climat délétère ou tout le monde voulait à tout prix sont scoop de l'année ; quitte à lancer des vents de panique alimenté par de fausses rumeurs non fondées. On peut tout de même regretter que les développeurs de BSD ont été prévenus de cette faille au dernier moment, ce qui fait qu'ils n'ont pas pu répondre rapidement à cette faille (dommage).

Conclusion

Meltdown et Spectre exploitent une vulnérabilité inhérente à l'exécution spéculative des instructions dans le CPU, une optimisation présente dans la plupart des CPU depuis plus de 10 ans.

La menace la plus directe et concrète concernant Meltdown se trouve surtout chez les fournisseurs d'infrastructure de type Cloud qui fournissent à leurs clients des machines virtuelles à noyau partagé via un hyperviseur (ce qui est le cas de tous les fournisseurs de cloud et de serveurs mutualisés etc.). C'est évident les OS guest partagent le même espace noyau, on peut très bien imaginer un client allant lire des informations dans l'espace noyau et récupérer des informations d'autres clients ou carrément des informations sensibles sur le système hôte directement ce qui aurait pour effet d'avoir les informations concernant tous les clients au passage. De plus, on le constate au niveau des pertes de performances qui se jouent surtout dans tout ce qui est appel système et surtout au niveau io-E/S (lecture/écriture sur les systèmes de fichiers) que dans le calcul brut.

Concernant Spectre, les choses se compliquent un peu plus. Déjà, la mitigation de Spectre dépend d'une mise à jour hardware du microcode des processeurs. Ce qui n'est pas une opération à prendre à légère au niveau des constructeurs car ils ne peuvent plus se permettre de sortir des fixs qui posent des problèmes aussi catastrophiques que le reboot intempestif des serveurs ou des problèmes de gestion de température etc. Quand on sait que la moindre coupure, ne serait-ce que de quelques secondes pour certaines entreprises peut engendrer des pertes colossales. De plus, la perte de performance n'étant pas négligeable, il va certainement falloir attendre encore un peu pour voir des véritables fixs arrivé sur le marché.

Au niveau de la perte de performance ce que l'on peut dire est qu'il y a réellement une perte de performance, c'est indéniable. Cepandant si vous êtes une personne qui va plus utilisé le calcule brute que ce soit pour (jouer, faire du graphisme etc.) les pertes ne se feront pas resentir car elle sont de l'ordre de moins de 1%. par contre pour des serveurs qui font beaucoup de transaction entrée/sortie c'est beaucoup plus compliqué car les pertes vont de -24 % en lecture et -40 % en écriture ce qui n'est pas du tout négligeable.

De plus comme évoqué dans les premières lignes de cet article, ces deux vulnérabilités que sont Meltdown et Spectre remettent en cause des concepts fondamentaux de la sécurité et poussent plus à revoir l'architecture globale des CPU plutôt que de simples fixs logiciels sortis dans l'urgence au détriment de pertes en performances énormissimes pour certains. Donc je pense que nous allons continuer à cohabiter avec ces vulnérabilités encore un bon moment. Peut-être 1 ou 2 ans encore, le temps que les fabricants trouvent une nouvelle méthode garantissant la sécurité de la transition entre les différents espaces noyau-utilisateur et utilisateur-noyau et une garantie au niveau des performances.