Go fuzzing Z3 ของ Google 12 พ.ย. 2026 ใช้ Gemini 4 Argon หาบั๊ก slice ได้ 3 จาก 3 ที่ฝังไว้ และเจอบั๊กแฝง 3 จุดใน skill-lint ที่ go test -fuzz พลาดสองตัว
โดย กองบรรณาธิการ THAI DATA อ่าน 7 นาที
สรุปประเด็นสำคัญ
- Karl Weinmeister ของ Google เผยแพร่วิธีใช้ Gemini 4 Argon แปลเส้นทางไปยังกับดัก bounds-check ในโค้ด Go เป็นกฎของตัวแก้ Z3 เมื่อ 12 พฤศจิกายน 2026
- วิธีนี้จับ 3 จาก 3 บั๊กที่ฝังไว้ในตัวแยกวิเคราะห์ทดสอบ และพบบั๊กแฝง 3 จุด ในเครื่องมือ skill-lint ของเขา โดยมี 175 สกิลของ Google ที่ต้องรักษาความสอดคล้อง
- go test -fuzz ที่ไม่ใส่ seed พลาดบั๊ก ParseTwoChunkFrame ทั้งการรัน 3 วินาที หนึ่งครั้งและ 60 วินาที สามครั้ง ทั้งที่ 19.4% ของคู่ (hdrLen, bodyLen) ที่ความยาว 59 ไบต์ทำให้ panic
- สาเหตุคือ Go ตรวจ slice กับ capacity ไม่ใช่ length ในการทดสอบ 10 วินาที จาก 386,741 ครั้งเรียก มี 89% เป็น view ของบัฟเฟอร์ร่วมราว 100 MB และมีเพียง 0.07% ที่ cap เท่ากับ len
- เมื่อตัด capacity ให้เท่า length ด้วย data[:len(data):len(data)] fuzzer ล้มทั้งสองบั๊กในทุกรอบ มัธยฐาน 0.28 วินาที และ 0.17 วินาที

Go fuzzing Z3 คือวิธีที่ Karl Weinmeister ผู้อำนวยการ Developer Relations ของ Google เผยแพร่เมื่อ 12 พฤศจิกายน 2026 ใช้โมเดล Gemini 4 Argon แปลเส้นทางไปยังกับดักตรวจขอบเขตในโค้ด Go เป็นกฎของตัวแก้ Z3 เพื่อหาอินพุตที่ทำให้โปรแกรม panic Go fuzzing Z3 จับบั๊กที่ฝังไว้ได้ 3 จาก 3 ตัว และพบบั๊กแฝง 3 จุดในเครื่องมือจริง ขณะที่ go test -fuzz พลาดบั๊กสองตัวในการตั้งค่าปกติ
Go fuzzing Z3 ทำงานอย่างไร
Go fuzzing Z3 เริ่มจากปัญหาของผู้เขียน ซึ่งดูแลโปรแกรมสกิลของ Google ที่มี 175 สกิล จึงเขียน skill-lint เป็น CLI ภาษา Go ตรวจไฟล์ SKILL.md และใช้การแก้ไขระดับบรรทัดที่ LLM สร้าง แม้มีชุดทดสอบยูนิตและ fuzz แล้ว ข้อผิดพลาดเล็ก ๆ เรื่อง slice หรือเลขบรรทัดในตัวแยกวิเคราะห์ Markdown และตัวใช้การแก้ไขอาจนอนเงียบจนไฟล์หรือช่วงบรรทัดแปลก ๆ ทำให้ panic
ใน Go ทุกการ slice หรือ index ที่อาจ panic ถูกคอมไพล์เป็นกับดัก bounds-check ที่ชัดเจน หลังตัวเพิ่มประสิทธิภาพตัดสิ่งที่พิสูจน์ได้ว่าปลอดภัยออกแล้ว ขั้นตอนของผู้เขียนมีดังนี้
- คอมไพล์ฟังก์ชันเป้าหมายด้วย
GOOS=linux GOARCH=riscv64 go build -gcflags="-S -d=ssa/check_bce/debug=1"เพื่อดูกับดัก runtime.panicBounds ที่เหลือ - ส่งซอร์ส Go และแอสเซมบลีให้ Gemini 4 Argon เขียนสคริปต์ Z3 ของเส้นทางไปกับดักแต่ละจุด พร้อมฟังก์ชันที่แก้แล้วเมื่อพบบั๊ก
- ให้ Z3 หาว่าเงื่อนไขเป็นจริงได้หรือไม่ ถ้าได้ (sat) จะได้ไบต์ตัวอย่าง
- ให้ go test รันไบต์เหล่านั้นกับฟังก์ชันจริง ถ้า panic ก็ได้ตัวอย่างบั๊กที่ยืนยันแล้ว
เวลาส่วนใหญ่ของ Go fuzzing Z3 อยู่ที่การแปลของ Argon ราว 1 นาทีต่อฟังก์ชัน ส่วน Z3 ใช้ไม่กี่มิลลิวินาที ในงาน Go fuzzing Z3 นี้ ผู้เขียนเลือก RISC-V เพราะเป็นชุดคำสั่งที่โพสต์ของ Anthropic ใช้กับการพิสูจน์ด้วย Rocq และ Go คอมไพล์ข้ามไปได้เลย

ควรเชื่อคำตอบของ Go fuzzing Z3 แค่ไหน
ผู้เขียนเขียนว่า Go fuzzing Z3 ไม่ใช่การพิสูจน์ความถูกต้องสมบูรณ์ เพราะใช้ตัวแก้แบบ SMT ที่พึ่งการแปลแอสเซมบลีของ LLM คำตอบ sat หมายถึงมีบั๊กแน่นอนเพราะ go test ยืนยันได้ ส่วน unsat เป็นเพียง "อาจถูกต้อง" คล้าย Bloom filter สำหรับบั๊ก ข้อดีคือได้ผลหาบั๊กส่วนใหญ่แบบวิธีเชิงรูปนัยด้วยแรงที่น้อยกว่ามาก ตัวอย่างที่ Anthropic ทำคือพิสูจน์คุณสมบัติไม่ crash ของไบนารีทั้งก้อนภายใต้งบ 64 MB ส่วนผู้เขียนตรวจรายฟังก์ชันบน slice ใหม่ 64 ไบต์ที่ cap เท่ากับ len
บั๊กที่ฝังไว้และเหตุที่ Go fuzzing พลาด
ผู้เขียนเริ่มทดสอบ Go fuzzing Z3 ด้วยตัวแยกวิเคราะห์ที่ปลอดภัย 3 ตัวเพื่อดูว่าไม่ติดธงโค้ดดี ตัวแยกคีย์ TOML ถูกตัดการตรวจออกหมดโดยคอมไพเลอร์เอง ส่วนตัวสแกนส่วนหัว HTTP และตัวถอดรหัส varint ได้ unsat จากนั้นเขียนตัวแยกที่มีบั๊กแฝง 3 ตัว
บั๊กแรกคือ ParseTwoChunkFrame ซึ่งคำนวณ total := hdrLen + bodyLen จาก uint8 สองตัว ผลรวมจึงวนที่ 256 ก่อนตรวจความยาว แต่ endBody ใช้ int เต็ม Z3 คืนตัวอย่างความยาว 59 ไบต์ที่ hdrLen เท่ากับ 48 และ bodyLen เท่ากับ 236 ผลรวมวนจาก 284 เป็น 28 ผ่านเกณฑ์ตรวจ แล้ว slice ไปถึงดัชนี 288 และ panic

ที่ความยาว 59 ไบต์ 19.4% ของคู่ (hdrLen, bodyLen) ทั้งหมดทำให้ crash แต่การรัน go test -fuzz แบบไม่ seed บน Go 1.27.3 ทั้ง 3 วินาที 1 ครั้ง และ 60 วินาที 3 ครั้ง พลาดทั้งหมด โดยล้างแคช fuzz ทุกครั้ง สาเหตุคือ Go ตรวจ slice กับ capacity ไม่ใช่ length ในการทดสอบ 10 วินาที มี 386,741 ครั้งเรียก โดย 89% เป็น view ของบัฟเฟอร์ร่วมราว 100 MB และมีเพียง 0.07% ที่ cap เท่ากับ len ด้วยความจุเหลือขนาดนั้น endBody (สูงสุด 514) จึงไม่เกิน cap ตัวแยกคืนไบต์เกินท้าย buf แทนการ panic
บั๊กที่สอง ExtractQuotedToken เจอจุดบอดเดียวกัน เมื่อเครื่องหมายแบ็กสแลชท้ายสายดันดัชนีเกินไป 1 Z3 หาได้ทันทีด้วยอินพุต 2 ไบต์ "\ ส่วนตัวที่สาม ParseMagicPacket เป็นตัวควบคุม เพราะบั๊กความยาว int8 มีเครื่องหมายทำให้ดัชนีติดลบ panic ไม่ว่า capacity เท่าใด fuzzer จึงเจอทุกรอบ
วิธีแก้ที่ทำให้ Go fuzzing จับได้เอง
วิธีแก้ของผู้เขียนคือตัด capacity ให้เท่า length ในฟังก์ชัน fuzz
data = data[:len(data):len(data)] // cap == len: slicing past the end now panics
หลังแก้ Go fuzzing ล้ม ParseTwoChunkFrame และ ExtractQuotedToken ในทั้ง 3 รอบ ด้วยมัธยฐาน 0.28 และ 0.17 วินาที
Go fuzzing Z3 กับ skill-lint
ทั้งไบนารีใหญ่เกินจะส่งให้ตัวแก้ แม้ตัวแยกวิเคราะห์ 10 บรรทัดก็ลิงก์สัญลักษณ์ของรันไทม์ Go 1,559 รายการ ไบนารี skill-lint ขนาด 28 MB มี 32,321 สัญลักษณ์ ผู้เขียนจึงใช้แนวคิด Functional Core, Imperative Shell ปล่อยแฟล็ก CLI การเรียกเครือข่ายและไฟล์ให้ go test -fuzz กับ go test -race และใช้ตัวแก้กับฟังก์ชันบริสุทธิ์และตัวช่วย slice ที่แยกออกมาเท่านั้น
จาก 5 ฟังก์ชันที่ทดสอบ 2 ตัวสแกนเนอร์ได้ unsat (รวมตัวแบ่งบรรทัด CRLF ที่มีการตรวจเหลือ 6 จุด) อีก 3 ตัวมีบั๊ก บั๊กที่ชัดที่สุดอยู่ใน ApplyReplacementChunks ซึ่งตรวจการแก้ไขที่ LLM สร้างกับจำนวนบรรทัดเริ่มต้นแล้วเรียงจากล่างขึ้นบน Z3 ตอบ sat ทันทีเพราะการเรียงตาม StartLine ไม่กันไม่ให้สองก้อนซ้อนกัน เช่น แทนบรรทัด 3 และ 4 ของไฟล์ 4 บรรทัดด้วยบรรทัดเดียวทำให้เหลือ 3 บรรทัด แล้วก้อนที่สองคือบรรทัด 2 ถึง 4 จะ slice lines[4:] แล้ว panic ด้วย slice bounds out of range [4:3] ในโค้ดที่ปล่อยจริง การตรวจต่อรอบเปลี่ยนเคสนี้เป็นข้อผิดพลาด range ที่ชวนสับสน และก้อนที่ซ้อนกันแต่ยังอยู่ในช่วงจะเขียนทับการแก้ไขก่อนหน้าโดยไม่แจ้ง วิธีแก้คือปฏิเสธก้อนที่ซ้อนกันหลังเรียง
บั๊กแฝงอีกสองจุดที่ Go fuzzing Z3 พบ
อีก 2 บั๊กเป็นความผิดพลาดด้านการนับ ในตัวแก้ในเครื่อง เลขบรรทัดเป้าหมายถูกตรวจครั้งเดียวต่อรายการก่อนลูปที่ลบบรรทัดและทำให้ slice หด เป็นความเสี่ยงแฝงถ้าตัวแก้คืนก้อนที่สองหลังการลบ แต่ปัจจุบัน DeleteLineFix คืนเพียงก้อนเดียว ส่วนตัวแพ็กพรอมต์เผื่อ 100 ไบต์สำหรับตัวห่อราว 70 ไบต์พร้อมพาธไฟล์ จึงเกินงบบริบท 2 MB ในโค้ดจริงเมื่อพาธยาวเกินราว 30 ไบต์ และ panic ในตัวช่วยที่แยกออกมา

ผู้เขียนระบุว่าเมื่อใช้ Go fuzzing Z3 ตรวจแบบโมดูลาร์ ต้องรวมการ์ดทางเข้าของฟังก์ชันเข้าไปในเป้าหมายด้วย ไม่อย่างนั้น Z3 จะคิดอินพุตที่ผิดเงื่อนไขซึ่งผู้เรียกควรกันไว้ เมื่อรวมการ์ดแล้ว คำตอบ sat แรกของทั้ง 6 ฟังก์ชันที่มีบั๊กทำให้ go test panic จริง โดยไม่ต้องกรองผลบวกลวง เมื่อแยกตัวช่วยออกจากงานไฟล์แล้ว fuzz 3 วินาทีก็จับทั้ง 3 บั๊กได้เช่นกัน และการรันซ้ำโดยลบคอมเมนต์ทั้งหมดได้ผลเหมือนเดิม
เลือกใช้ Go fuzzing Z3 เมื่อไร
ผู้เขียนสรุปว่าสำหรับโค้ด Go ส่วนใหญ่ การ fuzz ฟังก์ชันบริสุทธิ์โดยตัด capacity ให้เท่า length จะจับบั๊ก bounds ได้ในไม่กี่วินาที Go fuzzing Z3 คุ้มกับเวลาอีกราว 1 นาทีเมื่อกับดักรอดทั้งตัวพิสูจน์ช่วงของคอมไพเลอร์และ fuzz แบบเร็ว โดยเฉพาะตัวแยกวิเคราะห์และตัวแก้ไขที่คำนวณออฟเซ็ตจากส่วนหัวหลายฟิลด์หรือช่วงบรรทัด โมเดลตัวแก้แบบเธรดเดียวมองไม่เห็น data race หรือ deadlock โค้ดที่ทำงานพร้อมกันจึงยังต้องใช้ race detector และโปรไฟล์ goroutine leak ของ Go 1.27
ผลต่อองค์กรไทย
- เพิ่มบรรทัดเดียวในฟังก์ชัน Go fuzzing ตัด capacity ให้เท่า length แล้วรันซ้ำกับตัวแยกวิเคราะห์ที่ใช้อยู่ ถ้ามีบั๊กลักษณะเดียวกันจะเห็นภายในไม่กี่วินาที
- แยกตรรกะที่คำนวณออฟเซ็ตเป็นฟังก์ชันบริสุทธิ์ เพื่อให้ทั้ง Go fuzzing และ Z3 เข้าถึงได้
- ถือผลของ Go fuzzing Z3 เป็นสมมติฐาน เพราะ LLM เป็นผู้แปล ให้ go test ยืนยันทุกตัวอย่างก่อนแก้ ตามที่ผู้เขียนทำ
- ไม่ใช้แทนการตรวจ race สำหรับโค้ดที่ทำงานพร้อมกัน
ผลของ Go fuzzing Z3 ทั้งหมดมาจากการทดลองของผู้เขียนคนเดียวกับโค้ดของตนเอง ไม่ใช่เบนช์มาร์กอิสระ และเป็นการใช้โมเดล Gemini 4 Argon ที่เรายังไม่มีรายงานแยก ดูเรื่องโมเดลล่าสุดของ Google ได้ในGemini 4 Hydrogen
บริการของ THAI DATA CLOUD ที่เกี่ยวข้อง
ทีมพัฒนาที่อยากรัน fuzz นานหรือรันตัวแก้กับโค้ดจำนวนมากมักต้องการเครื่องแยกจากเครื่องทำงานประจำวัน Cloud Linux Server ของ THAI DATA CLOUD เลือก Ubuntu, AlmaLinux หรือ Debian ได้ มี Root Access เต็มรูปแบบ และตั้งอยู่ใน Data Center ไทยและสิงคโปร์ จึงใช้เป็นเครื่องทดสอบแยกได้ (THAI DATA CLOUD เป็นผู้ดำเนินงานเว็บไซต์นี้)
ติดตามข่าวโมเดลและเครื่องมือ AI เพิ่มเติมได้ที่หมวด AI
คำถามที่พบบ่อย
Go fuzzing Z3 ของ Google ทำงานอย่างไร?
Go fuzzing Z3 ทำโดยคอมไพล์ฟังก์ชัน Go เป็นแอสเซมบลี RISC-V เพื่อดูกับดัก runtime.panicBounds ที่คอมไพเลอร์ตัดไม่ได้ แล้วให้ Gemini 4 Argon เขียนสคริปต์ Z3 ของเส้นทางไปกับดักนั้น เมื่อ Z3 หาอินพุตที่ทำให้ panic ได้ go test จะรันไบต์นั้นกับฟังก์ชันจริงเพื่อยืนยันบั๊ก
ทำไม Go fuzzing พลาดบั๊กขอบเขต slice ที่ Z3 เจอ?
ใน Go fuzzing Go ตรวจนิพจน์ slice กับ capacity ไม่ใช่ length แต่ fuzzer ของ Go แทบไม่ส่ง slice ที่ไม่มีความจุเหลือ ในการทดสอบ 10 วินาที จาก 386,741 ครั้งเรียก มีเพียง 0.07% ที่ cap เท่ากับ len จึงไม่เกิด panic และบั๊กไม่ถูกจับ
แก้ปัญหา Go fuzzing พลาดบั๊ก bounds อย่างไร?
ใน Go fuzzing ให้ตัด capacity ให้เท่า length ในฟังก์ชัน fuzz ด้วย data = data[:len(data):len(data)] ทำให้การ slice เกิน length กลายเป็น panic ผู้เขียนรายงานว่า fuzzer จับ ParseTwoChunkFrame และ ExtractQuotedToken ได้ทุกรอบ ด้วยมัธยฐาน 0.28 และ 0.17 วินาที
Go fuzzing Z3 ใช้แทนการพิสูจน์ความถูกต้องได้ไหม?
ไม่ได้ ผู้เขียนระบุว่าโมเดลของ Go fuzzing Z3ที่ LLM เขียนไม่ใช่ระบบพิสูจน์ที่สมบูรณ์ คำตอบ unsat แปลว่า อาจถูกต้อง เท่านั้น แต่คำตอบ sat พร้อมไบต์ตัวอย่างยืนยันได้ด้วย go test และวิธีนี้มองไม่เห็น data race หรือ deadlock
แหล่งอ้างอิง
- Catch Go bugs that fuzzing misses with Gemini and a constraint solver — Google Developers Blog
- Google Developers Blog — Google
บุคคล องค์กร และสถานที่ในข่าวนี้
เลือกชื่อเพื่ออ่านข่าวทั้งหมดที่กล่าวถึง ตัวเลขคือจำนวนข่าว
- องค์กรและบริษัท
- Google246Anthropic78
- ประเทศและสถานที่
- สิงคโปร์279
- ผลิตภัณฑ์และเทคโนโลยี
- Gemini 4 Argon8Linux154Ubuntu86AlmaLinux66Debian77Gemini93
พบข้อมูลคลาดเคลื่อน? แจ้งกองบรรณาธิการ
