Pourquoi tenter d’interrompre le noyau NT 3.1 fait-il redémarrer ma machine 486DX4 ?
(retrocomputing.stackexchange.com)- Sous Windows NT 3.1, lorsqu’on déclenche une interruption Ctrl-C avec
i386kd, le système cible 486DX4 redémarre au lieu d’afficher l’invitekd>; la cause était un problème de compatibilité avec les 486 enhanced - Dans NTOSKRNL.EXE,
KiSaveProcessorControlStateetKiRestoreProcessorControlStateutilisent une comparaison word lorsqu’ils vérifient le type de CPU, ce qui fait qu’un 486 prenant en charge CPUID est pris à tort pour un Pentium ou plus récent - Comme
CpuTypeetCpuIDdu KPRCB sont lus ensemble, le numéro de modèle d’un 486 prenant en charge CPUID est interprété avec 256 de plus, ce qui entraîne un accès au registre CR4, absent des 486 - Le débogage noyau fonctionne normalement avec le 486DX-33 d’origine et avec un Am486DX4-NV8T write-through sans SMM ; le problème est donc circonscrit aux 486 enhanced fournissant l’instruction CPUID
- En patchant dans NTOSKRNL.EXE les deux emplacements
cmp ds:word_FFDFF138, 5en byte compare, le problème peut être corrigé aussi bien dans le Windows NT 3.1 Advanced Server d’origine que dans Windows NT 3.1 SP3
Environnement de reproduction et symptômes
- Après avoir installé Windows NT 3.1 sur un Compaq ProSignia 3080, une tentative de débogage noyau a été effectuée
- Ce système exécutait Windows NT 3.1 en exploitation réelle et semble être l’une des machines explicitement visées par Windows NT
- La RAM a été étendue à 128 Mo, et l’Intel 486DX-33 sur socket a été remplacé par un AMD enhanced 486DX4-SV8B
- Ce CPU prend en charge le cache write-back et le SMM, et est monté dans un socket avec adaptateur de tension
- La prise en charge du 486DX4 par le BIOS a été laissée de côté, et le CPU a été configuré par cavalier avec un multiplicateur 2x
- Sans prise en charge L1 write-back par le chipset, on s’attendait à ce qu’une configuration en multiplicateur 2x soit compatible logiciellement avec un Intel 80486DX2-66
- L’Intel 80486DX2-66 fait partie des options prises en charge par ce système
- L’installation de Windows NT 3.1 elle-même s’est déroulée normalement
- Le CD de Windows NT 3.1 incluant l’ensemble complet des symboles de débogage, une tentative de débogage noyau a été réalisée
- L’objectif était de comprendre pourquoi NetDDE laissait des erreurs dans le journal des événements
- Il existait aussi un problème de crash système avec une carte Ethernet EISA particulière, et l’hypothèse d’un défaut matériel restait ouverte
- Pour que la configuration de débogage noyau corresponde, il faut utiliser
i386kdfourni avec Windows NT 3.1, plutôt quekdountkdissus du kit de développement Windows 10 récent - Lorsqu’on tente une interruption Ctrl-C depuis
i386kd, la machine cible redémarre au lieu de fournir l’invitekd>
Causes écartées
- La mémoire a été vérifiée comme étant fonctionnelle
- Il ne s’agissait pas d’une corruption des fichiers système
- Aucun watchdog matériel redémarrant le système lorsque le noyau est arrêté pour débogage n’était actif
- L’adaptateur USB-série côté hôte communiquait correctement
- Bien qu’il semble s’agir d’un faux PL2301, il n’a pas envoyé par erreur une commande « reboot system » en lieu et place d’une commande du débogueur
- Le protocole KD possède effectivement une commande reboot system
- Le problème n’était pas non plus lié aux options de gestion à distance ou d’alerte de la carte mère
Cause réelle : incompatibilité entre les 486 enhanced et le noyau NT 3.1
- Le noyau Windows NT 3.1 n’est pas compatible avec les processeurs 486 enhanced
- Plus précisément, le problème apparaît avec les processeurs 486 fournissant l’instruction
CPUID - Le débogage noyau fonctionne normalement avec les CPU suivants
- Le 486DX-33 installé à l’origine
- L’Am486DX4-NV8T write-through, doté d’un ancien cœur non-enhanced sans SMM
- Si l’objectif est simplement de tester le débogage noyau de NT 3.1, il est préférable d’utiliser un CPU compatible par défaut avec Windows NT 3.1
- Pour corriger NT lui-même, il faut patcher le bug de détection du type de CPU dans NTOSKRNL.EXE
Chemin de code noyau où le problème se déclenche
- La cause directe de l’incompatibilité est un bug dans
KiSaveProcessorControlState- La fonction correspondante,
KiRestoreProcessorControlState, présente un bug similaire
- La fonction correspondante,
KiSaveProcessorControlStateest appelée depuis trois endroits dans NTOSKRNL.EXE- Lorsqu’une exception est renvoyée au débogueur noyau via
KdpTrap- Lors d’une interruption Ctrl-C, une exception breakpoint se produit dans la fonction de polling de break-in au sein de l’interruption de tick du minuteur
- Lorsque
KeBugCheckExest appelée, c’est-à-dire dans une situation de « écran bleu » - Lorsque
KiSaveProcessorStateest appelée- Si l’analyse du flot de contrôle de NTOSKRNL.EXE par IDA est complète, cette fonction n’est pas exportée et n’est pas non plus appelée en interne par NTOSKRNL ; elle ne semble donc pas se produire en pratique
- Lorsqu’une exception est renvoyée au débogueur noyau via
KiSaveProcessorControlStateenregistre l’état de contrôle du processeur dans une structureCONTEXTétendue- Elle enregistre CR0, CR2, CR3
- Sur Pentium et au-delà, elle enregistre aussi CR4
- Elle enregistre les registres de débogage DR0 à DR3, DR6 et DR7
- Elle enregistre aussi les paramètres globaux du mode protégé, comme l’adresse GDT, l’adresse IDT, le sélecteur TSS actif et le sélecteur LDT
Erreur d’interprétation des champs KPRCB
- La détection du type de processeur utilise une valeur du KPRCB
- Le KPRCB fait partie du KPCR
- Le KPRCB du processeur de démarrage, ou d’un système monoprocesseur, se trouve à l’adresse virtuelle
FFDFF120, codée en dur dans cette méthode
- Les champs liés au KPRCB de NT 3.1 documentés par Geoff Chappell sont les suivants
+018 CHAR CpuType+019 CHAR CpuID+01A UShort CpuStep
- Ces champs sont initialisés dans
KiSetProcessorType- L’octet à l’offset 18 est défini à 4 pour un processeur 486
- Il est défini à 5 pour un processeur Pentium
- Il est défini à 6 pour les processeurs Pentium Pro et Pentium II/III
- L’octet à l’offset 19 est un flag booléen indiquant si le processeur prend en charge CPUID et se comporte de manière « raisonnable »
- L’instruction de comparaison problématique lit un word, et non l’octet situé à l’adresse
FFDFF138FFDFF138se trouve 18h octets après le début du KPRCB- Par conséquent, non seulement
CpuType, mais aussi l’octet suivantCpuID, sont interprétés ensemble comme une partie du numéro de modèle
- Pour les processeurs prenant en charge CPUID, le numéro de modèle est considéré comme supérieur de 256 à sa valeur réelle
- Un 80-4-86 prenant en charge CPUID est traité par Windows NT 3.1 comme un 80-260-86
- 260 étant largement supérieur au seuil 5 du Pentium, le noyau juge que ce processeur doit disposer de CR4
- Comme les 486 enhanced n’ont pas de CR4, le problème se déclenche sur le chemin de break-in
Méthode de patch
- Une fois le bug confirmé, la correction est simple
- Dans NTOSKRNL.EXE, l’instruction
cmp ds:word_FFDFF138, 5n’apparaît que deux foisKiSaveProcessorControlStateKiRestoreProcessorControlState
- Aux deux emplacements, il faut remplacer la comparaison word par une comparaison byte
- Avec un éditeur hexadécimal, il faut patcher deux fois la séquence d’octets suivante
- Ancien :
66 83 3D 38 F1 DF FF 05 - Nouveau :
90 80 3D 38 F1 DF FF 05
- Ancien :
- Cette correction s’applique à la fois au NTOSKRNL.EXE de la distribution Windows NT 3.1 Advanced Server d’origine et à Windows NT 3.1 SP3
1 commentaires
Commentaires sur Hacker News
Il y a longtemps, j’avais créé un contrôle de port pour vérifier si un service était en vie, et le simple fait d’ouvrir quelques ports TCP sur des machines avait mis hors service la moitié de l’entreprise
C’était une époque complètement absurde
On a vraiment parcouru beaucoup de chemin
https://en.wikipedia.org/wiki/Ping_of_death
Certaines machines avaient aussi des vulnérabilités SMB non corrigées pendant des années. C’était déjà l’époque de Metasploit, donc avec quelques commandes on pouvait injecter VNC sur la plupart des hôtes Windows du réseau local. Aujourd’hui, au moins, la cadence des correctifs est incroyablement rapide
Ah, le terrier du rétrocomputing. Inoffensivement coupé des conséquences du monde réel, mais tellement satisfaisant quand même
Un vrai piège à miel pour geeks. J’ai fini par cliquer sur le bouton d’extension « voir 6 commentaires de plus »
Je vais peut-être pouvoir travailler un peu aujourd’hui
Mais un 486 ? Ça me rappelle trop d’écrans bleus et d’attentes interminables pendant que la machine swappait sur disque parce qu’elle ne pouvait pas gérer la mémoire. C’est encore trop tôt pour moi, je crois
« La solution est évidente » est vraiment une phrase juste
J’imagine qu’il a dû continuer à lire le désassemblage du code de trap jusqu’à repérer le problème. Je ne pense pas qu’il ait pu utiliser QEMU ou d’autres moyens de débogage
Il faut absolument lire jusqu’à la méthode enfouie dans le commentaire masqué à la fin
Ce ne sont pas forcément les commentaires les moins votés qui sont masqués, ni les plus récents, ni les plus anciens. Ça donne une impression d’aléatoire et c’est inutile
Si l’espace est un problème, je pense qu’une pagination serait préférable au masquage
Les vieux adaptateurs slotket utilisés pour la compatibilité sur des cartes Abit, Asus, et peut-être MSI, avaient de vraies capacités d’overclocking
Tout dépendait de la vitesse de la RAM et de la fréquence jusqu’à laquelle la carte mère tenait avec les réglages de multiplicateur. C’était l’époque assez précoce de l’overclocking, avec les Celeron et Pentium II
À un moment, pour des raisons de compatibilité, j’ai mis un Celeron 600 dans un adaptateur, puis dans un adaptateur Slot II vers Socket 370, dans une configuration comme [1], et ça tournait bien à 1,2 GHz. Je crois même l’avoir poussé jusqu’à 1,4 GHz sous Windows ME, avant de finir par griller ce CPU
[1]http://krick.3feetunder.com/370mod/
La réponse à la question était vraiment excellente
Le sujet est bon, et la réponse est très bonne aussi
C’est chouette que la personne qui a posé la question ait aussi posté la réponse
J’aime ce genre de choses. Je suis vraiment content que des gens s’intéressent au rétrocomputing
Documenter ce genre de problèmes et leurs solutions pour tout le monde est une bonne chose
Je savais que ce truc appelé CPUID était une mauvaise idée
Pourquoi la question et la réponse avaient-elles le même horodatage ?
Il a probablement estimé que publier cette information sous forme de questions-réponses sur SO survivrait plus longtemps qu’un billet de blog que personne ne trouverait
Est-ce mauvais signe d’avoir su de quoi il s’agissait sans même ouvrir le lien ?
Bon, je dois aller crier aux jeunes d’aujourd’hui de dégager de ma pelouse
On ne pouvait compter que sur son ingéniosité et, au besoin, sur quelques gourous joignables par téléphone fixe. Il n’y avait pas ce merveilleux Internet rempli de réponses à l’utilité très variable