क्यों gets()
खतरनाक है
पहला इंटरनेट वर्म ( मॉरिस इंटरनेट वर्म ) लगभग 30 साल पहले (1988-11-02) बच गया था, और इसका इस्तेमाल gets()
सिस्टम से सिस्टम के प्रचार के तरीकों में से एक के रूप में एक बफर ओवरफ्लो हुआ। मूल समस्या यह है कि फ़ंक्शन को यह पता नहीं है कि बफर कितना बड़ा है, इसलिए यह तब तक पढ़ना जारी रखता है जब तक कि यह एक नई लाइन या ईओएफ का सामना नहीं करता है, और यह दिए गए बफर की सीमा को ओवरफ्लो कर सकता है।
तुम भूल जाना चाहिए तुम कभी सुना है कि gets()
अस्तित्व में है।
C11 मानक ISO / IEC 9899: 2011 gets()
एक मानक फ़ंक्शन के रूप में समाप्त हो गया, जो कि ए थिंग थिंग ™ है (इसे औपचारिक रूप से ISO / IEC 9899 में 1999 और Cor.3 में 'अपवित्र' और 'पदावनत' के रूप में चिह्नित किया गया था) C99 के लिए 3, और फिर C11 में हटा दिया गया)। अफसोस की बात है कि यह पश्चगामी अनुकूलता के कारणों के लिए कई वर्षों तक (मतलब 'दशकों') पुस्तकालयों में बना रहेगा। यदि यह मेरे ऊपर होता, तो इसका कार्यान्वयन gets()
हो जाता:
char *gets(char *buffer)
{
assert(buffer != 0);
abort();
return 0;
}
यह देखते हुए कि आपका कोड किसी भी समय, जितनी जल्दी या बाद में दुर्घटनाग्रस्त हो जाएगा, यह बेहतर है कि मुसीबत को जल्द से जल्द दूर करें। मैं एक त्रुटि संदेश जोड़ने के लिए तैयार हूँ:
fputs("obsolete and dangerous function gets() called\n", stderr);
लिनक्स संकलन प्रणाली के आधुनिक संस्करण चेतावनी देते हैं यदि आप लिंक करते हैं gets()
- और कुछ अन्य कार्यों के लिए भी जिनमें सुरक्षा समस्याएं हैं ( mktemp()
...)।
के लिए विकल्प gets()
fgets ()
के रूप में हर किसी ने कहा, विहित विकल्प के लिए gets()
किया जाता है fgets()
निर्दिष्ट करने stdin
फ़ाइल धारा के रूप में।
char buffer[BUFSIZ];
while (fgets(buffer, sizeof(buffer), stdin) != 0)
{
...process line of data...
}
अभी तक किसी और ने जो उल्लेख किया है, gets()
उसमें न्यूलाइन शामिल नहीं है, लेकिन fgets()
है। तो, आपको fgets()
उस रैपर का उपयोग करने की आवश्यकता हो सकती है जो नई लाइन को हटाता है:
char *fgets_wrapper(char *buffer, size_t buflen, FILE *fp)
{
if (fgets(buffer, buflen, fp) != 0)
{
size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n')
buffer[len-1] = '\0';
return buffer;
}
return 0;
}
या और अच्छा:
char *fgets_wrapper(char *buffer, size_t buflen, FILE *fp)
{
if (fgets(buffer, buflen, fp) != 0)
{
buffer[strcspn(buffer, "\n")] = '\0';
return buffer;
}
return 0;
}
इसके अलावा, जैसा कि एक टिप्पणी में कैफ़े इंगित करता है और उसके जवाब में paxdiablo दिखाता है, fgets()
आपके पास एक लाइन पर डेटा बचा हो सकता है। मेरा आवरण कोड अगली बार पढ़ने के लिए उस डेटा को छोड़ देता है; यदि आप चाहें तो डेटा की बाकी लाइन को टटोलने के लिए आप इसे आसानी से संशोधित कर सकते हैं:
if (len > 0 && buffer[len-1] == '\n')
buffer[len-1] = '\0';
else
{
int ch;
while ((ch = getc(fp)) != EOF && ch != '\n')
;
}
अवशिष्ट समस्या यह है कि तीन अलग-अलग परिणाम राज्यों को कैसे रिपोर्ट किया जाए - ईओएफ या त्रुटि, लाइन रीड एंड नॉट ट्रंकेटेड, और आंशिक लाइन रीड लेकिन डेटा को छोटा किया गया।
यह समस्या gets()
इसलिए उत्पन्न नहीं होती है क्योंकि यह नहीं जानता कि आपका बफ़र कहाँ समाप्त होता है और अंत से परे बड़ी ख़ुशी है, अपने ख़ूबसूरत टेंडेड मेमोरी लेआउट पर कहर बरपाते हुए, यदि बफ़र आवंटित किया जाता है , तो अक्सर रिटर्न स्टैक ( स्टैक ओवरफ़्लो ) को गड़बड़ कर देता है। यदि बफ़र को गतिशील रूप से आवंटित किया जाता है, या बफर को स्टैटिकली आवंटित किया जाता है, तो अन्य कीमती ग्लोबल (या मॉड्यूल) वैरिएबल पर डेटा की प्रतिलिपि बनाकर, स्टैक या या नियंत्रण सूचना पर रौंद। इनमें से कोई भी एक अच्छा विचार नहीं है - वे 'अपरिभाषित व्यवहार' वाक्यांश का प्रतीक हैं।
वहाँ भी है टी.आर. 24,731-1 (सी मानक समिति से तकनीकी रिपोर्ट), जो काम करता है, सहित की एक किस्म के लिए सुरक्षित विकल्प प्रदान करता है gets()
:
§6.5.4.1 gets_s
फ़ंक्शन
सार
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
char *gets_s(char *s, rsize_t n);
क्रम-बाधाओं
s
अशक्त सूचक नहीं होगा। n
न तो शून्य के बराबर होना चाहिए और न ही RSIZE_MAX से अधिक होना चाहिए। एक नया-पंक्ति वर्ण, फ़ाइल का अंत, या पढ़ने में त्रुटि, n-1
पात्रों से पढ़ने के भीतर होगी
stdin
। 25)
3 यदि कोई रनटाइम-बाधा उल्लंघन है, s[0]
तो अशक्त वर्ण पर सेट किया गया है, और वर्णों को stdin
तब तक पढ़ा और खारिज किया जाता है जब तक कि एक नई-पंक्ति वर्ण पढ़ा नहीं जाता है, या अंत-फ़ाइल या एक पढ़ने में त्रुटि होती है।
विवरण
4 gets_s
फ़ंक्शन द्वारा n
इंगित की गई stdin
सरणी में, द्वारा निर्दिष्ट धारा से निर्दिष्ट वर्णों की संख्या से कम से कम एक पर पढ़ता है s
। कोई भी अतिरिक्त वर्ण नई-पंक्ति वर्ण (जो छूट गया है) के बाद या फ़ाइल के अंत के बाद पढ़ा जाता है। छोड़ी गई नई-पंक्ति वाला वर्ण पढ़े जाने वाले वर्णों की संख्या में नहीं गिना जाता है। अंतिम वर्ण को सरणी में पढ़ा जाने के तुरंत बाद एक अशक्त चरित्र लिखा जाता है।
5 यदि एंड- s[0]
टू -फाइल का सामना करना पड़ा है और कोई वर्ण सरणी में नहीं पढ़ा गया है, या यदि ऑपरेशन के दौरान कोई रीड एरर होती है, तो अशक्त चरित्र में सेट की जाती है, और s
अनिर्दिष्ट मान लेने के अन्य तत्व ।
अनुशंसित अभ्यास
6 fgets
फ़ंक्शन सही प्रकार से लिखे गए प्रोग्राम को परिणाम सरणी में संग्रहीत करने के लिए इनपुट लाइनों को सुरक्षित रूप से संसाधित करने की अनुमति देता है। सामान्य तौर पर इसके लिए यह आवश्यक होता है कि fgets
परिणाम सरणी में नए-पंक्ति वर्ण की उपस्थिति या अनुपस्थिति पर ध्यान देने वाले कॉलर्स । fgets
इसके बजाय (नए-लाइन वर्णों पर आधारित किसी भी आवश्यक प्रसंस्करण के साथ)
का उपयोग करने पर विचार करें gets_s
।
25)gets_s
समारोह, के विपरीत gets
, यह इनपुट की एक पंक्ति बफर यह स्टोर करने के लिए अतिप्रवाह के लिए एक क्रम-बाधा उल्लंघन करता है। इसके विपरीत fgets
, gets_s
इनपुट लाइनों और सफल कॉल के बीच एक-से-एक संबंध बनाए रखता है gets_s
। gets
ऐसे कार्यक्रम जो इस तरह के रिश्ते की उम्मीद करते हैं।
Microsoft Visual Studio कंपाइलर TR 24731-1 मानक के लिए एक सन्निकटन लागू करता है, लेकिन Microsoft और TR में लागू किए गए हस्ताक्षरों के बीच अंतर हैं।
C11 मानक, ISO / IEC 9899-2011, लाइब्रेरी के वैकल्पिक भाग के रूप में अनुलग्नक K में TR24731 शामिल है। दुर्भाग्य से, यह शायद ही कभी यूनिक्स जैसी प्रणालियों पर लागू किया गया है।
getline()
- पॉसिक्स
POSIX 2008 भी gets()
कॉल करने के लिए एक सुरक्षित विकल्प प्रदान करता है getline()
। यह गतिशील रूप से लाइन के लिए स्थान आवंटित करता है, इसलिए आपको इसे मुक्त करने की आवश्यकता है। यह लाइन की लंबाई पर सीमा को हटा देता है, इसलिए। यह उस डेटा की लंबाई भी देता है जो पढ़ा गया था, या -1
(और नहीं EOF
!), जिसका अर्थ है कि इनपुट में अशक्त बाइट्स को मज़बूती से संभाला जा सकता है। कहा जाता है एक 'अपने खुद के एकल चरित्र सीमांकक भिन्नता चुनें' getdelim()
; यह उपयोगी हो सकता है यदि आप आउटपुट के साथ काम कर रहे हैं find -print0
जहां फ़ाइल नामों के छोर ASCII NUL '\0'
वर्ण के साथ चिह्नित हैं, उदाहरण के लिए।
gets()
बफ़र_ओवरफ़्लो_टैक