2017-06-22 34 views
5

Code like this अपरिभाषित व्यवहार है, क्योंकि यह एक स्थानीय चर का उपयोग करता है जो अब दायरे में नहीं है (जिसका जीवनकाल समाप्त हो गया है)।आउट-ऑफ-स्कोप वैरिएबल तक पहुंच का पता लगाना

int main() { 
    int *a; 
    { 
     int b = 42; 
     a = &b; 
    } 
    printf("%d", *a); // UB! 
    return 0; 
} 

मेरा प्रश्न: स्वचालित रूप से इस तरह कीड़े का पता लगाने के लिए वहाँ अच्छा तकनीक हैं? ऐसा प्रतीत होता है कि यह पता लगाने योग्य होना चाहिए (चरम स्थान के निशान को अनुपयोगी के रूप में चिह्नित करना चाहिए, जब चर के दायरे से बाहर निकलते हैं, फिर शिकायत करें कि उस स्थान को एक्सेस किया गया है), लेकिन वालग्रिंड 3.10, क्लैंग 4 का पता सेंनिटर और अपरिभाषित व्यवहारकर्ता, और जीसीसी 6 का पतासाधिकारी और अपरिभाषित व्यवहारकर्ता सभी डॉन शिकायत नहीं

+0

मैं शर्त लगाता हूं कि शुद्धता करता है, लेकिन मुझे नहीं पता कि यह अब और कितना है कि वालग्रिंड और अन्य कम-मुक्त-मुक्त उपकरण बढ़ गए हैं। –

+0

सी और सी ++ एक ही भाषा नहीं हैं।वे इन दिनों बहुत अलग हैं। शायद गुजरता – tambre

+2

@ FrançoisAndrieux * प्रश्न उपकरण के लिए पूछ स्टैक ओवरफ़्लो पर विषय * लेकिन एक सरल rephrase "** कैसे ** मैं बाहर के दायरे चर का उपयोग कर सकते पता लगा सकते हैं" के लिए रवाना स्पष्ट रूप से कर रहे हैं। मुझे नहीं लगता कि प्रश्नकर्ता एक noob ... –

उत्तर

4

ऐसे वेलग्रिंड के रूप में विशेष संकलक समर्थन, गैर दखल स्मृति डिबगर के बिना फ्रेम कि क्षेत्र से बाहर चले गए हैं ढेर करने के लिए उपयोग का पता लगा सकते में , लेकिन कार्यों के भीतर scopes नहीं है। ऐसा इसलिए है क्योंकि संकलक (आमतौर पर) allocate all the memory for a stack frame in a single pass *। इसलिए एक ही फ़ंक्शन के भीतर स्कोप चर के बाहर पहुंच का पता लगाने के लिए, हमें "जहर" चर के लिए विशिष्ट कंपाइलर उपकरण की आवश्यकता होती है जो दायरे से बाहर हो गया है लेकिन जिसका संलग्न फ्रेम अभी भी मान्य है।

तकनीक ubsan AddressSanitizer, बजना और जीसीसी के हाल के संस्करणों में उपलब्ध द्वारा इस्तेमाल किया, replace stack access with access to specially allocated memory के लिए है:

आदेश में ढेर स्मृति के लिए लागू करने के लिए संगरोध हम ढेर करने के लिए ढेर को बढ़ावा देने की जरूरत है। [...] __asan_stack_malloc(real_stack, frame_size) थ्रेड-स्थानीय ढेर जैसी संरचना (नकली ढेर) से नकली फ्रेम (फ्रेम_साइज बाइट्स) आवंटित करता है। प्रत्येक नकली फ्रेम असुविधाजनक आता है और फिर रेडज़ोन को वाद्य यंत्र कोड में जहर दिया जाता है। __asan_stack_free(fake_stack, real_stack, frame_size) पूरे नकली फ्रेम को जहर देता है और इसे हटा देता है। उपयोग और उत्पादन के

उदाहरण:

$ g++ -std=c++11 a.cpp -fsanitize=address && env ASAN_OPTIONS='detect_stack_use_after_return=1' ./a.out 
ERROR: AddressSanitizer: stack-use-after-scope on address 0x7fd0e8300020 at pc 0x000000400c1b bp 0x7fff5b45ecf0 sp 0x7fff5b45ece8 
READ of size 4 at 0x7fd0e8300020 thread T0 
    #0 0x400c1a in main (a.out+0x400c1a) 
    #1 0x7fd0ebe18d5c in __libc_start_main (/lib64/libc.so.6+0x1ed5c) 
    #2 0x400a48 (a.out+0x400a48) 

Address 0x7fd0e8300020 is located in stack of thread T0 at offset 32 in frame 
    #0 0x400b26 in main (a.out+0x400b26) 

    This frame has 1 object(s): 
    [32, 36) 'b' <== Memory access at offset 32 is inside this variable 

ध्यान दें कि क्योंकि यह महंगा है यह संकलन समय (-fsanitize=address) और रन टाइम पर (ASAN_OPTIONS='detect_stack_use_after_return=1') में दोनों अनुरोध किया जाना चाहिए। न्यूनतम संस्करणों के बारे में; यह जीसीसी 7.1.0 और क्लैंग ट्रंक के साथ काम करता है, लेकिन स्पष्ट रूप से क्लैंग के किसी भी रिलीज़ संस्करण नहीं है, इसलिए यदि आप एक रिलीज कंपाइलर का उपयोग करना चाहते हैं तो आपको जीसीसी का उपयोग करना होगा।


* पर विचार करें कि इन दोनों कार्यों (-O0 पर जीसीसी द्वारा जैसे) संकलन समान मशीन कोड के लिए, इसलिए वहाँ कोई रास्ता नहीं है ** एक गैर दखल स्मृति डिबगर के लिए उन दोनों के बीच अंतर बताने में:

int f() { 
    int* a; 
    { 
     int b = 42; 
     a = &b; 
    } 
    return *a; 
} 

int g() { 
    int* a; 
    int b = 42; 
    a = &b; 
    return *a; 
} 

** कड़ाई से बोलते हुए, यदि डीबग प्रतीकों उपलब्ध हैं तो डीबगर उपलब्ध चर और बाहर जाने वाले चर ट्रैक कर सकता है। लेकिन आम तौर पर यदि आपके पास डीबग प्रतीकों उपलब्ध हैं तो आपके पास स्रोत कोड है, इसलिए प्रोग्राम को उपकरण के साथ पुन: संकलित कर सकते हैं।

+0

'VALGRIND_MAKE_MEM_NOACCESS' हो सकता है उपकरण 'memcheck' के साथ' valgrind' में अमान्य स्मृति को स्पष्ट रूप से चिह्नित करने के लिए प्रयोग किया जाता है। बेशक, एक स्वचालित समाधान सामान्य रूप से बेहतर है। –

+0

बहुत उपयोगी। धन्यवाद। मैंने कुछ और परीक्षण किया, और ऐसा लगता है कि जीसीसी 7.1 और क्लैंग 5.x दोनों के विकास के निर्माण स्थानीय पहचान के बाद स्टैक उपयोग का पता लगा सकते हैं (लेकिन फंक्शन छोड़ने के बाद जरूरी नहीं है) 'detect_stack_use_after_return = 1' सेट किए बिना। इसमें http://apt.llvm.org/ से क्लैंग 5.x का आधिकारिक विकास निर्माण शामिल है, इसलिए मुझे कम से कम जांचना और इसे स्वयं बनाना नहीं है। –

4

हां। लिंट इस के लिए डिज़ाइन किया गया है। हम इसे एम्बेडेड सिस्टम और ऑटोमोटिव सिस्टम में बहुत उपयोग करते हैं। You can use the online demo to test out how well it would work for you. अपने विशिष्ट मामला है, अपने शासन MISRA:2012:18.6.

नमूना रन


FlexeLint for C/C++ (Unix) Vers. 9.00L, Copyright Gimpel Software 1985-2014 
--- Module: misra3.c (C) 
     _ 
    1 int main() { 
misra3.c 1 Note 970: Use of modifier or type 'int' outside of a typedef [MISRA 2012 Directive 4.6, advisory] 
misra3.c 1 Note 9075: external symbol 'main(void)' defined without a prior declaration [MISRA 2012 Rule 8.4, required] 
      _ 
    2  int *a; 
misra3.c 2 Note 970: Use of modifier or type 'int' outside of a typedef [MISRA 2012 Directive 4.6, advisory] 
    3  { 
       _ 
    4   int b = 42; 
misra3.c 4 Note 970: Use of modifier or type 'int' outside of a typedef [MISRA 2012 Directive 4.6, advisory] 
         _ 
    5   a = &b; 
misra3.c 5 Info 733: Assigning address of auto variable 'b' to outer scope symbol 'a' [MISRA 2012 Rule 18.6, required] 
    6  } 
      _ 
    7  printf("%d", *a); // UB! 
misra3.c 7 Info 718: Symbol 'printf' undeclared, assumed to return int [MISRA 2012 Rule 17.3, mandatory] 
misra3.c 7 Warning 586: function 'printf' is deprecated. [MISRA 2012 Rule 21.6, required] 
misra3.c 7 Info 746: call to function 'printf()' not made in the presence of a prototype 
    8  return 0; 
        _ 
    9 } 

misra3.c 9 Info 783: Line does not end with new-line 
misra3.c 9 Note 954: Pointer variable 'a' (line 2) could be declared as pointing to const [MISRA 2012 Rule 8.13, advisory] 

/// Start of Pass 2 /// 

--- Module: misra3.c (C) 
    1 int main() { 
    2  int *a; 
    3  { 
    4   int b = 42; 
    5   a = &b; 
    6  } 
    7  printf("%d", *a); // UB! 
    8  return 0; 
    9 } 

--- Global Wrap-up 

Warning 526: Symbol 'printf()' (line 7, file misra3.c) not defined 
Warning 628: no argument information provided for function 'printf()' (line 7, file misra3.c) 
+0

यह इतना कचरा पैदा करता है ताकि संकेत के मूल्य संदिग्ध है – Slava

+0

@Slava आमतौर पर हम "के नाम से सुरक्षित के लिए उपयोग" है ओवरराइड कि कुछ नियमों को अक्षम, SNR व्यवहार्य रखते हुए। इसके अलावा, अगर हम उत्पादन में कुछ भी चाहते हैं, तो मिशन महत्वपूर्ण आरटीओएस सिस्टम, हमारे पास आम तौर पर एमआईएसआरए जैसे मानक का पालन करने के लिए कोड के लिए बीमा/कानूनी आवश्यकताएं होती हैं। – DevNull

+1

धन्यवाद भगवान मैं की चोटी पर इस नौकरशाही बकवास क्या हम पहले से ही – Slava

संबंधित मुद्दे