सी में बफर ओवरफ्लो के कारण स्कैनफ को कैसे रोका जाए?


83

मैं इस कोड का उपयोग करता हूं:

संभावित बफर ओवरफ्लो को रोकने के लिए सबसे अच्छा तरीका क्या होगा ताकि इसे यादृच्छिक लंबाई के तारों को पारित किया जा सके?

मुझे पता है कि मैं उदाहरण के लिए कॉल करके इनपुट स्ट्रिंग को सीमित कर सकता हूं:

लेकिन मैं उपयोगकर्ता इनपुटों को संसाधित करने में सक्षम होना पसंद करूंगा। या यह स्कैनफ का उपयोग करके सुरक्षित रूप से नहीं किया जा सकता है और मुझे फ़िज़ का उपयोग करना चाहिए?

जवाबों:


66

अपनी पुस्तक द प्रैक्टिस ऑफ प्रोग्रामिंग (जो पढ़ने में अच्छी तरह से लायक है) में, कार्निगन और पाइक इस समस्या पर चर्चा करते हैं, और वे इसे फ़ंक्शन snprintf()के scanf()परिवार को पारित करने के लिए सही बफर आकार के साथ स्ट्रिंग बनाने के लिए उपयोग करके हल करते हैं। प्रभाव में:

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


ध्यान दें कि scanf()फ़ंक्शन के परिवार का POSIX 2008 (2013) संस्करण mस्ट्रिंग इनपुट के लिए एक प्रारूप संशोधक (एक असाइनमेंट-आवंटन चरित्र) का समर्थन करता है (%s , %c, %[)। एक char *तर्क लेने के बजाय , यह एक char **तर्क लेता है , और इसे पढ़े जाने वाले मूल्य के लिए आवश्यक स्थान आवंटित करता है:

यदि sscanf()फ़ंक्शन सभी रूपांतरण विशिष्टताओं को पूरा करने में विफल रहता है, तो सभी मेमोरी जो इसे समान %msरूपांतरणों के लिए आवंटित की जाती है, फ़ंक्शन वापस आने से पहले मुक्त हो जाती है।


@ नम: हाँ, यह होना चाहिए buflen-1- धन्यवाद। फिर आपको अहस्ताक्षरित अंडरफ़्लो (बल्कि बड़ी संख्या में लपेटना) के बारे में चिंता करना होगा, इसलिए ifपरीक्षण। मैं व्यथित रूप से इसे बदलने की कोशिश कर रहा हूँ assert(), अगर किसी के साथ यह assert()पहले हो ifकि विकास के दौरान आग लग जाए, तो कोई भी लापरवाह हो सकता है। मैंने ध्यान से दस्तावेज़ीकरण की समीक्षा नहीं की है कि %0sइसका क्या मतलब है sscanf()- परीक्षण बेहतर हो सकता है if (buflen < 2)
जोनाथन लेफ्लर

तो snprintfएक स्ट्रिंग बफर के लिए कुछ डेटा लिखता है, और sscanfउस बनाए गए स्ट्रिंग से पढ़ता है। वास्तव में यह कहां से आता scanfहै कि यह स्टड से पढ़ता है?
krb686

यह भी काफी भ्रामक है कि आप अपने परिणाम स्ट्रिंग के लिए "प्रारूप" शब्द का उपयोग करते हैं और इस प्रकार "प्रारूप" में पहले तर्क के रूप में पास करते हैं, snprintfफिर भी यह वास्तविक प्रारूप पैरामीटर नहीं है।
krb686

@ krb686: यह कोड इसलिए लिखा गया है ताकि स्कैन किया जाने वाला डेटा पैरामीटर में हो dataऔर इसलिए sscanf()उपयुक्त है। यदि आप इसके बजाय मानक इनपुट से पढ़ना चाहते हैं, तो dataपैरामीटर ड्रॉप करें और scanf()इसके बजाय कॉल करें । formatचर के लिए नाम के विकल्प के रूप में जो कॉल में प्रारूप स्ट्रिंग बन जाता है sscanf(), आप इसका नाम बदलने के हकदार हैं यदि आप चाहें, लेकिन इसका नाम गलत नहीं है। मुझे यकीन नहीं है कि क्या विकल्प समझ में आता है; होगा in_formatयह किसी भी स्पष्ट करने? मैं इसे इस कोड में बदलने की योजना नहीं बना रहा हूं; यदि आप इस विचार का उपयोग अपने कोड में करते हैं तो आप कर सकते हैं।
जोनाथन लेफ़लर

1
@ मब्राहम: यह अभी भी macOS Sierra 10.12.5 (2017-06-06 तक) के तहत सही है - macOS scanf()को समर्थन के रूप में प्रलेखित नहीं किया गया है %ms, हालांकि यह उपयोगी होगा।
जोनाथन लेफ़लर

31

यदि आप gcc का उपयोग कर रहे हैं, तो आप aस्कैन करने के लिए GNU- एक्सटेंशन स्पेसियर का उपयोग कर सकते हैं जिसमें स्कैनफ () आवंटित करें:

संपादित करें: जैसा कि जोनाथन ने बताया, आपको scanfमैन पेजों से परामर्श करना चाहिए क्योंकि विनिर्देश भिन्न हो सकता है ( %m) और आपको संकलन करते समय कुछ परिभाषित करने में सक्षम करने की आवश्यकता हो सकती है।


8
यह GNU C कंपाइलर का उपयोग करने की तुलना में glibc (GNU C लाइब्रेरी) का उपयोग करने का एक मुद्दा है।
जोनाथन लेफ़लर

3
और ध्यान दें कि POSIX 2008 मानक mसमान काम करने के लिए संशोधक प्रदान करता है । देखते हैं scanf()। आपको यह जांचने की आवश्यकता होगी कि आपके द्वारा उपयोग किए जाने वाले सिस्टम इस संशोधक का समर्थन करते हैं या नहीं।
जोनाथन लेफ़लर

4
GNU (जैसा कि किसी भी दर पर Ubuntu 13.10 पर पाया जाता है) समर्थन करता है %ms। संकेतन (आउटपुट पर, यह हेक्साडेसिमल फ्लोटिंग पॉइंट डेटा का अनुरोध करता है) %aका एक पर्याय है %f। जीएनयू मैन पेज scanf()कहता है: _ यह उपलब्ध नहीं है यदि प्रोग्राम के साथ संकलित किया गया है gcc -std=c99या gcc -D_ISOC99_SOURCE (जब तक कि _GNU_SOURCEयह भी निर्दिष्ट नहीं किया गया है), उस स्थिति में aइसे फ़्लोटिंग-पॉइंट नंबर्स (ऊपर देखें) के लिए एक विनिर्देशक के रूप में व्याख्या की जाती है ।_
जोनाथन लेफ़लर

8

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

उपरोक्त इनपुट स्ट्रीम तक को छोड़ देता है, लेकिन न्यूलाइन ( \n) वर्ण को शामिल नहीं करता है। इसका getchar()सेवन करने के लिए आपको जोड़ना होगा । यह भी जांचें कि क्या आप अंत-धारा तक पहुंच गए हैं:

और इसके बारे में है।


2
क्या आप feofएक बड़े संदर्भ में कोड डाल सकते हैं ? मैं पूछ रहा हूं कि उस फ़ंक्शन का अक्सर गलत उपयोग किया जाता है।
रोलैंड इलिग

1
arrayजरूरत हैchar array[LENGTH+1];
jxh

4

सीधे इस्तेमाल कर रहा है scanf(3) और इसके वेरिएंट में कई समस्याएं हैं। आमतौर पर, उपयोगकर्ताओं और गैर-संवादात्मक उपयोग मामलों को इनपुट की लाइनों के संदर्भ में परिभाषित किया जाता है। ऐसा मामला देखना दुर्लभ है, जहाँ यदि पर्याप्त वस्तुएँ नहीं मिली हैं, तो अधिक लाइनें समस्या का समाधान करेंगी, फिर भी यह स्कैनफ़ के लिए डिफ़ॉल्ट मोड है। (यदि कोई उपयोगकर्ता पहली पंक्ति में एक संख्या दर्ज करना नहीं जानता था, तो दूसरी और तीसरी पंक्ति शायद मदद नहीं करेगी।)

कम से कम यदि आप fgets(3)जानते हैं कि आपके प्रोग्राम को कितनी इनपुट लाइनों की आवश्यकता होगी, और आपके पास कोई भी बफर ओवरफ्लो नहीं होगा ...


1

इनपुट की लंबाई को सीमित करना निश्चित रूप से आसान है। आप एक लूप का उपयोग करके एक मनमाने ढंग से लंबे इनपुट को स्वीकार कर सकते हैं, एक बार में थोड़ा पढ़ सकते हैं, फिर से स्ट्रिंग के लिए आवश्यक जगह आवंटित कर सकते हैं ...

लेकिन यह बहुत काम की बात है, इसलिए अधिकांश सी प्रोग्रामर कुछ मनमाने लंबाई पर इनपुट काट देते हैं। मुझे लगता है कि आप यह पहले से ही जानते हैं, लेकिन () का उपयोग करके आप पाठ की मनमानी मात्रा को स्वीकार करने की अनुमति नहीं दे रहे हैं - आपको अभी भी एक सीमा निर्धारित करने की आवश्यकता है।


तो क्या किसी को पता है कि स्कैनफ के साथ ऐसा कैसे करना है?
गोई

3
एक लूप में फिट्स का उपयोग करने से आप पाठ की मनमानी मात्रा को स्वीकार कर सकते realloc()हैं - बस अपने बफर को निगलना जारी रखें ।
bdonlan

1

यह ऐसा काम नहीं है जो एक फ़ंक्शन बनाने के लिए है जो आपके स्ट्रिंग के लिए आवश्यक मेमोरी आवंटित कर रहा है। यह कुछ समय पहले लिखी गई एक छोटी सी क्रिया है, मैं इसे हमेशा तार में पढ़ने के लिए उपयोग करता हूं।

यह रीड स्ट्रिंग या यदि कोई मेमोरी त्रुटि NULL होती है, तो वापस कर देगा। लेकिन ध्यान रखें कि आपको अपनी स्ट्रिंग को मुक्त () करना होगा और हमेशा इसकी वापसी मूल्य की जांच करनी होगी।


sizeof (char)परिभाषा के अनुसार है 1। आपको इसकी आवश्यकता नहीं है।
रास्तजेडी

आमतौर पर एक ही स्तर पर पॉइंटर एलोकेशन / फ्रीज रखना अच्छा होता है, जिसका अर्थ है कि आपके फंक्शन को अपने आप मेमोरी को आवंटित नहीं करना चाहिए, क्योंकि कॉल करने वाले को इसे फ्रीज करना होगा। अधिकांश मानक पुस्तकालय / पॉज़िक्स फ़ंक्शंस इस सिद्धांत का पालन करते हुए या तो एक स्टैटिक स्ट्रिंग (जैसे strerror(3)) लौटाते हैं या (जैसे ( strerror_r(3)- या scanf(3)) में पास किए गए पूर्व-आवंटित स्ट्रिंग की अपेक्षा करते हैं ...
माइकल बीयर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.