Un Sun 3 est arrivé sur mon bureau le 4 Janvier 1988, et il a
aussitôt manifesté le bug que j'ai localisé et corrigé dans la soirée,
pour autant que je me souvienne, car les fichiers que je garde de cette
époque eux sont datés du 7 Janvier, sans doute suite à une recopie.
Tout était prévu pour traiter le cas des années bissextiles, mais il y
avait un bug dans le code. En cette circonstance Sun a été victime de
l'anthropomorphisme des fabricants de chips. Leur système, comme tous
les Unix, utilisait des dates en secondes depuis 1970 et les
traditionnelles fonctions de conversion en notations humaine
asctime, ctime et localtime qui
tenaient parfaitement compte des années bissextiles ainsi que des
fuseaux horaires et des heures d'été (américaines seulement à cette
epoque).
Leur problème est venu de ce que pour conserver l'heure pendant
que la machine est hors tension, ils utilisaient un chip du commerce
avec une pile incorporée qui lui comptait en heures, minutes,
secondes, mois, jours, année. Il leur fallait donc dans le noyau du
système d'exploitation une fonction de conversion pour mettre à jour
le chip chaque fois qu'on change l'heure de la machine. Ils en ont
donc réécrit une, dans laquelle ils ont mis un superbe bug, une
vieille erreur classique d'effet de bord sur une macro dans le langage de
programmation C. Pour leur conversion, ils avaient besoin de savoir
combien de secondes il y a dans chaque mois. C'est facile d'obtenir ça
par consultation d'une table initialisée, qu'ils avaient appelée
monthsec[], mais il y a le problème de février en années
bissextiles, d'où la macro MONTHSEC, qui dit que ce qu'on
veut c'est ce que donne la table sauf si l'année est bissextile et le
mois février, cas auquel c'est 29*24*3600 :
#define MONTHSEC(mon, year) ((year%4 == 0) && mon == 2) ? \
(29*24*60*60) : monthsec[mon-1]
jusque là tout va bien.
Là où ça bugge, c'est à la sortie d'une boucle où il est écrit:
t += MONTHSEC(--mon, year);
Il se produit alors un effet de bord dans la macro
MONTHSEC, dû à la décrémentation de mon
dans les arguments de la macro, (--mon), et comme
mon figure 2 fois dans cette macro il y est décrémenté 2
fois ... non pas toujours, parce que la première occurrence de
mon, la comparaison ((mon) == 2) n'est
évaluée que si l'année est bissextile, donc le code avait donné
l'illusion d'être correct jusque-là. À noter qu'il aurait à nouveau
donné cette illusion durant le mois de février de cette même année,
car dans ce cas c'est la deuxième occurence de mon qui n'est pas évaluée !
À noter que personne ne se serait jamais aperçu de rien tant qu'on n'essayait pas de mettre la machine à l'heure. Mais il se trouve qu'on tournait à cette epoque à l'INRIA et dans beaucoup d'autres endroits un protocole de synchronisation entre machines qui n'arrêtait pas de les mettre à l'heure, de pas grand chose d'ailleurs, mais avec le bug ça suffisait pour faire faire n'importe quoi à leur heure.