---
title: "Le bug de Word 97 disparaissait dès qu’on lançait le débogueur"
locale: "fr"
url: "https://irz.fr/fr/articles/word97-debugger-cpu-erratum-fr"
markdown_url: "https://irz.fr/fr/articles/word97-debugger-cpu-erratum-fr.md"
category: "tech"
tags: ["Word 97", "débogage", "CPU", "erratum", "release engineering"]
published_at: "2026-08-31T12:50:00.000Z"
author: "Léa Perrin"
translation: "https://irz.fr/en/articles/word97-debugger-cpu-erratum-en.md"
---

# Le bug de Word 97 disparaissait dès qu’on lançait le débogueur

À la fin du développement de Word 97, un crash reproductible disparaissait sous débogueur. Microsoft a fini par remonter à un erratum CPU, puis corrigé un seul motif binaire avec un NOP plutôt que changer de compilateur.

Le test savait faire planter Word 97. La panne restait sporadique, mais le script la ramenait assez souvent pour qu’une équipe en fin de release n’ait aucune envie de jouer à la roulette avec ses utilisateurs.

Puis quelqu’un lançait le débogueur.

Plus de crash.

Raymond Chen raconte cette histoire près de trente ans plus tard à partir du souvenir d’un collègue. Le bug touchait un chemin de code assez courant et avait été classé **ship-stopping** : suffisamment grave pour empêcher la sortie si l’équipe ne comprenait pas ce qui se passait. Le laboratoire avait même un script capable de provoquer le plantage avec une assez bonne fiabilité.[1](https://devblogs.microsoft.com/oldnewthing/20260826-00/?p=112649)

Le problème était donc reproductible jusqu’au moment précis où l’on ajoutait l’outil censé montrer ce qui cassait.

Chen ne donne pas la raison exacte de cette disparition. C’est important de le garder ainsi. Un débogueur peut modifier beaucoup de choses, depuis l’implantation de breakpoints jusqu’au timing ou à l’état du processus, mais le billet ne dit pas laquelle jouait ici. L’histoire devient plus intéressante si on ne remplit pas le trou à sa place.

> **Le bug se dérobe**
> - Un script de test fait planter Word 97 assez régulièrement.: 1
> - Le chemin est suffisamment courant pour bloquer la sortie.: 2
> - Sous débogueur, le crash cesse de se produire.: 3
> - L’enquête doit continuer sans explication directe du point de rupture.: 4
> La source ne précise pas pourquoi le débogueur modifie le comportement. Cette partie reste inconnue.

## Un autre observateur

Une idée envisagée paraît aujourd’hui presque excessive : sortir un **ICE**, un in-circuit emulator.[1](https://devblogs.microsoft.com/oldnewthing/20260826-00/?p=112649)

Dans le récit de Chen, ce matériel remplace physiquement le CPU de la machine cible. Un câble rejoint le socket du processeur et l’émulateur reproduit les signaux électriques qu’aurait générés le vrai composant. L’équipe peut alors inspecter mémoire et événements depuis l’extérieur du système qu’elle essaie de comprendre.[1](https://devblogs.microsoft.com/oldnewthing/20260826-00/?p=112649)

L’idée avait une logique presque physique : puisque l’observation logicielle changeait le résultat, il fallait tenter d’observer la machine sans lui injecter le même environnement de debug.

L’ICE ne sera finalement pas le héros de l’histoire.

Les développeurs remarquent quelque chose de beaucoup plus banal dans le parc de machines qui plantent : la plupart viennent du **même fabricant**, et elles ont été construites avant une certaine date. Une fois ce regroupement visible, ils trouvent un erratum CPU compatible avec la période de fabrication des systèmes touchés.[1](https://devblogs.microsoft.com/oldnewthing/20260826-00/?p=112649)

Chen ne nomme ni le fabricant ni le modèle de processeur. Il ne donne pas non plus le motif exact d’instructions concerné. Le bon niveau de détail s’arrête donc ici : **un erratum matériel connu pouvait être déclenché par une séquence de code particulière, sous des conditions précises**.

Cela change complètement la nature du bug. Le code de Word pouvait être valide du point de vue du jeu d’instructions et tout de même provoquer un résultat incorrect sur certaines révisions de processeur.

## Cent cinquante motifs

Le compilateur utilisé par l’équipe avait déjà reçu une mise à jour capable d’éviter la séquence dangereuse.[1](https://devblogs.microsoft.com/oldnewthing/20260826-00/?p=112649) La correction apparemment propre était donc disponible : reconstruire Word avec ce compilateur et laisser son générateur de code faire le travail.

L’équipe choisit de ne pas le faire.

À la place, elle écrit un outil qui parcourt les binaires existants et cherche la séquence susceptible de déclencher l’erratum. Il en trouve **environ 150 occurrences**.[1](https://devblogs.microsoft.com/oldnewthing/20260826-00/?p=112649)

Ce nombre est intéressant parce qu’une reprise secondaire de l’histoire peut facilement le faire disparaître. *The Register* résume le passage en disant, en substance, qu’une occurrence fut trouvée.[5](https://www.theregister.com/software/2026/08/28/word-97s-ghost-in-the-machine-the-crash-that-vanished-the-moment-anyone-looked-at_it/5293263) Le billet primaire est plus précis : il y avait environ 150 motifs candidats.

Le processeur demandait cependant une condition supplémentaire. La séquence devait **traverser une frontière de page mémoire**. Parmi les quelque 150 occurrences, une seule satisfaisait cette condition.[1](https://devblogs.microsoft.com/oldnewthing/20260826-00/?p=112649)

Et c’est celle que le script de test avait réussi à trouver dans le monde réel.

> **150 → 1**
> - séquences binaires correspondant au motif dangereux: ≈150
> - séquence qui traverse aussi une frontière de page: 1
> - correctif ajouté à l’endroit fautif: 1 NOP
> - raison documentée pour la disparition sous débogueur: 0
> Le scan transforme un crash mystérieux en une condition matérielle extrêmement étroite.

Une frontière de page est exactement le genre de détail qui transforme un bug de laboratoire en fantôme. Deux copies du même motif machine peuvent être inoffensives ou problématiques selon leur adresse. Le code ne porte donc pas, à lui seul, toute la condition du crash : son **placement en mémoire** compte aussi.

La rareté venait donc moins du motif lui-même que de son adresse. Cent cinquante candidats dans le binaire, un seul placé de façon à réunir toutes les conditions.

## Ne pas recompiler

Pourquoi refuser le nouveau compilateur si celui-ci savait déjà éviter l’erratum ? Parce que Word 97 était arrivé dans la partie du cycle où un changement propre peut être plus dangereux qu’un patch laid.

Chen utilise le terme Microsoft **escrow**. Dans un autre billet de 2026, il l’explique comme un build considéré comme celui qui sera livré s’il atteint les objectifs de qualité et de fiabilité. Sa traduction pratique est presque administrative : c’est fini, on ne touche plus à rien sauf vraie urgence.[2](https://devblogs.microsoft.com/oldnewthing/20260623-00/?p=112462)

Changer de compilateur à ce moment-là ne modifie pas seulement la séquence fautive. Il peut régénérer une grande partie du code machine.

Le risque n’est même pas nécessairement que le nouveau compilateur contienne un bug. Chen donne un exemple plus désagréable : un bug préexistant dans le code source peut rester masqué par l’ancienne disposition des variables locales. Un autre compilateur change cette disposition, et une variable non initialisée qui semblait toujours contenir une valeur acceptable reçoit soudain zéro.[1](https://devblogs.microsoft.com/oldnewthing/20260826-00/?p=112649)

Le nouveau compilateur pourrait alors être parfaitement correct et tout de même faire apparaître un vieux défaut. Il suffit qu’il change l’endroit où les données atterrissent en mémoire.

Cette méfiance n’est pas une coquetterie historique. Chen raconte ailleurs un bug Windows 3.1 où une correction minuscule ajoute une variable globale. Ce simple ajout déplace les autres variables en mémoire et révèle un vieux bug de corruption qui écrivait jusque-là, par chance, sur une valeur déjà nulle.[4](https://devblogs.microsoft.com/oldnewthing/20091104-01/?p=16143)

À la fin d’un produit, **la taille du diff source mesure mal la taille réelle du changement**.

> **Deux correctifs**
> - Évite automatiquement la séquence, mais régénère beaucoup de code et modifie dispositions et timings potentiels.: Nouveau compilateur
> - Change uniquement l’occurrence qui satisfait toutes les conditions de l’erratum CPU.: Patch binaire
> À ce stade du cycle, l’objectif n’est plus l’élégance du correctif : c’est la réduction de sa surface de régression.

## Un octet inutile

Le choix final tient dans une instruction : `nop`.

NOP signifie *no operation*. Le processeur avance, mais l’instruction n’est censée produire aucun effet fonctionnel utile. Ici, l’équipe l’insère dans le motif fautif afin que la séquence qui déclenche l’erratum ne se présente plus telle quelle au CPU.[1](https://devblogs.microsoft.com/oldnewthing/20260826-00/?p=112649)

Ce n’est pas un correctif du processeur. Ce n’est pas davantage une réparation conceptuelle du code source de Word. C’est un contournement ciblé d’une condition matérielle connue.

Chen a raconté en 2011 une autre histoire où ce genre de chirurgie était courant, cette fois à propos d’anciennes révisions **B1 du 80386** et de Windows 95.[3](https://devblogs.microsoft.com/oldnewthing/20110112-00/?p=11773) Plusieurs errata de ces puces dépendaient de l’enchaînement exact de deux types d’instructions. Insérer un NOP entre elles suffisait parfois à « casser » la combinaison dangereuse. Dans certains cas, même le NOP ordinaire appartenait à une catégorie gênante et il fallait utiliser une variante préfixée.[3](https://devblogs.microsoft.com/oldnewthing/20110112-00/?p=11773)

Ce billet de 2011 n’identifie pas le processeur de l’incident Word 97. Ce sont deux histoires différentes. Il fournit simplement un précédent concret : sur x86, un NOP peut servir de **cale mécanique dans le flux d’instructions**, non parce qu’il accomplit quelque chose mais parce qu’il empêche deux choses de se retrouver dans la mauvaise configuration.

Le patch Word 97 reprend exactement cette philosophie de mitigation minimale.

## Le bug connu

Il y a quelque chose de contre-intuitif dans cette décision. L’équipe connaît désormais un nouveau compilateur qui sait produire du code plus sûr face à cet erratum. Elle choisit tout de même de garder l’ancien outil et de modifier directement le binaire.

Au début d’un projet, patcher directement un binaire pour éviter un bug CPU mérite au minimum quelques sourcils levés. À quelques mètres de la ligne d’arrivée, le calcul est différent. Le produit entier a déjà accumulé des semaines ou des mois de tests sur une combinaison précise de sources, compilateur, options et binaires. Changer cette combinaison jette une partie de cette connaissance empirique.

Le NOP, lui, introduit un risque très localisé. L’équipe peut vérifier que l’unique occurrence à cheval sur la page ne déclenche plus le bug et que le scénario de test cesse de planter, sans demander à tout Word 97 de redevenir un produit neuf.

> **Connu / inconnu**
> - Une séquence, une condition de frontière de page, un groupe de machines affectées, un test qui reproduit.: Bug connu
> - Ce qu’un changement large de toolchain pourrait déplacer dans des milliers d’autres chemins déjà testés.: Régression inconnue
> Dans l’end game, accepter un défaut compris peut parfois être moins risqué que déclencher une nouvelle classe de défauts.

C’est la leçon que Chen répète dans ses histoires de fin de cycle : plus la sortie approche, plus la vitesse normale du développement doit ralentir.[4](https://devblogs.microsoft.com/oldnewthing/20091104-01/?p=16143)

On continue à corriger les bugs graves. Mais chaque correction doit payer pour la quantité de monde qu’elle déplace autour d’elle.

## Le fantôme reste

Il reste un morceau non résolu, et c’est très bien ainsi : pourquoi le débogueur faisait-il disparaître ce crash précis ?

Le récit publié ne le dit pas.[1](https://devblogs.microsoft.com/oldnewthing/20260826-00/?p=112649) Peut-être que l’équipe de l’époque le savait, peut-être que ce détail s’est perdu, peut-être qu’il n’était simplement pas nécessaire une fois l’erratum identifié. Nous n’avons pas à choisir à sa place.

Ce que l’histoire établit suffit déjà.

Un crash logiciel menait vers un défaut matériel. Le motif dangereux existait environ 150 fois mais une seule copie se trouvait au mauvais endroit. Un compilateur plus récent savait éviter le problème, mais le changer aurait élargi le risque à tout le produit. Une instruction qui ne fait rien a donc constitué le correctif le plus prudent.

Le correctif tient donc dans une instruction conçue pour ne rien faire. Pour une fois, l’absence d’effet était exactement la fonctionnalité recherchée.

## References

1. [Raymond Chen / Microsoft — In the product end game, every change carries significant risk, episode 2, 26 août 2026](https://devblogs.microsoft.com/oldnewthing/20260826-00/?p=112649)
2. [Raymond Chen / Microsoft — Microspeak elaborated: Isn’t escrow just a release candidate by another name?, 23 juin 2026](https://devblogs.microsoft.com/oldnewthing/20260623-00/?p=112462)
3. [Raymond Chen / Microsoft — My, what strange NOPs you have!, 12 janvier 2011](https://devblogs.microsoft.com/oldnewthing/20110112-00/?p=11773)
4. [Raymond Chen / Microsoft — In the product end game, every change carries significant risk, 4 novembre 2009](https://devblogs.microsoft.com/oldnewthing/20091104-01/?p=16143)
5. [The Register — Word 97's ghost in the machine, 28 août 2026](https://www.theregister.com/software/2026/08/28/word-97s-ghost-in-the-machine-the-crash-that-vanished-the-moment-anyone-looked-at_it/5293263)
