1P by GN⁺ 7시간전 | ★ favorite | 댓글 1개
  • macOS의 TCP 타임스탬프 카운터(tcp_now) 가 부팅 후 약 49.7일이 지나면 32비트 오버플로로 인해 내부 TCP 시계가 정지
  • 이로 인해 TIME_WAIT 상태의 연결이 만료되지 않고 누적되어 임시 포트가 해제되지 않음
  • 시간이 지나면 임시 포트 고갈로 인해 새로운 TCP 연결이 모두 실패하고, 기존 연결만 유지됨
  • ICMP(ping)은 정상 작동하지만, TCP 전체 기능이 마비되어 재부팅 외에는 복구 불가
  • 장기 가동되는 macOS 서버·빌드 머신·CI 환경은 49일 17시간 주기로 이 문제에 노출되며, 커널 수정 전까지 주기적 재부팅 필요

배경: TCP의 기본 개념

  • TCP 연결은 종료 시 즉시 사라지지 않고 TIME_WAIT 상태에 들어가며, 이는 지연된 패킷 처리와 신뢰성 있는 종료를 위한 단계임
    • 오래된 패킷이 새 연결로 잘못 해석되는 것을 방지하고, 마지막 ACK 손실 시 재전송을 처리하기 위함
  • TIME_WAIT 지속 시간은 2 × MSL(Maximum Segment Lifetime) 로 정의되며, macOS에서는 약 30초로 설정되어 있음
  • MSL은 TCP 세그먼트가 네트워크에서 생존할 수 있는 최대 시간으로, RFC 793에서는 2분으로 정의되었으나 현대 시스템에서는 훨씬 짧게 설정됨
  • 32비트 부호 없는 정수 오버플로는 값이 최대치(4,294,967,295)를 넘으면 0으로 되돌아가는 현상이며, macOS의 TCP 타임스탬프(tcp_now)는 부팅 이후 밀리초 단위로 증가하는 32비트 카운터로, 49일 17시간 2분 47.296초 후 오버플로 발생

발견: 49.7일 후 TCP 연결 중단 현상

  • Photon의 iMessage 모니터링용 Mac 서버들이 24/7로 운영되며, 2026년 3월 30일 부팅 후 정확히 49.7일이 지난 시점에 새 TCP 연결이 모두 실패하는 현상 발생
    • 기존 연결과 ICMP(ping)는 정상 작동했으나, 새로운 TCP 소켓 생성이 불가능
  • 원인은 XNU 커널의 TCP 타임스탬프 카운터(tcp_now) 오버플로로, 단조 증가 검증 로직이 wraparound 이후 갱신을 차단하여 내부 TCP 시계가 정지
  • TIME_WAIT 연결이 만료되지 않아 임시 포트가 해제되지 않고 누적, 결국 재부팅 외에는 복구 불가
  • 재부팅 후 동일한 현상이 49.7일 주기로 반복됨

실험 설계: 오버플로 전후의 TCP 동작 비교

  • 가설: 오버플로 이후 TIME_WAIT 가비지 컬렉션이 멈춘다면, 오버플로 전후의 단기 TCP 연결 생성 패턴에 차이가 나타남
    • 오버플로 전: TIME_WAIT 30초 후 정상 만료
    • 오버플로 후: TIME_WAIT 무한 지속
  • 세 단계로 구성된 테스트 스크립트 실행
    1. 모니터링 단계: 오버플로 35분 전부터 5분 전까지 TIME_WAIT 수를 10초 간격으로 기록
    2. 폭발 단계: 오버플로 전후 10분 동안 2초마다 약 15개의 짧은 TCP 연결 생성
    3. 관찰 단계: 연결 생성 중단 후 TIME_WAIT 변화를 모니터링

결과: 오버플로 이후 TIME_WAIT 정체

  • 오버플로 전에는 TIME_WAIT 수가 0~200 사이에서 안정적으로 순환하며 정상적인 회수 동작 확인
  • 오버플로 직후부터 TIME_WAIT 수가 계속 증가하며, 더 이상 만료되지 않음
  • Machine B의 경우 2,828개의 TIME_WAIT 연결이 84초 후에도 하나도 회수되지 않았고, 이후에도 지속적으로 누적
  • Machine A 또한 수동 확인 결과 TIME_WAIT 수가 단조 증가, 복구 불가 상태

근본 원인: XNU 커널의 tcp_now 32비트 오버플로

  • tcp_nowbsd/netinet/tcp_var.h에 정의된 밀리초 단위 32비트 카운터로, 부팅 이후 경과 시간을 추적
  • calculate_tcp_clock() 함수에서 (uint32_t)now.tv_sec * 1000 연산이 49.7일 이후 최대값을 초과하며 wraparound 발생
  • if (tmp < current_tcp_now) 조건문으로 인해, 오버플로 시 기존 값이 새 값보다 커져 갱신이 차단되고 tcp_now영구 정지
  • TIME_WAIT 만료 검사는 tcp_now를 기준으로 수행되므로, 시계가 멈추면 만료 조건이 항상 거짓이 되어 회수 불가

연쇄 효과: TCP 전체 기능 정지로 확산

  • 수 분 후: TIME_WAIT 회수 중단, 단기 연결이 많은 워크로드에서 점진적 문제 발생
  • 수 시간 후: TIME_WAIT 수천 개 누적, 임시 포트 고갈
  • 포트 고갈 후: 새로운 TCP 연결이 SYN_SENT 상태에서 실패, 기존 연결만 유지
  • CPU 부하 급증: 커널이 TIME_WAIT 큐를 계속 스캔하며 부하 증가
  • 결과적으로 TCP 완전 마비, ICMP만 정상 작동
  • 유일한 복구 방법은 재부팅, 이후 다시 49.7일 카운트 재시작

추가 증거 및 관련 사례

  • RFC 7323은 1ms 단위 32비트 타임스탬프의 부호 비트 래핑이 약 24.8일마다 발생함을 명시
    • macOS의 경우 전체 32비트 오버플로(49.7일)로, RFC에서 다루는 원격 타임스탬프 문제와는 별개의 로컬 커널 결함
  • Apple 커뮤니티 및 오픈소스 프로젝트에서 동일 증상 다수 보고
    • TCP 연결 불가, ping 정상, 재부팅만 해결, 수주간 가동 후 발생
    • Podman issue #12495 등에서 동일한 패턴 확인
  • 공통점: TCP만 실패, ICMP 정상, 재부팅 필요, 수주 단위 발생 주기

영향 범위

  • 49일 17시간 이상 연속 가동된 macOS 시스템에서 발생 가능
  • 일반 사용자는 주기적 업데이트로 재부팅되어 영향 적음
  • 고위험 환경
    • 장기 가동 서버 플릿
    • macOS 기반 CI/CD 빌드 서버
    • Mac Pro 워크스테이션
    • 원격 관리형 코로케이션 Mac
    • 빌드 팜·테스트 인프라용 Mac mini 클러스터

재현 절차

  • 부팅 시각으로부터 오버플로 예상 시점 계산
  • 오버플로 전후 TIME_WAIT 수를 모니터링
  • 오버플로 시점에 다수의 짧은 TCP 연결 생성
  • 2분 후 TIME_WAIT 수가 감소하지 않으면 버그 재현 성공

9.5시간 후 관찰된 시스템 상태

  • TIME_WAIT 연결이 단 한 개도 회수되지 않고 지속 증가
  • SYN_SENT 상태의 실패 연결이 3,000개 이상 누적
  • 기존 연결만 유지되고 신규 연결 불가
  • Machine B의 평균 부하가 49.74까지 상승, 커널이 TIME_WAIT 큐 스캔에 과도한 CPU 사용

결론

  • 단 하나의 32비트 정수와 if (tmp < current_tcp_now) 조건문이 49.7일 후 TCP 전체를 정지시키는 시한폭탄으로 작동
  • 개발·테스트·코드 리뷰 단계에서는 발견되기 어려운 유형의 결함이며, 실제 운영 환경에서만 드러남
  • Photon은 여러 서버에서 동일 현상을 재현했고, 오버플로 전에는 정상 회수, 이후에는 TIME_WAIT 누적이 명확히 확인됨
  • tcp_now가 멈추면 커널의 TCP 시계가 정지하며, 시스템은 겉보기엔 정상이나 TCP 포트가 모두 소진됨
  • 장기 가동 macOS 시스템 관리자는 49일 17시간 2분 47초를 기억해야 하며, 재부팅 주기 조정 또는 커널 수정 전까지 주기적 재부팅이 필요
  • Photon은 현재 재부팅 없이 tcp_now를 복구하는 우회 해결책을 개발 중임
Hacker News 의견들
  • 내 iMac이 가끔 아무 연결도 안 되던 이유가 이제야 이해됨
    업타임(uptime) 때문이었다는 걸 전혀 몰랐음

  • 글을 읽다 보니 AI가 쓴 느낌이 너무 강해서, Apple에 실제로 문의했는지 궁금했음
    물론 버그는 중요하지만, 과장된 표현이 많다고 느낌
    대부분의 사용자는 영향을 거의 받지 않을 것 같음
    Mac을 잠자기 모드로 두면 TCP 스택이 리셋되기 때문에 문제를 피할 수도 있을 듯함
    결국 Apple이 수정하겠지만, 지금 당장 패닉할 일은 아님

    • 나도 이 문제를 겪은 것 같음
      자동 절전이 꺼진 MacBook이 약 50일 정도 켜져 있었는데, ping은 되지만 TCP 연결이 전혀 안 되는 현상이 있었음
      Wi-Fi를 바꾸거나 유선 연결을 해도 해결 안 됐고, 재부팅하니 바로 정상으로 돌아왔음
    • Apple에 문의는 안 했고, 블로그 작성자가 직접 고치려는 듯함
      “재부팅보다 나은 대체 해결책을 개발 중이며, 그 전까지는 주기적으로 재부팅하라”고 했다고 함
    • 나도 Mac Mini를 24시간 켜두는데, 가끔 네트워크가 멈추면 Wi-Fi 어댑터를 껐다 켜면 해결됨
      그럴 때가 재부팅하기 좋은 타이밍
    • 실제로 Apple에 보고했고, 내부 시스템에 등록되었다고 함
  • 요즘 AI가 쓴 블로그 글은 읽기가 너무 힘듦
    문체가 부자연스럽고 핵심에 도달하기까지 너무 오래 걸림

    • 나도 같음. AI가 쓴 글은 읽는 게 피곤하고 집중이 안 됨
    • AI가 요약한 내용만 보면 간단함 — Mac이 tcp_now 시계가 오버플로우될 때 롤오버를 허용하지 않는다는 문제임
  • “50일 동안 테스트할 개발자는 없다”는 말에 동의하지 않음
    실제로는 시간을 가속해 시뮬레이션 테스트를 하면 됨

    • Linux 커널에서는 이런 문제를 잡기 위해 jiffies 카운터를 부팅 시점에 오버플로우 직전 값으로 초기화한다고 함
    • macOS는 하드웨어 시계를 사용해서 슬립 중에는 멈춤
      이런 경우엔 calculate_tcp_clock 같은 함수를 수정해 업타임을 인자로 전달하면 검증이 가능함
    • 이런 방식은 비디오 게임 테스트에서도 흔히 사용됨
  • 이 버그는 OpenClaw뿐 아니라 모든 TCP 연결에 영향을 미침

    • 개별 연결이 오래 지속될 필요는 없음
      macOS 업타임이 49.7일을 넘으면 모든 TCP 연결이 영향을 받기 시작함
    • “이제 OpenClaw가 세상에서 제일 중요한 것처럼 보인다”는 농담도 있었음
  • 내 여러 macOS 장비는 600~1000일 이상 켜져 있는데, TCP 연결이 정상적으로 만료되고 있음
    커널 버전은 각각 20.6.0과 17.7.0임
    그래서 이 버그가 특정 버전 이후에만 발생하는 듯함

    • 분석을 보니 tcp_now 값이 오버플로우 직전에서 멈추고, 타이머 계산이 잘못된 wraparound로 인해 음수가 되어 비교가 실패함
      잠깐의 기간 동안 TIME_WAIT 연결이 쌓일 수는 있지만, 원문은 과도하게 반응했고 LLM이 쓴 글 같음
    • 실제로 이 버그는 작년 macOS 26에서 새로 도입된 코드에서 생겼다고 함
      관련 GitHub 링크
  • 이런 문제는 다양한 소프트웨어에서 반복됨
    예전에 Guild Wars 서버에서도 비슷한 일이 있었는데, 오버플로우를 빨리 유도하기 위해 GetTickCount()에 특정 값을 더해 테스트했음

    • 오버플로우를 다루는 시스템은 시작 직후 바로 오버플로우를 유도해 테스트해야 함
  • 이 버그는 Windows 95의 49.7일 버그를 떠올리게 함
    관련 글

    • 나도 그 마법 같은 숫자를 어디서 봤는지 기억하려고 했음
    • 말 그대로 “새로운 옛날 문제” 같음
    • Boeing 787의 51일 전원 재부팅 이슈도 비슷한 사례임
    • 그래서 49.7일이라는 숫자가 익숙했던 것임
  • OpenClaw와 이 버그가 무슨 관련이 있는지 궁금함

  • 이 문제는 Linux 커널 스케줄러의 208일 버그를 떠올리게 함
    참고 링크