20 puntos por darjeeling 22 일 전 | 9 comentarios | Compartir por WhatsApp

Se descubrió un bug en el kernel XNU de macOS que provoca una parálisis total del networking TCP tras exactamente 49 días, 17 horas, 2 minutos y 47 segundos de funcionamiento continuo. La causa es un overflow de entero de 32 bits en el contador interno de timestamps TCP del kernel (tcp_now). ping sigue respondiendo, pero ya no se puede establecer ninguna conexión TCP nueva, y por ahora la única solución es reiniciar.


Cómo se descubrió

Photon operaba 24/7 una flota de servidores Mac que monitorea el estado del servicio de iMessage. El 30 de marzo de 2026, justo al cumplirse 49.7 días desde el último reinicio, varias máquinas empezaron silenciosamente a rechazar nuevas conexiones TCP. ping seguía funcionando y las conexiones existentes se mantenían, pero fallaba cualquier intento de abrir sockets nuevos.

Tras reiniciar para recuperar el servicio, el equipo eligió dos máquinas (A y B) que alcanzarían el mismo punto crítico en pocos días y diseñó un experimento en vivo.


Principio técnico del bug

El contador problemático tcp_now

En el kernel XNU, tcp_now es un entero sin signo de 32 bits que cuenta en milisegundos el tiempo transcurrido desde el arranque. El valor máximo que puede representar un entero de 32 bits es 4,294,967,295 ms, lo que equivale exactamente a 49 días, 17 horas, 2 minutos y 47 segundos.

¿Por qué el contador se “congela”?

El código de actualización de tcp_now tiene una guarda simple para “evitar que el reloj retroceda”:

if (tmp < current_tcp_now) {  
    os_atomic_cmpxchg(&tcp_now, tmp, current_tcp_now, ...);  
}  

En el momento del overflow, el current_tcp_now recién calculado vuelve a enrollarse cerca de 0, mientras que el tmp existente está cerca del valor máximo. Entonces la condición tmp < current_tcp_now queda falsa para siempre, así que tcp_now se queda detenido en ese valor. El reloj TCP del kernel se detuvo.

Por qué TIME_WAIT nunca expira

Cuando una conexión TCP se cierra, el kernel registra su hora de expiración como tcp_now + 30 segundos. Un recolector de basura recorre periódicamente estas entradas y libera la conexión si tcp_now >= hora de expiración. Pero si tcp_now se congela, esa condición nunca vuelve a ser verdadera, por lo que las conexiones en TIME_WAIT jamás se recolectan.


Resultados del experimento

El equipo observó la cantidad de conexiones en TIME_WAIT mientras generaba varias conexiones TCP de vida corta por segundo durante 5 minutos antes y 5 minutos después del overflow.

Tramo Estado
Antes del overflow TIME_WAIT se mantenía estable en ~200 (expiración normal cada 30 segundos)
Justo después del overflow La expiración se detiene y TIME_WAIT empieza a crecer de forma monotónica
84 segundos después de detener la creación de conexiones TIME_WAIT, que debería haber llegado a 0, en cambio aumentó (2,828 → 2,837)
9.5 horas después del overflow Machine A: 4,888, Machine B: 8,217 — no se recolectó ni una sola conexión

Después de 9.5 horas, también se habían acumulado más de 3,000 conexiones en estado SYN_SENT, y el load average de Machine B se disparó hasta 49.74.


Entornos afectados

Las Mac de consumo general suelen reiniciarse antes de los 49 días por actualizaciones del sistema operativo, así que el impacto es menor. Pero los siguientes entornos tienen alto riesgo:

  • Flotas de servidores con alta disponibilidad durante largos periodos
  • Servidores macOS de build para CI/CD (Jenkins, runners self-hosted de GitHub Actions)
  • Workstations Mac Pro (renderizado o compilación de larga duración)
  • Mac en colocation administradas en forma remota
  • Granjas de build con Mac mini e infraestructura de pruebas

Respuesta actual y futura

El equipo está desarrollando un workaround para corregir directamente tcp_now congelado sin reiniciar. Hasta entonces, solo hay una medida temporal:

Programa un reinicio antes de los 49 días, 17 horas, 2 minutos y 47 segundos.


Bugs históricos similares

Este bug pertenece a una larga línea histórica de bugs por overflow de enteros: el crash de 49.7 días de Windows 95/98, el problema del año 2038 (Y2K38), el rollover del número de semana de GPS y la killscreen del nivel 256 de Pac-Man pertenecen a la misma familia.


Original: Photon Blog, 2026.04.07

9 comentarios

 
brilliant08 22 일 전

Ahora macOS también cumple sus 49 días.

 
bungker 17 일 전

jajajajaja

 
roxie 21 일 전

zzzzz

 
devil1032 21 일 전

Como se trata de un problema relacionado con el tiempo, me hace pensar en el Y2K... 🤖..

 
savvykang 21 일 전

Los humanos repiten los mismos errores.

 
shincad 20 일 전

Así que de verdad hay que reiniciar antes de los 49 días.

En realidad, nunca se debería comparar el tiempo absoluto con <...

if ((int32_t)(tmp - current_tcp_now) < 0) {
os_atomic_cmpxchg(&tcp_now, tmp, current_tcp_now, ...);
}
Se tendría que hacer así, viendo la diferencia entre los dos valores... pero los humanos siempre terminamos cometiendo los mismos errores.

 
icosahedron 21 일 전

Viendo cosas así, capaz que en 2038 de verdad se arma un caos.

 
princox 21 일 전

Vaya, esto de verdad no tiene sentido....

 
jic5760 21 일 전

Entonces, ¿cómo es que las instancias Mac de AWS o GitHub no tuvieron problemas todo este tiempo...?