ระเบิดเวลาที่ซ่อนอยู่ใน macOS นาน 49 วัน — เจาะรายละเอียดบั๊กเคอร์เนลที่ทำให้เครือข่าย TCP หยุดทำงานทั้งหมด
(photon.codes)พบว่ามีบั๊กในเคอร์เนล XNU ของ macOS ที่ทำให้เครือข่าย TCP เป็นอัมพาตโดยสมบูรณ์หลังทำงานต่อเนื่องครบ 49 วัน 17 ชั่วโมง 2 นาที 47 วินาที พอดี สาเหตุคือ 32-bit integer overflow ของตัวนับ timestamp ภายในเคอร์เนลสำหรับ TCP (tcp_now) ping ยังตอบสนองตามปกติ แต่จะไม่สามารถสร้างการเชื่อมต่อ TCP ใหม่ได้เลย และตอนนี้วิธีแก้เพียงอย่างเดียวคือรีบูต
ที่มาของการค้นพบ
Photon ดูแลฟลีตของ Mac เซิร์ฟเวอร์ที่ใช้มอนิเตอร์สถานะบริการ iMessage แบบ 24/7 อยู่แล้ว เมื่อวันที่ 30 มีนาคม 2026 หลายเครื่องเริ่มปฏิเสธการเชื่อมต่อ TCP ใหม่อย่างเงียบ ๆ ในจังหวะที่ผ่านจากการรีบูตครั้งล่าสุดมาแล้ว 49.7 วันพอดี ping ปกติ การเชื่อมต่อเดิมก็ยังอยู่ แต่ความพยายามทั้งหมดในการเปิด socket ใหม่ล้มเหลว
หลังรีบูตเพื่อกู้คืนบริการ ทีมจึงเลือกเครื่องสองเครื่อง (A, B) ที่จะถึงจุดวิกฤตเดียวกันในอีกไม่กี่วัน และออกแบบ การทดลองแบบสด ขึ้นมา
กลไกทางเทคนิคของบั๊ก
ตัวนับที่มีปัญหา tcp_now
tcp_now ในเคอร์เนล XNU คือ จำนวนเต็ม unsigned 32 บิตที่นับเวลาที่ผ่านไปนับจากบูตเป็นหน่วยมิลลิวินาที ค่าสูงสุดที่ 32 บิตเก็บได้คือ 4,294,967,295ms — คิดเป็นเวลา 49 วัน 17 ชั่วโมง 2 นาที 47 วินาทีพอดี
ทำไมตัวนับถึง “ค้าง”
ในโค้ดอัปเดต tcp_now มี guard แบบง่าย ๆ เพื่อ “ป้องกันไม่ให้นาฬิกาย้อนกลับ” อยู่ดังนี้:
if (tmp < current_tcp_now) {
os_atomic_cmpxchg(&tcp_now, tmp, current_tcp_now, ...);
}
ตอนเกิด overflow ค่า current_tcp_now ที่คำนวณใหม่จะวนกลับไปใกล้ 0 แต่ tmp เดิมยังอยู่ใกล้ค่าสูงสุด ทำให้เงื่อนไข tmp < current_tcp_now เป็น false ตลอดไป และ tcp_now ก็ค้างอยู่ที่ค่านั้นทันที นั่นหมายความว่า นาฬิกา TCP ของเคอร์เนลหยุดเดิน
ทำไม TIME_WAIT ถึงไม่หมดอายุ
เมื่อการเชื่อมต่อ TCP ถูกปิด เคอร์เนลจะบันทึกเวลาหมดอายุเป็น tcp_now + 30 วินาที จากนั้น garbage collector จะสแกนเป็นระยะ และปล่อยการเชื่อมต่อเมื่อ tcp_now >= เวลาหมดอายุ แต่เมื่อ tcp_now ค้าง เงื่อนไขนี้จะ ไม่มีวันเป็นจริง ทำให้การเชื่อมต่อในสถานะ TIME_WAIT ไม่ถูกเก็บกู้คืนอีกเลย
ผลการทดลอง
ทีมสร้างการเชื่อมต่อ TCP อายุสั้นหลายรายการต่อวินาทีเป็นเวลาอย่างละ 5 นาทีก่อนและหลัง overflow พร้อมสังเกตจำนวน TIME_WAIT
| ช่วง | สถานะ |
|---|---|
| ก่อน overflow | TIME_WAIT คงที่ราว ~200 รายการ (หมดอายุตามปกติทุก 30 วินาที) |
| ทันทีหลัง overflow | การหมดอายุหยุดลง และ TIME_WAIT เริ่มเพิ่มขึ้นแบบทางเดียว |
| 84 วินาทีหลังหยุดสร้างการเชื่อมต่อ | TIME_WAIT ที่ควรเป็น 0 กลับ เพิ่มขึ้น (2,828 → 2,837) |
| 9.5 ชั่วโมงหลัง overflow | Machine A: 4,888 รายการ, Machine B: 8,217 รายการ — ไม่มีสักรายการเดียวที่ถูกเก็บกู้คืน |
หลังผ่านไป 9.5 ชั่วโมง การเชื่อมต่อสถานะ SYN_SENT ก็สะสมเกิน 3,000 รายการ และ load average ของ Machine B พุ่งไปถึง 49.74
สภาพแวดล้อมที่ได้รับผลกระทบ
Mac สำหรับผู้บริโภคทั่วไปมักถูกรีบูตก่อนครบ 49 วันจากการอัปเดต OS อยู่แล้ว จึงได้รับผลกระทบน้อยกว่า แต่สภาพแวดล้อมต่อไปนี้ถือว่าเสี่ยงสูง:
- ฟลีตเซิร์ฟเวอร์ที่ต้องทำงานต่อเนื่องยาวนานโดยไม่หยุด
- macOS CI/CD build server (Jenkins, GitHub Actions self-hosted runner)
- เวิร์กสเตชัน Mac Pro (รันงานเรนเดอร์หรือคอมไพล์เป็นเวลานาน)
- Mac ที่ colocate และบริหารจัดการจากระยะไกล
- build farm และโครงสร้างพื้นฐานทดสอบที่ใช้ Mac mini
การรับมือในปัจจุบันและต่อจากนี้
ตอนนี้ทีมกำลังพัฒนา workaround ที่จะแก้ค่า tcp_now ที่ค้างโดยตรงโดยไม่ต้องรีบูต แต่ก่อนจะมีวิธีนั้น มาตรการชั่วคราวมีเพียงข้อเดียว:
กำหนดตารางรีบูตก่อนครบ 49 วัน 17 ชั่วโมง 2 นาที 47 วินาที
บั๊กในประวัติศาสตร์ที่คล้ายกัน
บั๊กนี้อยู่ในตระกูลบั๊ก integer overflow ที่มีประวัติยาวนาน: ปัญหาเครื่องล่มที่ 49.7 วันใน Windows 95/98, ปัญหาปี 2038 (Y2K38), การ rollover ของหมายเลขสัปดาห์ GPS และ kill screen ของ Pac-Man ด่าน 256 ล้วนเป็นญาติประเภทเดียวกัน
ต้นฉบับ: Photon Blog, 2026.04.07
9 ความคิดเห็น
ช่วงนี้ macOS ถึงกับจัดงานครบ 49 วันกันเลยนะ
55555555
zzzzz
พอพูดว่าเป็นปัญหาเรื่องเวลาก็นึกถึง Y2K ขึ้นมาเลย.. 🤖..
มนุษย์มักทำผิดพลาดแบบเดิมซ้ำแล้วซ้ำเล่า
งั้นก็ต้องรีบูตจริง ๆ ก่อนครบ 49 วันสินะ
ที่จริงแล้วเรื่องแบบนี้ห้ามเอาเวลาไปเทียบด้วย
<โดยตรงเด็ดขาด..if ((int32_t)(tmp - current_tcp_now) < 0) {
os_atomic_cmpxchg(&tcp_now, tmp, current_tcp_now, ...);
}
ควรดูความต่างของสองค่านี้แบบนี้มากกว่า... แต่มนุษย์เราก็มักจะทำพลาดแบบเดิมซ้ำ ๆ อยู่เสมอครับ
พอเห็นอะไรแบบนี้แล้ว ก็รู้สึกว่าปี 2038 อาจจะเกิดเรื่องวุ่นวายจริง ๆ ก็ได้นะ
ว้าว อันนี้เหลือเชื่อจริง ๆ เลย....
แล้ว mac instance ของ AWS หรือ GitHub ถึงที่ผ่านมาไม่มีปัญหาได้อย่างไรกัน...?