문제

내가 사용하는 C 코드를 컴파일하려고 할 때 gets() GCC와 기능하면 다음 경고를받습니다.

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

나는 이것이 스택 보호 및 보안과 관련이 있다는 것을 기억하지만 정확히 이유는 확실하지 않습니다.

이 경고를 제거하려면 어떻게해야하며 왜 사용에 대한 경고가 있습니까? gets()?

만약에 gets() 너무 위험합니까? 그렇다면 왜 우리는 그것을 제거 할 수 없습니까?

도움이 되었습니까?

해결책

사용하기 위해 gets 안전하게, 당신은 당신이 읽을 문자 수를 정확히 알아야하므로 버퍼를 충분히 크게 만들 수 있습니다. 당신은 당신이 어떤 데이터를 읽을 것인지 정확히 알고있는 경우에만 알게 될 것입니다.

사용하는 대신 gets, 당신은 사용하고 싶습니다 fgets, 서명이 있습니다

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

(fgets, 전체 줄을 읽으면 '\n' 문자열에서; 당신은 그것을 다루어야합니다.)

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

다른 팁

gets() 위험한

첫 인터넷 벌레 (The 모리스 인터넷 벌레) 약 30 년 전에 탈출하여 (1988-11-02) gets() 그리고 시스템에서 시스템으로 전파하는 방법 중 하나로서 버퍼 오버플로. 기본 문제는 기능이 버퍼가 얼마나 큰지 알지 못하므로 Newline을 찾거나 EOF가 발생할 때까지 계속 읽고 버퍼의 한계를 넘칠 수 있습니다.

당신은 당신이 그 말을 들어 본 적이 잊어야합니다 gets() 존재했다.

C11 표준 ISO/IEC 9899 : 2011 제거 gets() 표준 함수로서, 좋은 것 ™ (공식적으로 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() Newline이 포함되어 있지 않습니다 fgets() 하다. 따라서 래퍼를 사용해야 할 수도 있습니다. fgets() Newline을 삭제합니다.

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;
}

또한, AS 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() 버퍼가 어디에서 끝나고 끝을 넘어서는 트램 플라플을 알지 못하기 때문에 아름답게 묶인 메모리 레이아웃에 혼란을 겪고 종종 리턴 스택을 엉망으로 만듭니다 (A 스택 오버플로) 버퍼가 스택에 할당되거나 버퍼가 동적으로 할당되거나 버퍼가 정적으로 할당 된 경우 다른 귀중한 글로벌 (또는 모듈) 변수에 대한 데이터를 복사하는 경우 제어 정보에 대한 트램 퍼퍼가 할당 된 경우. 이것들 중 어느 것도 좋은 생각이 아닙니다. 그들은 '정의되지 않은 행동'이라는 문구를 표현합니다.


또한 있습니다 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 널 포인터가되어서는 안됩니다. n rsize_max보다 0과 같거나 크지 않아야합니다. 새 라인 캐릭터, 파일 종료 또는 읽기 오류는 읽기 내에서 발생합니다. n-1 문자 stdin.25)

3 런타임 제약 위반이있는 경우 s[0] 널 캐릭터로 설정되며 캐릭터는 읽고 폐기됩니다. stdin 새 라인 문자가 읽거나 파일 종료 또는 읽기 오류가 발생할 때까지.

설명

4 gets_s 함수는 n스트림에서 BY를 가리 킵니다 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에는 라이브러리의 선택적 부분으로 부록 K의 TR24731이 포함됩니다. 불행히도 UNIX와 같은 시스템에서는 거의 구현되지 않습니다.


getline() - posix

Posix 2008은 또한 안전한 대안을 제공합니다 gets() ~라고 불리는 getline(). 선을 동적으로 할당하므로 해방해야합니다. 따라서 라인 길이의 제한을 제거합니다. 또한 읽은 데이터의 길이를 반환하거나 -1 (그리고 아닙니다 EOF!), 이것은 입력의 널 바이트가 안정적으로 처리 될 수 있음을 의미합니다. '자신만의 단일 문자 구분 기호기 선택'변형도 있습니다. getdelim(); 출력을 처리하는 경우 유용 할 수 있습니다. find -print0 파일 이름의 끝에 ASCII NUL이 표시되는 곳 '\0' 예를 들어 캐릭터.

왜냐하면 gets 바이트를 얻는 동안 어떤 종류의 점검도하지 않습니다. Stdin 그리고 그들을 어딘가에 넣습니다. 간단한 예 :

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

gets(array1);

이제 먼저 원하는 문자 수를 입력 할 수 있습니다. gets 신경 쓰지 않을 것입니다. 둘째, 배열 크기에 걸쳐 바이트를 넣는 바이트 (이 경우에는 array1)는 그들이 메모리에서 찾은 모든 것을 덮어 쓰기 때문에 gets 그것들을 쓸 것입니다. 이전 예에서 이것은 당신이 입력 한 경우 "abcdefghijklmnopqrts" 어쩌면 예측할 수 없을 정도로, 그것은 또한 덮어 쓸 것입니다 array2 또는 무엇이든.

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

사용해서는 안됩니다 gets 버퍼 오버플로를 멈출 수있는 방법이 없기 때문입니다. 사용자가 버퍼에 맞을 수있는 것보다 더 많은 데이터를 입력하면 부패가 발생하거나 악화 될 가능성이 높습니다.

사실, ISO는 실제로 풀이 gets C11에서 (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 버퍼 오버 플로우를 방지하지만 발신자에게 발생한 일에 대해 알리고 초과 문자를 지우므로 다음 입력 작업에 영향을 미치지 않도록합니다.

당신이 원하는대로 자유롭게 사용하십시오. 나는 "당신이 잘하고 싶어하는 일"라이센스 :-) 아래에 그것을 발표합니다.

fgets.

Stdin에서 읽으려면 :

char string[512];

fgets(string, sizeof(string), stdin); /* no buffer overflows here, you're safe! */

API를 깨지 않고 API 기능을 제거 할 수 없습니다. 그렇다면 많은 응용 프로그램이 더 이상 컴파일하거나 실행되지 않습니다.

이것이 바로 그 이유입니다 하나의 참조 제공 :

배열을 넘치는 선을 읽으면 정의되지 않은 동작이 나타납니다. fgets () 사용이 권장됩니다.

나는 최근에, a USENET POST로 comp.lang.c, 저것 gets() 표준에서 제거되고 있습니다. 우후

위원회가 드래프트에서 gets ()를 제거하기 위해 투표 (만장일치로) 투표했다는 것을 알게되어 기쁩니다.

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.

나는 아직도 gets 라이브러리에서 "누구나 여전히 그것에 의존하는 경우를 대비하여": 구현을 동등한 것으로 바꾸십시오.

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

이것은 아무도 여전히 그것에 의존하지 않도록하는 데 도움이 될 것입니다. 고맙습니다.

gets() 사용자가 프롬프트에 너무 많이 입력하여 프로그램을 충돌시킬 수 있기 때문에 위험합니다. 사용 가능한 메모리의 끝을 감지 할 수 없으므로 목적을 위해 너무 작은 메모리를 할당하면 SEG 결함과 충돌이 발생할 수 있습니다. 때로는 사용자가 사람의 이름을위한 프롬프트에 1000 글자를 입력 할 가능성이 거의 없지만 프로그래머로서 프로그램을 방탄해야합니다. (너무 많은 데이터를 보내서 사용자가 시스템 프로그램을 충돌시킬 수있는 경우 보안 위험 일 수도 있습니다).

fgets() 표준 입력 버퍼에서 몇 개의 문자 수를 제거 할 수 있으므로 변수를 오버런하지 않습니다.

C는 기능이 위험하며 매우 비용이 많이 드는 실수였습니다. Tony Hoare는 그의 대화에서 "Null References : The Billion Dollar Mission"에서 구체적인 언급을 위해 그것을 싱글합니다.

http://www.infoq.com/presentations/null-references-the-billion-dollar-mistake-tony-hoare

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

바라건대 이것은 전체 대화에 대한 당신의 식욕을 자극합니다. 이것은 우리가 언어에서보다 공식적인 정확성 증거가 필요한 방법과 언어 디자이너가 프로그래머가 아닌 언어의 실수에 대해 어떻게 비난 해야하는지에 주목합니다. 이것은 나쁜 언어 디자이너들이 '프로그래머 자유'의 모습으로 프로그래머들에게 비난을 낼 수있는 모든 모호한 이유 인 것 같습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top