1 pontos por GN⁺ 21 일 전 | 1 comentários | Compartilhar no WhatsApp
  • O contador de timestamp TCP (tcp_now) do macOS sofre overflow de 32 bits cerca de 49,7 dias após o boot, fazendo com que o relógio interno do TCP pare
  • Com isso, conexões em estado TIME_WAIT não expiram e ficam se acumulando, impedindo a liberação de portas efêmeras
  • Com o tempo, o esgotamento de portas efêmeras faz com que todas as novas conexões TCP falhem, mantendo apenas as conexões já existentes
  • O ICMP (ping) continua funcionando normalmente, mas toda a funcionalidade do TCP fica paralisada, sem possibilidade de recuperação além de reiniciar
  • Servidores macOS, máquinas de build e ambientes de CI com longa duração de execução ficam expostos a esse problema em um ciclo de 49 dias e 17 horas, exigindo reinicializações periódicas até que o kernel seja corrigido

Contexto: conceitos básicos de TCP

  • Quando uma conexão TCP é encerrada, ela não desaparece imediatamente; entra no estado TIME_WAIT, uma etapa necessária para lidar com pacotes atrasados e garantir um encerramento confiável
    • Isso evita que pacotes antigos sejam interpretados incorretamente como parte de uma nova conexão e permite retransmitir o último ACK se ele se perder
  • A duração do TIME_WAIT é definida como 2 × MSL (Maximum Segment Lifetime) e, no macOS, está configurada em cerca de 30 segundos
  • MSL é o tempo máximo que um segmento TCP pode permanecer vivo na rede; no RFC 793 ele é definido como 2 minutos, mas em sistemas modernos costuma ser bem menor
  • Overflow de inteiro sem sinal de 32 bits é o fenômeno em que o valor volta para 0 ao ultrapassar o máximo (4.294.967.295). No macOS, o timestamp TCP (tcp_now) é um contador de 32 bits em milissegundos que aumenta desde o boot, e o overflow ocorre após 49 dias, 17 horas, 2 minutos e 47,296 segundos

Descoberta: interrupção das conexões TCP após 49,7 dias

  • Os servidores Mac da Photon para monitoramento do iMessage operavam 24/7 e, em 30 de março de 2026, exatamente 49,7 dias após o boot, ocorreu um fenômeno em que todas as novas conexões TCP passaram a falhar
    • As conexões existentes e o ICMP (ping) continuavam funcionando, mas não era mais possível criar novos sockets TCP
  • A causa foi o overflow do contador de timestamp TCP (tcp_now) no kernel XNU, em que a lógica de validação de crescimento monotônico bloqueia atualizações após o wraparound, fazendo o relógio interno do TCP parar
  • Como as conexões TIME_WAIT não expiravam, as portas efêmeras deixavam de ser liberadas e se acumulavam, tornando a recuperação impossível sem reiniciar
  • Após o reboot, o mesmo fenômeno voltava a se repetir em ciclos de 49,7 dias

Desenho do experimento: comparação do comportamento do TCP antes e depois do overflow

  • Hipótese: se a coleta de lixo de TIME_WAIT parar após o overflow, o padrão de criação de conexões TCP curtas deve mudar antes e depois do evento
    • Antes do overflow: TIME_WAIT expira normalmente após 30 segundos
    • Depois do overflow: TIME_WAIT permanece indefinidamente
  • Foi executado um script de teste em três etapas
    1. Etapa de monitoramento: registro da contagem de TIME_WAIT a cada 10 segundos, de 35 minutos antes até 5 minutos antes do overflow
    2. Etapa de explosão: criação de cerca de 15 conexões TCP curtas a cada 2 segundos durante 10 minutos ao redor do instante do overflow
    3. Etapa de observação: monitoramento da variação do TIME_WAIT após interromper a criação de conexões

Resultado: TIME_WAIT estagnado após o overflow

  • Antes do overflow, a quantidade de TIME_WAIT circulava de forma estável entre 0 e 200, confirmando um comportamento normal de reciclagem
  • Logo após o overflow, o número de TIME_WAIT passou a subir continuamente e deixou de expirar
  • No caso da Machine B, 2.828 conexões TIME_WAIT não tiveram nenhuma reciclagem nem após 84 segundos, e continuaram se acumulando depois disso
  • Na Machine A, a verificação manual também mostrou crescimento monotônico do TIME_WAIT, em um estado sem recuperação possível

Causa raiz: overflow de 32 bits de tcp_now no kernel XNU

  • tcp_now é um contador de 32 bits em milissegundos definido em bsd/netinet/tcp_var.h, usado para rastrear o tempo decorrido desde o boot
  • Na função calculate_tcp_clock(), a operação (uint32_t)now.tv_sec * 1000 ultrapassa o valor máximo após 49,7 dias, provocando wraparound
  • Por causa da condição if (tmp < current_tcp_now), no momento do overflow o valor existente fica maior que o novo valor, então a atualização é bloqueada e o tcp_now fica permanentemente parado
  • Como a verificação de expiração do TIME_WAIT é feita com base em tcp_now, quando o relógio para a condição de expiração se torna sempre falsa, impedindo qualquer reciclagem

Efeito em cascata: paralisação de toda a funcionalidade do TCP

  • Após alguns minutos: a reciclagem de TIME_WAIT para, e cargas com muitas conexões curtas começam a apresentar problemas gradualmente
  • Após algumas horas: milhares de entradas TIME_WAIT se acumulam, levando ao esgotamento de portas efêmeras
  • Depois do esgotamento das portas: novas conexões TCP falham no estado SYN_SENT, enquanto as conexões existentes permanecem
  • Aumento acentuado de carga de CPU: o kernel continua varrendo a fila TIME_WAIT, elevando a carga
  • Resultado final: paralisação completa do TCP, com apenas o ICMP funcionando normalmente
  • A única forma de recuperação é reiniciar, o que reinicia novamente a contagem dos 49,7 dias

Evidências adicionais e casos relacionados

  • O RFC 7323 afirma que o wrap do bit de sinal em timestamps de 32 bits com resolução de 1 ms ocorre a cada cerca de 24,8 dias
    • No caso do macOS, trata-se do overflow completo de 32 bits (49,7 dias), um defeito local do kernel separado do problema de timestamps remotos tratado pela RFC
  • O mesmo sintoma foi relatado diversas vezes em comunidades da Apple e projetos open source
    • Impossibilidade de estabelecer conexões TCP, ping funcionando normalmente, solução apenas com reboot e ocorrência após semanas de uptime
    • O mesmo padrão também aparece em casos como o issue #12495 do Podman
  • Padrões em comum: falha apenas no TCP, ICMP normal, necessidade de reboot e ciclo de ocorrência de várias semanas

Escopo do impacto

  • Pode ocorrer em sistemas macOS com mais de 49 dias e 17 horas de funcionamento contínuo
  • Usuários comuns tendem a ser pouco afetados porque atualizações periódicas normalmente exigem reinicialização
  • Ambientes de alto risco
    • Frotas de servidores com longa execução
    • Servidores de build CI/CD baseados em macOS
    • Workstations Mac Pro
    • Macs em colocation gerenciados remotamente
    • Clusters de Mac mini usados em fazendas de build e infraestrutura de testes

Procedimento de reprodução

  • Calcular o instante previsto do overflow a partir do horário de boot
  • Monitorar a contagem de TIME_WAIT antes e depois do overflow
  • Gerar várias conexões TCP curtas no momento do overflow
  • Se, após 2 minutos, a contagem de TIME_WAIT não diminuir, a reprodução do bug foi bem-sucedida

Estado do sistema observado após 9,5 horas

  • Nem uma única conexão TIME_WAIT foi reciclada, e a contagem continuou subindo
  • Mais de 3.000 conexões com falha no estado SYN_SENT se acumularam
  • Apenas as conexões existentes permaneceram; novas conexões eram impossíveis
  • A carga média da Machine B chegou a 49,74, com o kernel consumindo CPU em excesso ao varrer a fila TIME_WAIT

Conclusão

  • Um único inteiro de 32 bits e a condição if (tmp < current_tcp_now) funcionam como uma bomba-relógio que para todo o TCP após 49,7 dias
  • É um tipo de falha difícil de detectar em fases de desenvolvimento, teste e code review, aparecendo apenas em ambientes reais de operação
  • A Photon reproduziu o mesmo fenômeno em vários servidores e confirmou claramente que, antes do overflow há reciclagem normal, depois dele há acúmulo de TIME_WAIT
  • Quando tcp_now para, o relógio TCP do kernel congela e o sistema parece normal à primeira vista, mas todas as portas TCP acabam se esgotando
  • Administradores de sistemas macOS com longa execução devem se lembrar de 49 dias, 17 horas, 2 minutos e 47 segundos e precisam de ajustar o ciclo de reinicialização ou reiniciar periodicamente até que o kernel seja corrigido
  • A Photon está atualmente desenvolvendo uma solução alternativa para restaurar tcp_now sem reiniciar

1 comentários

 
GN⁺ 21 일 전
Comentários do Hacker News
  • Agora finalmente entendi por que meu iMac às vezes ficava sem conseguir fazer nenhuma conexão
    Eu não fazia ideia de que era por causa do uptime

  • Enquanto lia o texto, a sensação de que ele foi escrito por IA era forte demais, então fiquei curioso se realmente entraram em contato com a Apple
    Claro que o bug é importante, mas achei que havia muita linguagem exagerada
    A maioria dos usuários provavelmente quase não será afetada
    Se você deixar o Mac em modo de suspensão, a stack TCP é resetada, então talvez dê para evitar o problema assim
    No fim, a Apple vai corrigir isso, mas não é motivo para pânico agora

    • Acho que também passei por esse problema
      Um MacBook com a suspensão automática desativada ficou ligado por cerca de 50 dias, e eu tinha a situação de ping funcionar, mas conexões TCP não funcionarem de jeito nenhum
      Trocar o Wi‑Fi ou usar conexão cabeada não resolveu, e assim que reiniciei tudo voltou ao normal
    • Não entraram em contato com a Apple, e parece que o autor do blog está tentando corrigir por conta própria
      Disseram que estão “desenvolvendo uma solução alternativa melhor do que reiniciar e, até lá, é para reiniciar periodicamente”
    • Eu também deixo um Mac Mini ligado 24 horas por dia, e às vezes, quando a rede trava, desligar e ligar o adaptador Wi‑Fi resolve
      Nesses momentos, é uma boa hora para reiniciar
    • De fato relataram isso à Apple, e disseram que foi registrado no sistema interno
  • Hoje em dia está muito difícil ler posts de blog escritos por IA
    O estilo é artificial e demora demais para chegar ao ponto

    • Também sou assim. Ler texto escrito por IA é cansativo e difícil de manter a concentração
    • Se olhar só o resumo feito por IA, é simples — o problema é que no Mac não é permitido rollover quando o relógio tcp_now sofre overflow
  • Não concordo com a frase “não existe desenvolvedor que vá testar por 50 dias”
    Na prática, dá para fazer testes de simulação acelerando o tempo

    • No kernel do Linux, para capturar esse tipo de problema, dizem que o contador de jiffies é inicializado no boot com um valor logo antes do overflow
    • O macOS usa o relógio de hardware, então ele para durante o modo sleep
      Nesses casos, dá para validar alterando uma função como calculate_tcp_clock para passar o uptime como argumento
    • Esse tipo de abordagem também é comum em testes de videogames
  • Esse bug afeta não só o OpenClaw, mas todas as conexões TCP

    • Não é necessário que uma conexão individual dure muito tempo
      Quando o uptime do macOS passa de 49,7 dias, todas as conexões TCP começam a ser afetadas
    • Também teve a piada de que “agora o OpenClaw parece ser a coisa mais importante do mundo”
  • Vários dos meus dispositivos macOS ficam ligados há mais de 600 a 1000 dias, e as conexões TCP estão expirando normalmente
    As versões do kernel são 20.6.0 e 17.7.0, respectivamente
    Então parece que esse bug só acontece a partir de certas versões

    • Pela análise, o valor de tcp_now para logo antes do overflow, e por causa de um wraparound incorreto no cálculo do timer ele vira negativo, fazendo a comparação falhar
      Pode haver um curto período em que conexões TIME_WAIT se acumulam, mas o texto original exagerou na reação e parecia ter sido escrito por um LLM
    • Na prática, dizem que esse bug surgiu em código novo introduzido no macOS 26 no ano passado
      Link relacionado no GitHub
  • Esse tipo de problema se repete em vários softwares
    Antigamente aconteceu algo parecido nos servidores de Guild Wars, e eles testavam somando um valor específico a GetTickCount() para induzir o overflow mais rápido

    • Sistemas que lidam com overflow devem forçar um overflow logo após a inicialização para testar
  • Esse bug lembra o bug dos 49,7 dias do Windows 95
    Texto relacionado

  • Fiquei curioso sobre qual é a relação entre o OpenClaw e esse bug

  • Esse problema lembra o bug dos 208 dias no escalonador do kernel Linux
    Link de referência