2009-05-22 19 views
23

मैं विंडोज़ को कुछ कोड पोर्ट कर रहा हूं, और माइक्रोसॉफ्ट कंपाइलर (विजुअल सी ++ 8) मुझे बता रहा है कि strerror() असुरक्षित है।मैं स्ट्रेरर का उपयोग क्यों नहीं कर सकता?

माइक्रोसॉफ्ट से सभी सुरक्षित स्ट्रिंग सामानों में परेशान कारक को अलग करना, मैं वास्तव में देख सकता हूं कि कुछ बहिष्कृत कार्य खतरनाक हैं। लेकिन मैं समझ नहीं पा रहा हूं कि strerror() के साथ क्या गलत हो सकता है। यह एक कोड (int) लेता है, और उस कोड को ज्ञात नहीं होने पर संबंधित स्ट्रिंग या खाली स्ट्रिंग देता है।

खतरे कहां है?

क्या सी में कोई अच्छा विकल्प है?

क्या सी ++ में कोई अच्छा विकल्प है?

[संपादित करें]

कुछ अच्छा जवाब था करने के बाद, और अब समझ कुछ कार्यान्वयन पर्याप्त पागल वास्तव में एक आम साझा बफर करने के लिए लिखने के लिए हो सकता है कि - एक एकल धागा भीतर reentrancy के लिए असुरक्षित, कभी नहीं धागे के बीच मन! - मेरा सवाल बंद हो रहा है "मैं इसका उपयोग क्यों नहीं कर सकता, और विकल्प क्या हैं?" "क्या सी और/या सी ++ में कोई सभ्य, संक्षिप्त विकल्प हैं?"

अग्रिम

+28

आप इसका उपयोग नहीं कर सकते क्योंकि माइक्रोसॉफ्ट का कहना है कि "आईएसओ सी मानक शापित हो जाएगा - हम आपको इसका उपयोग नहीं करने देंगे, जब तक कि आप चेतावनी के साथ चेतावनी या त्रुटि को ओवरराइड न करें"। उन्होंने memcpy() पर भी प्रतिबंध लगा दिया है - यह हास्यास्पद है क्योंकि आप इसे बताते हैं कि कितने बाइट कॉपी करने के लिए हैं और यदि आप इसके बारे में नहीं सोच सकते हैं और जानते हैं कि memcpy() को कॉल करने से पहले बाइट्स की संख्या के लिए लक्ष्य स्थान में पर्याप्त जगह है , आप सी या सी ++ में एक टीम लेखन कोड में शामिल नहीं हैं। –

उत्तर

18

strerror धन्यवाद पदावनत क्योंकि यह धागा सुरक्षित नहीं है है। strerror एक आंतरिक स्थिर बफर पर काम करता है, जिसे अन्य, समवर्ती धागे द्वारा ओवरराइट किया जा सकता है। आपको strerror_s नामक एक सुरक्षित संस्करण का उपयोग करना चाहिए।

सुरक्षित संस्करण के लिए आवश्यक है कि बफर आकार को फ़ंक्शन में पास किया जाए ताकि यह सत्यापित किया जा सके कि बफर इसे लिखने से पहले काफी बड़ा है, जिससे बफर ओवररन्स से बचने में मदद मिलती है जो दुर्भावनापूर्ण कोड को निष्पादित करने की अनुमति दे सकती है।

+3

एर, सवाल यह था: _Why_ इसे असुरक्षित माना जाता है? आपने मुझे कुछ भी ठीक नहीं बताया है। – JamieH

+10

वह _did_ आपको बताता है क्यों। यह एक आंतरिक स्थिर बफर पर काम करता है - यानी, जो थ्रेड में साझा किया जाता है। यह असुरक्षित है। – nsayer

+1

आह, हाँ। मैं क्षमाप्रार्थी हूं। अब, अगला प्रश्न - दुनिया में बड़े पैमाने पर - है: क्यों बिल्ली में कोई भी इसे इस तरह कार्यान्वित करेगा !? – JamieH

5

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

इस कल्पना कीजिए:

Thread #1: 
char * error = strerror(1); 
            Thread #2 
            char * error = strerror(2); 
printf(error); 

strerror() के कार्यान्वयन पर निर्भर करता है, इस कोड को त्रुटि कोड 2 के लिए बाहर त्रुटि कोड प्रिंट, त्रुटि कोड के लिए नहीं 1.

+6

से पहले वापस चला जाता है यह किस तंत्र से बदल जाएगा? संभवतः तारों को परिभाषित किया गया है - स्थिर रूप से - सी रनटाइम लाइब्रेरी के कार्यान्वयन के आंतरिक में, और इस प्रकार कभी नहीं बदलेगा? या यह गलत है, और वे वास्तव में गतिशील आधार पर बदल सकते हैं? – JamieH

+1

आप इसे क्यों मानेंगे? हां, यह इस तरह किया जा सकता है, लेकिन यह पूरी तरह से किया जा सकता है - हम नहीं जानते;) .. – beef2k

+3

मुख्य समस्या यह है कि पॉज़िक्स को थ्रेड-सुरक्षित होने के लिए स्ट्रेरर() की आवश्यकता नहीं होती है। आपको इसके बजाय strerror_r() का उपयोग करना होगा। विंडोज़ पर strerror_s() का उपयोग करें। –

1

हालांकि मैं माइक्रोसॉफ्ट के कारणों को पता नहीं है , मुझे लगता है कि स्ट्रेरर एक गैर-कॉन्स char * देता है, जिसका अर्थ है कि कुछ जोखिम है कि कुछ मैरी प्रैंकस्टर ने आपके द्वारा किए गए संदेश को संशोधित करने और संशोधित करने से पहले कहा है।

+2

यदि यह "कॉन्स char *" है, तो वही मैरी प्रैंकस्टर (char *) को डालने का प्रयास कर सकता है और इसके बाद कोड बदल सकता है;) .. "कॉन्स्ट" के रूप में कुछ घोषित करने का मतलब यह नहीं है कि इसे बदला नहीं जा सकता है। यह सिर्फ संकलक को एक अनुकूलन संकेत देता है। – beef2k

+3

मैं मानता हूं कि, एक शुद्ध कॉन्स्टेस परिप्रेक्ष्य से, यह सच है, लेकिन मुझे दृढ़ता से संदेह है कि कॉन्स की कमी सिर्फ ऐतिहासिक है, और उपयोगकर्ताओं को स्ट्रिंग की सामग्री को उसी तरह से बदलने के लिए बाध्य नहीं किया जाता है जैसा वे हैं मानक लाइब्रेरी के ऐसे कई गैर-आधार-लेकिन-होना चाहिए। _If_ यह मामला है, फिर भी मैं स्ट्रेरर() के बहिष्कार के लिए कोई कारण नहीं देख सकता। – JamieH

+1

इसे कॉन्स चार बनाते हुए * कह रहा है कि चीज़ को बदला नहीं जाना चाहिए, इसे थ्रेड सुरक्षित बनाने के लिए पर्याप्त होगा * यदि * आप सुनिश्चित करते हैं कि आपने कॉल को सिंक्रनाइज़ किया है ताकि इसका उपयोग अंतःस्थापित न हो। किसी भी मामले में, आप प्रोग्रामर को अपने प्रोग्राम को क्रैश करने के लिए मना नहीं कर सकते हैं। वह अभी भी एक सरणी बना सकता है और सरणी सीमाओं से बाहर लिख सकता है। और टर्मिनेट या कुछ का उपयोग कर अन्य धागे को मार सकता है :) वापसी को एक कॉन्स char * बनाना आकस्मिक परिवर्तनों के खिलाफ एक प्रभावी सुरक्षा है। –

16

strerror स्वयं ही असुरक्षित नहीं है। थ्रेडिंग से पहले पुराने दिनों में यह एक समस्या नहीं थी। धागे के साथ, दो या दो से अधिक धागे strerror को एक अपरिभाषित स्थिति में लौटा बफर छोड़कर कॉल कर सकते हैं। सिंगल थ्रेडेड प्रोग्राम्स के लिए इसे strerror का उपयोग करने में कोई दिक्कत नहीं होनी चाहिए जब तक कि वे libc में कुछ अजीब गेम नहीं खेल रहे हों, जैसे कि डीएलएल में सभी ऐप्स के लिए सामान्य मेमोरी।

यह एक ही कार्यक्षमता के लिए एक नया इंटरफेस नहीं है पता करने के लिए:

int strerror_r(int errnum, char *buf, size_t buflen); 

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

नोट: उपरोक्त प्रोटोटाइप एक्सएसआई स्पेक है। यह प्रति मंच या कंपाइलर विकल्पों या #define प्रतीकों के साथ भिन्न हो सकता है। जीएनयू, उदाहरण के लिए, कि बनाता है या अपने स्वयं के संस्करण उपलब्ध आधार पर एक #define

+1

और TR24731 भी है जो strerror_s() को परिभाषित करता है। –

+0

"थ्रेडिंग से पहले पुराने दिनों में बस एक समस्या नहीं थी"। यह फिर से प्रवेश नहीं कर रहा है, लेकिन फिर कभी सिग्नल हैंडलर से कॉल करने योग्य होने का दावा नहीं किया गया। –

+0

कोई समस्या नहीं, सिग्नल हैंडलर से इसे 'longjmp';) –

2

पर एक संक्षिप्त आवरण के लिए, आप के रूप में उपयोग कर सकते हैं STLSoft के stlsoft::error_desc,:

std::string errstr = stlsoft::error_desc(errno); 

कोड को देखते हुए, ऐसा लगता है कि यह strerror() के संदर्भ में लागू किया गया है, जिसका अर्थ है कि यह थ्रेड के भीतर पुनर्वितरण के लिए सुरक्षित होगा (यानी यदि किसी दिए गए कथन के भीतर कई बार उपयोग किया जाता है), लेकिन यह मल्टीथ्रेडिंग समस्या को संबोधित नहीं करता है।

वे दोषों के लिए बहुत तेजी से रिलीज चक्र संचालित करने लगते हैं, तो आप एक mod ​​का अनुरोध करने का प्रयास कर सकते हैं?

+0

धन्यवाद। मैं कोशिश कर सकता हूँ। – JamieH

12

कुछ अच्छा जवाब था करने के बाद, और अब समझ कुछ कार्यान्वयन पर्याप्त पागल वास्तव में एक आम साझा बफर करने के लिए लिखने के लिए हो सकता है कि - एक एकल धागा भीतर reentrancy के लिए असुरक्षित, धागे के बीच कोई बात नहीं! - मेरा सवाल बंद हो रहा है "मैं इसका उपयोग क्यों नहीं कर सकता, और विकल्प क्या हैं?" "क्या सी और/या सी ++ में कोई सभ्य, संक्षिप्त विकल्प हैं?"

Posix strerror_r() निर्दिष्ट करता है, और Windows पर आप strerror_s() है, जो थोड़ा अलग है, लेकिन एक ही लक्ष्य है उपयोग कर सकते हैं। मैं यह कर:

#define BAS_PERROR(msg, err_code)\ 
    bas_perror(msg, err_code, __FILE__, __LINE__) 

void bas_perror (const char* msg, int err_code, const char* filename, 
       unsigned long line_number); 


void 
bas_perror (const char* usr_msg, int err_code, const char* filename, 
      unsigned long line_number) 
{ 
    char sys_msg[64]; 

#ifdef _WIN32 
    if (strerror_s(sys_msg, sizeof sys_msg, err_code) != 0) 
    { 
    strncpy(sys_msg, "Unknown error", taille); 
    sys_msg[sizeof sys_msg - 1] = '\0'; 
    } 
#else 
    if (strerror_r(err_code, sys_msg, sizeof sys_msg) != 0) 
    { 
    strncpy(sys_msg, "Unknown error", sizeof sys_msg); 
    sys_msg[sizeof sys_msg - 1] = '\0'; 
    } 
#endif 

    fprintf(stderr, "%s: %s (debug information: file %s, at line %lu)\n", 
      usr_msg, sys_msg, filename, line_number); 
} 

मैं इस समारोह में लिखा था क्योंकि Posix धागे कार्यों errno को संशोधित नहीं करते हैं, वे एक त्रुटि कोड के बजाय वापस जाएँ। तो यह फ़ंक्शन मूल रूप से perror() जैसा ही है, सिवाय इसके कि यह आपको errno के अलावा एक त्रुटि कोड प्रदान करने की अनुमति देता है, और कुछ डीबगिंग जानकारी भी प्रदर्शित करता है। आप इसे अपनी जरूरत के अनुसार अनुकूलित कर सकते हैं।

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