क्या यह संकलक को क्रैश करने के लिए अपरिभाषित व्यवहार वाले स्रोत कोड के लिए कानूनी है?


85

मान लीजिए कि मैं कुछ खराब लिखित C ++ स्रोत कोड को संकलित करने के लिए जाता हूं जो अपरिभाषित व्यवहार को आमंत्रित करता है, और इसलिए (जैसा कि वे कहते हैं) "कुछ भी हो सकता है"।

सी + + भाषा विनिर्देश क्या एक "अनुरूप" संकलक में स्वीकार्य है, के परिप्रेक्ष्य से, इस परिदृश्य में "कुछ भी" संकलक दुर्घटनाग्रस्त हो जाता है (या मेरे पासवर्ड चोरी करना, या अन्यथा दुर्व्यवहार या संकलन-समय पर गलत करना), या है अपरिभाषित-व्यवहार की गुंजाइश विशेष रूप से सीमित होती है जब परिणामी निष्पादन योग्य रन हो सकते हैं?


22
"यूबी यूबी है। इसके साथ जियो" ... कोई इंतजार नहीं। "कृपया एक MCVE पोस्ट करें।" ... इंतज़ार नही। मुझे सभी रिफ्लेक्सियों के लिए सवाल पसंद है जो इसे अनुचित तरीके से चलाता है। :-)
युन्नोस्क

14
वास्तव में कोई सीमा नहीं है, यही कारण है कि यह कहा जाता है कि यूबी नाक के राक्षसों को बुला सकता है ।
कुछ प्रोग्रामर ने

15
यूबी एसओ पर लेखक को एक प्रश्न पोस्ट कर सकता है । : पी
तनवीर बदर

45
चाहे जो भी सी ++ मानक कहता है, अगर मैं एक संकलक लेखक था, तो मैं निश्चित रूप से इसे अपने संकलक में बग के रूप में मानता हूं। इसलिए यदि आप इसे देख रहे हैं, तो एक दोष रिपोर्ट दर्ज करें।
जॉन

9
@LeifWillerts यह 80 के दशक में वापस आ गया था। मुझे सटीक निर्माण याद नहीं है, लेकिन लगता है कि यह एक कंवर्टेड वैरिएबल प्रकार का उपयोग करने पर टिका है। एक प्रतिस्थापन में डालने के बाद मेरे पास एक "मैं क्या सोच रहा था - चीजें उस तरह से काम नहीं करती हैं" पल। मैंने मशीन को रिबूट करने के लिए कंस्ट्रक्टर को अस्वीकार करने के लिए दोषी नहीं ठहराया। मुझे संदेह है कि आज कोई भी उस कंपाइलर से भिड़ जाएगा। यह 68000 माइक्रोप्रोसेसर को लक्षित करने वाले HP 64000 के लिए HP C क्रॉस कंपाइलर था।
अवी बर्जर

जवाबों:


71

अपरिभाषित व्यवहार की मानक परिभाषा इस प्रकार है:

[Defns.undefined]

व्यवहार जिसके लिए यह अंतर्राष्ट्रीय मानक कोई आवश्यकता नहीं है

[नोट: यह अंतर्राष्ट्रीय मानक व्यवहार की किसी भी स्पष्ट परिभाषा को छोड़ देता है या जब कोई प्रोग्राम गलत निर्माण या गलत डेटा का उपयोग करता है, तो अपरिभाषित व्यवहार की उम्मीद की जा सकती है। अनुज्ञेय अपरिभाषित व्यवहार की स्थिति की अनदेखी पूरी तरह से अप्रत्याशित परिणामों के साथ होती है, अनुवाद या कार्यक्रम निष्पादन के दौरान व्यवहार करने के लिए पर्यावरण की एक विशेषता तरीके से (नैदानिक ​​संदेश जारी करने के साथ या बिना), अनुवाद या निष्पादन को समाप्त करने के साथ होता है। एक नैदानिक ​​संदेश)। कई गलत कार्यक्रम निर्माण अपरिभाषित व्यवहार को प्रस्तुत नहीं करते हैं; उनका निदान किया जाना आवश्यक है। एक स्थिर अभिव्यक्ति का मूल्यांकन कभी भी अपरिभाषित के रूप में निर्दिष्ट व्यवहार को प्रदर्शित नहीं करता है। - अंतिम नोट]

जबकि नोट स्वयं ही प्रामाणिक नहीं है, यह वर्णन करता है कि व्यवहार कार्यान्वयन की एक श्रृंखला प्रदर्शनी के लिए जानी जाती है। तो संकलक को क्रैश करना (जो अनुवाद अचानक समाप्त हो रहा है), उस नोट के अनुसार वैध है। लेकिन वास्तव में, जैसा कि मानक पाठ कहता है, मानक निष्पादन या अनुवाद के लिए कोई सीमा नहीं रखता है। यदि कोई कार्यान्वयन आपके पासवर्ड चुराता है, तो यह मानक में उल्लिखित किसी अनुबंध का उल्लंघन नहीं है।


43
उस ने कहा, यदि आप वास्तव में किसी भी सैंडबॉक्सिंग के बिना, संकलन-समय पर मनमाने कोड को निष्पादित करने के लिए एक कंपाइलर प्राप्त कर सकते हैं , तो विभिन्न सुरक्षा लोगों को इसके बारे में जानने के लिए बहुत रुचि होगी । वही कंपाइलर सेगफॉल्टिंग के लिए जाता है।
केविन

67
केविन ने जो कहा उसके लिए डिट्टो। पिछले करियर में C / C ++ / etc कंपाइलर इंजीनियर के रूप में, हमारी स्थिति यह थी कि अपरिभाषित व्यवहार आपके प्रोग्राम को क्रैश कर सकता है , आपके आउटपुट डेटा को स्क्रू कर सकता है, आपके घर को आग लगा सकता है, जो भी हो। लेकिन कंपाइलर को कभी भी क्रेश नहीं होना चाहिए चाहे वो कोई भी इनपुट हो। (यह सहायक त्रुटि संदेश नहीं दे सकता है, लेकिन यह केवल CTHULHU TAKE WHEEL और segfaulting को चीखने के बजाय कुछ प्रकार के नैदानिक ​​और निकास का उत्पादन करना चाहिए।)
Ti Strga

8
@TiStrga मैं शर्त लगाता हूं कि Cthulhu एक भयानक F1 ड्राइवर बनाएगी।
ज़ेटा-बैंड

35
"यदि कोई कार्यान्वयन आपके पासवर्ड चुराता है, तो यह मानक में उल्लिखित किसी अनुबंध का उल्लंघन नहीं है।" यह इस बात की परवाह किए बिना है कि क्या कोड में UB है या नहीं? मानक केवल यह निर्धारित करता है कि संकलित कार्यक्रम को क्या करना चाहिए - एक संकलक जो कोड को सही ढंग से संकलित करता है लेकिन प्रक्रिया में आपके पासवर्ड चुराता है मानक का अवज्ञा नहीं करेगा।
कारमिस्टर

8
@Carmeister, ऊह, यह एक अच्छा बिंदु है, मैं जब भी उन लोगों को "यूबी को परमाणु युद्ध शुरू करने के लिए संकलक की अनुमति देता हूं" की बहस को याद दिलाना सुनिश्चित करूंगा। फिर।
ilkachachu

8

UB के अधिकांश प्रकार, जिनके बारे में हम आमतौर पर चिंता करते हैं, जैसे NULL-deref या शून्य से विभाजित, रनटाइम UB हैं। एक फ़ंक्शन को संकलित करना जो रनटाइम यूबी का कारण होगा यदि निष्पादित होने पर कंपाइलर को दुर्घटना का कारण नहीं होना चाहिए। जब तक शायद यह साबित नहीं हो सकता कि कार्य (और फ़ंक्शन के माध्यम से पथ) निश्चित रूप से कार्यक्रम द्वारा निष्पादित किया जाएगा

(2 विचार: हो सकता है कि मैंने संकलन समय पर टेम्प्लेट / कॉन्स्टैक्स आवश्यक मूल्यांकन पर विचार नहीं किया है। संभवतः उस दौरान यूबी को अनुवाद के दौरान मनमाने ढंग से अजीबता पैदा करने की अनुमति दी जाती है, भले ही परिणामी फ़ंक्शन को कभी नहीं कहा जाता हो।)

अनुवाद के दौरान व्यवहार आईएसओ सी ++ बोली का हिस्सा में @ कथाकार के जवाब आईएसओ सी मानक में प्रयुक्त भाषा के समान है। C constexprसंकलन समय पर टेम्प्लेट या अनिवार्य eval शामिल नहीं करता है ।

लेकिन मजेदार तथ्य : आईएसओ सी एक नोट में कहता है कि यदि अनुवाद को समाप्त कर दिया जाता है, तो यह एक नैदानिक ​​संदेश के साथ होना चाहिए। या "अनुवाद के दौरान व्यवहार ... एक दस्तावेज तरीके से"। मुझे नहीं लगता कि "स्थिति को पूरी तरह से अनदेखा करना" अनुवाद को रोकने के रूप में पढ़ा जा सकता है।


पुराने उत्तर, अनुवाद से पहले मैंने UB के बारे में सीखा। यह रनटाइम-यूबी के लिए सही है, हालांकि, और इस प्रकार संभावित रूप से अभी भी उपयोगी है।


यूबी जैसी कोई चीज नहीं है जो संकलन के समय होती है। यह संकलक को निष्पादन के एक निश्चित मार्ग के साथ दिखाई दे सकता है , लेकिन C ++ शर्तों में ऐसा नहीं हुआ है जब तक कि निष्पादन एक फ़ंक्शन के माध्यम से निष्पादन के उस पथ तक नहीं पहुंचता है।

एक प्रोग्राम में दोष, जो इसे असंभव बनाना भी यूबी नहीं है, वे वाक्यविन्यास त्रुटियां हैं। इस तरह के एक कार्यक्रम सी ++ शब्दावली में "अच्छी तरह से गठित नहीं है" (यदि मेरे पास मेरा मानक सही है)। एक कार्यक्रम अच्छी तरह से बन सकता है लेकिन इसमें यूबी होता है। अपरिभाषित व्यवहार और बीमार गठन के बीच अंतर, कोई नैदानिक ​​संदेश की आवश्यकता नहीं है

जब तक मैं कुछ गलत नहीं कर रहा हूं, आईएसओ सी ++ को इस कार्यक्रम को सही ढंग से संकलित करने और निष्पादित करने की आवश्यकता है, क्योंकि निष्पादन कभी भी शून्य से विभाजन तक नहीं पहुंचता है। (अभ्यास (में Godbolt ), अच्छा compilers सिर्फ काम कर निष्पादनयोग्य बनाते हैं। जीसीसी / बजना के बारे में चेतावनी x / 0लेकिन इस, तब भी जब के अनुकूलन। लेकिन वैसे भी, हम बताने के लिए कोशिश कर रहे हैं कम आईएसओ सी ++ कार्यान्वयन की गुणवत्ता होने के लिए अनुमति देता है। तो जीसीसी जाँच / क्लैंग शायद ही एक उपयोगी परीक्षण है जो पुष्टि करता है कि मैंने कार्यक्रम को सही ढंग से लिखा है।

int cause_UB() {
    int x=0;
    return 1 / x;      // UB if ever reached.
 // Note I'm avoiding  x/0  in case that counts as translation time UB.
 // UB still obvious when optimizing across statements, though.
}

int main(){
    if (0)
        cause_UB();
}

इसके लिए एक उपयोग-केस में सी प्रीप्रोसेसर, या constexprचर और उन चरों पर ब्रांचिंग शामिल हो सकती है , जो कुछ रास्तों में बकवास की ओर जाता है जो स्थिरांक के उन विकल्पों के लिए कभी नहीं पहुंचते हैं।

निष्पादन के पथ जो संकलन-समय-दृश्यमान UB का कारण बनते हैं, उन्हें कभी भी ग्रहण नहीं किया जा सकता है, उदाहरण के लिए x86 के लिए संकलक ud2परिभाषा के रूप में (अवैध अनुदेश अपवाद का कारण) का उत्सर्जन कर सकता है cause_UB()। या एक फ़ंक्शन के भीतर, यदि यूबी if()को साबित करने के लिए लीड की एक तरफ , शाखा को हटाया जा सकता है।

लेकिन कंपाइलर को अभी भी बाकी सभी चीज़ों को सही और सही तरीके से संकलित करना है । सभी पथ जो एनकाउंटर नहीं करते हैं (या मुठभेड़ के लिए सिद्ध नहीं किए जा सकते हैं) यूबी को अभी भी एएसएम के लिए संकलित किया जाना चाहिए, जैसे कि सी ++ एब्सट्रैक्ट मशीन इसे चला रही थी।


आप तर्क दे सकते हैं कि बिना शर्त संकलन-समय-दृश्यमान UB mainइस नियम का अपवाद है। या अन्यथा संकलन-समय-सिद्ध है कि निष्पादन शुरू होने की mainगारंटी यूबी तक पहुंचती है।

मैं अभी भी तर्क देता हूं कि कानूनी संकलक व्यवहार में एक ग्रेनेड का उत्पादन शामिल है जो अगर चलता है तो विस्फोट होता है । या अधिक प्रशंसनीय, इसकी एक परिभाषा mainमें एक एकल अवैध निर्देश शामिल हैं। मेरा तर्क है कि यदि आप कभी भी कार्यक्रम नहीं चलाते हैं, तो अभी तक कोई यूबी नहीं हुआ है। संकलक को स्वयं विस्फोट करने की अनुमति नहीं है, IMO।


शाखाओं के अंदर संभव या सिद्ध यूबी युक्त कार्य

यूबी निष्पादन के किसी भी मार्ग पर पिछले सभी कोड को "दूषित" करने के लिए समय पर पीछे की ओर पहुंचता है। लेकिन व्यवहार में संकलक केवल उस नियम का लाभ उठा सकते हैं जब वे वास्तव में यह साबित कर सकते हैं कि निष्पादन के मार्ग संकलित समय-दृश्यमान यूबी के लिए नेतृत्व करते हैं। जैसे

int minefield(int x) {
    if (x == 3) {
        *(char*)nullptr = x/0;
    }

    return x * 5;
}

कंपाइलर को एएसएम बनाना पड़ता है जो x3 से अधिक सभी के लिए काम करता है, उन बिंदुओं तक जहां x * 5इंट्रूमिन और INT_MAX पर हस्ताक्षरित ओवरफ्लो यूबी का कारण बनता है। यदि इस फ़ंक्शन को कभी नहीं बुलाया जाता है x==3, तो बेशक कार्यक्रम में कोई यूबी नहीं है और लिखित रूप में काम करना चाहिए।

हम if(x == 3) __builtin_unreachable();जीएनयू सी में संकलक को बताने के लिए लिख सकते हैं जो xनिश्चित रूप से 3 नहीं है।

व्यवहार में सामान्य कार्यक्रमों में सभी जगह "माइनफील्ड" कोड होता है। जैसे किसी पूर्णांक द्वारा कोई भी विभाजन कंपाइलर का वादा करता है कि यह गैर-शून्य है। कोई भी सूचक deref कंपाइलर से वादा करता है कि वह गैर-पूर्ण है।


3

यहाँ "कानूनी" का क्या अर्थ है? कुछ भी जो C मानक या C ++ मानक के विपरीत नहीं है, इन मानकों के अनुसार कानूनी है। यदि आप किसी कथन को निष्पादित करते हैं i = i++;और परिणामस्वरूप डायनासोर दुनिया भर में ले जाते हैं, तो यह मानकों का खंडन नहीं करता है। हालाँकि यह भौतिकी के नियमों का खंडन करता है, इसलिए ऐसा होने वाला नहीं है :-)

यदि अपरिभाषित व्यवहार आपके कंपाइलर को क्रैश कर देता है, तो यह C या C ++ मानक का उल्लंघन नहीं करता है। हालांकि इसका मतलब यह है कि संकलक की गुणवत्ता (और शायद होनी चाहिए) में सुधार किया जा सकता है।

सी मानक के पिछले संस्करणों में, ऐसे कथन थे जो त्रुटियां थीं या अपरिभाषित व्यवहार पर निर्भर नहीं थीं:

char* p = 1 / 0;

एक निरंतर 0 को एक char * को असाइन करने की अनुमति है। एक गैर-शून्य स्थिरांक की अनुमति नहीं है। चूंकि 1/0 का मान अपरिभाषित व्यवहार है, इसलिए यह अपरिभाषित व्यवहार है कि संकलक को इस कथन को स्वीकार करना चाहिए या नहीं। (आजकल, 1/0 "पूर्णांक स्थिर अभिव्यक्ति" की परिभाषा को पूरा नहीं करता है)।


3
सटीक होना: दुनिया भर के डायनासोर भौतिकी के किसी भी नियम का विरोध नहीं करते (जैसे जुरासिक पार्क भिन्नता)। यह सिर्फ अत्यधिक संभावना नहीं है। :)
अजीब
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.