Qui est le coupable ? Le processeur ! Retour sur un bogue important des SkyLake & Kaby Lake Intel

Posté par  . Édité par bubar🦥, Benoît Sibaud, Davy Defaud, ZeroHeure, Nils Ratusznik et patrick_g. Modéré par Benoît Sibaud. Licence CC By‑SA.
42
6
juil.
2017
Matériel

Certains d’entre vous ont peut‐être vu passer l’information : les derniers processeurs Intel des familles Skylake et Kaby Lake sont victimes d’un bogue lorsque l’hyper‐threading est activé. On trouve par exemple un article sur Ars Technica, et Debian propose des instructions détaillées pour corriger le problème en mettant à jour le microcode (firmware) du processeur.

Cette dépêche propose revenir sur les événements qui ont mené à la découverte du problème. Xavier Leroy le décrit en détail dans un article sur le blog de l’équipe Gallium, dont je proposerai un résumé pour les lecteurs francophones.

Tout commence en avril 2016 lorsqu’un SIOU (Serious Industrial OCaml User), comme il les appelle, le contacte en privé pour lui signaler un bogue dans un de leur logiciel : ce dernier subit des erreurs de segmentation de manière aléatoire après un certain temps. Il n’arrive pas à reproduire le bogue sur sa propre machine et le côté aléatoire du bogue lui fait soupçonner un problème matériel chez le client (mémoire vive défectueuse, surchauffe…). Il leur propose de tester leur mémoire et de désactiver l’hyper‐threading. La mémoire était bonne, mais il ne teste pas la désactivation (ce qui aurait résolu le problème).

De son côté, le client fait ses tests et aboutit aux résultats suivants : le bogue est présent avec la version 4.03 mais pas la 4.02.3 du compilateur OCaml, avec GCC mais pas Clang (l’environnement d’exécution d’OCaml est en C), sur GNU/Linux et Windows mais pas macOS (ce qui se comprend, ce dernier utilisant Clang). Les coupables semblent identifiés : OCaml 4.03 et GCC, et le client suppose qu’il y a une erreur dans le code C de l’environnement d’exécution d’OCaml.

Début mai 2016, le client offre un accès à sa machine à Xavier Leroy pour qu’il puisse identifier le problème. Il analyse des vidages mémoire (dumps) post‐plantage, voit bien des problèmes avec le ramasse‐miettes, mais ne comprend pas ce qui peut causer un tel comportement dans son code. Il fait alors des tests en lançant le programme en parallèle (1, 2, 4, 8 ou 16 instances) et, là, tout devient clair : pas de bogue quand l’hyper‐threading n’est pas utilisé. Ils font des tests en le désactivant dans le BIOS et le problème ne se manifeste plus.

Cela aurait pu en rester là : le client était satisfait de pouvoir utiliser une version de l’environnement d’exécution avec Clang, et Xavier Leroy ne sachant pas comment signaler le problème à Intel en reste là. Mais, début 2017, un autre SIOU fait un rapport de bogue sur le système de suivi d’OCaml. Les symptômes étaient similaires et la discussion sur le ticket fut la suivante :

  • douze heures après l’ouverture, une des ingénieurs précise que tous les ordinateurs qui ont pu reproduire le bogue ont un processeur de la famille Skylake ;
  • le lendemain, Xavier Leroy signale son expérience passée et propose de désactiver l’hyper‐threading ;
  • le jour suivant, un autre ingénieur du SIOU rapporte qu’en désactivant l’hyper‐threading le problème disparaît ;
  • en parallèle, il constate que si l’environnement d’exécution est compilé avec gcc -O1 et non gcc -O2 alors le bogue disparaît. Ce qui permet de comprendre pourquoi cela apparaît avec la version 4.03, qui est celle inaugurant l’option -O2 par défaut pour l’environnement d’exécution ;
  • Mark Shinwell contacte des collègues chez Intel et s’occupe de rapporter le problème au support client d’Intel.

Enfin, cinq mois plus tard, Debian publie une mise à jour du microcode des processeurs Intel et Intel publie, en avril, une mise à jour des spécifications de la 6e génération de ses processeurs. On trouve à la page 65 de ce document une mention du problème SKL150 qui était à l’origine de tous ces bogues, présenté en ces termes chez Debian :

SKL150 - Short loops using both the AH/BH/CH/DH registers and
the corresponding wide register may result in unpredictable
system behavior. Requires both logical processors of the same
core (i.e. sibling hyperthreads) to be active to trigger, as
well as a “complex set of micro‐architectural conditions”.

Pour ceux que cela intéresse et qui comprennent l’assembleur (ce qui n’est pas mon cas), le problème venait de ce bout de code du ramasse‐miettes d’OCaml :

hd = Hd_hp (hp);
/*...*/
Hd_hp (hp) = Whitehd_hd (hd);

Qui après expansion des macros donne :

hd = *hp;
/*...*/
*hp = hd & ~0x300;

Avec Clang, cela donnait :

movq    (%rbx), %rax
[...]
andq    $-769, %rax             # imm = 0xFFFFFFFFFFFFFCFF
movq    %rax, (%rbx)

Tandis que le code optimisé de GCC donnait :

movq    (%rdi), %rax
[...]
andb    $252, %ah
movq    %rax, (%rdi)

Qui pouvait lever le bogue du processeur s’il se trouvait dans une petite boucle ?

Ce bogue sur ces processeurs impacte tous les systèmes d’exploitation. Le correctif du microcode pour la génération Skylake existe donc depuis avril, car Intel distribue ses mises à jour à toutes et tous, permettant aux mainteneurs des distributions de réaliser l’empaquetage afin de les rendre disponibles.

Cependant, il n’en va pas de même pour la génération Kaby Lake, pour laquelle Intel ne distribue ses correctifs de microcodes qu’aux seuls constructeurs ou assembleurs. Il résulte de cette situation une grande disparité des disponibilités pour cette mise à jour : certains constructeurs l’ont déjà proposée, d’autres ne le font pas.

Au final, il semblerait que Skylake se soit transformé en Skyfall et que la légendaire crainte gauloise que le ciel leur tombe sur la tête était fondée ! :-D

Aller plus loin

  • # Intel ..

    Posté par  (site web personnel) . Évalué à -7.

    … de plus en plus classe.

  • # Microcode Kaby Lake indisponible au public ?

    Posté par  (site web personnel) . Évalué à 6.

    Cependant il n'en va pas de même pour la génération Kaby Lake, pour laquelle Intel ne distribue ses correctifs de micro-codes qu'aux seuls constructeurs ou assembleurs. Il résulte de cette situation une grande disparité des disponibilités pour cette mise à jour : certains constructeurs l'ont déjà proposée, d'autres ne le font pas.

    Quelqu'un sait pourquoi ce n'est pas disponible pour tout le monde ? Ce sera dans la prochaine mise à jour du package de microcode ou Intel ne veut pas le distribuer pour une certaine raison ?

    S'il y a un problème, il y a une solution; s'il n'y a pas de solution, c'est qu'il n'y a pas de problème.

    • [^] # Re: Microcode Kaby Lake indisponible au public ?

      Posté par  . Évalué à 3.

      Même question. J'ai un i3 Kaby Lake, et je serais très intéressé d'en savoir plus sur les raisons de l'absence de mise à jour, et sur les moyens prévus par Intel pour la rendre disponible

      D'après cette page, ma version du microcode (48) ne résous pas le problème, et il faut passer par une mise à jour du BIOS/EFI, l'ennui c'est que le constructeur de ma carte mère (ASUS) ne founit quasiment pas d'information sur le contenu des mises à jour…

      • [^] # Re: Microcode Kaby Lake indisponible au public ?

        Posté par  . Évalué à 10.

        comme je le disais dans le journal associé, les constructeurs prennent leur temps pour proposer les MÀJ, quand ils n'ont pas tout simplement abandonné le support. Asus fait généralement plusieurs années de support logiciel (4 ans de MÀJ sur ma carte AM3) et sont assez réactif.
        J'imagine que chez les constructeurs, l'équipe plus ou moins grande qui s'occupe de travailler sur les BIOS fournis par AMI, AWARD ou Phenix a pour priorité les plates-formes récentes. Je pense même que ce sont les fabricants de BIOS qui fournissent eux-mêmes les MÀJ (AMI utilise un SVN à accès restreint).
        Sans compter qu'il faut tester un minimum la stabilité des MÀJ, il vaut mieux qu'ils prennent un peu de temps là dessus parce que depuis l'UEFI on ne peut plus downgrader sans un programmateur SPI (et encore, parfois la seule solution c'est de racheter une EEPROM préflashée).

        Mais oui, Intel n'a pas choisi le mode de propagation le plus rapide. Hélas.

  • # Optimisation de GCC et ouverture du correctif

    Posté par  . Évalué à 6. Dernière modification le 06 juillet 2017 à 14:44.

    Quelqu'un pourrait me dire pourquoi gcc préfère faire un and sur un octet plutôt que sur qword ?
    Je suis un peu plus habitué au monde RISC où beaucoup d'instructions prennent un cycle à s'exécuter, est-ce parce qu'un andb est plus rapide qu'un andq ?

    Enfin, est-ce que le correctif d'Intel est open source ? J'imagine bien que la solution consiste à trouver le motif problématique et à remplacer un registre par un autre mais je serai curieux de voir la solution.
    À ce sujet Intel donne-t-elle plus d'informations sur le problème en matériel ? À quoi est-ce du ? Un mauvais routage, etc. ?

    • [^] # Re: Optimisation de GCC et ouverture du correctif

      Posté par  (site web personnel) . Évalué à 6.

      Cela a probablement à voir avec la taille de l'instruction, un andq nécessitant 64 bits de valeur immédiate pour 8 pour un andb. Sur les processeurs récents, la pression à l'accès mémoire est le goulot d'étranglement principal, beaucoup plus que le temps d'exécution d'instructions simples comme un and.

      • [^] # Re: Optimisation de GCC et ouverture du correctif

        Posté par  . Évalué à 10.

        Un ANDQ nécessite 2 octets de code opération lorsque la valeur (l'opérande) est sur 64 bits, sinon 3 octets de code opération lorsque la valeur est sur 8 bits. C'est à cause de la compatibilité avec le mode x86 qui a déjà consommé presque tous les codes opérations disponibles :

        AND RAX,0x12 --> 48 83 E0 12
        AND RAX,0x1234 --> 48 25 34 12 00 00
        AND RAX,0x123456 --> 48 25 34 12 00 00
        AND RAX,0x12345678 --> 48 25 56 34 12 00

        Un MOV utilise 1 octet de plus pour le code opération (donc 3 ou 4 octets). Ce qui fait que lorsqu'on veut mettre tout ou partie du registre à zéro il est plus économe de faire AND RAX,0 que MOV RAX,0 (idem pour mettre à 1 avec OR).

         

        Un ANDB (hérité du x86) nécessite 1 octet de code opération et un octet d'opérande :
        AND AL,0x12 --> 24 12

        Un MOV a la même taille.

        • [^] # Re: Optimisation de GCC et ouverture du correctif

          Posté par  . Évalué à 2.

          Ma question va peut-être paraître bête mais vu que le code opération est stocké dans un registre de, je suppose, 32 ou 64 bits du coup même si le code d'opération est sur 8 bits il sera étendu avec des 0 ?
          Du coup d'un point de vue manipulation ça ne devrait pas changer grand chose ?

          • [^] # Re: Optimisation de GCC et ouverture du correctif

            Posté par  . Évalué à 2.

            Le code opération (op code) est le « numéro » de l'instruction. Par exemple « 48 83 E0 » qui veut dire « AND RAX,[donnée sur 8 bits] ».

            L'opérande est « étendue » lorsque nécessaire, toujours avec des zéros.
            L'opérande n'est pas nécessairement chargée dans un registre. Avec un AND par exemple c'est le résultat qui est chargé dans le registre.

             

            Du coup d'un point de vue manipulation ça ne devrait pas changer grand chose ?

            Je n'ai pas compris la question.

            • [^] # Re: Optimisation de GCC et ouverture du correctif

              Posté par  . Évalué à 0.

              En fait je parlais de l'op code.
              Lorsque l'instruction pointée par EIP est lue son op code est mis dans un registre mais si l'op code est par exemple 0X48 83 E0 et qu'il est mis dans un register 32 bits il deviendra 0x00 48 83 E0 du coup ça ne va pas changer grand chose point de vue manipulation de l'op code ?

              • [^] # Re: Optimisation de GCC et ouverture du correctif

                Posté par  . Évalué à 3.

                Lorsque l'instruction pointée par EIP est lue son op code est mis dans un registre

                Trouves dans quel registre l'op code est stocké, et tu auras ta réponse.
                Cela dit, ta question ne veut toujours rien dire : la « manipulation de l'op code » ça n'a strictement aucun sens.

  • # Pas de chance...

    Posté par  (site web personnel) . Évalué à 5.

    Je suis tombé dessus !

    J'observe des arrêts sur image où la souris n'est plus active, le clavier non plus. Dans certains cas, l'accès par ssh fonctionne, dans d'autres, le réseau est mort. Il n'y a rien de prédictible…

    Par ailleurs ma carte mère MSI Z270 refuse l'accès au BIOS, il faut faire un reset du BIOS AMI pour pouvoir y accéder de nouveau. Je ne sais pas si c'est lié, mais ce n'est pas impossible.

    Heureusement que c'est chez moi, si j'avais fait acheter cette machine à un ami ou à une association, on m'aurait à coup sûr dit que c'est de la faute à Linux… Merci Intel !

    • [^] # Re: Pas de chance...

      Posté par  . Évalué à 5.

      j'observe des arrêts sur image où la souris n'est plus active, le clavier non plus. Dans certains cas, l'accès par ssh fonctionne, dans d'autres, le réseau est mort. Il n'y a rien de prédictible…

      J'observe aussi ce phénomène sur ma machine de boulot et pourtant je suis sur un i7-4790S, donc Haswell …

      • [^] # Re: Pas de chance...

        Posté par  . Évalué à 6.

        Il y a plein de raisons que ça merdouille au niveau matériel, il suffit d'une petite puce un peu hors spec ou d'une mauvaise soudure BGA.

        J'ai eu le coup d'une barrette de RAM un peu foireuse sur certains secteurs qui causait des crashs de Firefox dans certaines conditions et même parfois des kernel panic. Je l'ai diagnostiquée grace à l'IGP car elle faisait planter l'affichage de Memtest ou même du POST quand insérée seule sur différentes CM que ce soit sur du AMD ou du Intel. Memtest n'a pourtant jamais rien trouvé même en prenant soin de désactiver le GPU intégré pour qu'il teste toute la mémoire.

        Dans tous les cas, la première chose à faire c'est de retirer tous les réglages d'overclocking ou undervolting, surtout ceux qui se font automatiquement chez certains constructeurs.

        Enfin, le bug pour les Skylake/Kabylake concerne le SMT dans des conditions très spécifiques donc à voir si ça continue de planter en désactivant l'HyperThreading.

    • [^] # Re: Pas de chance...

      Posté par  . Évalué à 4.

      pareil, maintenant mon ordinateur est castré (plus d'hyper-threading) ; je vais voir si il y a un changement concernant la stabilité. J'attends de voir le microcode disponible sur Ubuntu.

    • [^] # Re: Pas de chance...

      Posté par  . Évalué à 7.

      J'observe des arrêts sur image où la souris n'est plus active, le clavier non plus. Dans certains cas, l'accès par ssh fonctionne, dans d'autres, le réseau est mort. Il n'y a rien de prédictible…

      Grosso modo, tu as des symptômes qui ne correspondent pas au bug décrit dans la dépêche, donc tu en déduis que le bug décrit dans la dépêche est le responsable ?

      Ton problème ressemble surtout à un problème matériel classique (genre mémoire défectueuse), à moins que ce soit un bug dans un pilote.

      • [^] # Re: Pas de chance...

        Posté par  . Évalué à 1.

        Ton problème ressemble surtout à un problème matériel classique (genre mémoire défectueuse), à moins que ce soit un bug dans un pilote.

        Ou à la non désactivation de la fréquence dynamique pour le CPU dans le bios ce qui peut produire ce genre de problèmes.

        • [^] # Re: Pas de chance...

          Posté par  . Évalué à 2.

          Ou à la non désactivation de la fréquence dynamique pour le CPU dans le bios ce qui peut produire ce genre de problèmes.

          Cela me paraît étonnant. Le CPU tourne à des fréquences pour lesquelles il a été validé… Sauf si tu parles de fonctionnalités d'overclocking intégrées à la carte-mère ?

          • [^] # Re: Pas de chance...

            Posté par  . Évalué à 1.

            Je parle de l'intel turbo boost ;) sur mon i7-5775C si celui ci est activé dans le bios le pc fige aléatoirement lors du "boost" du processeur ;)

  • # Contourner le problème

    Posté par  . Évalué à 7.

    Ce commentaire est là pour indiquer la solution (workaround) retenue par l'équipe en charge du développement du compilateur OCaml afin de maintenir des optimisations du niveau -O2 de GCC, sans pour autant risquer de soulever les bugs des CPU. Elle pourra intéresser des développeurs de logiciels C qui ne veulent pas prendre ce risque.

    Elle est expliquée dans cette PR sur github et consiste à passer l'option -fno-tree-vrp à GCC. Il semblerait que ce soit cette passe d'optimisation qui génère le code assembleur « coupable », d'après les investigations réalisées par les gens de chez Ahref et rapportées dans leur article Skylake bug: a detective story.

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.