1 poin oleh GN⁺ 13 hari lalu | Belum ada komentar. | Bagikan ke WhatsApp
  • 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
    1. Tahap pemantauan: mencatat jumlah TIME_WAIT setiap 10 detik mulai 35 menit sebelum overflow hingga 5 menit sebelumnya
    2. Tahap ledakan: membuat sekitar 15 koneksi TCP pendek setiap 2 detik selama 10 menit di sekitar momen overflow
    3. 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.

Belum ada komentar.