[c] scanf가 C에서 버퍼 오버플로를 일으키는 것을 방지하는 방법은 무엇입니까?

이 코드를 사용합니다.

while ( scanf("%s", buf) == 1 ){

임의 길이의 문자열을 전달할 수 있도록 가능한 버퍼 오버플로를 방지하는 가장 좋은 방법은 무엇입니까?

예를 들어 다음을 호출하여 입력 문자열을 제한 할 수 있다는 것을 알고 있습니다.

while ( scanf("%20s", buf) == 1 ){

하지만 사용자가 입력 한 모든 것을 처리 할 수 ​​있기를 원합니다. 아니면 scanf를 사용하여 안전하게 수행 할 수없고 fgets를 사용해야합니까?



답변

Kernighan과 Pike는 저서 The Practice of Programming (읽을 가치가 있음)에서이 문제를 논의하고 함수 패밀리에 snprintf()전달할 올바른 버퍼 크기를 가진 문자열을 만드는 데 사용하여 문제를 해결합니다 scanf(). 사실상:

int scanner(const char *data, char *buffer, size_t buflen)
{
    char format[32];
    if (buflen == 0)
        return 0;
    snprintf(format, sizeof(format), "%%%ds", (int)(buflen-1));
    return sscanf(data, format, buffer);
}

이것은 여전히 ​​’버퍼’로 제공되는 크기로 입력을 제한합니다. 더 많은 공간이 필요하면 메모리 할당을 수행하거나 메모리 할당을 수행하는 비표준 라이브러리 기능을 사용해야합니다.


참고의 POSIX 2008 (2013) 버전 것으로 scanf()기능의 가족이 형식 수정 지원 m문자열 입력에 대한 (할당 할당 문자) ( %s, %c, %[). char *인수 를받는 대신 char **인수를 사용하고 읽는 값에 필요한 공간을 할당합니다.

char *buffer = 0;
if (sscanf(data, "%ms", &buffer) == 1)
{
    printf("String is: <<%s>>\n", buffer);
    free(buffer);
}

은 IF sscanf()함수는 모든 변환 사양을 만족하지, 모든이에 할당 된 메모리 %ms-like 변환은 함수가 반환하기 전에 해제됩니다.


답변

gcc를 사용하는 경우 GNU 확장 a지정자를 사용하여 scanf ()가 입력을 저장할 메모리를 할당하도록 할 수 있습니다.

int main()
{
  char *str = NULL;

  scanf ("%as", &str);
  if (str) {
      printf("\"%s\"\n", str);
      free(str);
  }
  return 0;
}

편집 : Jonathan이 지적했듯이 scanf지정자가 다를 수 있고 ( %m) 컴파일 할 때 특정 정의를 활성화해야 할 수 있으므로 man 페이지를 참조 해야합니다.


답변

대부분의 경우 fgetssscanf작업을 수행합니다. 다른 것은 입력이 잘 포맷 된 경우 자신 만의 파서를 작성하는 것입니다. 또한 두 번째 예제를 안전하게 사용하려면 약간의 수정이 필요합니다.

#define LENGTH          42
#define str(x)          # x
#define xstr(x)         str(x)

/* ... */
int nc = scanf("%"xstr(LENGTH)"[^\n]%*[^\n]", array);

위는 줄 바꿈 ( \n) 문자를 포함하지 않고 입력 스트림을 버립니다 . getchar()이것을 사용하려면를 추가해야합니다 . 또한 스트림의 끝에 도달했는지 확인하십시오.

if (!feof(stdin)) { ...

그게 다야.


답변

직접 사용 scanf(3) 및 그 변형은 많은 문제를 제기합니다. 일반적으로 사용자 및 비대화 형 사용 사례는 입력 라인으로 정의됩니다. 충분한 객체가 발견되지 않으면 더 많은 줄이 문제를 해결할 수있는 경우는 드물지만 이것이 scanf의 기본 모드입니다. (사용자가 첫 번째 줄에 숫자를 입력하는 것을 몰랐다면 두 번째와 세 번째 줄은 도움이되지 않을 것입니다.)

적어도 fgets(3)프로그램에 필요한 입력 라인 수 를 알고 있고 버퍼 오버플로가 발생하지 않으면 …


답변

입력 길이를 제한하는 것이 확실히 더 쉽습니다. 루프를 사용하여 한 번에 조금씩 읽고 필요에 따라 문자열 공간을 다시 할당하여 임의의 긴 입력을 허용 할 수 있습니다.

그러나 그것은 많은 작업이므로 대부분의 C 프로그래머는 임의의 길이로 입력을 잘라냅니다. 이미 알고 있다고 가정하지만 fgets ()를 사용하면 임의의 양의 텍스트를 허용 할 수 없습니다. 여전히 제한을 설정해야합니다.


답변

문자열에 필요한 메모리를 할당하는 함수를 만드는 것은 그리 많은 작업이 아닙니다. 그것은 내가 얼마 전에 작성한 약간의 c- 함수이며, 나는 항상 그것을 문자열로 읽는 데 사용합니다.

읽기 문자열을 반환하거나 메모리 오류가 발생하면 NULL을 반환합니다. 그러나 문자열을 free ()해야하며 항상 반환 값을 확인해야합니다.

#define BUFFER 32

char *readString()
{
    char *str = malloc(sizeof(char) * BUFFER), *err;
    int pos;
    for(pos = 0; str != NULL && (str[pos] = getchar()) != '\n'; pos++)
    {
        if(pos % BUFFER == BUFFER - 1)
        {
            if((err = realloc(str, sizeof(char) * (BUFFER + pos + 1))) == NULL)
                free(str);
            str = err;
        }
    }
    if(str != NULL)
        str[pos] = '\0';
    return str;
}


답변