20 คะแนน โดย darjeeling 22 일 전 | 9 ความคิดเห็น | แชร์ทาง WhatsApp

พบว่ามีบั๊กในเคอร์เนล 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 ความคิดเห็น

 
brilliant08 22 일 전

ช่วงนี้ macOS ถึงกับจัดงานครบ 49 วันกันเลยนะ

 
bungker 17 일 전

55555555

 
roxie 21 일 전

zzzzz

 
devil1032 21 일 전

พอพูดว่าเป็นปัญหาเรื่องเวลาก็นึกถึง Y2K ขึ้นมาเลย.. 🤖..

 
savvykang 21 일 전

มนุษย์มักทำผิดพลาดแบบเดิมซ้ำแล้วซ้ำเล่า

 
shincad 20 일 전

งั้นก็ต้องรีบูตจริง ๆ ก่อนครบ 49 วันสินะ

ที่จริงแล้วเรื่องแบบนี้ห้ามเอาเวลาไปเทียบด้วย < โดยตรงเด็ดขาด..

if ((int32_t)(tmp - current_tcp_now) < 0) {
os_atomic_cmpxchg(&tcp_now, tmp, current_tcp_now, ...);
}
ควรดูความต่างของสองค่านี้แบบนี้มากกว่า... แต่มนุษย์เราก็มักจะทำพลาดแบบเดิมซ้ำ ๆ อยู่เสมอครับ

 
icosahedron 21 일 전

พอเห็นอะไรแบบนี้แล้ว ก็รู้สึกว่าปี 2038 อาจจะเกิดเรื่องวุ่นวายจริง ๆ ก็ได้นะ

 
princox 21 일 전

ว้าว อันนี้เหลือเชื่อจริง ๆ เลย....

 
jic5760 21 일 전

แล้ว mac instance ของ AWS หรือ GitHub ถึงที่ผ่านมาไม่มีปัญหาได้อย่างไรกัน...?