2010-06-25 30 views
16

में सी और सी ++, free(my_pointer) क्रैश होने पर दो बार बुलाए जाने पर क्रैश क्यों होता है।क्यों दो बार

क्यों? आकार के साथ प्रत्येक malloc की बहीखाता है। जब पहली free कहा जाता है तो यह पहचानता है कि यह किस आकार के साथ आवंटित किया गया था, इसलिए हमें मुफ्त कॉल के साथ आकार पास करने की आवश्यकता नहीं है।

चूंकि यह हर चीज़ जानता है क्योंकि यह दूसरी बार क्यों जांचता है और कुछ भी नहीं करता है?

या तो मुझे समझ में नहीं आता malloc/free व्यवहार या free सुरक्षित रूप से लागू नहीं किया गया है।

+1

क्या आप त्रुटि कोड कहां से एक कोड स्निपेट पोस्ट कर सकते हैं? – ChadNC

+1

डाउनवोट क्यों? सुनिश्चित नहीं है कि यह एक डुप्ली है, लेकिन यह वास्तव में एक अच्छा सवाल है। –

+3

सी एक ऐसी भाषा है जो गारंटीकृत सुरक्षा पर गति और प्रोग्रामर नियंत्रण को महत्व देती है। आपको उन आदतों को विकसित करने की उम्मीद है जो आपको यादृच्छिक चीजें करने और पकड़े जाने की उम्मीद करने के बजाए सुरक्षित रखेंगे।यहां तक ​​कि यदि आप चाहते हैं कि बहीखाता करना संभव था, तो यह धीमा हो जाएगा, और मैं अन्य देवताओं को रखने के लिए गति मूल्य का भुगतान नहीं करना चाहता हूं (हम ब्रैकेट प्रकारों को ब्रेस करते हैं, यह सोचते हैं कि यह हमेशा अन्य देवताओं को सुरक्षा नेट की आवश्यकता है) सुरक्षित । –

उत्तर

27

आप आवंटित नहीं की गई स्मृति पर free कॉल करने के लिए अनुमति नहीं है, मानक कहा गया है कि काफी स्पष्ट रूप (थोड़ा दूसरे शब्दों में बयान, मेरे जोर):

free समारोह अंतरिक्ष के लिए द्वारा अपने तर्क पुनः आवंटित की जाती किए जाने की ओर इशारा का कारण बनता है, यानी, आगे आवंटन के लिए उपलब्ध कराया गया है। यदि तर्क एक शून्य सूचक है, तो कोई कार्रवाई नहीं होती है। अन्यथा, अगर तर्क किसी स्मृति प्रबंधन फ़ंक्शन द्वारा लौटाए गए पॉइंटर से मेल नहीं खाता है, या यदि किसी कॉल द्वारा खाली या पुनर्विक्रय करने के लिए स्थान को हटा दिया गया है, तो व्यवहार अपरिभाषित है।

उदाहरण के लिए, तो क्या होता है, अगर पता कर रहे हैं डबल-मुक्त एक नए ब्लॉक के बीच और कोड है कि यह आवंटित में पुनः आवंटन किया गया है सिर्फ वहाँ कुछ है कि एक असली malloc की तरह दिखाई देता स्टोर करने के लिए हुआ हेडर को अवरुद्ध करें? पसंद:

+- New pointer +- Old pointer 
v     v 
+------------------------------------+ 
|     <Dodgy bit>  | 
+------------------------------------+ 

कैओस, यही है।

मेमोरी आवंटन फ़ंक्शन एक श्रृंखला के समान उपकरण हैं और बशर्ते आप उन्हें सही तरीके से उपयोग करें, आपको कोई समस्या नहीं होनी चाहिए। आप उन्हें दुरुपयोग अगर, हालांकि, परिणाम अपनी खुद की गलती है, या तो भ्रष्ट स्मृति या बुरा, या अपनी बाहों में से एक को काटने :-)


और के बारे में टिप्पणी कर रहे हैं:

.. यह दोगुनी मुक्त स्थान के बारे में एंडुसर को गहराई से संवाद कर सकता है।

आप डबल-मुक्त एक ब्लॉक नहीं है सुनिश्चित करने के लिए malloc और free कॉल का एक रिकॉर्ड रखने के लघु, मैं इस व्यावहारिक होने के रूप में नहीं देख सकता। इसके लिए एक विशाल ओवरहेड की आवश्यकता होगी और अभी भी सभी समस्याओं को ठीक नहीं करेगा।

क्या होगा यदि:

  • धागा एक आवंटित और पता 42
  • धागा आबंटित स्मृति एक पते 42 पर स्मृति को मुक्त कर दिया और उसका उपयोग करना शुरू कर दिया।
  • धागा ए ने स्मृति को दूसरी बार मुक्त कर दिया।
  • थ्रेड सी आवंटित स्मृति को पता 42 और इसे उपयोग करना शुरू कर दिया।

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

नहीं, मुझे लगता है कि वर्तमान malloc और free ठीक है, बशर्ते आप उन्हें सही तरीके से उपयोग करें। हर तरह से अपने स्वयं के संस्करण को लागू करने के लिए कुछ विचार दें, मुझे इसके साथ कुछ भी गलत नहीं लगता है, लेकिन मुझे संदेह है कि आप कुछ सुंदर कांटेदार प्रदर्शन मुद्दों में भाग लेंगे।


आपfree चारों ओर अपने स्वयं के आवरण को लागू करना चाहते हैं हैं, तो आप विशेष रूप से नीचे myFreeXxx कॉल की तरह कुछ के साथ, यह सुरक्षित (एक छोटे से प्रदर्शन हिट की कीमत पर) बना सकते हैं:

#include <stdio.h> 
#include <stdlib.h> 

void myFreeVoid (void **p) { free (*p); *p = NULL; } 
void myFreeInt (int **p) { free (*p); *p = NULL; } 
void myFreeChar (char **p) { free (*p); *p = NULL; } 

int main (void) { 
    char *x = malloc (1000); 
    printf ("Before: %p\n", x); 
    myFreeChar (&x); 
    printf ("After: %p\n", x); 
    return 0; 
} 

कोड का नतीजा यह है कि आप अपने सूचक के सूचक के साथ myFreeXxx कॉल कर सकते हैं और यह दोनों करेंगे:

  • स्मृति मुक्त करें; और
  • पॉइंटर को न्यूल पर सेट करें।

उस बाद का मतलब है कि, यदि आप पॉइंटर को फिर से मुक्त करने का प्रयास करते हैं, तो यह कुछ भी नहीं करेगा (क्योंकि मुक्त न्यूल मानक द्वारा कवर किया जाता है)।

यह सभी स्थितियों, से बचाने नहीं अगर आप कहीं और सूचक की एक प्रतिलिपि बनाने, मूल, नकल मुक्त जैसे तो मुक्त:

char *newptr = oldptr; 
myFreeChar (&oldptr);  // frees and sets to NULL. 
myFreeChar (&newptr);  // double-free because it wasn't set to NULL. 

यदि आप ' सी 11 का उपयोग करने के लिए फिर से, प्रत्येक प्रकार के लिए एक अलग फ़ंक्शन को स्पष्ट रूप से कॉल करने से बेहतर तरीका है कि सी ने समय कार्य ओवरलोडिंग संकलित किया है। आप जबकि अभी भी प्रकार की सुरक्षा के लिए अनुमति देता है सही समारोह कॉल करने के लिए सामान्य चयन का उपयोग कर सकते हैं:

#include <stdio.h> 
#include <stdlib.h> 

void myFreeVoid (void **p) { free (*p); *p = NULL; } 
void myFreeInt (int **p) { free (*p); *p = NULL; } 
void myFreeChar (char **p) { free (*p); *p = NULL; } 
#define myFree(x) _Generic((x), \ 
    int** : myFreeInt, \ 
    char**: myFreeChar, \ 
    default: myFreeVoid )(x) 

int main (void) { 
    char *x = malloc (1000); 
    printf ("Before: %p\n", x); 
    myFree (&x); 
    printf ("After: %p\n", x); 
    return 0; 
} 
इसी के साथ

, तो आप बस myFree फोन और यह प्रकार के आधार पर सही समारोह का चयन करेंगे।

+0

यह सच है..हमें दो बार मुफ्त में कॉल करने की अनुमति नहीं है ... लेकिन मुझे मुफ्त() कार्यान्वयन में सुधार का दायरा दिखाई देता है .. दुर्घटनाग्रस्त होने की बजाय .. यह दोगुना मुक्त स्थान के बारे में अंतःस्थापित करने के लिए गहराई से संवाद कर सकता है बुककीपिंग है .... क्या मैं सही हूँ? –

+0

मुझे संदेह है कि ऑपरेशन की गारंटी के लिए अधिकांश कार्यान्वयन में निश्चित रूप से पर्याप्त बहीखाता है (निश्चित रूप से मैंने जो देखा है)। और यदि आप पर्याप्त परिचय देते हैं, तो लगभग निश्चित रूप से प्रदर्शन प्रभाव होंगे। अद्यतन देखें लेकिन बुनियादी सारांश यह है कि वर्तमान दृष्टिकोण संतुलन गति और सुरक्षा को अच्छी तरह से संतुलित करते हैं। – paxdiablo

+0

और संभावित प्रदर्शन प्रभाव के अलावा, पल कुछ ढेर ढेर डेटा संरचनाओं के साथ चल रहा है, सी रनटाइम कोड निष्पादन पथ के साथ कुछ मैलवेयर गड़बड़ देने से पहले निरस्त होना चाहिए। – ninjalj

3

कारण है कि यह) दूसरी बार की जांच नहीं करता है जब वह दूसरी मुक्त करने के लिए किसी भी आवंटित आकार नहीं मिला सकता है (

free() समारोह अपने आप में एक अतिरिक्त जांच फोन सभी सही मामलों में अपने कार्यक्रम को धीमा होगा । आपको दोहरी मुफ्त नहीं करनी चाहिए। एक प्रोग्रामर के रूप में स्मृति प्रबंधन आपकी जिम्मेदारी है; ऐसा करने में विफलता एक प्रोग्रामिंग त्रुटि है। यह सी के दर्शन का हिस्सा है: यह आपको आवश्यक सभी शक्ति देता है, लेकिन परिणामस्वरूप, पैर में खुद को शूट करना आसान बनाता है।

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

3

अच्छा सवाल। जैसा कि आप ध्यान देते हैं, मॉलोक और फ्री आमतौर पर आवंटन से पहले कुछ बाइट्स में अक्सर बहीखाता का कुछ रूप करते हैं।लेकिन इस बारे में सोचें:

  1. मॉलोक कुछ स्मृति - बहीखाता डेटा जोड़ता है।
  2. इसे मुफ्त में - स्मृति को पूल में वापस कर दिया जाता है।
  3. आप या कोई अन्य मॉलोक की कुछ और मेमोरी है, जो पुराने आवंटन के साथ शामिल हो सकती है या नहीं हो सकती है।
  4. आप पुराने पॉइंटर को फिर से मुक्त करते हैं।

ढेर (मॉलोक के लिए एक मुक्त प्रबंधन के लिए कोड) इस बिंदु पर पहले ही खो गया है और/या बहीखाता डेटा को अधिलेखित कर दिया गया है, क्योंकि स्मृति ढेर पर वापस आ गई है!

इसलिए दुर्घटनाएं। इसे प्रदान करने का एकमात्र तरीका को हर आवंटन को किसी डेटाबेस में कहीं भी याद किया जाएगा, जो असंबद्ध हो जाएगा। तो वे ऐसा नहीं करते हैं। इसके बजाए, डबल-फ्री न करें याद रखें। :)

+0

"ढेर (मॉलोक के लिए कोड एक मुफ़्त प्रबंधन) इस बिंदु पर पहले ही खो गया है और/या बहीखाता डेटा को अधिलेखित कर दिया गया है, क्योंकि स्मृति ढेर पर वापस आ गई है!" ... जैसा आपने कहा था .. जानकारी खो दी गई है ... यह निष्कर्ष निकाला जा सकता है कि यह पहले से ही मुक्त() या गलत पता है। और प्रोग्रामर को सुप्रसिद्ध रूप से संवाद करें ..लेकिन उस स्थान से मुक्त करने की कोशिश नहीं करनी चाहिए .... और क्रैश एप्लिकेशन ... क्योंकि उसने ढेर में कुछ भी भ्रष्ट नहीं किया है ... सिर्फ एक गलत सूचक भेज दिया है जिसे पहले से ही बुककीपिंग विवरण के साथ मान्य किया गया है और इसका कोई संदर्भ नहीं मिला है .. –

+1

आह! लेकिन अगर लौटाई गई स्मृति पहले से किसी और द्वारा पुन: उपयोग की जा चुकी है, तो बहीखाता क्षेत्र में * क्या होना चाहिए किसी और के डेटा द्वारा प्रतिस्थापित किया जाना चाहिए, फिर उस डेटा को मुफ्त में पढ़ता है और मानता है कि यह ढेर ट्रैकिंग को संदर्भित करता है ... वास्तव में, यह बिल्कुल बहीखाता डेटा नहीं है। तो मुक्त खरपतवार और दुर्घटनाओं में चला जाता है। –

+0

"तो मुफ्त में खरपतवार और दुर्घटनाओं में चला जाता है"। या कुछ और खराब है। एक हमलावर ने बहीखाता जानकारी तैयार की है() कुछ महत्वपूर्ण सूचक, जैसे एसईएच या अगले गतिशील फ़ंक्शन को ओवरराइट करना समाप्त होता है। – ninjalj

1

आप कहते हैं:

से समझ नहीं क्यों। आकार के साथ हर मॉलोक() की बहीखाता है।

नहीं necesarilly। मैं dlmalloc के बारे में थोड़ा सा समझाऊंगा (glibc, uClibc, ... में उपयोग किया जाता है)।

Dlmalloc मुक्त स्थान के ब्लॉक ट्रैक करता है। दो संगत मुक्त ब्लॉक नहीं हो सकते हैं, वे तुरंत विलय कर रहे हैं। आवंटित ब्लॉक बिल्कुल ट्रैक नहीं किए जाते हैं! आवंटित ब्लॉक में बहीखाता जानकारी (इस ब्लॉक का आकार, पिछले ब्लॉक का आकार और कुछ झंडे) के लिए कुछ अतिरिक्त जगह है। जब एक आवंटित ब्लॉक मुक्त होता है() 'डी, dlmalloc इसे दोगुनी-लिंक्ड सूची में डालता है।

बेशक

, यह सब बेहतर this dlmalloc article

9

आप अपने व्यवहार बदल किया जा सकता है पर समझाया गया है। यदि यह तुरंत दुर्घटनाग्रस्त हो जाता है तो सुरक्षित तरीके से कार्यान्वित किया गया है। मैं प्रमाणित कर सकता हूं कि यह मुफ्त() कई चंद्रमाओं के लिए सामान्य व्यवहार नहीं था। सामान्य सीआरटी कार्यान्वयन ने फिर कोई जांच नहीं की। तेज़ और उग्र, यह केवल ढेर की आंतरिक संरचना को भ्रष्ट कर देगा, आवंटन श्रृंखलाओं को गड़बड़ कर देगा।

किसी भी निदान के बिना, कार्यक्रम भ्रष्टाचार के बाद लंबे समय से दुर्व्यवहार या दुर्घटनाग्रस्त हो जाएगा। के बिना संकेत क्यों कि इस तरह से गलत व्यवहार किया गया, क्रैश कोड जो वास्तव में दुर्घटना के लिए ज़िम्मेदार नहीं था। एक heisenbug, समस्या निवारण के लिए बहुत मुश्किल है।

यह आधुनिक सीआरटी या ओएस हीप कार्यान्वयन के लिए अब आम नहीं है। इस तरह के अपरिभाषित व्यवहार बहुत मैलवेयर द्वारा शोषक है। और यह आपके जीवन को एक पूरी तरह से आसान बनाता है, आपको तुरंत अपने कोड में बग मिल जाएगा। इसने मुझे पिछले कुछ सालों से परेशानी से दूर रखा है, लंबे समय तक अवांछनीय ढेर भ्रष्टाचार को खत्म नहीं किया है। अच्छी बात।

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