gets 함수가 왜 그렇게 위험해서 사용하지 말아야합니까?

Nov 08 2009

gets()GCC와 함께 함수 를 사용하는 C 코드를 컴파일하려고 하면 다음 경고가 표시됩니다.

(.text + 0x34) : 경고 :`gets '함수는 위험하므로 사용해서는 안됩니다.

이것이 스택 보호 및 보안과 관련이 있다는 것을 기억하지만 그 이유는 정확히 모르겠습니다.

이 경고를 어떻게 제거 할 수 있으며 사용에 대한 경고가 나타나는 이유는 gets()무엇입니까?

경우 gets()매우 위험하다 왜 우리는 그것을 제거 할 수 없습니다?

답변

188 ThomasOwens Nov 08 2009 at 01:56

gets안전하게 사용하려면 버퍼를 충분히 크게 만들 수 있도록 읽을 문자 수를 정확히 알아야합니다. 읽을 데이터를 정확히 알고있는 경우에만 알 수 있습니다.

을 사용하는 대신 서명이있는 gets을 사용하려고합니다.fgets

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

( fgets, 전체 행을 읽으면 '\n'문자열에를 남깁니다 .이를 처리해야합니다.)

1999 년 ISO C 표준까지 공식 언어의 일부로 남아 있었지만 2011 표준에 의해 공식적으로 제거되었습니다. 대부분의 C 구현은 여전히이를 지원하지만 최소한 gcc는이를 사용하는 모든 코드에 대해 경고를 발행합니다.

176 JonathanLeffler Nov 30 2010 at 08:51

gets()위험한 가요

최초의 인터넷 웜 ( Morris Internet Worm )은 약 30 년 전에 (1988-11-02) 탈출 gets()하여 시스템에서 시스템으로 전파하는 방법 중 하나로 버퍼 오버플로를 사용했습니다. 기본적인 문제는 함수가 버퍼의 크기를 모르기 때문에 개행을 찾거나 EOF를 만날 때까지 계속 읽고 주어진 버퍼의 경계를 넘칠 수 있다는 것입니다.

들은 적이 있다는 사실을 잊어야합니다 gets().

C11 표준 ISO / IEC 9899 : 2011 gets()은 A Good Thing ™ 표준 기능으로 제거 되었습니다 (ISO / IEC 9899 : 1999 / Cor.3 : 2007에서 공식적으로 '노후화 됨'및 '사용되지 않음'으로 표시됨 — 기술 정정표). C99의 경우 3이고 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()입니다. 따라서 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;
}

또한 caf 가 주석에서 지적하고 paxdiablo 가 그의 답변에서 보여 주듯이 fgets()한 줄에 데이터가 남아있을 수 있습니다. 내 래퍼 코드는 해당 데이터를 다음에 읽을 수 있도록 남겨 둡니다. 원하는 경우 나머지 데이터 라인을 먹기 위해 쉽게 수정할 수 있습니다.

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

잔여 문제는 EOF 또는 오류, 줄 읽기 및 잘리지 않음, 부분 줄 읽기이지만 데이터가 잘린 세 가지 결과 상태를보고하는 방법입니다.

이 문제는 gets()버퍼가 끝나는 곳을 모르고 끝을 넘어서 즐겁게 짓밟아서 아름답게 손질 된 메모리 레이아웃에 혼란을 일으키고 버퍼가 할당 된 경우 종종 반환 스택 ( Stack Overflow )을 엉망 으로 만들기 때문에 발생 하지 않습니다 . 스택, 또는 버퍼가 동적으로 할당 된 경우 제어 정보를 짓밟거나 버퍼가 정적으로 할당 된 경우 다른 귀중한 전역 (또는 모듈) 변수를 통해 데이터를 복사합니다. 이것들 중 어느 것도 좋은 생각이 아닙니다. 그들은 '정의되지 않은 행동'이라는 문구를 요약합니다.


다음을 포함한 다양한 기능에 대한보다 안전한 대안을 제공 하는 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);

런타임 제약

s널 포인터가 아니어야합니다. n0과 같거나 RSIZE_MAX보다 클 수 없습니다. 개행 문자, 파일 끝 또는 읽기 오류가에서 n-1문자를 읽는 동안 발생합니다 stdin. 25)

3 런타임 제약 조건 위반이있는 경우이 s[0]널 문자로 설정되고 stdin개행 문자를 읽거나 파일 끝 또는 읽기 오류가 발생할 때까지 문자를 읽고 버립니다 .

기술

4이 gets_s함수 n는에 의해 지정된 스트림에서에 의해 지정된 문자 수보다 1 개 적은 것을 에 의해 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은 라이브러리의 선택적 부분으로 Annex K에 TR24731을 포함합니다. 불행히도 유닉스 계열 시스템에서는 거의 구현되지 않습니다.


getline() — POSIX

POSIX 2008 년도에 안전한 대안 제공 gets()이라고를 getline(). 라인을위한 공간을 동적으로 할당하므로 결국 해제해야합니다. 따라서 선 길이에 대한 제한이 제거됩니다. 또한, 판독, 또는 한 데이터의 길이 반환 -1(그리고 EOF입력에 널 (null) 바이트가 안정적으로 처리 할 수있는 수단!). 라는 '자신의 단일 문자 구분 기호 선택'변형도 있습니다 getdelim(). 예를 들어 find -print0파일 이름의 끝이 ASCII NUL '\0'문자 로 표시된 출력을 처리하는 경우 유용 할 수 있습니다 .

23 Jack Nov 08 2009 at 02:03

때문에 gets로부터 바이트를 가져 오는 동안 검사의 종류를하지 않습니다 stdin을 어딘가에을 넣어. 간단한 예 :

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

gets(array1);

이제 먼저 원하는 문자 수를 입력 할 gets수 있습니다. 신경 쓰지 않아도됩니다. 둘째, 그것들을 넣은 배열의 크기를 초과하는 바이트 (이 경우 array1)는 그것들을 쓸 것이기 때문에 그들이 메모리에서 찾은 모든 것을 덮어 gets쓸 것입니다. 앞의 예에서 이것은 "abcdefghijklmnopqrts"예측할 수없는 입력이 있을 경우 에도 덮어 쓰게 됨을 의미합니다 array2.

이 함수는 일관된 입력을 가정하기 때문에 안전하지 않습니다. 절대 사용하지 마십시오!

17 paxdiablo Nov 30 2010 at 08:56

gets버퍼 오버플로를 중지 할 방법이 없으므로 사용하지 마십시오 . 사용자가 버퍼에 들어갈 수있는 것보다 더 많은 데이터를 입력하면 손상되거나 더 나빠질 가능성이 큽니다.

실제로 ISO는 C 표준에서 제거 하는 단계를 gets밟았습니다 (C11에서는 C99에서 더 이상 사용되지 않음). 이는 이전 버전과의 호환성이 얼마나 높은지 고려할 때 해당 기능이 얼마나 나빴는지 표시해야합니다.

올바른 방법 은 사용자로부터 읽는 문자를 제한 할 수 있으므로 파일 핸들 fgets과 함께 함수 를 사용하는 것입니다 stdin.

그러나 이것은 또한 다음과 같은 문제가 있습니다.

  • 사용자가 입력 한 추가 문자는 다음 번에 선택됩니다.
  • 사용자가 너무 많은 데이터를 입력했다는 빠른 알림이 없습니다.

이를 위해 거의 모든 C 코더는 경력의 어느 시점에서보다 유용한 래퍼를 작성할 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버퍼 오버플로를 방지 하는 것과 동일한 보호 기능을 제공 하지만 발생한 상황을 호출자에게 알리고 초과 문자를 지워 다음 입력 작업에 영향을주지 않도록합니다.

원하는대로 자유롭게 사용하십시오. "당신이 원하는 것을 수행하십시오"라이센스에 따라 릴리스합니다. :-)

14 ThiagoSilveira Nov 30 2010 at 08:28

fgets .

stdin에서 읽으려면 :

char string[512];

fgets(string, sizeof(string), stdin); /* no buffer overflows here, you're safe! */
9 GerdKlima Nov 08 2009 at 01:58

API를 중단하지 않고는 API 함수를 제거 할 수 없습니다. 원한다면 많은 응용 프로그램이 더 이상 컴파일되거나 실행되지 않습니다.

이것이 하나의 참조가 제공 하는 이유입니다 .

s가 가리키는 배열을 오버플로하는 행을 읽으면 정의되지 않은 동작이 발생합니다. fgets () 사용을 권장합니다.

5 pmg Nov 08 2009 at 02:21

나는에, 최근에 읽은 에 유즈넷 게시물comp.lang.c , gets()표준에서 제거지고 있습니다. 우후

위원회가 방금 초안에서 gets ()를 제거하기 위해 투표했다는 사실을 알게되어 기쁠 것입니다.

5 YuHao Oct 06 2013 at 13:15

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.

5 AradhanaMohanty Aug 22 2017 at 16:19

gets()사용자가 프롬프트에 너무 많이 입력하여 프로그램을 중단시킬 수 있기 때문에 위험합니다. 사용 가능한 메모리의 끝을 감지 할 수 없으므로 목적에 비해 너무 적은 양의 메모리를 할당하면 세그 오류 및 충돌이 발생할 수 있습니다. 때때로 사용자가 사람의 이름을 입력하는 프롬프트에 1000 개의 문자를 입력 할 가능성이 거의없는 것처럼 보이지만 프로그래머로서 우리는 프로그램을 방탄으로 만들어야합니다. (사용자가 너무 많은 데이터를 전송하여 시스템 프로그램을 중단시킬 수있는 경우 보안 위험이 될 수도 있습니다).

fgets() 표준 입력 버퍼에서 가져 오는 문자 수를 지정할 수 있으므로 변수가 오버런되지 않습니다.

3 user3717661 May 01 2016 at 08:00

C get 함수는 위험하며 비용이 많이 드는 실수였습니다. Tony Hoare는 그의 강연 "Null References : The Billion Dollar Mistake"에서 구체적으로 언급했습니다.

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

전체 시간은 볼 가치가 있지만 30 분부터 그의 댓글보기는 39 분 정도의 비판을받습니다.

바라건대 이것은 전체 강연에 대한 여러분의 욕구를 불러 일으키고, 우리가 언어에서 더 공식적인 정확성 증명이 필요한 방법과 언어 디자이너가 프로그래머가 아닌 언어의 실수에 대해 어떻게 비난을 받아야 하는지를 주목하게합니다. 이것은 나쁜 언어의 디자이너가 '프로그래머 자유'를 가장하여 프로그래머에게 책임을지게하는 모호한 이유 인 것 같습니다.

2 SteveSummit Apr 01 2016 at 04:52

gets"누군가가 여전히 그것에 의존하고있는 경우를 대비 하여 "라이브러리에 여전히 포함하고있는 C 라이브러리 관리자에게 진지한 초대를하고 싶습니다 .

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

이것은 아무도 그것에 의존하지 않는지 확인하는 데 도움이 될 것입니다. 감사합니다.