ทำไมฟังก์ชั่นที่ได้รับมีอันตรายมากจนไม่ควรใช้?


229

เมื่อฉันพยายามรวบรวมรหัส C ที่ใช้gets()ฟังก์ชันกับ GCC ฉันได้รับคำเตือนนี้:

(.text + 0x34): คำเตือน: ฟังก์ชั่น `รับ 'เป็นอันตรายและไม่ควรใช้

ฉันจำได้ว่าสิ่งนี้เกี่ยวข้องกับการป้องกันและความปลอดภัยของกองซ้อน แต่ฉันไม่แน่ใจว่าทำไม

ฉันจะลบคำเตือนนี้ได้อย่างไรและทำไมจึงมีคำเตือนเกี่ยวกับการใช้ gets() ?

หากgets()อันตรายดังนั้นทำไมเราไม่สามารถลบได้



คำตอบ:


179

ในการใช้getsอย่างปลอดภัยคุณต้องรู้จำนวนตัวอักษรที่คุณอ่านอย่างชัดเจนเพื่อที่จะทำให้บัฟเฟอร์ของคุณใหญ่พอ คุณจะรู้ว่าถ้าคุณรู้ว่าคุณกำลังอ่านข้อมูลอะไรอยู่

แทนที่จะใช้getsคุณต้องการใช้fgetsซึ่งมีลายเซ็น

char* fgets(char *string, int length, FILE * stream);

( fgetsถ้ามันอ่านทั้งบรรทัดจะออกจาก'\n'อยู่ในสตริงคุณจะต้องจัดการกับสิ่งนั้น)

มันยังคงเป็นส่วนหนึ่งของภาษาอย่างเป็นทางการตามมาตรฐาน ISO C 1999 แต่มันถูกลบอย่างเป็นทางการโดย 2011 มาตรฐาน การใช้งาน C ส่วนใหญ่ยังคงสนับสนุนอยู่ แต่อย่างน้อย gcc จะออกคำเตือนสำหรับรหัสที่ใช้


79
จริงๆแล้วมันไม่ใช่ gcc ที่เตือนมันเป็น glibc ซึ่งมี pragma หรือคุณลักษณะgets()ที่ทำให้คอมไพเลอร์เปล่งคำเตือนเมื่อใช้งาน
fuz

@fuz จริงๆแล้วมันไม่ได้เป็นเพียงคอมไพเลอร์ที่เตือน: คำเตือนที่ยกมาใน OP ถูกพิมพ์โดย linker!
Ruslan

163

ทำไมถึงเป็นgets()อันตราย

หนอนอินเทอร์เน็ตตัวแรก ( Morris Internet Worm ) หนีไปเมื่อประมาณ 30 ปีที่แล้ว (1988-11-02) และมันใช้gets()และบัฟเฟอร์ล้นเป็นหนึ่งในวิธีการแพร่กระจายจากระบบหนึ่งสู่อีกระบบหนึ่ง ปัญหาพื้นฐานคือฟังก์ชั่นไม่ทราบว่าบัฟเฟอร์มีขนาดใหญ่เท่าใดดังนั้นจึงอ่านต่อไปจนกว่าจะพบบรรทัดใหม่หรือพบ EOF และอาจล้นเกินขอบเขตของบัฟเฟอร์ที่ได้รับ

คุณควรลืมคุณเคยได้ยินสิ่งที่gets()มีอยู่

มาตรฐาน C11 ISO / IEC 9899: 2011 ตัดออกgets()เป็นฟังก์ชั่นมาตรฐานซึ่งเป็นสิ่งที่ดี™ (มันถูกทำเครื่องหมายอย่างเป็นทางการว่า 'ล้าสมัย' และ 'เลิกใช้แล้ว' ใน ISO / IEC 9899: 1999 / Cor.3: 2007 - Corrigendum ทางเทคนิค 3 สำหรับ C99 และลบออกใน C11) น่าเศร้าที่มันยังคงอยู่ในห้องสมุดเป็นเวลาหลายปี (หมายถึง 'ทศวรรษ') ด้วยเหตุผลของความเข้ากันได้ย้อนหลัง ถ้ามันขึ้นอยู่กับฉันการดำเนินการของgets()จะกลายเป็น:

char *gets(char *buffer)
{
    assert(buffer != 0);
    abort();
    return 0;
}

ระบุว่ารหัสของคุณจะขัดข้องไม่ช้าก็เร็วจะดีกว่าที่จะแก้ปัญหาให้เร็วกว่าในภายหลัง ฉันพร้อมที่จะเพิ่มข้อความแสดงข้อผิดพลาด:

fputs("obsolete and dangerous function gets() called\n", stderr);

ระบบคอมไพล์ Linux รุ่นใหม่สร้างคำเตือนหากคุณลิงค์gets()- และสำหรับฟังก์ชั่นอื่น ๆ ที่มีปัญหาด้านความปลอดภัย ( mktemp(), ... )

ทางเลือกในการ gets()

fgets ()

ดังที่คนอื่น ๆ บอกกันแล้วทางเลือกที่ยอมรับได้gets()คือการfgets()ระบุstdinว่าเป็นสตรีมไฟล์

char buffer[BUFSIZ];

while (fgets(buffer, sizeof(buffer), stdin) != 0)
{
    ...process line of data...
}

สิ่งที่ไม่มีใครอื่น ๆ ที่กล่าวถึงก็คือว่าgets()ไม่รวมถึงการขึ้นบรรทัดใหม่ แต่fgets()ไม่ ดังนั้นคุณอาจต้องใช้ wrapper รอบ ๆ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()คุณอาจมีข้อมูลที่เหลืออยู่ในบรรทัด รหัส wrapper ของฉันทำให้ข้อมูลนั้นถูกอ่านในครั้งถัดไป คุณสามารถแก้ไขได้อย่างง่ายดายเพื่อฮุบส่วนที่เหลือของสายข้อมูลถ้าคุณต้องการ:

        if (len > 0 && buffer[len-1] == '\n')
            buffer[len-1] = '\0';
        else
        {
             int ch;
             while ((ch = getc(fp)) != EOF && ch != '\n')
                 ;
        }

ปัญหาที่เหลือคือวิธีการรายงานสถานะผลลัพธ์สามแบบ - EOF หรือข้อผิดพลาดการอ่านบรรทัดและไม่ถูกตัดทอนและการอ่านบรรทัดบางส่วน แต่ข้อมูลถูกตัดทอน

ปัญหานี้ไม่ได้เกิดขึ้นกับgets()เพราะไม่ทราบว่าปลายกันชนของคุณและกระทืบเท้าอย่างสนุกสนานเกินปลายอลหม่านในรูปแบบหน่วยความจำของคุณมีแนวโน้มที่สวยงามมักจะล้อขึ้นผลตอบแทนสแต็ค (เป็นกองมากเกิน ) ถ้าบัฟเฟอร์จะถูกจัดสรรบน สแต็คหรือการเหยียบย่ำข้อมูลการควบคุมหากมีการจัดสรรบัฟเฟอร์แบบไดนามิกหรือคัดลอกข้อมูลผ่านตัวแปร (หรือโมดูล) ที่มีค่าอื่น ๆ ทั่วโลกหากบัฟเฟอร์ได้รับการจัดสรรแบบคงที่ สิ่งเหล่านี้ไม่มีความคิดที่ดี - พวกเขาเป็นตัวอย่างที่ชัดเจนของวลี 'พฤติกรรมที่ไม่ได้กำหนด'


นอกจากนี้ยังมีTR 24731-1 (รายงานทางเทคนิคจากคณะกรรมการมาตรฐาน C) ซึ่งให้ทางเลือกที่ปลอดภัยกว่าสำหรับฟังก์ชั่นที่หลากหลายรวมถึงgets():

§6.5.4.1 gets_sฟังก์ชั่น

สรุป

#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
char *gets_s(char *s, rsize_t n);

Runtime- จำกัด

sจะต้องไม่เป็นตัวชี้โมฆะ nจะต้องไม่เท่ากับศูนย์หรือมากกว่า RSIZE_MAX ตัวละครใหม่สายสิ้นสุดของแฟ้มหรือข้อผิดพลาดในการอ่านให้เกิดขึ้นภายในการอ่าน ตัวอักษรจากn-1 25)stdin

3 หากมีการละเมิดข้อ จำกัด รันไทม์s[0]ถูกตั้งค่าเป็นอักขระโมฆะและตัวอักษรจะถูกอ่านและทิ้งstdinจนกว่าจะมีการอ่านอักขระบรรทัดใหม่หรือสิ้นสุดไฟล์หรือเกิดข้อผิดพลาดในการอ่าน

ลักษณะ

4 gets_sฟังก์ชั่นอ่านมากที่สุดคนหนึ่งน้อยกว่าจำนวนตัวอักษรที่ระบุโดยn จากกระแสที่ชี้ไปตามลงไปในอาร์เรย์ที่ชี้ไปตามstdin sไม่มีการอ่านอักขระเพิ่มเติมหลังอักขระบรรทัดใหม่ (ซึ่งถูกละทิ้ง) หรือหลังสิ้นสุดไฟล์ อักขระขึ้นบรรทัดใหม่ที่ถูกทิ้งจะไม่นับรวมตามจำนวนอักขระที่อ่าน อักขระ null ถูกเขียนทันทีหลังจากอ่านอักขระสุดท้ายลงในอาร์เรย์

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 รวมถึง TR24731 ในภาคผนวก K เป็นส่วนเสริมของห้องสมุด น่าเสียดายที่มันไม่ค่อยมีการใช้งานบนระบบที่เหมือน Unix


getline() - POSIX

POSIX 2008 นอกจากนี้ยังมีทางเลือกที่ปลอดภัยที่จะเรียกว่าgets() getline()มันจัดสรรพื้นที่สำหรับบรรทัดแบบไดนามิกดังนั้นคุณจะต้องมีอิสระ จะลบข้อ จำกัด เกี่ยวกับความยาวบรรทัดดังนั้น นอกจากนี้ยังส่งคืนความยาวของข้อมูลที่อ่านหรือ-1(และไม่ใช่EOF!) ซึ่งหมายความว่าไบต์ว่างในอินพุตสามารถจัดการได้อย่างน่าเชื่อถือ นอกจากนี้ยังมีรูปแบบ 'เลือกเองคั่นตัวเดียวของคุณเรียกว่าgetdelim(); สิ่งนี้มีประโยชน์หากคุณกำลังจัดการกับเอาต์พุตจากfind -print0จุดสิ้นสุดของชื่อไฟล์ที่ถูกทำเครื่องหมายด้วยอักขระ ASCII NUL '\0'ตัวอย่างเช่น


8
นอกจากนี้ยังควรชี้ให้เห็นว่าfgets()และfgets_wrapper()รุ่นของคุณจะปล่อยส่วนต่อท้ายของบรรทัดที่ยาวเกินไปในบัฟเฟอร์อินพุตเพื่อให้อ่านได้โดยฟังก์ชั่นอินพุตถัดไป ในหลายกรณีคุณจะต้องอ่านและทิ้งอักขระเหล่านี้
caf

5
ฉันสงสัยว่าทำไมพวกเขาจึงไม่เพิ่ม fgets () ทางเลือกที่อนุญาตให้ใช้ฟังก์ชั่นของมันได้โดยไม่ต้องโทรไปหา strlen โง่ ๆ ตัวอย่างเช่นตัวแปร fgets ซึ่งส่งคืนจำนวนไบต์ที่อ่านลงในสตริงจะทำให้ง่ายสำหรับโค้ดที่จะดูว่าไบต์สุดท้ายที่อ่านเป็นบรรทัดใหม่ หากพฤติกรรมการส่งผ่านตัวชี้โมฆะสำหรับบัฟเฟอร์ถูกกำหนดเป็น "อ่านแล้วละไม่เกิน n-1 ไบต์จนถึงบรรทัดใหม่ถัดไป" นั่นจะทำให้โค้ดสามารถทิ้งหางของเส้นที่มีความยาวเกินได้อย่างง่ายดาย
supercat

2
@supercat: ใช่ฉันเห็นด้วย - มันน่าเสียดาย วิธีที่ใกล้ที่สุดคือ POSIX getline()และความสัมพันธ์getdelim()ซึ่งจะส่งคืนความยาวของคำสั่ง 'บรรทัด' ที่อ่านโดยการจัดสรรพื้นที่ตามที่จำเป็นเพื่อให้สามารถเก็บทั้งบรรทัดได้ แม้อาจทำให้เกิดปัญหาหากคุณจบลงด้วยไฟล์ JSON บรรทัดเดียวที่มีขนาดหลายกิกะไบต์ คุณสามารถซื้อหน่วยความจำทั้งหมดนั้นได้ไหม (และในขณะที่เราอยู่ที่นี่เราสามารถมีstrcpy()และstrcat()ตัวแปรที่ส่งกลับตัวชี้ไปยังไบต์ที่สิ้นสุดได้หรือไม่ ฯลฯ )
Jonathan Leffler

4
@supercat: ปัญหาอื่น ๆfgets()คือถ้าไฟล์มีค่า null คุณไม่สามารถบอกได้ว่าข้อมูลมีจำนวนเท่าใดหลังจากค่า null ถึงค่าสิ้นสุดบรรทัด (หรือ EOF) strlen()สามารถรายงานได้ถึงไบต์ว่างในข้อมูลเท่านั้น หลังจากนั้นมันเป็นการคาดเดาและดังนั้นจึงผิดอย่างแน่นอน
Jonathan Leffler

7
"ลืมไปเลยว่าคุณเคยได้ยินเรื่องนี้gets()มาก่อน" เมื่อฉันทำสิ่งนี้ฉันพบมันอีกครั้งและกลับมาที่นี่ คุณแฮ็คสแต็คโอเวอร์โฟลว์เพื่อรับ upvotes หรือไม่
candied_orange

21

เพราะ getsไม่ได้ทำการตรวจสอบใด ๆ ในขณะรับไบต์จากstdinและวางไว้ที่อื่น ตัวอย่างง่ายๆ:

char array1[] = "12345";
char array2[] = "67890";

gets(array1);

ตอนนี้ก่อนอื่นคุณได้รับอนุญาตให้ป้อนจำนวนอักขระที่คุณต้องการgetsจะไม่สนใจมัน ประการที่สองไบต์ที่มีขนาดใหญ่กว่าของอาร์เรย์ที่คุณวางไว้ (ในกรณีนี้array1) จะเขียนทับสิ่งที่พบในหน่วยความจำเพราะgetsจะเขียน ในตัวอย่างก่อนหน้านี้หมายความว่าหากคุณป้อนข้อมูล"abcdefghijklmnopqrts"อาจคาดเดาไม่ได้ก็จะเขียนทับarray2หรืออะไรก็ตาม

ฟังก์ชั่นไม่ปลอดภัยเพราะมันถือว่าอินพุตที่สอดคล้องกัน ไม่เคยใช้มัน!


3
สิ่งที่ทำให้getsไม่สามารถใช้งานได้ทันทีคือไม่มีพารามิเตอร์ length / count ที่ต้องใช้ ถ้ามันอยู่ที่นั่นมันก็แค่ฟังก์ชั่นมาตรฐานธรรมดา C อีกตัว
ตำนาน 2k

@ legends2k: ฉันอยากรู้ว่าการใช้งานที่ตั้งใจไว้getsคืออะไรและทำไมไม่มีตัวแปร fgets มาตรฐานที่สร้างความสะดวกให้กับเคสที่ใช้งานโดยที่บรรทัดใหม่ไม่ต้องการเป็นส่วนหนึ่งของอินพุต
supercat

1
@supercat getsนั้นเป็นชื่อที่ถูกออกแบบมาเพื่อรับสตริงstdinอย่างไรก็ตามเหตุผลที่ไม่มีพารามิเตอร์ขนาดอาจมาจากจิตวิญญาณของ C : ไว้วางใจโปรแกรมเมอร์ ฟังก์ชั่นนี้ถูกลบออกในC11และการแทนที่ที่ได้รับgets_sจะใช้ในขนาดของอินพุตบัฟเฟอร์ ฉันไม่รู้ว่าfgetsส่วนไหน
ตำนาน 2k

@ legends2k: บริบทเดียวที่ฉันเห็นซึ่งgetsอาจจะยกโทษให้ได้ก็คือหากมีใครใช้ระบบ I / O ที่บัฟเฟอร์ฮาร์ดแวร์ซึ่งไม่สามารถส่งบรรทัดไปตามความยาวที่แน่นอนและอายุการใช้งานของโปรแกรม สั้นกว่าอายุการใช้งานของฮาร์ดแวร์ ในกรณีนี้หากฮาร์ดแวร์ไม่สามารถส่งบรรทัดที่ยาวเกิน 127 ไบตได้มันอาจเป็นเหตุผลที่ทำให้getsบัฟเฟอร์ 128- ไบต์ได้ แต่ฉันคิดว่าข้อดีของการสามารถระบุบัฟเฟอร์ที่สั้นลงเมื่อคาดหวังว่าอินพุตที่เล็กกว่าจะปรับให้เหมาะสม ราคา
supercat

@ legends2k: จริง ๆ แล้วสิ่งที่อาจเป็นอุดมคติจะต้องมี "ตัวชี้สตริง" ระบุไบต์ที่จะเลือกระหว่างรูปแบบสตริง / บัฟเฟอร์ / บัฟเฟอร์ - ข้อมูลที่แตกต่างกันสองสามค่าด้วยค่า prefix ไบต์ที่ระบุโครงสร้างที่มี ไบต์คำนำหน้า [plus padding] รวมถึงขนาดบัฟเฟอร์ขนาดที่ใช้และที่อยู่ของข้อความจริง รูปแบบดังกล่าวจะทำให้เป็นไปได้สำหรับรหัสที่จะส่งผ่านสายอักขระย่อย (ไม่เพียงหาง) ของสายอื่นโดยไม่ต้องคัดลอกอะไรและจะอนุญาตให้วิธีการเช่นgetsและstrcatยอมรับอย่างปลอดภัยเท่าที่จะพอดี
supercat

16

คุณไม่ควรใช้getsเนื่องจากไม่มีวิธีหยุดบัฟเฟอร์ล้น หากผู้ใช้พิมพ์ข้อมูลมากกว่าที่จะพอดีกับบัฟเฟอร์ของคุณคุณจะพบว่าเกิดความเสียหายหรือแย่ลง

ในความเป็นจริง ISO ได้ดำเนินการขั้นตอนการลบออก getsจากมาตรฐาน C (ตั้งแต่ C11 ถึงแม้ว่าจะถูกเลิกใช้ใน C99) ซึ่งเมื่อพิจารณาว่าพวกเขาให้คะแนนความเข้ากันได้แบบย้อนหลังได้สูงเพียงใด

สิ่งที่ถูกต้องคือการใช้fgetsฟังก์ชั่นที่มีตัวstdinจัดการไฟล์เนื่องจากคุณสามารถ จำกัด ตัวละครที่อ่านจากผู้ใช้

แต่นี่ก็มีปัญหาเช่น:

  • อักขระพิเศษที่ป้อนโดยผู้ใช้จะถูกเลือกในครั้งถัดไป
  • ไม่มีการแจ้งเตือนอย่างรวดเร็วว่าผู้ใช้ป้อนข้อมูลมากเกินไป

ด้วยเหตุนี้ Coder เกือบทุกจุดในอาชีพของพวกเขาจะเขียนเสื้อคลุมที่มีประโยชน์มากขึ้นfgetsเช่นกัน นี่คือของฉัน:

#include <stdio.h>
#include <string.h>

#define OK       0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
    int ch, extra;

    // Get line with buffer overrun protection.
    if (prmpt != NULL) {
        printf ("%s", prmpt);
        fflush (stdout);
    }
    if (fgets (buff, sz, stdin) == NULL)
        return NO_INPUT;

    // If it was too long, there'll be no newline. In that case, we flush
    // to end of line so that excess doesn't affect the next call.
    if (buff[strlen(buff)-1] != '\n') {
        extra = 0;
        while (((ch = getchar()) != '\n') && (ch != EOF))
            extra = 1;
        return (extra == 1) ? TOO_LONG : OK;
    }

    // Otherwise remove newline and give string back to caller.
    buff[strlen(buff)-1] = '\0';
    return OK;
}

ด้วยรหัสทดสอบบางอย่าง:

// Test program for getLine().

int main (void) {
    int rc;
    char buff[10];

    rc = getLine ("Enter string> ", buff, sizeof(buff));
    if (rc == NO_INPUT) {
        printf ("No input\n");
        return 1;
    }

    if (rc == TOO_LONG) {
        printf ("Input too long\n");
        return 1;
    }

    printf ("OK [%s]\n", buff);

    return 0;
}

มันมีการป้องกันแบบเดียวกับfgetsที่มันป้องกันบัฟเฟอร์ล้น แต่ก็แจ้งผู้โทรว่าเกิดอะไรขึ้นและลบอักขระส่วนเกินออกเพื่อไม่ให้ส่งผลต่อการดำเนินการอินพุตครั้งถัดไปของคุณ

รู้สึกอิสระที่จะใช้มันตามที่คุณต้องการฉันขอปล่อยภายใต้ "ทำในสิ่งที่คุณควรจะ" ใบอนุญาต :-)


ที่จริงมาตรฐาน C99 เดิมไม่แน่ชัดคัดค้านgets()ทั้งในส่วน 7.19.7.7 ซึ่งจะมีการกำหนดหรือในส่วน 7.26.9 <stdio.h>ทิศทางห้องสมุดในอนาคตและส่วนย่อยสำหรับ ไม่มีแม้แต่เชิงอรรถว่ามันอันตราย (ต้องบอกว่าฉันเห็น "มันเลิกใช้ใน ISO / IEC 9899: 1999 / Cor.3: 2007 (E))" ในคำตอบของYu Hao ) แต่ C11 ลบมันออกจากมาตรฐาน - และไม่ใช่ก่อนเวลา!
Jonathan Leffler

int getLine (char *prmpt, char *buff, size_t sz) { ... if (fgets (buff, sz, stdin) == NULL)ซ่อนsize_tที่จะแปลง int จะจับค่าที่แปลกประหลาดของ szsz > INT_MAX || sz < 2sz
chux - Reinstate Monica

if (buff[strlen(buff)-1] != '\n') {เป็นช่องโหว่ของแฮ็กเกอร์เนื่องจากตัวอักษรตัวแรกของผู้ใช้ที่ชั่วร้ายที่ป้อนอาจเป็นอักขระแบบ null ที่แสดงผลbuff[strlen(buff)-1]ได้ while (((ch = getchar())...มีปัญหาหากผู้ใช้ป้อนอักขระว่าง
chux - Reinstate Monica


6

คุณไม่สามารถลบฟังก์ชั่น API โดยไม่ทำให้ API แตกได้ หากคุณต้องการแอปพลิเคชันจำนวนมากจะไม่รวบรวมหรือเรียกใช้เลย

นี่คือเหตุผลที่การอ้างอิงเดียวให้:

การอ่านบรรทัดที่ล้นอาร์เรย์ที่ชี้ไปยังผลลัพธ์ในลักษณะการทำงานที่ไม่ได้กำหนด แนะนำให้ใช้ fgets ()


4

ฉันอ่านเมื่อเร็ว ๆ นี้ในUSENET โพสต์ไปcomp.lang.cที่gets()กำลังถูกลบออกจากมาตรฐาน ดีจัง

คุณยินดีที่จะรู้ว่าคณะกรรมการที่ลงคะแนน (เป็นเอกฉันท์ตามที่ปรากฏ) เพื่อลบรับ () จากร่างเช่นกัน


3
มันยอดเยี่ยมมากที่มันถูกลบออกจากมาตรฐาน อย่างไรก็ตามการใช้งานส่วนใหญ่จะให้เป็น 'ตอนนี้ส่วนขยายที่ไม่ได้มาตรฐาน' เป็นเวลาอย่างน้อย 20 ปีข้างหน้าเนื่องจากความเข้ากันได้ย้อนหลัง
Jonathan Leffler

1
ใช่ถูกต้อง แต่เมื่อคุณคอมไพล์ด้วยgcc -std=c2012 -pedantic ...gets () จะไม่ผ่าน (ผมเพิ่งทำขึ้น-stdพารามิเตอร์)
PMG

4

ใน C11 (ISO / IEC 9899: 201x) gets()ถูกลบไปแล้ว (คัดค้านใน ISO / IEC 9899: 1999 / Cor.3: 2007 (E))

นอกจากนี้fgets()C11 ยังแนะนำทางเลือกใหม่ที่ปลอดภัยgets_s():

C11 K.3.5.4.1 gets_sฟังก์ชั่น

#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
char *gets_s(char *s, rsize_t n);

อย่างไรก็ตามในส่วนแนวปฏิบัติfgets()ที่แนะนำยังคงเป็นที่ต้องการ

fgetsฟังก์ชั่นช่วยให้โปรแกรมถูกเขียนได้อย่างปลอดภัยเส้นที่นำเข้ากระบวนการที่ยาวเกินไปที่จะเก็บในอาร์เรย์ผล โดยทั่วไปสิ่งนี้กำหนดให้ผู้โทรที่fgetsต้องใส่ใจกับการมีหรือไม่มีอักขระบรรทัดใหม่ในอาร์เรย์ผลลัพธ์ พิจารณาใช้fgets(พร้อมกับการประมวลผลที่จำเป็นใด ๆ ที่อยู่บนพื้นฐานของตัวละครใหม่เส้น) gets_sแทน


3

gets()เป็นอันตรายเนื่องจากผู้ใช้สามารถขัดข้องของโปรแกรมโดยพิมพ์ลงในพรอมต์มากเกินไป ไม่สามารถตรวจพบจุดสิ้นสุดของหน่วยความจำที่มีอยู่ดังนั้นหากคุณจัดสรรหน่วยความจำน้อยเกินไปสำหรับจุดประสงค์มันอาจทำให้เกิดข้อผิดพลาดและความผิดพลาดของ seg บางครั้งดูเหมือนว่าไม่น่าเป็นไปได้มากที่ผู้ใช้จะพิมพ์ตัวอักษร 1,000 ตัวเป็นพรอมต์สำหรับชื่อบุคคล แต่ในฐานะโปรแกรมเมอร์เราจำเป็นต้องทำให้โปรแกรมของเราเป็นกระสุน (อาจเป็นความเสี่ยงด้านความปลอดภัยหากผู้ใช้สามารถขัดข้องของโปรแกรมระบบโดยส่งข้อมูลมากเกินไป)

fgets() อนุญาตให้คุณระบุจำนวนอักขระที่ถูกนำออกจากบัฟเฟอร์อินพุตมาตรฐานดังนั้นจึงไม่เกินตัวแปร


โปรดทราบว่าอันตรายที่แท้จริงนั้นไม่สามารถที่จะทำให้โปรแกรมของคุณพังแต่ในความสามารถที่จะทำให้มันรันโค้ดโดยอำเภอใจ (โดยทั่วไปแล้วการใช้ประโยชน์จากพฤติกรรมที่ไม่ได้กำหนด )
Tanz87

2

ฉันต้องการขยายการเชิญอย่างจริงจังให้กับผู้ดูแลห้องสมุด C ที่ยังคงgetsอยู่ในห้องสมุดของพวกเขา "ในกรณีที่ทุกคนยังคงขึ้นอยู่กับมัน": โปรดแทนที่การใช้งานของคุณด้วยสิ่งที่เทียบเท่า

char *gets(char *str)
{
    strcpy(str, "Never use gets!");
    return str;
}

สิ่งนี้จะช่วยให้แน่ใจว่าไม่มีใครยังคงขึ้นอยู่กับมัน ขอบคุณ.


2

ฟังก์ชั่น C ได้รับอันตรายและผิดพลาดมาก Tony Hoare พูดถึงเรื่องนี้โดยเฉพาะในการพูดคุยของเขา "Null References: The ผิดพลาดพันล้านดอลลาร์":

http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare

ทั้งชั่วโมงคุ้มค่าที่จะดู แต่สำหรับความเห็นของเขาจาก 30 นาทีต่อมาโดยเฉพาะจะได้รับคำวิจารณ์ประมาณ 39 นาที

หวังว่าสิ่งนี้จะกระตุ้นความอยากของคุณสำหรับการพูดคุยทั้งหมดซึ่งดึงดูดความสนใจว่าเราต้องการการพิสูจน์ความถูกต้องอย่างเป็นทางการในภาษามากขึ้นอย่างไรและนักออกแบบภาษาควรถูกตำหนิสำหรับความผิดพลาดในภาษาของพวกเขาอย่างไร สิ่งนี้ดูเหมือนจะเป็นเหตุผลที่น่าสงสัยอย่างยิ่งสำหรับนักออกแบบภาษาที่ไม่ดีเพื่อผลักดันความผิดไปยังโปรแกรมเมอร์ในหน้ากากของ 'โปรแกรมเมอร์อิสระ'

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.