Technology

เจาะลึกเบื้องหลัง Spectre และ Meltdown สุดยอดบัคแห่งยุค มันทำงานอย่างไร

By Arnon Puitrakul - 18 ธันวาคม 2025

เจาะลึกเบื้องหลัง Spectre และ Meltdown สุดยอดบัคแห่งยุค มันทำงานอย่างไร

หากเราพูดถึงบัคในคอมพิวเตอร์ เราน่าจะคุ้นเคยกับอะไรที่มันสามารถแก้ไขได้ด้วย Software Update แล้วจบ ไม่มีอะไรต่อจากนั้น แต่ใครมันจะไปคิดว่า เราจะได้รู้จักกับช่องโหว่อย่าง Spectre และ Meltdown ที่มันฝังรากลึกในเทคนิคการออกแบบ CPU กว่า 20 ปี เคยเป็นฮีโร่ที่ทำให้ CPU เร็วขึ้น แต่กลับกลายเป็นประตูสู่หายนะที่ร้ายแรงอันดับต้น ๆ ของโลก Hardware เลยทีเดียว

วันนี้เราจะพามาดำดิ่งลงไปในฝากระดอง CPU เพื่อทำความเข้าใจกับกลไก Microarchitecture ที่ทำให้เกิดช่องโหว่นี้กัน และปัจจุบันวิศวกร เขามีวิธีการแก้ปัญหาทั้งชั่วคราว และตลอดไปได้อย่างไร

ปูพื้นฐานทางด้านสถาปัตยกรรมคอมพิวเตอร์กันก่อน

เรื่องราวทั้งหมดที่จะเล่านี้ ค่อนข้างลงลึกในเรื่องของสถาปัตยกรรมคอมพิวเตอร์มาก ๆ ดังนั้น เพื่อทำให้ทุกคนเข้าใจได้ง่ายขึ้น เราจะขอปูพื้นฐานความรู้ทางด้านคอมพิวเตอร์ที่ CPU สมัยใหม่ใช้เทคนิคทั้งสามนี้ผสานกัน เพื่อเป็นองค์ประกอบของหายนะนี้

อย่างแรกคือ Instruction Pipelining และ Out-of-order Execution เมื่อก่อนเวลา CPU จะประมวลผลอะไรสักอย่าง มันจะต้องทำงานเป็นขั้น ๆ (Sequential) ไล่เรียงจากคำสั่งที่ 1,2,3,.... ไปเรื่อย ๆ จนทำงานสิ้นสุด และน่าจะเป็นวิธีการทำงานของเครื่องคอมพิวเตอร์ที่หลาย ๆ คนเข้าใจกันด้วย แต่ปัญหาคือ บางคำสั่งมันจำเป็นจะต้องรอข้อมูลจาก RAM ซึ่งมันเสียเวลามาก ๆ ทำให้วิศวกรบอกว่า ไม่ ๆ เอาละ คำสั่งไหนไม่ต้องรอก็หยิบมันมาทำก่อน เราเรียกว่า Out-of-order Execution (OoO) คือ ให้ CPU ข้ามไปหยิบคำสั่งที่ 2,3,4 ที่พร้อมกว่ามาทำก่อน แล้วค่อยเอามาเรียงผลลัพธ์คืนกลับไปทีหลัง ดังนั้น ระบบเอง ก็จะไม่เห็นเลยว่า CPU มันสลับอะไรไปแค่ไหน เพราะมันก็จะเห็นผลลัพธ์ที่ CPU เขาคืนค่าสลับกลับมาเรียบร้อยแล้ว

อย่างที่ 2 คือคำว่า Speculative Execution กลับไปที่เรื่องการประมวลผลอีกครั้ง เวลาเราเขียน Code น้อยครั้งมาก ๆ ที่เราจะเขียนมันเป็นเส้นตรงจริง ๆ เรามักจะมีทางแยกเกิดขึ้น เช่นการใช้ if condition ทั้งหลาย เมื่อก่อน เราอาจจะรอจนถึงทางแยกแล้วนั่งคิดแล้วค่อยเดินไปต่อ แต่ CPU สมัยใหม่บอกว่า ไม่ ๆ เราจะไม่รอแบบนั้นละ เราจะใช้ Branch Predictor เดาไปเลยว่า มันน่าจะไปทางไหน ถ้าเดาถูก ก็คือ โยนผลลัพธ์กลับได้เลย แต่ถ้าไม่ก็ต้อง Flush หรือ Rollback ผลลัพธ์ที่ทำไว้ออกไป แล้วเริ่มประมวลผลเส้นทางที่ถูกต้องใหม่ ด้วยวิธีการนี้ ถ้าเดาถูกเยอะ ๆ ก็จะทำให้การทำงานมันเร็วกว่าเดิมแบบโคตร ๆ เลยละ

และคำสุดท้ายคือ Side Channel Attack คำนี้มันจะเข้าใจยากนิดนึง แต่ให้นึกภาพว่า การโจมตีปกติ มันเหมือนเราพยายาม พังประตู หรือเดากุญแจเพื่อเข้าไปเอาของ แต่ Side Channel Attack มันเหมือนกับการเอา หูไปแนบกำแพงมากกว่า เพื่อฟังเสียงภายในบ้าน ในทางปฏิบัติ บางครั้ง Hacker ไม่สามารถถอดแบบบเข้ารหัส หรือ Bypass Password โดยตรง แต่เป็นการสังเกตผลข้างเคียงทางฟิสิกส์ที่เกิดขึ้น เช่น เวลาที่ใช้ว่า คิดเลขนี้ใช้เวลานานกว่าอีกเลขหรือไม่ หรือ เป็นการกินไฟ ตอนถอดรหัสนี้กำลังไฟที่ใช้เพิ่มขึ้นหรือไม่ หรือจะเป็นเสียง เอาว่า มีคนพยายามขโมยข้อมูลจาก RAM ผ่านคลื่นแม่เหล็กไฟฟ้ามาแล้วเหมือนกัน

หรือพูดให้ง่ายกว่านั้น หากเราต้องการจะขโมยของในตู้เซฟ การโจมตีปกติ จะเหมือนกับการพยายามจัดการกับกลไกล๊อค หรือไม่ก็เอาเครื่องเจาะกรีดเปิดมันซะเลย แต่ Side Channel Attack มันเหมือนกับเราเอา Stethoscope มาแนบกับตู้เซฟค่อย ๆ หมุนไปเรื่อย ๆ โจรไม่รู้หรอกว่า รหัสคืออะไร แต่ฟังเสียงคลิกของเฟือง พอหมุนไปเลข 5 ปรากฏว่า มีเสียง โจรก็จะเดาได้ว่า อ่อ เลขหลักแรกน่าจะเป็นเลข 5 แค่นั้นเลย

Meltdown (CVE-2014-5754)

Meltdown เป็นช่องโหว่ที่มันทำตามชื่อของมันเป๊ะ ๆ นั่นคือ การละลาย แต่ในที่นี้มันคือ การทำลายกำแพงที่สำคัญอันนึงในระบบที่เรียกว่า Kernel Isolation เดิมที่ในระบบ จะต้องมีการแยก User Space ที่เป็นหน่วยความจำส่วนที่ใช้รัน App ของเรา ไม่ว่าเราจะเปิดโปรแกรมอะไร มันก็จะเอามาทำงานอยู่ในหน่วยความจำส่วนนี้ และ Kernel Space เป็นหน่วยความจำที่ OS จะสงวนเอาไว้ให้ตัวเองทำงาน หาก User Application พยายามอ่านหน่วยความจำในส่วนนี้ CPU จะต้องโยน Exception ด่าพ่องออกมาทันที ห้ามให้เรียก หรือเขียนเด็ดขาด

ช่องโหว่นี้ Hacker ใช้ประโยชน์จาก Race Condition ระหว่าง การโหลดข้อมูล และการตรวจสิทธิ์ วิธีการคือ Hacker จะเริ่มจากการเขียน Code สั่งโหลดข้อมูลจาก Kernel Space ปกติ ถ้าเราบอกว่า มีเพียงแค่ OS เท่านั้นจะตรวจสอบสิทธิ์ก่อนว่า คนเรียนเป็น OS หรือเปล่า วิศวกรบอกว่า โน ๆ ทำแบบนั้นมันจะช้า ระหว่างที่ตรวจสอบสิทธิ์ก็โหลดข้อมูลมารอที่ Register (หน่วยความจำภายใน CPU) ชั่วคราวก่อน

ความอร่อยมันอยู่ที่ว่า เสี้ยววินาทีที่ข้อมูลอยู่ใน Register (Transient Window) Hacker จะสั่งให้เอาค่าใน Register ตัวนั้นไปคูณกับ 4096 หรือก็คือ ขนาด Page Size สิ่งที่ CPU จะทำคือ มันจะไปโหลดข้อมูลความลับมา แล้วเอามาคำนวณคูณให้เสร็จ เช่น ความลับคือ เลข 7 มันก็จะเอา 7 คูณกับ 4096 ได้ออกมาคือ 28,672 จากนั้นมันก็จะไปอ่านข้อมูลตำแหน่งที่ 28,672 บน RAM เอามาเก็บไว้ใน Cache เตรียมพร้อมละ ทีนี้พอการตรวจสอบสิทธิ์เสร็จแล้ว CPU จะล้าง 7 ออกจาก Register ไปแต่ข้อมูลที่อยู่ใน Cache ตำแหน่ง 28,672 ยังค้างอยู่ ตอนนี้ Hacker ยังไม่รู้นะว่า คำตอบคือเท่าไหร่

จากขั้นตอนนี้ Hacker จะมี Probe Array อยู่ในมือละ เขาจะเล่นเกมจับเวลาไปเรื่อย ๆ ทีละ 4,096 ช่อง เริ่มต้นจากอ่าน Array[0] ช้างมาก แปลว่าข้อมูลไม่ได้อยู่ใน Cache อ่านต่อ Array[4096] ก็ยังช้าอยู่ไม่ใช่อีก อ่านไปเรื่อย ๆ จนถึง Array[28672] ปรากฏว่า เร็ว เพราะข้อมูลมันรออยู่ที่ Cache แล้ว ก็แปลว่า นี่แหละคือข้อมูลของเรา เขาก็แค่เอาตำแหน่งไปหาร 4,096 กลับมา ก็จะได้ 7 นั่นคือคำตอบ

สรุปสั้น ๆ ง่าย ๆ คือ เราไม่สามารถเอาเลข 7 มาตรง ๆ ได้ เราก็เลยบอกให้ CPU เอาค่านั้นแหละ ไปคูณกับ 4,096 แล้วเก็บไว้ใน Cache พอค่าไหนอ่านได้เร็วแปลว่า Cache Hit นั่นแหละคือคำตอบ นี่แหละ เราอ่านตรง ๆ พังประตูไม่ได้ เราก็เงี่ยหูฟังก็เรียบร้อยแล้ว จากช่องโหว่นี้เลยทำให้ Application ที่รันอยู่ใน User Space สามารถอ่านข้อมูลใน Kernel Space ได้นั่นเอง

Spectre (CVE-2017-5753 & 5715)

ถ้า Meltdown คือการละลายประตูบ้าน Spectre มันคือการหลอกเจ้าของบ้านให้เปิดประตูให้เรา ง่าย ๆ คือ มันพยายามเล่นกับระบบ Branch Prediction บน CPU โดยตรง มันจะมี 2 ท่าที่เล่นกันหลัก ๆ

if (x < array1_size) {
  temp = array2[array1[x] *4096];
}

ท่าแรกคือ การหลอกให้ CPU มันอ่านข้อมูลที่เกินขอบเขตที่อนุญาติ เริ่มจาก การล้างสมองกันก่อน Hacker จะส่งค่า x ที่ถูกต้องเข้าไปรัว ๆ หลาย ๆ รอบ CPU มันก็จะเรียนรู้ว่า if ที่อยู่บรรทัดแรก มันจริงเสมอเลยนิน่า ทีนี้ Branch Predictor จะจำไว้ว่า ถ้าเจอทางแยกนี้ให้เลี้ยวขวาได้เลย หรือก็คือ ทำในวงเล็บโลดดด ไม่ต้องเช็คแล้วมันช้า

จากนั้นก็จะสับขาหลอก ด้วยการส่ง x ที่ผิด เช่นชี้ไปที่ Memory ลับของ Kernel เข้าไป ซึ่งในความเป็นจริง ถ้า ข้างใน if เป็นเท็จ มันก็ควรจะล้างค่าออกไป แต่มันจำแล้วว่า รอบนี้ก็คงจริงเหมือนเดิมแหละ งั้นเราเดาไปทำบรรทัดที่ 2 และ 3 ต่อไปทันที

ซึ่งในบรรทัดที่ 2 นี่แหละ ตัวเด็ดเลย คือ จะไปอ่านข้อมูลลับที่อยู่นอก Array (array1[x]) แล้วไปโหลดใส่ array2 ลง Cache ข้อมูลก็คือรั่วเรียบร้อยแล้ว วินาทีต่อมา CPU รู้ตัวแล้วว่า if เป็นเท็จมันก็จะ Rollback ทุกอย่าง แต่ array2 ที่โหลดมา ติดอยู่ใน Cache แล้ว ก็จะใช้ Side Channel เช็ค array2 ก็จะรู้เลขลับในตอนแรกทันที

ท่าที่ 2 เด็ดกว่าเดิม ไม่ได้หลอกแค่ if/else ละ แต่หลอกเป้าหมายที่จะกระโดดไป ซึ่งเป้าหมายคือ การหลอกให้ CPU กระโดดไปรัน Code ที่ Hacker เตรียมเอาไว้นั่นเอง ภายใน CPU มันจะมีสมุดจดเล่มนึงเรียกว่า Branch Target Buffer (BTB) เอาไว้จดว่า ถ้าเจอคำสั่งกระโดดตรงนี้ ครั้งที่แล้วมันโดดไปที่ Address ไหน

วิธีการคือ Hacker ก็จะรันโปรแกรมตัวเอง วนลูปเพื่อสร้าง Pattern การกระโดดซ้ำ ๆ ไปที่ Address นึง สมมุติว่าเป็น X แต่ X นั้น Hacker จงใจเลือกให้มันหน้าตาคล้ายกับ Address ในโปรแกรมของเหยื่อ เนื่องจาก BTB ใน CPU รุ่นเก่า มันใช้ร่วมกันระหว่างโปรแกรม มีเล่มเดียว ทำให้เขียนทับประวัติการกระโดดของโปรแกรมเหยื่อได้ ความพีคคือ เมื่อเหยื่อทำงานถึงจุดที่ต้องกระโดดแล้ว CPU จะเปิด BTB ดูละ อ่อ ต้องไปที่ X สินะ มันก็จะเดาว่า ต้องโดดไปหา X ซึ่งมันจะเป็นโปรแกรมที่ทำให้ข้อมูลรั่วออกมา แม้สุดท้าย CPU จะรู้แล้วว่า มันไม่ใช่ มันกระโดดผิด และจะ Rollback กลับมา แต่โปรแกรมที่รันไปล่วงหน้านั้นทิ้งของไว้ใน Cache เรียบร้อย รอให้ดูดกลับมาแล้ว

จริง ๆ มันมีอีก Variance นึงคือ CVE-2017-5754 ลองไปหาข้อมูลเพิ่มเติมได้ เพราะอันนี้ยังไม่เคย Hand-on ด้วยตัวเองขนาดนั้น กลัวเล่าผิดเหมือนกันฮา ๆ

หลาย ๆ คนมักจะบอกว่า Spectre น่ากลัวกว่า Meltdown มาก ๆ เพราะ Meltdown ส่วนใหญ่มันข้ามเส้นระหว่าง Kernel และ User Space ได้ แต่ Spectre มันข้ามกำแพงระหว่าง Tab ใน Web Browser ได้ เช่น เว็บ A ใช้ Spectre แอบอ่านข้อมูลรหัสผ่านธนาคารที่อยู่ใน Tab B ก็คือ แตกเลยละ นั่นทำให้ Chrome เปลี่ยนสถาปัตยกรรมเป็นการแยก Process ต่อเว็บไปเลย และเมื่อกี้ เราบอกว่าเว็บ ใช่ครับ เราไม่ต้องโหลด Executable File มาลง เราสามารถเขียน Javascript รันบนหน้าเว็บ พอเราเข้าเว็บปุ๊บ Script ก็จะรันผ่าน Web Browser แล้วเริ่มขโมยข้อมูลได้เลย และที่สำคัญที่สุด มันไม่ใช่ Bug แต่มันคือ Feature ที่พยายามคาดเดา ที่เป็นหัวใจการทำงานของความเร็วบน CPU สมัยใหม่ หากปิด Feature นี้ คอมพิวเตอร์จะช้าลงอย่างมหาศาล เราจึงทำได้แค่การหาทางอ้อม ๆ เพื่อป้องกันมันเท่านั้น

เราสามารถเอา Meltdown และ Spectre มาสร้างความหายนะร่วมกันได้อย่างไร

อาจจะถามว่า ทำไมทั้งสองช่องโหว่นี้เรามักจะพูดมันเหมือนเป็นเรื่องเดียวกัน แต่จริง ๆ แล้วมันเป็นช่องโหว่คนละตัวกัน งั้นเราลองมาดูกันดีกว่า เราจะใช้งานมันร่วมกันมาสร้างความหายนะได้อย่างไร

จริง ๆ ทั้งสองช่องโหว่นี้ถึงมันจะใช้ช่องโหว่จาก Speculative Execution เหมือนกัน แต่มันเก่งไม่เหมือนกัน Meltdown เก่งเรื่อง การกวาดข้อมูลจาก Kernel ได้เร็วและเยอะมาก ๆ แต่ Spectre เก่งเรื่องการเจาะจง และหลบหลีกระบบป้องกันไว้หาทางเข้า ทำให้ส่วนใหญ่ หากมีการสร้าง Attack Chain โดยใช้ทั้งสองช่องโหว่นี้ ก็มักจะใช้ Spectre เป็นตัวเปิด และ Meltdown ปิดเกม

ตัวอย่างเช่น เราอยากจะเจาะผ่านระบบที่เรียกว่า KASLR (Kernel Address Space Layout Randomisation) หรือก็คือ OS จะสุ่มตำแหน่งของ Kernel ใน Memory ทุกครั้งที่เปิดเครื่องใหม่ ถึงแม้ว่าเราจะใช้ Meltdown กวาดข้อมูลออกจาก Kernel ได้ แต่ก็ไม่รู้อยู่ดีว่า Kernel อยู่ตรงไหน งั้นเราก็เอา Spectre มาหาก่อนว่า Pointer ที่ชี้ไปยัง Kernel คือ Address ไหน แล้วถึงจะเริ่มกวาด Kernel Memory ด้วย Meltdown เอา Password Hash และ Private Keys ออกมา

Patching วิธีแก้ปัญหาระยะสั้น

แน่นอนว่า เราไม่สามารถเรียกคืน CPU ทั้งโลกกลับมาได้ งั้นเราจะต้องแก้ปัญหาด้วยการ Update Software บางอย่าง โดยแลกมากับ Performance ที่ต่ำลง

อย่างแรกคือ การ Implement Kernel Page Table Isolation (KPTI) จากเดิมที่ Kernel Memory ถูก Map รวมกันกับ User Space เราก็จะแยก Map ของ Memory นี้ออกจากกันอย่างเด็ดขาด ดังนั้นถ้าโปรแกรมมีการเรียก System Call ฝั่ง CPU จะต้องสลับตาราง Memory และล้าง TLB ออกใหม่ให้หมด ทำให้ Performance ก็จะลดลงนั่นเอง

อย่างที่ 2 คือการพยายามแก้ Spectre ด้วย 2 วิธีใหญ่ ๆ คือ Retpoline เป็นการแก้ Compiler โดยจะเปลี่ยนคำสั่ง Indirect Branch ให้เป็น Infinity Loop เพื่อขับ CPU ไม่ให้เดามั่วซั่วจนกว่าจะรู้ Address ที่แท้จริง กับอีกอย่างคือ Update Microcode ที่จะใส่ Indirect Branch Prediction Barrier (IBPB) เข้าไป เป็นคำสั่งให้ OS ล้างสมองประวัติการเดาของ CPU ทุกครั้งที่มีการสลับโปรแกรม ป้องกันไม่ให้โปรแกรมนึงไปวางยาอีกโปรแกรมนึง

Permanent Solution: CPU Redesign

แต่แน่นอนว่า หลังจากการค้นพบปัญหานี้ผู้ผลิต CPU ได้ทำการรื้อแบบใหม่ เพื่อแก้ปัญหานี้อย่างถาวร

ในการแก้ Meltdown ก็จะออกแบบวงจรใหม่เพื่อตรวจสอบสิทธิ์ให้เกิดขึ้น พร้อมกัน หรือ ก่อน การโหลดข้อมูล ถ้าสิทธิ์ไม่ผ่าน ข้อมูลจะไม่มีวันไปแตะหรือได้สัมผัสที่ Cache เลย ทำให้เราสามารถเอา KPTI ออกไปได้เลย ความเร็วก็จะกลับมาเหมือนเดิม

และ Spectre คือ การทำ Partitioned BTB คือ การแบ่ง Branch Target Buffer ให้แยกขาดจากกันในแต่ละ Application หรือ Thread ทำให้ไม่สามารถข้ามเส้นมามายุ่งกับของชาวบ้านได้ พอมันเป็นการเพิ่มคำสั่งใน Hardware Level มันก็จะทำงานได้เร็วกว่า Software Patch เป็นอย่างมาก ทำให้เราแทบจะไม่เสีย Performance เลย

เราได้รับบทเรียนอะไรจากเรื่องนี้

Spectre และ Meltdown สอนให้โลกของ Computer Science ว่า ตลอดมา เราเขียนโปรแกรมโดยเชื่อว่า Hardware เป็นกล่องดำที่สมบูรณ์แบบ แต่ความจริงคือ การออกแบบที่รีดประสิทธิภาพสูงสุดออกมา แต่กลับกัน มันก็ได้สร้างช่องโหว่ที่เรามองไม่เห็นมานานมาก ๆ และในยุคต่อไป Side-Channel Resistant จะกลายเป็นหัวข้อหลักในการออกแบบ CPU ไม่แพ้เรื่องการทำให้ CPU เร็วและประหยัดพลังงานอย่างแน่นอน