macOS에서 49.7일 후 TCP 네트워킹이 중단되는 버그 발견
(photon.codes)- 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 무한 지속
- 세 단계로 구성된 테스트 스크립트 실행
- 모니터링 단계: 오버플로 35분 전부터 5분 전까지 TIME_WAIT 수를 10초 간격으로 기록
- 폭발 단계: 오버플로 전후 10분 동안 2초마다 약 15개의 짧은 TCP 연결 생성
- 관찰 단계: 연결 생성 중단 후 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_now는bsd/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일 버그를 떠올리게 함
참고 링크- 그리고 Boeing 787의 전원 주기 문제도 같은 맥락임