- Penghitung timestamp TCP (
tcp_now) di macOS mengalami overflow 32-bit sekitar 49,7 hari setelah boot, sehingga jam TCP internal berhenti
- Akibatnya, koneksi dalam status TIME_WAIT tidak kedaluwarsa dan terus menumpuk, sehingga port sementara tidak dilepas
- Seiring waktu, port sementara habis, menyebabkan semua koneksi TCP baru gagal, sementara koneksi yang sudah ada tetap bertahan
- ICMP (
ping) tetap berfungsi normal, tetapi seluruh fungsi TCP lumpuh, dan pemulihan tidak mungkin dilakukan selain dengan reboot
- Server macOS, build machine, dan lingkungan CI yang berjalan lama terpapar masalah ini setiap 49 hari 17 jam, sehingga memerlukan reboot berkala sampai ada perbaikan kernel
Latar belakang: konsep dasar TCP
- Saat koneksi TCP ditutup, koneksi tidak langsung hilang, melainkan masuk ke status TIME_WAIT, yaitu tahap untuk menangani paket yang terlambat dan memastikan terminasi yang andal
- Ini bertujuan mencegah paket lama salah ditafsirkan sebagai koneksi baru, serta menangani retransmisi bila ACK terakhir hilang
- Durasi TIME_WAIT didefinisikan sebagai 2 × MSL (Maximum Segment Lifetime), dan di macOS disetel sekitar 30 detik
- MSL adalah waktu maksimum segmen TCP dapat bertahan di jaringan; RFC 793 mendefinisikannya sebagai 2 menit, tetapi pada sistem modern nilainya jauh lebih pendek
- Overflow bilangan bulat tak bertanda 32-bit adalah fenomena saat nilai kembali ke 0 setelah melewati batas maksimum (4.294.967.295). Timestamp TCP macOS (
tcp_now) adalah penghitung 32-bit yang bertambah dalam satuan milidetik sejak boot, sehingga overflow terjadi setelah 49 hari 17 jam 2 menit 47,296 detik
Penemuan: koneksi TCP terhenti setelah 49,7 hari
- Server Mac milik Photon untuk pemantauan iMessage berjalan 24/7, dan pada 30 Maret 2026, tepat 49,7 hari setelah boot, terjadi fenomena di mana semua koneksi TCP baru gagal
- Koneksi yang sudah ada dan ICMP (
ping) tetap normal, tetapi pembuatan soket TCP baru tidak lagi memungkinkan
- Penyebabnya adalah overflow penghitung timestamp TCP (
tcp_now) di kernel XNU, di mana logika validasi kenaikan monoton memblokir pembaruan setelah wraparound sehingga jam TCP internal berhenti
- Karena koneksi TIME_WAIT tidak kedaluwarsa, port sementara tidak dilepas dan terus menumpuk, hingga akhirnya tidak bisa dipulihkan tanpa reboot
- Setelah reboot, fenomena yang sama berulang dengan siklus 49,7 hari
Desain eksperimen: membandingkan perilaku TCP sebelum dan sesudah overflow
- Hipotesis: jika garbage collection TIME_WAIT berhenti setelah overflow, maka pola pembuatan koneksi TCP jangka pendek akan berbeda sebelum dan sesudah overflow
- Sebelum overflow: TIME_WAIT kedaluwarsa normal setelah 30 detik
- Setelah overflow: TIME_WAIT bertahan tanpa batas
- Skrip uji dijalankan dalam tiga tahap
- Tahap pemantauan: mencatat jumlah TIME_WAIT setiap 10 detik mulai 35 menit sebelum overflow hingga 5 menit sebelumnya
- Tahap ledakan: membuat sekitar 15 koneksi TCP pendek setiap 2 detik selama 10 menit di sekitar momen overflow
- Tahap observasi: memantau perubahan TIME_WAIT setelah pembuatan koneksi dihentikan
Hasil: TIME_WAIT macet setelah overflow
- Sebelum overflow, jumlah TIME_WAIT berputar stabil antara 0~200, yang menunjukkan mekanisme pembersihan normal
- Tepat setelah overflow, jumlah TIME_WAIT terus meningkat dan tidak lagi kedaluwarsa
- Pada Machine B, 2.828 koneksi TIME_WAIT tidak satu pun dibersihkan bahkan setelah 84 detik, dan terus menumpuk sesudahnya
- Machine A juga menunjukkan hasil pemeriksaan manual bahwa jumlah TIME_WAIT meningkat secara monoton dan tidak dapat dipulihkan
Akar masalah: overflow 32-bit tcp_now di kernel XNU
tcp_now adalah penghitung 32-bit berbasis milidetik yang didefinisikan di bsd/netinet/tcp_var.h untuk melacak waktu sejak boot
- Di fungsi
calculate_tcp_clock(), operasi (uint32_t)now.tv_sec * 1000 melampaui nilai maksimum setelah 49,7 hari dan menyebabkan wraparound
- Karena kondisi
if (tmp < current_tcp_now), saat overflow nilai lama menjadi lebih besar daripada nilai baru sehingga pembaruan diblokir dan tcp_now berhenti permanen
- Pemeriksaan kedaluwarsa TIME_WAIT dilakukan berdasarkan
tcp_now, jadi ketika jam berhenti, kondisi kedaluwarsa selalu bernilai salah dan pembersihan tidak pernah terjadi
Efek berantai: meluas menjadi penghentian total fungsi TCP
- Beberapa menit kemudian: pembersihan TIME_WAIT berhenti, dan beban kerja dengan banyak koneksi pendek mulai terdampak secara bertahap
- Beberapa jam kemudian: ribuan TIME_WAIT menumpuk, menyebabkan kehabisan port sementara
- Setelah port habis: koneksi TCP baru gagal dalam status SYN_SENT, dan hanya koneksi lama yang tetap bertahan
- Lonjakan beban CPU: kernel terus memindai antrean TIME_WAIT sehingga beban meningkat
- Hasil akhirnya adalah kelumpuhan total TCP, sementara ICMP tetap berfungsi normal
- Satu-satunya cara pemulihan adalah reboot, lalu hitungan 49,7 hari dimulai lagi dari awal
Bukti tambahan dan kasus terkait
- RFC 7323 menyebutkan bahwa wrapping bit tanda pada timestamp 32-bit dengan satuan 1 ms terjadi sekitar setiap 24,8 hari
- Pada macOS, masalahnya adalah overflow 32-bit penuh (49,7 hari), yaitu cacat kernel lokal yang terpisah dari masalah timestamp jarak jauh yang dibahas di RFC
- Banyak laporan gejala yang sama di komunitas Apple dan proyek open source
- Koneksi TCP tidak bisa dibuat,
ping tetap normal, hanya reboot yang menyelesaikan, dan terjadi setelah sistem berjalan berminggu-minggu
- Pola yang sama juga terlihat pada Podman issue #12495 dan lainnya
- Kesamaannya: hanya TCP yang gagal, ICMP tetap normal, perlu reboot, siklus kemunculan hitungan minggu
Cakupan dampak
- Dapat terjadi pada sistem macOS yang berjalan terus-menerus lebih dari 49 hari 17 jam
- Pengguna umum cenderung kurang terdampak karena pembaruan berkala biasanya disertai reboot
- Lingkungan berisiko tinggi
- armada server yang berjalan lama
- server build CI/CD berbasis macOS
- workstation Mac Pro
- Mac colocation dengan pengelolaan jarak jauh
- klaster Mac mini untuk build farm dan infrastruktur pengujian
Langkah reproduksi
- Hitung waktu perkiraan overflow berdasarkan waktu boot
- Pantau jumlah TIME_WAIT sebelum dan sesudah overflow
- Buat banyak koneksi TCP pendek pada saat overflow
- Jika setelah 2 menit jumlah TIME_WAIT tidak berkurang, reproduksi bug dinyatakan berhasil
Kondisi sistem yang diamati setelah 9,5 jam
- Tidak satu pun koneksi TIME_WAIT dibersihkan, dan jumlahnya terus meningkat
- Lebih dari 3.000 koneksi gagal dalam status SYN_SENT menumpuk
- Hanya koneksi lama yang bertahan, koneksi baru tidak bisa dibuat
- Rata-rata beban Machine B naik hingga 49,74, karena kernel memakai CPU secara berlebihan untuk memindai antrean TIME_WAIT
Kesimpulan
- Hanya satu bilangan bulat 32-bit dan kondisi
if (tmp < current_tcp_now) bertindak sebagai bom waktu yang menghentikan seluruh TCP setelah 49,7 hari
- Ini adalah jenis cacat yang sulit ditemukan pada tahap pengembangan, pengujian, maupun code review, dan baru tampak di lingkungan produksi nyata
- Photon berhasil mereproduksi fenomena yang sama di beberapa server, dan secara jelas mengonfirmasi bahwa sebelum overflow pembersihan berjalan normal, sesudahnya TIME_WAIT menumpuk
- Ketika
tcp_now berhenti, jam TCP kernel ikut berhenti; sistem tampak normal di permukaan, tetapi semua port TCP pada akhirnya habis
- Administrator sistem macOS yang berjalan lama perlu mengingat 49 hari 17 jam 2 menit 47 detik, dan
menyesuaikan siklus reboot atau melakukan reboot berkala sampai ada perbaikan kernel
- Photon saat ini sedang mengembangkan solusi sementara untuk memulihkan
tcp_now tanpa reboot
Belum ada komentar.