HeadlinesBriefing HeadlinesBriefing.com

Go रनटाइम बग: 32-बिट एम्बेडेड लिनक्स क्रैश

Hacker News •
×

हमारा दैनिक काम आमतौर पर लिनक्स और सुरक्षा विषयों के चारों ओर घूमता रहता है, सॉफ्टवेयर स्टैक के गहराई में। फिर भी, आपकी सोच से अधिक बार, हम उन एप्लिकेशनों को डीबग करने में समाप्त हो जाते हैं जो बहुत ऊपर चल रहे होते हैं। कभी-कभी ऐसी समस्या की जड़ लिनक्स कर्नेल में होती है, कभी-कभी कहीं और। इस ब्लॉग पोस्ट में हम दिखाते हैं कि हमने Go रनटाइम के अंदर एक बग कैसे खोजा और ठीक किया। हाल ही में एक ग्राहक ने बताया कि उनकी एम्बेडेड लिनक्स प्रणाली पर एक Go में लिखित एप्लिकेशन समय-समय पर क्रैश हो जाता है। क्रैश हमेशा एक ही घातक त्रुटि के साथ होता है, जिसका हस्ताक्षर है: runtime: netpoll: eventfd ready for 5।起初 हमने सोचा कि एप्लिकेशन स्वयं बगयुक्त है। लेकिन जब हमने त्रुटि को अधिक निकट से जांचा, तो यह Go के नेटपोल मैकेनिज्म के आंतरिक अनुमान के नहीं रहने जैसा दिखा। त्रुटि संदेश src/runtime/netpoll_epoll.go में netpoll() से आता है: if ev. Events != linux. EPOLLIN { throw("runtime: netpoll: eventfd ready for something unexpected") }। इस कोड पथ में, नेटपोल कोड EPOLLIN को केवल फायरिंग इवेंट के रूप में अपेक्षा करता है, लेकिन इसे 5 मिला, जो EPOLLIN|EPOLLOUT है। क्यों एपोल अचानक EPOLLIN से अधिक इवेंट रिपोर्ट करने लगा, जबकि कोड ने केवल EPOLLIN के लिए अनुरोध किया था? गहराई में जाने से पहले, हमने त्रुटि संदेश को एक खोज इंजन में डाल दिया, उम्मीद करते हुए कि किसी ने पहले इसी समस्या का सामना किया होगा। इससे हमें सीधे Go प्रोजेक्ट के इश्यू ट्रैकर में एक रिपोर्ट तक ले गया: runtime: netpoll: eventfd ready for something unexpected। इस इश्यू में ठीक वही घातक त्रुटि का वर्णन है जो हमने देखी। साथ ही, यह एक 32-बिट ARM एम्बेडेड लिनक्स प्रणाली पर भी हुई! रिपोर्टर ने यह भी नोट किया कि क्रैश लंबे समय तक चलने वाले एप्लिकेशन में होता है। यह हमारे ग्राहक के विवरण से भी मेल खाता था। बिंगो! यह इश्यू मार्च 2025 से खुला और अनसुलझा पड़ा हुआ था। Go के मेंटेनर्स ने भी इस समस्या को ठीक करने के एक प्रयास को अस्वीकार कर दिया था। इस इश्यू के टिप्पणियों से हमने जाना कि यह घातक त्रुटि केवल 32-बिट ARM और i386 लिनक्स प्रणालियों पर ही दिखाई दी, सभी प्रकार के कर्नेल संस्करणों के साथ। कुछ कर्नेल काफी पुराने थे, अन्य हाल के। किसी भी रिपोर्टर ने x86_64 या arm64 प्रणाली पर इसे नहीं देखा। प्रारंभ में हमने संदेह किया कि epoll 32-बिट ARM या i386 पर अलग तरह से व्यवहार करता है। हमने इस विचार को तेजी से त्याग दिया क्योंकि epoll कर्नेल में जनरिक कोर कोड है। क्यों यह केवल 32-बिट ARM या i386 पर स्प्यूरियस इवेंट सेट लौटाएगा? फिर भी, त्रुटि का केवल 32-बिट प्रणालियों पर दिखना हमें परेशान करता रहा। अगले कदम के रूप में, हमने Go के नेटपोल कोड में epoll के उपयोग की समीक्षा की। एक LLM की मदद से, हमने src/runtime/netpoll_epoll.go की समीक्षा की, 32-बिट के खतरों पर ध्यान केंद्रित करते हुए, जैसे पूर्णांक रूपांतरण। समीक्षा से पता चला कि Go का नेटपोल कोड struct epoll_event के data फ़ील्ड का उपयोग करता है। लिनक्स epoll कर्नेल में एक 8 बाइट कुकी संग्रहित कर सकता है और इसे फायरिंग इवेंट के हिस्से के रूप में उपयोगकर्ता स्थान पर लौटाता है। एप्लिकेशन इस कुकी का उपयोग एक इवेंट को मेटाडेटा से जोड़ने के लिए करते हैं, उदाहरण के लिए अलग-अलग इवेंट स्रोतों को अलग करने के लिए। struct epoll_event { __poll_t events; /* ev.

Events in Go netpoll */ __u64 data; /* ev. Data in Go netpoll */ }; Go रनटाइम के गहराई में, मुख्य इवेंट हैंडलर को जानने की आवश्यकता होती है कि क्या एक इवेंट इवेंट fd या सॉकेट fd से संबंधित है। यह ev. Data की तुलना अपने आंतरिक इवेंट fd ऑब्जेक्ट के पते से करके निर्धारित करता है: if *(**uintptr)(unsafe.

Pointer(&ev. Data)) == &netpoll Event Fd {...}। कोड में आगे बढ़ते हुए, हमने देखा कि ev. Data या तो netpoll Event Fd के कच्चे पॉइंटर को रखता है या प्रति-सॉकेट ऑब्जेक्ट poll Desc के टैग किए गए पॉइंटर को रखता है। पॉइंटर टैग एक काउंटर है, fdseq, जो रिसाइक्ल किए गए poll Desc ऑब्जेक्ट को अलग करता है। अब तक सब कुछ ठीक था। एक ही फ़ील्ड में कच्चे और टैग किए गए पॉइंटर को मिलाना हमें संदिग्ध लगा। लेकिन यह क्रैश से कैसे संबंधित है, यह अभी तक स्पष्ट नहीं था। Go द्वारा मेमोरी में टैग किए गए पॉइंटर कैसे रखे जाते हैं, इसकी जांच करने से अंततः मूल कारण पता चला। 32-बिट प्रणालियों पर, एक पॉइंटर 4 बाइट्स घेरता है जबकि data फ़ील्ड 8 बाइट्स की अपेक्षा करता है। इस आकार के असंगति से पॉइंटर मान संग्रहीत करते समय ट्रंकेशन होता है। ऊपर के 4 बाइट्स रूपांतरण के दौरान शून्य कर दिए जाते हैं। जब रनटाइम बाद में कटे हुए पॉइंटर की तुलना मूल इवेंट fd पते से करता है, तो...

स्रोत: Hacker News · HeadlinesBriefing द्वारा सारांशित