왜 gets()
위험한가
최초의 인터넷 웜 ( 모리스 인터넷 웜 )은 약 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()
버퍼가 끝나는 곳을 모르고 끝을 넘어가는 것을 알지 못하기 때문에 아름답게 돌린 메모리 레이아웃을 혼란스럽게 만들고 버퍼가 할당되면 반환 스택 ( 스택 오버플로 )을 망칠 수 있기 때문에 발생 하지 않습니다 . 버퍼가 동적으로 할당되면 스택 또는 제어 정보를 짓밟거나 버퍼가 정적으로 할당되면 다른 중요한 전역 (또는 모듈) 변수를 통해 데이터를 복사합니다. 이것들 중 어느 것도 좋은 생각이 아닙니다. '정의되지 않은 행동'이라는 문구를 나타냅니다.
도있다 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
0과 같거나 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
POSIX 2008 년도에 안전한 대안 제공 gets()
이라고를 getline()
. 라인을위한 공간을 동적으로 할당하므로 결국이를 해제해야합니다. 따라서 줄 길이에 대한 제한이 제거됩니다. 또한 읽은 데이터의 길이 -1
( 또는 EOF
!가 아님)를 반환하므로 입력의 널 바이트를 안정적으로 처리 할 수 있습니다. '자신의 단일 문자 구분 기호 선택'변형도 있습니다 getdelim()
. 예를 들어, find -print0
파일 이름 끝이 ASCII NUL '\0'
문자 로 표시 되는 위치 의 출력을 처리 할 때 유용 할 수 있습니다 .
gets()
Buffer_overflow_attack