- El contador de marcas de tiempo TCP (
tcp_now) de macOS sufre un desbordamiento de 32 bits alrededor de 49.7 días después del arranque, lo que hace que el reloj TCP interno se detenga - Como resultado, las conexiones en estado TIME_WAIT no expiran y se acumulan, por lo que los puertos efímeros no se liberan
- Con el paso del tiempo, el agotamiento de puertos efímeros hace que fallen todas las conexiones TCP nuevas, mientras las conexiones existentes se mantienen
- ICMP (
ping) sigue funcionando con normalidad, pero toda la funcionalidad de TCP queda inutilizada, y no se puede recuperar sin reiniciar - Los servidores macOS, máquinas de compilación y entornos CI que operan durante largos periodos quedan expuestos a este problema en ciclos de 49 días y 17 horas, y requieren reinicios periódicos hasta que se corrija el kernel
Contexto: conceptos básicos de TCP
- Cuando una conexión TCP termina, no desaparece de inmediato, sino que entra en estado TIME_WAIT, una etapa necesaria para manejar paquetes retrasados y garantizar un cierre confiable
- Esto evita que paquetes antiguos se interpreten erróneamente como parte de una conexión nueva y permite retransmitir el último ACK si se pierde
- La duración de TIME_WAIT se define como 2 × MSL (Maximum Segment Lifetime), y en macOS está configurada en aproximadamente 30 segundos
- MSL es el tiempo máximo durante el cual un segmento TCP puede sobrevivir en la red; en el RFC 793 se definía como 2 minutos, aunque en sistemas modernos suele ser mucho menor
- El desbordamiento de enteros sin signo de 32 bits ocurre cuando un valor supera el máximo (4,294,967,295) y vuelve a 0; la marca de tiempo TCP de macOS (
tcp_now) es un contador de 32 bits en milisegundos que aumenta desde el arranque, por lo que se desborda tras 49 días, 17 horas, 2 minutos y 47.296 segundos
Hallazgo: interrupción de conexiones TCP tras 49.7 días
- Los servidores Mac de Photon para monitoreo de iMessage operaban 24/7, y el 30 de marzo de 2026 ocurrió un fenómeno en el que todas las nuevas conexiones TCP fallaban exactamente 49.7 días después del arranque
- Las conexiones existentes e ICMP (
ping) seguían funcionando, pero ya no era posible crear nuevos sockets TCP
- Las conexiones existentes e ICMP (
- La causa fue el desbordamiento del contador de marcas de tiempo TCP (
tcp_now) en el kernel XNU, donde la lógica de validación de incremento monótono bloqueó las actualizaciones tras el wraparound y el reloj TCP interno quedó detenido - Como las conexiones TIME_WAIT no expiraban, los puertos efímeros dejaron de liberarse y se acumularon, haciendo imposible la recuperación sin reiniciar
- Después de reiniciar, el mismo fenómeno volvió a repetirse en ciclos de 49.7 días
Diseño experimental: comparación del comportamiento TCP antes y después del desbordamiento
- Hipótesis: si la recolección de basura de TIME_WAIT se detiene tras el desbordamiento, debería observarse una diferencia en el patrón de creación de conexiones TCP cortas antes y después del evento
- Antes del desbordamiento: TIME_WAIT expira normalmente tras 30 segundos
- Después del desbordamiento: TIME_WAIT persiste indefinidamente
- Se ejecutó un script de prueba compuesto por tres fases
- Fase de monitoreo: registrar la cantidad de TIME_WAIT cada 10 segundos desde 35 minutos antes hasta 5 minutos antes del desbordamiento
- Fase de explosión: generar alrededor de 15 conexiones TCP cortas cada 2 segundos durante 10 minutos alrededor del desbordamiento
- Fase de observación: monitorear los cambios en TIME_WAIT después de detener la generación de conexiones
Resultados: estancamiento de TIME_WAIT después del desbordamiento
- Antes del desbordamiento, la cantidad de TIME_WAIT circulaba de forma estable entre 0 y 200, confirmando un comportamiento normal de recolección
- Inmediatamente después del desbordamiento, la cantidad de TIME_WAIT siguió aumentando y dejó de expirar por completo
- En el caso de Machine B, 2,828 conexiones TIME_WAIT seguían sin recolectarse ni una sola después de 84 segundos, y continuaron acumulándose
- En Machine A, la verificación manual también mostró que la cantidad de TIME_WAIT aumentaba de forma monótona y el sistema no podía recuperarse
Causa raíz: desbordamiento de 32 bits de tcp_now en el kernel XNU
tcp_nowes un contador de 32 bits en milisegundos definido enbsd/netinet/tcp_var.hque rastrea el tiempo transcurrido desde el arranque- En la función
calculate_tcp_clock(), la operación(uint32_t)now.tv_sec * 1000supera el valor máximo después de 49.7 días y provoca el wraparound - Debido a la condición
if (tmp < current_tcp_now), durante el desbordamiento el valor existente se vuelve mayor que el nuevo y se bloquea la actualización, dejandotcp_nowdetenido permanentemente - La comprobación de expiración de TIME_WAIT se realiza en función de
tcp_now, por lo que, si el reloj se detiene, la condición de expiración siempre resulta falsa y ya no es posible recolectarlas
Efecto en cadena: se extiende hasta detener toda la funcionalidad TCP
- Tras unos minutos: se detiene la recolección de TIME_WAIT y empiezan a aparecer problemas graduales en cargas con muchas conexiones cortas
- Tras unas horas: se acumulan miles de entradas TIME_WAIT y se produce agotamiento de puertos efímeros
- Después del agotamiento de puertos: las nuevas conexiones TCP fallan en estado SYN_SENT y solo se mantienen las ya existentes
- Aumento brusco de carga de CPU: el kernel sigue escaneando la cola de TIME_WAIT, elevando la carga
- El resultado final es una parálisis total de TCP, mientras ICMP sigue funcionando con normalidad
- El único método de recuperación es reiniciar, tras lo cual vuelve a comenzar la cuenta regresiva de 49.7 días
Evidencia adicional y casos relacionados
- RFC 7323 especifica que el bit de signo de una marca de tiempo de 32 bits en unidades de 1 ms se envuelve aproximadamente cada 24.8 días
- En macOS, se trata del desbordamiento completo de 32 bits (49.7 días), un fallo local del kernel distinto del problema de marcas de tiempo remotas tratado en el RFC
- En la comunidad de Apple y en proyectos de código abierto se han reportado múltiples casos con los mismos síntomas
- Imposibilidad de establecer conexiones TCP,
pingfuncionando, solo se resuelve reiniciando, y aparece tras varias semanas de operación - El mismo patrón también se observa en Podman issue #12495, entre otros
- Imposibilidad de establecer conexiones TCP,
- Elementos en común: solo falla TCP, ICMP funciona, hay que reiniciar, ocurre en ciclos de varias semanas
Alcance del impacto
- Puede ocurrir en sistemas macOS que lleven más de 49 días y 17 horas encendidos de forma continua
- Los usuarios comunes se ven poco afectados porque suelen reiniciar por actualizaciones periódicas
- Entornos de alto riesgo
- Flotas de servidores de larga duración
- Servidores de compilación CI/CD basados en macOS
- Estaciones de trabajo Mac Pro
- Macs en colocation administradas de forma remota
- Clústeres de Mac mini para granjas de compilación e infraestructura de pruebas
Procedimiento de reproducción
- Calcular el momento esperado del desbordamiento a partir de la hora de arranque
- Monitorear la cantidad de TIME_WAIT antes y después del desbordamiento
- Generar muchas conexiones TCP cortas en el momento del desbordamiento
- Si después de 2 minutos la cantidad de TIME_WAIT no disminuye, la reproducción del bug fue exitosa
Estado del sistema observado 9.5 horas después
- Las conexiones TIME_WAIT no se recolectaron ni una sola vez y siguieron aumentando
- Se acumularon más de 3,000 conexiones fallidas en estado SYN_SENT
- Solo se mantuvieron las conexiones existentes y no fue posible crear nuevas
- La carga promedio de Machine B subió hasta 49.74, porque el kernel consumía demasiada CPU escaneando la cola de TIME_WAIT
Conclusión
- Un único entero de 32 bits y la condición
if (tmp < current_tcp_now)actúan como una bomba de tiempo que detiene por completo TCP después de 49.7 días - Es un tipo de fallo difícil de detectar en etapas de desarrollo, pruebas o revisión de código, y solo se manifiesta en entornos reales de operación
- Photon reprodujo el mismo fenómeno en varios servidores y confirmó claramente que antes del desbordamiento la recolección funcionaba normalmente, y después TIME_WAIT se acumulaba
- Cuando
tcp_nowse detiene, también se detiene el reloj TCP del kernel; el sistema parece estar normal a simple vista, pero todos los puertos TCP terminan agotados - Los administradores de sistemas macOS de larga duración deben recordar el umbral de 49 días, 17 horas, 2 minutos y 47 segundos, y ajustar los ciclos de reinicio o reiniciar periódicamente hasta que se corrija el kernel
- Photon está desarrollando actualmente una solución temporal para restaurar
tcp_nowsin reiniciar
1 comentarios
Comentarios de Hacker News
Ahora por fin entiendo por qué a veces mi iMac se quedaba sin ninguna conexión
Jamás me había dado cuenta de que era por el uptime
Mientras leía el artículo, me dio demasiado la impresión de que estaba escrito por IA, así que me pregunté si realmente habían contactado a Apple
Claro, el bug es importante, pero sentí que había mucho lenguaje exagerado
A la mayoría de los usuarios probablemente casi no les afecte
Si dejas la Mac en modo de reposo, el stack TCP se reinicia, así que quizá así se pueda evitar el problema
Al final Apple lo va a corregir, pero no es motivo para entrar en pánico ahora mismo
Tenía una MacBook con el reposo automático desactivado que llevaba como 50 días encendida, y pasaba que respondía al ping pero no funcionaba ninguna conexión TCP
No se arregló ni cambiando de Wi‑Fi ni conectándola por cable; en cuanto la reinicié, todo volvió a la normalidad
Según dijo, está “desarrollando una solución alternativa mejor que reiniciar, y mientras tanto recomienda reiniciar periódicamente”
En esos momentos, es buena hora para reiniciar
Últimamente los posts de blog escritos por IA se han vuelto muy difíciles de leer
El estilo se siente poco natural y tarda demasiado en llegar al punto
tcp_nowse desbordaNo estoy de acuerdo con eso de que “ningún desarrollador va a probar durante 50 días”
En la práctica basta con hacer pruebas de simulación acelerando el tiempo
En estos casos, si se modifica una función como
calculate_tcp_clockpara pasarle el uptime como argumento, se puede verificarEste bug no afecta solo a OpenClaw, sino a todas las conexiones TCP
Si el uptime de macOS supera los 49.7 días, todas las conexiones TCP empiezan a verse afectadas
Varios de mis equipos macOS llevan entre 600 y 1000 días encendidos, y las conexiones TCP siguen expirando normalmente
Las versiones del kernel son 20.6.0 y 17.7.0 respectivamente
Así que parece que este bug solo ocurre a partir de ciertas versiones
tcp_nowse queda detenido justo antes del desbordamiento, y por un wraparound incorrecto en el cálculo de temporizadores pasa a ser negativo, así que la comparación fallaPuede haber un breve periodo en el que se acumulen conexiones TIME_WAIT, pero el texto original reaccionaba de forma exagerada y parecía escrito por un LLM
Enlace relacionado en GitHub
Este tipo de problema se repite en distintos programas
Hace tiempo pasó algo parecido en los servidores de Guild Wars, donde probaron sumándole cierto valor a
GetTickCount()para provocar antes el desbordamientoEste bug recuerda al bug de los 49.7 días de Windows 95
Artículo relacionado
Me pregunto qué relación hay entre OpenClaw y este bug
Este problema me recuerda al bug de los 208 días del scheduler del kernel de Linux
Enlace de referencia