LOWNLAB News
โปรแกรมมิ่งระดับ 7/10sigma star gmbh

ตามล่าบั๊กใน Go runtime บนระบบฝังตัว 32 บิต ที่ซ่อนตัวมาตั้งแต่ Go 1.14

Hunting Down a Go Runtime Bug on 32-Bit Embedded Systems

บล็อกของ sigma star เล่าการไล่ล่าบั๊กที่ทำให้แอปพลิเคชัน Go บนระบบฝังตัว Linux สถาปัตยกรรม ARM 32 บิต crash ด้วยข้อความ "runtime: netpoll: eventfd ready for something unexpected" โดยเกิดเฉพาะกับ workload ที่รันต่อเนื่องยาวนานเท่านั้น

สาเหตุรากคือ Go runtime เก็บทั้ง raw pointer ที่ชี้ไปยัง netpollEventFd และ tagged pointer ของ pollDesc ไว้ในฟิลด์ ev.Data ขนาด 8 ไบต์เดียวกัน บนระบบ 32 บิตแบบ little-endian สิ่งนี้สร้างปัญหา aliasing เพราะ tagged pointer จะบรรจุ tag (4 ไบต์) ไว้ครึ่งล่างและ address (4 ไบต์) ไว้ครึ่งบน ขณะที่ raw pointer ใช้แค่ 4 ไบต์ล่าง เมื่อโค้ดเปรียบเทียบโดยมอง ev.Data เป็น uintptr ขนาด 4 ไบต์ ค่า tag (fdseq) จึงไปทับกับ address ของ pointer

ผลคือหลังจาก pollDesc ถูกรีไซเคิลไปแล้วหลายล้านครั้ง ค่า fdseq จะโตจนบังเอิญตรงกับ address ของ &netpollEventFd พอดี ทำให้ netpoll เข้าใจผิดว่า file descriptor ของ socket คือ event file descriptor — นี่คือเหตุผลว่าทำไมบั๊กปรากฏเฉพาะเมื่อรันไปนาน ๆ และไม่เคยเกิดบนระบบ 64 บิตเลย

การวินิจฉัยทำโดยตรวจสอบการจัดการ pointer ใน src/runtime/netpoll_epoll.go และวิเคราะห์ memory layout ของ struct epoll_event ซึ่งเป็นตัวเปิดช่องให้เกิด aliasing บนสถาปัตยกรรม 32 บิต little-endian โดยเฉพาะ

วิธีแก้คือแทนที่จะเก็บ raw pointer ไปยัง netpollEventFd ก็เก็บ pollDesc ที่เป็น nil ในรูป tagged pointer แทน ทำให้ ev.Data บรรจุ tagged pointer อย่างสม่ำเสมอ ปัญหา aliasing จึงหมดไป ไทม์ไลน์ของบั๊กนี้กินเวลานาน — ถูกใส่เข้ามาตั้งแต่ Go 1.14 ในปี 2020 มีรายงานจากชุมชนครั้งแรกเดือนมีนาคม 2025 และเพิ่ง merge fix ในปี 2026

แหล่งอ้างอิง

อ่านต้นฉบับที่ sigma star gmbh

https://sigma-star.at/blog/2026/08/go-runtime-netpoll-bug/