1 points par GN⁺ 26 일 전 | 1 commentaires | Partager sur WhatsApp
  • Le compteur d’horodatage TCP (tcp_now) de macOS subit un débordement 32 bits environ 49,7 jours après le démarrage, ce qui fige l’horloge TCP interne
  • En conséquence, les connexions en état TIME_WAIT n’expirent plus et s’accumulent, empêchant la libération des ports éphémères
  • Avec le temps, l’épuisement des ports éphémères fait échouer toutes les nouvelles connexions TCP, tandis que les connexions existantes restent maintenues
  • ICMP (ping) continue de fonctionner normalement, mais l’ensemble des fonctions TCP est paralysé, sans possibilité de récupération autrement qu’avec un redémarrage
  • Les serveurs macOS, machines de build et environnements CI fonctionnant sur de longues durées sont exposés à ce problème selon un cycle de 49 jours et 17 heures, ce qui impose des redémarrages périodiques tant qu’un correctif du noyau n’est pas disponible

Contexte : notions de base sur TCP

  • Une connexion TCP ne disparaît pas immédiatement à sa fermeture : elle passe en état TIME_WAIT, une étape destinée à gérer les paquets retardés et à garantir une fermeture fiable
    • Cela évite qu’anciens paquets soient interprétés à tort comme appartenant à une nouvelle connexion, et permet de gérer la retransmission en cas de perte du dernier ACK
  • La durée de TIME_WAIT est définie comme 2 × MSL (Maximum Segment Lifetime) ; sur macOS, elle est réglée à environ 30 secondes
  • Le MSL est la durée maximale pendant laquelle un segment TCP peut survivre sur le réseau ; la RFC 793 la fixe à 2 minutes, mais les systèmes modernes utilisent généralement des valeurs bien plus courtes
  • Le débordement d’un entier non signé 32 bits correspond au retour à 0 après dépassement de la valeur maximale (4 294 967 295). Sur macOS, l’horodatage TCP (tcp_now) est un compteur 32 bits incrémenté en millisecondes depuis le démarrage, et le débordement survient après 49 jours 17 heures 2 minutes 47,296 secondes

Découverte : interruption des connexions TCP après 49,7 jours

  • Les serveurs Mac de Photon dédiés à la surveillance d’iMessage fonctionnaient 24/7, et le 30 mars 2026, exactement 49,7 jours après le démarrage, toutes les nouvelles connexions TCP ont commencé à échouer
    • Les connexions existantes et ICMP (ping) continuaient de fonctionner, mais il devenait impossible d’ouvrir de nouveaux sockets TCP
  • La cause est un débordement du compteur d’horodatage TCP (tcp_now) dans le noyau XNU : une logique de validation de croissance monotone bloque la mise à jour après le wraparound, ce qui fige l’horloge TCP interne
  • Les connexions TIME_WAIT n’expirent donc plus, les ports éphémères ne sont jamais libérés et s’accumulent, rendant toute récupération impossible sans redémarrage
  • Après redémarrage, le même phénomène se reproduit à nouveau tous les 49,7 jours

Conception de l’expérience : comparer le comportement TCP avant et après le débordement

  • Hypothèse : si le garbage collection de TIME_WAIT s’arrête après le débordement, le schéma de création de connexions TCP courtes doit différer avant et après celui-ci
    • Avant le débordement : expiration normale de TIME_WAIT après 30 secondes
    • Après le débordement : persistance indéfinie de TIME_WAIT
  • Exécution d’un script de test en trois phases
    1. Phase de surveillance : enregistrement du nombre de TIME_WAIT toutes les 10 secondes, de 35 minutes avant le débordement à 5 minutes avant
    2. Phase d’explosion : création d’environ 15 connexions TCP courtes toutes les 2 secondes pendant 10 minutes autour du débordement
    3. Phase d’observation : surveillance de l’évolution de TIME_WAIT après arrêt de la génération de connexions

Résultats : stagnation de TIME_WAIT après le débordement

  • Avant le débordement, le nombre de connexions TIME_WAIT oscillait de façon stable entre 0 et 200, confirmant un recyclage normal
  • Juste après le débordement, le nombre de connexions TIME_WAIT augmente continuellement et n’expire plus
  • Sur la machine B, 2 828 connexions TIME_WAIT n’avaient toujours pas été recyclées 84 secondes plus tard, et le cumul a continué ensuite
  • Sur la machine A également, les vérifications manuelles ont montré une augmentation monotone du nombre de TIME_WAIT, sans récupération possible

Cause racine : débordement 32 bits de tcp_now dans le noyau XNU

  • tcp_now est un compteur 32 bits en millisecondes défini dans bsd/netinet/tcp_var.h, utilisé pour suivre le temps écoulé depuis le démarrage
  • Dans la fonction calculate_tcp_clock(), l’opération (uint32_t)now.tv_sec * 1000 dépasse la valeur maximale après 49,7 jours, provoquant un wraparound
  • À cause de la condition if (tmp < current_tcp_now), la valeur existante devient supérieure à la nouvelle après débordement, ce qui bloque la mise à jour et fige définitivement tcp_now
  • Comme l’expiration de TIME_WAIT est évaluée par rapport à tcp_now, si l’horloge s’arrête, la condition d’expiration devient toujours fausse, empêchant tout recyclage

Effet en chaîne : propagation jusqu’à l’arrêt complet de TCP

  • Après quelques minutes : l’arrêt du recyclage de TIME_WAIT provoque des problèmes progressifs sur les charges avec beaucoup de connexions courtes
  • Après quelques heures : des milliers de TIME_WAIT s’accumulent, entraînant un épuisement des ports éphémères
  • Une fois les ports épuisés : les nouvelles connexions TCP échouent en état SYN_SENT, et seules les connexions existantes restent actives
  • Hausse brutale de la charge CPU : le noyau continue à scanner la file TIME_WAIT, augmentant la charge
  • Au final, paralysie complète de TCP, tandis qu’ICMP continue de fonctionner normalement
  • La seule méthode de récupération est le redémarrage, après quoi le compteur de 49,7 jours repart de zéro

Éléments supplémentaires et cas similaires

  • La RFC 7323 précise qu’avec des horodatages 32 bits à 1 ms, l’enroulement du bit de signe se produit environ tous les 24,8 jours
    • Dans le cas de macOS, il s’agit d’un débordement complet sur 32 bits (49,7 jours), soit un défaut local du noyau distinct des problèmes d’horodatage distant abordés par la RFC
  • De nombreux cas identiques ont été signalés dans la communauté Apple et dans des projets open source
    • Impossible d’établir des connexions TCP, ping reste normal, seul un redémarrage résout le problème, après plusieurs semaines de fonctionnement
    • Le même schéma apparaît notamment dans la Podman issue #12495
  • Points communs : seul TCP échoue, ICMP fonctionne, redémarrage nécessaire, cycle de survenue de plusieurs semaines

Périmètre d’impact

  • Le problème peut survenir sur les systèmes macOS fonctionnant en continu plus de 49 jours et 17 heures
  • Les utilisateurs classiques sont peu touchés, car les mises à jour périodiques entraînent généralement des redémarrages
  • Environnements à haut risque
    • Flottes de serveurs tournant sur de longues durées
    • Serveurs de build CI/CD basés sur macOS
    • Stations de travail Mac Pro
    • Mac en colocation administrés à distance
    • Clusters de Mac mini utilisés pour des fermes de build ou des infrastructures de test

Procédure de reproduction

  • Calculer l’instant estimé du débordement à partir de l’heure de démarrage
  • Surveiller le nombre de TIME_WAIT avant et après le débordement
  • Générer un grand nombre de connexions TCP courtes au moment du débordement
  • Si le nombre de TIME_WAIT ne diminue pas après 2 minutes, la reproduction du bug est réussie

État du système observé après 9,5 heures

  • Aucune connexion TIME_WAIT n’a été recyclée, et leur nombre a continué à augmenter
  • Plus de 3 000 connexions en échec à l’état SYN_SENT se sont accumulées
  • Seules les connexions existantes étaient maintenues, toute nouvelle connexion étant impossible
  • La charge moyenne de la machine B est montée jusqu’à 49,74, le noyau consommant excessivement du CPU pour scanner la file TIME_WAIT

Conclusion

  • Un simple entier 32 bits et la condition if (tmp < current_tcp_now) agissent comme une bombe à retardement capable d’arrêter complètement TCP après 49,7 jours
  • Il s’agit d’un type de défaut difficile à détecter en phase de développement, de test ou de revue de code, et qui ne se révèle qu’en conditions réelles d’exploitation
  • Photon a reproduit le même phénomène sur plusieurs serveurs, en confirmant clairement un recyclage normal avant le débordement, puis une accumulation de TIME_WAIT après celui-ci
  • Lorsque tcp_now se fige, l’horloge TCP du noyau s’arrête ; le système semble fonctionner en apparence, mais tous les ports TCP finissent par être épuisés
  • Les administrateurs de systèmes macOS à longue durée de fonctionnement doivent retenir l’échéance de 49 jours 17 heures 2 minutes 47 secondes ; des redémarrages périodiques sont nécessaires tant qu’un ajustement du noyau ou un correctif n’est pas disponible
  • Photon développe actuellement une solution de contournement permettant de restaurer tcp_now sans redémarrage

1 commentaires

 
GN⁺ 26 일 전
Avis Hacker News
  • Je comprends enfin pourquoi mon iMac n’avait parfois plus aucune connectivité
    Je n’avais absolument pas réalisé que c’était à cause de l’uptime

  • En lisant l’article, j’ai eu une forte impression qu’il avait été écrit par une IA, et je me suis demandé si Apple avait réellement été contacté
    Bien sûr, le bug est important, mais j’ai trouvé qu’il y avait beaucoup d’expressions exagérées
    La plupart des utilisateurs seront probablement très peu affectés
    On peut aussi sans doute éviter le problème en mettant le Mac en veille, puisque cela réinitialise la pile TCP
    Apple finira par corriger ça, mais il n’y a pas lieu de paniquer pour l’instant

    • J’ai l’impression d’avoir eu ce problème moi aussi
      Mon MacBook, avec la veille automatique désactivée, était resté allumé environ 50 jours, et j’avais le phénomène suivant : le ping fonctionnait, mais aucune connexion TCP ne passait
      Changer de Wi‑Fi ou passer en filaire n’a rien résolu, et tout est revenu à la normale dès le redémarrage
    • Apple n’a pas été contacté, et l’auteur du blog semble vouloir corriger ça lui-même
      Il aurait dit qu’il développait une solution de contournement meilleure qu’un redémarrage et que, d’ici là, il fallait redémarrer périodiquement
    • Moi aussi je laisse mon Mac Mini allumé 24 h/24, et quand le réseau se bloque de temps en temps, désactiver puis réactiver l’adaptateur Wi‑Fi règle le problème
      C’est alors le bon moment pour redémarrer
    • Le problème a bien été signalé à Apple et enregistré dans leur système interne
  • En ce moment, les billets de blog écrits par une IA sont vraiment pénibles à lire
    Le style est peu naturel et met beaucoup trop de temps à aller à l’essentiel

    • Pareil pour moi. Lire un texte écrit par une IA est fatigant et n’aide pas à rester concentré
    • Si on ne regarde que le résumé fait par l’IA, c’est simple : le problème est que le Mac ne gère pas le rollover lorsque l’horloge tcp_now déborde
  • Je ne suis pas d’accord avec l’idée que « personne ne va tester pendant 50 jours »
    En réalité, il suffit de faire des tests de simulation en accélérant le temps

    • Dans le noyau Linux, pour attraper ce genre de problème, le compteur jiffies est initialisé au démarrage à une valeur juste avant le débordement
    • macOS utilise l’horloge matérielle, qui s’arrête pendant le sommeil
      Dans ce cas, on peut vérifier le comportement en modifiant une fonction comme calculate_tcp_clock pour passer l’uptime en argument
    • Cette approche est aussi couramment utilisée pour les tests de jeux vidéo
  • Ce bug affecte non seulement OpenClaw, mais toutes les connexions TCP

    • Il n’est pas nécessaire qu’une connexion individuelle dure longtemps
      Une fois que l’uptime de macOS dépasse 49,7 jours, toutes les connexions TCP commencent à être affectées
    • Il y avait aussi une blague disant qu’« OpenClaw semble maintenant être la chose la plus importante au monde »
  • Plusieurs de mes machines macOS tournent depuis plus de 600 à 1000 jours, et les connexions TCP expirent normalement
    Leurs versions du noyau sont respectivement 20.6.0 et 17.7.0
    Donc ce bug semble n’apparaître qu’à partir de certaines versions

    • D’après l’analyse, la valeur de tcp_now se fige juste avant le débordement, et un wraparound incorrect dans le calcul du timer la rend négative, ce qui fait échouer la comparaison
      Des connexions TIME_WAIT peuvent s’accumuler pendant une brève période, mais le texte original surréagissait et donnait l’impression d’avoir été écrit par un LLM
    • En réalité, ce bug proviendrait d’un code nouvellement introduit l’an dernier dans macOS 26
      Lien GitHub associé
  • Ce genre de problème se répète dans différents logiciels
    Il y a longtemps, quelque chose de similaire était arrivé sur les serveurs de Guild Wars, et ils avaient testé en ajoutant une certaine valeur à GetTickCount() pour provoquer plus vite le débordement

    • Les systèmes qui gèrent des débordements devraient être testés en provoquant un débordement immédiatement après le démarrage
  • Ce bug rappelle le bug des 49,7 jours de Windows 95
    Article associé

  • Je me demande quel est le rapport entre OpenClaw et ce bug

  • Ce problème rappelle le bug des 208 jours de l’ordonnanceur du noyau Linux
    Lien de référence