[c] 표준 라이브러리의 어떤 기능을 피해야합니까?

스택 오버플로에서 일부 C 함수는 “구식”또는 “피해야 함”을 읽었습니다. 이런 종류의 기능에 대한 몇 가지 예와 그 이유를 알려 주시겠습니까?

이러한 기능에 대한 대안은 무엇입니까?

안전하게 사용할 수 있습니까? 모범 사례가 있습니까?



답변

더 이상 사용되지 않는 함수
안전하지 않음 이러한 함수
의 완벽한 예는 gets () 입니다. 대상 버퍼의 크기를 알 수있는 방법이 없기 때문입니다. 결과적으로 gets ()를 사용하여 입력을 읽는 모든 프로그램에는 버퍼 오버플로 취약점이 있습니다. 비슷한 이유로 strcpy () 대신 strncpy () 를 사용 하고 strcat () 대신 strncat () 을 사용해야합니다 .

그러나 더 많은 예에는 임시 파일 덮어 쓰기와 관련된 잠재적 보안 문제 로 인해 tmpfile ()mktemp () 함수 가 포함되며 더 안전한 mkstemp () 함수 로 대체됩니다 .

재진입이 아닌
다른 예로는 재진입이 불가능하고 (따라서 스레드 안전이 보장되지 않는 ) gethostbyaddr ()gethostbyname () 이 재진입 getaddrinfo ()freeaddrinfo () 로 대체되었습니다 .

여기에서 패턴을 알아 차릴 수 있습니다. 보안 부족 (안전하게 구현할 수있는 충분한 정보를 서명에 포함하지 않았을 가능성이 있음) 또는 재전송하지 않는 것이 일반적인 사용 중단 원인입니다.

구식, 비
이식성 일부 다른 함수는 기능을 복제하고 다른 변형처럼 이식 가능하지 않기 때문에 단순히 더 이상 사용되지 않습니다. 예를 들어, bzero () 대신 memset () 대신 사용되지 않습니다 .

스레드 안전성 및 재진입
게시물에서 스레드 안전성 및 재진입에 대해 질문했습니다. 약간의 차이가 있습니다. 공유되고 변경 가능한 상태를 사용하지 않는 함수는 재진입 가능합니다. 예를 들어, 필요한 모든 정보가 함수로 전달되고 필요한 버퍼도 함수로 전달되면 (함수에 대한 모든 호출에서 공유하지 않고) 재진입이 가능합니다. 즉, 독립적 인 매개 변수를 사용하여 서로 다른 스레드가 실수로 상태를 공유 할 위험이 없습니다. 재진입은 스레드 안전성보다 더 강력한 보증입니다. 여러 스레드에서 동시에 사용할 수있는 함수는 스레드로부터 안전합니다. 다음과 같은 경우 함수는 스레드로부터 안전합니다.

  • 재진입 가능 (즉, 통화간에 상태를 공유하지 않음) 또는 :
  • 재진입은 아니지만 공유 상태에 필요한 동기화 / 잠금을 사용합니다.

일반적으로 단일 UNIX 사양IEEE 1003.1 (예 : “POSIX”)에서 재진입이 보장되지 않는 함수는 스레드로부터 안전하다고 보장되지 않습니다. 즉, 재진입이 보장되는 기능 만 다중 스레드 응용 프로그램에서 이식 가능하게 사용할 수 있습니다 (외부 잠금 없음). 그러나 이러한 표준의 구현이 재진입이 아닌 함수를 스레드 세이프하도록 선택할 수 없다는 것을 의미하지는 않습니다. 예를 들어, Linux는 스레드 안전성에 대한 보증 (단일 UNIX 사양을 넘어서)을 추가하기 위해 재진입이 아닌 함수에 동기화를 자주 추가합니다.

문자열 (일반적으로 메모리 버퍼)
또한 문자열 / 배열에 근본적인 결함이 있는지 물었습니다. 어떤 사람들은 이것이 사실이라고 주장 할 수 있지만, 저는 언어에 근본적인 결함이 없다고 주장합니다. C 및 C ++에서는 배열의 길이 / 용량을 별도로 전달해야합니다 (다른 언어에서와 같이 “.length”속성이 아님). 이것은 결함이 아닙니다. 모든 C 및 C ++ 개발자는 필요한 곳에 길이를 매개 변수로 전달하기 만하면 올바른 코드를 작성할 수 있습니다. 문제는이 정보를 필요로하는 여러 API가이를 매개 변수로 지정하지 못했다는 것입니다. 또는 일부 MAX_BUFFER_SIZE 상수가 사용된다고 가정합니다. 이러한 API는 이제 더 이상 사용되지 않으며 배열 / 버퍼 / 문자열 크기를 지정할 수있는 대체 API로 대체되었습니다.

Scanf (마지막 질문에 대한 답변)
개인적으로 C ++ iostreams 라이브러리 (std :: cin, std :: cout, << 및 >> 연산자, std :: getline, std :: istringstream, std :: ostringstream)를 사용합니다. 등), 그래서 나는 일반적으로 그것을 다루지 않습니다. 하지만 순수 C를 사용해야한다면 개인적으로 fgetc () 또는 getchar ()strtol () , strtoul () 등과 함께 사용하고 수동으로 구문 분석합니다. varargs 또는 형식 문자열. 즉, 내가 아는 한 [f] scanf () , [f] printf () 에는 문제가 없습니다.등. 형식 문자열을 직접 작성하는 한 임의의 형식 문자열을 전달하거나 사용자 입력이 형식 문자열로 사용되도록 허용하지 않으며 적절한 경우 <inttypes.h>에 정의 된 형식 지정 매크로를 사용합니다 . (참고로 snprintf ()sprintf () 대신 사용해야 하지만 형식 문자열의 사용이 아닌 대상 버퍼의 크기를 지정하지 못한 것과 관련이 있습니다.) C ++에서 boost :: format 은 varargs없이 printf와 같은 형식을 제공 한다는 점도 지적해야합니다 .


답변

다시 한 번 사람들은 str 함수의 “n”버전이 안전한 버전이라는 우스꽝스러운 주장을 만트라처럼 반복하고 있습니다.

그것이 그들이 의도 한 것이라면 항상 null로 문자열을 종료합니다.

“n”버전의 함수는 고정 길이 필드 (예 : 초기 파일 시스템의 디렉토리 항목)와 함께 사용하도록 작성되었습니다. 여기서 문자열이 필드를 채우지 않는 경우에만 널 종결자가 필요합니다. 이것은 또한 함수가 대체로 사용되는 경우 무의미하게 비효율적 인 이상한 부작용이있는 이유이기도합니다. 예를 들어 strncpy ()를 사용하십시오.

s2가 가리키는 배열이 n 바이트보다 짧은 문자열이면 모두 n 바이트가 기록 될 때까지 s1이 가리키는 배열의 복사본에 널 바이트가 추가됩니다.

파일 이름을 처리하기 위해 할당 된 버퍼는 일반적으로 4KB이기 때문에 성능이 크게 저하 될 수 있습니다.

“가정”안전한 버전을 원하면 항상 문자열을 nul로 종료하고 부작용이없는 strl 루틴 (strlcpy, strlcat 등)을 얻거나 직접 작성하십시오. 이들은 조용히 문자열을자를 수 있기 때문에 실제로 안전하지 않다는 점에 유의하십시오. 이것은 실제 프로그램에서 가장 좋은 조치는 아닙니다. 이것이 괜찮은 경우도 있지만 치명적인 결과를 초래할 수있는 상황도 많이 있습니다 (예 : 의료 처방전 인쇄).


답변

여기에 몇 가지 대답은 strncat()over 사용 을 제안합니다 strcat(). strncat()(및 strncpy())도 피해야한다고 제안합니다 . 올바르게 사용하기 어렵고 버그로 이어지는 문제가 있습니다.

  • 의 길이 매개 변수 strncat()는 대상 버퍼의 크기보다는 대상에 복사 할 수있는 최대 문자 수와 관련이 있습니다 (정확히 세 번째 점 참조). 이로 인해 strncat()특히 여러 항목이 대상에 연결되는 경우 사용하기 가 더 어려워집니다.
  • 결과가 잘 렸는지 확인하기 어려울 수 있습니다 (중요하거나 중요하지 않을 수 있음).
  • 일회성 오류가 발생하기 쉽습니다. C99 표준에 따르면 “이로 가리키는 배열에서 끝날 수있는 최대 문자 수 s1strlen(s1)+n+1strncat( s1, s2, n)

strncpy()또한 직관적 인 방식으로 사용하려고하면 버그가 발생할 수있는 문제가 있습니다. 대상이 null로 종료된다는 보장은 없습니다. '\0'(적어도 특정 상황에서는) 버퍼의 마지막 위치에 직접 을 드롭하여 해당 코너 케이스를 구체적으로 처리해야합니다 .

나는 OpenBSD strlcat()와 같은 것을 사용하는 것이 좋습니다. strlcpy()(어떤 사람들은 그 기능을 싫어한다는 것을 알고 있습니다. strncat()/ 보다 안전하게 사용하기가 훨씬 쉽다고 생각합니다 strncpy()).

Todd Miller와 Theo de Raadt가 strncat()및 문제에 대해 말한 내용은 다음 과 strncpy()같습니다.

때 발생하는 몇 가지 문제가 있습니다 strncpy()strncat()안전한 버전으로 사용 strcpy()하고 strcat(). 두 함수 모두 NUL 종료와 길이 매개 변수를 서로 다르고 직관적이지 않은 방식으로 처리하여 숙련 된 프로그래머조차도 혼란스럽게합니다. 또한 잘림이 발생하는시기를 쉽게 감지 할 수있는 방법을 제공하지 않습니다. … 이러한 모든 문제 중에서 길이 매개 변수로 인한 혼란과 NUL 종료 관련 문제가 가장 중요합니다. 잠재적 인 보안 허점에 대해 OpenBSD 소스 트리를 감사했을 때 strncpy()strncat(). 이 모든 것이 악용 가능한 보안 허점을 초래하지는 않았지만 안전한 문자열 작업 사용 strncpy()및 사용에 대한 규칙 strncat()이 널리 오해되고 있음을 분명히했습니다 .

OpenBSD의 보안 감사는 이러한 기능에 대한 버그가 “만연”하다는 것을 발견했습니다. 과 달리 gets()이러한 기능 안전하게 사용할 수 있지만 실제로는 인터페이스가 혼란스럽고 직관적이지 않으며 올바르게 사용하기 어렵 기 때문에 많은 문제가 있습니다. Microsoft도 분석을 수행했으며 (그들이 게시했을 수있는 데이터의 양을 알 수는 없지만) 결과적으로 금지되었습니다 (또는 최소한 매우 권장하지 않음- ‘금지’가 절대적이지 않을 수 있음). 의 사용 strncat()strncpy()(주 기능).

추가 정보가있는 일부 링크 :


답변

절대 사용 해서는 안되는 표준 라이브러리 함수 :

setjmp.h

  • setjmp(). 와 함께 longjmp()이러한 함수는 사용하기에 매우 위험한 것으로 널리 인식되고 있습니다. 이러한 함수는 스파게티 프로그래밍으로 이어지고, 정의되지 않은 다양한 형태의 동작과 함께 제공되며, 스택에 저장된 값에 영향을주는 등 프로그램 환경에서 의도하지 않은 부작용을 일으킬 수 있습니다. 참조 : MISRA-C : 2012 규칙 21.4, CERT C MSC22-C .
  • longjmp(). 을 참조하십시오 setjmp().

stdio.h

  • gets(). 이 함수는 디자인에 따라 안전하지 않았기 때문에 C 언어 (C11에 따라)에서 제거되었습니다. 이 함수는 이미 C99에서 사용되지 않는 것으로 플래그가 지정되었습니다. fgets()대신 사용하십시오 . 참조 : ISO 9899 : 2011 K.3.5.4.1, 참고 404 참조.

stdlib.h

  • atoi()기능 군. 이들은 오류 처리가 없지만 오류가 발생할 때마다 정의되지 않은 동작을 호출합니다. 기능 strtol()군 으로 대체 할 수있는 완전히 불필요한 기능. 참조 : MISRA-C : 2012 규칙 21.7.

string.h

  • strncat(). 종종 오용되는 어색한 인터페이스가 있습니다. 대부분 불필요한 기능입니다. 에 대한 설명도 참조하십시오 strncpy().
  • strncpy(). 이 함수의 의도는 strcpy(). 유일한 목적은 항상 유닉스 시스템에서 고대 문자열 형식을 처리하는 것이었고 표준 라이브러리에 포함 된 것은 알려진 실수입니다. 이 함수는 null 종료없이 문자열을 떠날 수 있고 프로그래머가 종종 잘못 사용하는 것으로 알려져 있기 때문에 위험합니다. 참조 : strlcpy 및 strlcat이 안전하지 않은 것으로 간주되는 이유는 무엇입니까? .

주의해서 사용해야하는 표준 라이브러리 함수 :

assert.h

  • assert(). 오버 헤드가 발생하며 일반적으로 프로덕션 코드에서 사용해서는 안됩니다. 오류를 표시하지만 반드시 전체 프로그램을 종료하지 않는 응용 프로그램 별 오류 처리기를 사용하는 것이 좋습니다.

signal.h

stdarg.h

  • va_arg()기능 군. C 프로그램에 가변 길이 기능이 있다는 것은 거의 항상 잘못된 프로그램 설계를 나타냅니다. 매우 구체적인 요구 사항이없는 한 피해야합니다.

stdio.h
일반적 으로이 전체 라이브러리는 잘못 정의 된 동작과 형 안전성이 떨어지는 수많은 경우가 있기 때문에 프로덕션 코드에는 권장되지 않습니다 .

  • fflush(). 출력 스트림에 사용하기에 완벽합니다. 입력 스트림에 사용되는 경우 정의되지 않은 동작을 호출합니다.
  • gets_s(). gets()C11 경계 검사 인터페이스 에 포함 된 안전한 버전입니다 . fgets()C 표준 권장 사항에 따라 대신 사용 하는 것이 좋습니다. 참조 : ISO 9899 : 2011 K.3.5.4.1.
  • printf()기능 군. 정의되지 않은 동작이 많고 형식 안전성이 좋지 않은 리소스가 많은 함수입니다. sprintf()또한 취약점이 있습니다. 이러한 기능은 프로덕션 코드에서 피해야합니다. 참조 : MISRA-C : 2012 규칙 21.6.
  • scanf()기능 군. 에 대한 설명을 참조하십시오 printf(). 또한- scanf()올바르게 사용하지 않으면 버퍼 오버런에 취약합니다. fgets()가능하면 사용하는 것이 좋습니다. 참조 : CERT C INT05-C , MISRA-C : 2012 규칙 21.6.
  • tmpfile()기능 군. 다양한 취약성 문제가 있습니다. 참조 : CERT C FIO21-C .

stdlib.h

  • malloc()기능 군. C90에서 잘 알려진 문제를 알고 있으므로 결과를 캐스팅하지 않아도 호스트 시스템에서 사용하기에 완벽합니다 . 그만큼malloc()기능의 제품군은 애플리케이션을 자립에 사용해서는 안됩니다. 참조 : MISRA-C : 2012 규칙 21.3.

    또한 realloc()이전 포인터를의 결과로 덮어 쓰는 경우 위험 realloc()합니다. 함수가 실패하면 누수가 발생합니다.

  • system(). 많은 오버 헤드가 있으며 이식 가능하지만 시스템 별 API 함수를 대신 사용하는 것이 더 좋습니다. 잘못 정의 된 다양한 동작이 제공됩니다. 참조 : CERT C ENV33-C .

string.h

  • strcat(). 에 대한 설명을 참조하십시오 strcpy().
  • strcpy(). 복사 할 데이터의 크기를 알 수 없거나 대상 버퍼보다 ​​크지 않는 한 사용하기에 완벽합니다. 들어오는 데이터 크기를 확인하지 않으면 버퍼 오버런이있을 수 있습니다. strcpy()그 자체의 잘못은 아니지만 호출 응용 프로그램의 strcpy()안전하지 않은 것은 대부분 Microsoft에서 만든 신화입니다 .
  • strtok(). 호출자 문자열을 변경하고 내부 상태 변수를 사용하므로 다중 스레드 환경에서 안전하지 않을 수 있습니다.


답변

어떤 사람들은 그 주장 것 strcpystrcat찬성, 피해야 strncpy하고 strncat. 이것은 제 생각에 다소 주관적입니다.

사용자 입력을 다룰 때는 반드시 피해야합니다. 의심 할 여지가 없습니다.

코드에서 “지금까지”당신이 사용자로부터 만 알고 버퍼가 충분히있다, strcpy그리고 strcat을 계산하기 때문에 좀 더 효율적으로 할 수있다 n그들의 사촌에 전달하는 것은 불필요 할 수있다.


답변

기피

  • strtok 다중 스레드 프로그램의 경우 스레드로부터 안전하지 않습니다.
  • gets 버퍼 오버플로를 일으킬 수 있으므로


답변

이름이 암시하는 strncpy()범용 대체품이 아닌 것을 다시 추가 할 가치가 strcpy()있습니다. 널 종료자가 필요하지 않은 고정 길이 필드 용으로 설계되었습니다 (원래 UNIX 디렉토리 항목과 함께 사용하도록 설계되었지만 암호화 키 필드와 같은 항목에 유용 할 수 있음).

그러나 다음 strncat()의 대체물 로 사용하는 것은 쉽습니다 strcpy().

if (dest_size > 0)
{
    dest[0] = '\0';
    strncat(dest, source, dest_size - 1);
}

( 확실히 0이 아닌 if것을 알고있는 일반적인 경우에는 테스트가 삭제 될 수 있습니다 dest_size.)