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


90

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

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

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


3
아는 것이 아주 좋은 것이라고 생각합니다. C의 일부 기능은 실제로 피해야하며 교육 목적으로 사용해야합니다.
INS

@Felix, 앞으로 하나의 (정답으로 표시된) 답을 편집하는 것이 더 쉬울 것입니다. 시간이 지남에 따라 바뀔 가능성이있는 답변과 관련하여 실제로 대기 중입니다. 아마 Jeff는 향후 몇 년 동안 최신 답변을 유지하는 사람들에게 '관리자'배지를 제공 할 것입니다.
Tim Post

1
@Tim Post : 좋아요, 댓글을 삭제하겠습니다.
Felix Kling 2010-04-02

3
strncpy()대한 일반적인 대체물로 strcpy()사용하지 않을 것이며 strncat()상상할 수있는 가장 직관적이지 않은 인터페이스를 가지고 있기 때문에 절대 사용하지 않을 것입니다. 길이 매개 변수가 무엇을 지정하는지 알고 있습니까?
Jonathan Leffler

2
strncpy와 strncat을 사용하는 것은 거의 항상 실수입니다. strcpy 및 strcat 대신 사용해서는 안됩니다 !! scanf와 sprintf는 사용 방법을 안다면 완벽하게 사용할 수 있습니다 ...
R .. GitHub STOP HELPING ICE

답변:


58

더 이상 사용되지 않는 함수
안전하지 않음 이러한 함수
의 완벽한 예는 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와 같은 형식을 제공 한다는 점도 지적해야합니다 .


4
"Deprecated"는 강력한 단어로, C ++ 표준을 논의 할 때 특정한 의미를 갖습니다. 그런 의미에서 gets (), strcpy () 등은 더 이상 사용되지 않습니다.

4
"C 표준에서 더 이상 사용되지 않음", "Michael Aaron Safyan에 의해 더 이상 사용되지 않음"및 "[인용 필요]에 대해 잘 알고있는 알 수없는 사람에 의해 사용되지 않음"을 구분하는 한. 질문 C 표준이 아니라 선호하는 코딩 스타일에 관한 것이므로 두 번째 두 가지는 적절합니다. 그러나 Neil처럼 나는 당신의 진술이 첫 번째 의미를 암시하려는 것이 아니라는 것을 깨닫기 전에 이중 시험을 요구했습니다.
Steve Jessop

11
strncpy또한 일반적으로 피해야합니다. 대부분의 프로그래머가 가정하는대로 수행하지 않습니다. 종료를 보장하지 않으며 (버퍼 오버런으로 이어짐) 더 짧은 문자열을 채 웁니다 (경우에 따라 성능이 저하 될 수 있음).
Adrian McCarthy

2
@Adrian : 동의합니다 . n-less 변형에 대한 현명한 대체 도 strncpy()아니고 더 나쁜 것도 아닙니다 strncat().
Jonathan Leffler

4
이 답변은 "strcpy를 strncpy로 대체하십시오. 이유는 모르겠지만 Microsoft가 저에게 지시합니다."라는 말도 안되는 교리를 퍼뜨립니다. strncpy는 strcpy의 안전한 버전이 아닙니다! 겉으로는 훨씬 더 안전하지 않습니다. strlcpy 및 strlcat이 안전하지 않은 것으로 간주되는 이유를 참조하십시오 . .
Lundin

24

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

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

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

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

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

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


1
당신은 옳지 만 strncpy(), 틀 렸습니다 strncat(). strncat()고정 길이 필드와 함께 사용하도록 설계되지 않았습니다. 실제로 strcat()연결되는 문자 수를 제한 하도록 설계되었습니다 . strcat()여러 연결을 수행 할 때 버퍼에 남아있는 공간을 추적 하여이를 "안전한 "것으로 사용하는 것이 매우 쉽고 "안전한 "으로 사용하는 것이 더 쉽습니다 strcpy()(대상 버퍼의 첫 번째 문자를 '\0'before 로 설정하여 그것을 호출). strncat() 항상 대상 문자열을 종료하고 추가 '\0's를 작성하지 않습니다 .
caf

2
@caf-예,하지만 strncat ()은 대상 버퍼의 길이가 아닌 복사 할 최대 길이를 매개 변수로 사용하므로 완전히 쓸모가 없습니다. 버퍼 오버 플로우를 방지하려면 현재 대상 문자열의 길이를 알아야합니다. 그 이유를 알고 있다면 strlcat () 소스 문자열이 아닌 strncat ()을 다시 사용해야하는 이유를 알고 있어야합니다. 대상 문자열의 끝입니다.
Dipstick 2010-04-02

그것은 당신 strncat()이 그것이 항상 목적지를 무효화 하는 것은 아니며 고정 길이 필드와 함께 사용하도록 설계 되었다는 것을 암시한다는 사실을 여전히 바꾸지 않습니다. 둘 다 틀 렸습니다.
caf

2
@chrisharris : strncat()소스 문자열의 길이에 관계없이 제대로 작동하지만 strcat()그렇지 않습니다. 여기서 문제 strlcat()는 표준 C 함수가 아니라는 것입니다.
David Thornley 2010-04-02

@caf-strncat ()에 대해 정확합니다. 나는 위에서 지적했듯이 완전히 쓸모가 없기 때문에 실제로 사용하지 않았습니다. 따라서 여전히 피해야합니다.
Dipstick 2010-04-02

19

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

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

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


1
문자열 외부의 문자열 길이를 추적하는 것이 훨씬 좋습니다. 이와 같이 두 문자열 (-with-length)을 안전하게 연결하는 것은 간단한 계산 (필요한 버퍼 크기를 얻기 위해) 가능한 재 할당 및 memmove(). (글쎄, memcpy()문자열이 독립적 인 일반적인 경우에 사용할 수 있습니다 .)
Donal Fellows

1
두 번째 요점은 잘못되었습니다. strncat() 항상 대상 문자열을 종료합니다.
caf

1
두 번째 요점은에 대해 잘못되었습니다 strncat(). 그러나 strncpy()다른 문제가있는에 대해 정확합니다 . strncat()을위한 합리적인 대체 strcat()하지만 strncpy()위한 합리적인 대체하지 않습니다 strcpy().
David Thornley 2010-04-02

2
caf와 David는 strncat()항상 null로 끝나지 않는 내 주장에 대해 100 % 옳습니다 . 나는의 동작 strncat()strncpy()약간 을 혼동했습니다 (피해야 할 기능인 또 다른 이유-비슷한 동작을 암시하는 이름이 있지만 실제로는 중요한 방식으로 다르게 동작합니다 ...). 이 문제를 수정하고 추가 정보를 추가하기 위해 내 답변을 수정했습니다.
Michael Burr

1
참고 char str[N] = ""; strncat(str, "long string", sizeof(str));N이 충분히 큰이되지 않을 경우 버퍼 오버 플로우입니다. 이 strncat()기능은 오용하기 너무 쉽습니다. 사용해서는 안됩니다. strncat()안전하게 사용할 수 있다면 memmove()또는 memcpy()대신 사용할 수 있습니다 (더 효율적일 것입니다).
Jonathan Leffler

9

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

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(). 호출자 문자열을 변경하고 내부 상태 변수를 사용하므로 다중 스레드 환경에서 안전하지 않을 수 있습니다.

컴파일 타임에 조건을 해결할 수 있다면 static_assert()대신 추천하는 것이 좋습니다 assert(). 또한 sprintf()거의 항상 교체 할 수 있으므로 snprintf()조금 더 안전합니다.
user694733

1
strtok()함수 A에서 사용 한다는 것은 (a) 함수가 strtok()A가 사용하는 동안에 도 사용 하는 다른 함수를 호출 할 수 없음을 의미하고, (b) A를 호출하는 함수가 A를 호출 할 때 사용할 수 없음을 의미 strtok()합니다. 즉, using strtok()콜 체인을 독살합니다. strtok()다른 사용자가 strtok()라이브러리 코드를 호출하는 것을 방지하기 위해 사용 하는 것을 문서화해야하기 때문에 라이브러리 코드에서 안전하게 사용할 수 없습니다 .
Jonathan Leffler

strncpy제로 기록 할 때 사용하기에 적합한 함수 패딩 제로 종료 문자열 또는 크기가 큰 대상으로서 적어도되는 제로 패딩의 버퍼 중 하나로부터 취해진 데이터와 문자열 버퍼. 제로 패딩 된 버퍼는 그다지 일반적이지 않지만 정확히 모호하지는 않습니다.
supercat

7

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

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

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


4
그리고 더 좋은 방법은 가능한 경우 strlcatstrlcpy대상 문자열의 'N'버전이 없기 때문에 보증 NULL 종료.
Dan Andreatta 2010

나에게 조기 최적화처럼 들립니다. 그러나 IIRC strncpy()n필요한 경우 nul 문자를 사용하여 정확히 바이트를 기록 합니다. Dan으로서 안전한 버전을 사용하는 것이 최선의 선택입니다.
Bastien Léonard

@Dan, 이러한 기능은 불행히도 표준이 아니므로이 토론에 속하지 않습니다
Eli Bendersky

@Eli :하지만 strncat()정확하고 안전하게 사용하기 어려울 수 있다는 사실 (그리고 피해야 한다는 사실 )이 주제입니다.
Michael Burr

@Michael : 정확하고 안전하게 사용하는 것이 어렵다고 말하지 않겠습니다. 조심해서 잘 작동합니다
Eli Bendersky 2010-04-02

6

기피

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

2
그들은 약간 다른 경우입니다. 프로그램이 다중 스레드가 아니거나 어떻게 든 그것에 대한 액세스를 잠근다는 것을 알고 있다면 strtok를 안전하게 사용할 수 있지만 안전하게 사용할 수 없으므로 절대 사용해서는 안됩니다.
jcoder 2010

9
의 문제는 strtok()스레드 안전성을 약간 뛰어 넘습니다. 사용 strtok()하는 동안 코드에서 호출 할 수있는 함수가 없다는 것을 확실히 알지 못하는 한 단일 스레드 프로그램에서도 안전 하지 않습니다 (또는 사용 하지 마십시오 (또는 strtok()상태를 엉망으로 만들 것입니다). 당신 아래에서). 사실, 다중 스레드 플랫폼을 대상으로하는 대부분의 컴파일러는 의 정적 데이터에 strtok()대해 스레드 로컬 저장소를 사용하여 스레드가 이동하는 한의 잠재적 인 문제를 strtok()처리합니다. 그러나 그것은 당신이 (동일한 스레드에있는) 동안 그것을 사용하는 다른 함수의 문제를 여전히 해결하지 못합니다.
Michael Burr

실제로 strtok가 많은 문제를 겪고 있기 때문에 누구에게도 strtok를 사용하도록 권장하고 싶지 않기 때문에 지금 당장 입을 다물 것입니다. 안전하게 사용하는 것이 가능 하지만 안전하게 사용하는 것은 불가능 하지만 Get과는 다르다는 점을 지적하고 싶었습니다 .
jcoder

@Jimmy : 종종 비표준 라이브러리 확장이 있거나 직접 작성할 수 있습니다. 에 큰 문제는 strtok()그렇게 가장 좋은 대체 주위 버퍼 값을 유지하고 그것을 전달하는 요구에 작업에 정확하게 하나의 버퍼를 유지한다는 것이다.
데이빗 쏜리

strcspn필요한 대부분의 작업을 수행합니다. 다음 토큰 구분 기호를 찾습니다. 의 정상적인 변형을 다시 구현할 수 있습니다 strtok.
bluss

5

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

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

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

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


5

또한 Microsoft의 금지 된 API 목록을 확인하십시오 . 이들은 종종 오용되고 보안 문제를 유발하기 때문에 Microsoft 코드에서 금지 된 API (여기에 이미 나열된 많은 API 포함)입니다.

그들 모두에 동의하지 않을 수도 있지만 모두 고려할 가치가 있습니다. 오용으로 인해 많은 보안 버그가 발생했을 때 API를 목록에 추가합니다.


2

NUL로 끝나는 문자열을 다루는 거의 모든 함수는 잠재적으로 안전하지 않습니다. 외부 세계에서 데이터를 수신하고 str * () 함수를 통해 데이터를 조작하는 경우 재앙에 대비할 수 있습니다.


2

sprintf를 잊지 마세요. 이것은 많은 문제의 원인입니다. 대안 인 snprintf에는 때때로 다른 구현이있어 코드를 이식 불가능하게 만들 수 있기 때문에 사실입니다.

  1. 리눅스 : http://linux.die.net/man/3/snprintf

  2. 창 : http://msdn.microsoft.com/en-us/library/2ts7cx93%28VS.71%29.aspx

1 (리눅스)의 경우 반환 값은 전체 버퍼를 저장하는 데 필요한 데이터의 양입니다 (주어진 버퍼의 크기보다 작 으면 출력이 잘림).

2 (windows)의 경우 출력이 잘린 경우 반환 값은 음수입니다.

일반적으로 다음과 같은 기능은 피해야합니다.

  1. 버퍼 오버 플로우 안전 (많은 기능이 이미 여기에 언급되어 있음)

  2. 스레드 안전 / 재진입이 아님 (예 : strtok)

각 기능의 매뉴얼에서 다음과 같은 키워드를 검색해야합니다 : safe, sync, async, thread, buffer, bugs


2
_sprintf()표준 snprintf()IIRC가 도착 하기 전에 Microsoft에서 만들었습니다 . ´StringCbPrintf () ´snprintf()그래도 매우 유사합니다 .
Bastien Léonard

당신이 사용할 수있는 sprintf경우에 안전하게 어떻게 든 : sprintf(buffer,"%10s",input);제한 복사 NB가있는 경우 (10 바이트 buffer입니다 char buffer[11]데이터가 바람더라도 그립니다 안전합니다.
장 - 프랑수아 파브르

2

scanf안전하게 사용하는 것은 매우 어렵습니다 . 를 잘 사용하면 scanf버퍼 오버플 로 를 피할 수 있지만 요청 된 유형에 맞지 않는 숫자를 읽을 때 정의되지 않은 동작에 여전히 취약합니다. 대부분의 경우에서 fgets자체 분석 한 다음 (사용 sscanf, strchr등은) 더 나은 옵션입니다.

그러나 나는 " scanf항상 피하라"고 말하지 않을 것이다 . scanf그 용도가 있습니다. 예를 들어, char10 바이트 길이의 배열 에서 사용자 입력을 읽고 싶다고 가정 해 보겠습니다 . 후행 줄 바꿈이 있으면 제거하려고합니다. 사용자가 개행 문자 앞에 9 개 이상의 문자를 입력하면 버퍼에 처음 9 개 문자를 저장하고 다음 개행까지 모든 것을 버립니다. 넌 할 수있어:

char buf[10];
scanf("%9[^\n]%*[^\n]", buf));
getchar();

이 관용구에 익숙해지면 다음보다 짧고 어떤면에서 더 깔끔합니다.

char buf[10];
if (fgets(buf, sizeof buf, stdin) != NULL) {
    char *nl;
    if ((nl = strrchr(buf, '\n')) == NULL) {
        int c;
        while ((c = getchar()) != EOF && c != '\n') {
            ;
        }
    } else {
        *nl = 0;
    }
}

0

모든 문자열 복사 / 이동 시나리오-strcat (), strncat (), strcpy (), strncpy () 등- 몇 가지 간단한 휴리스틱을 적용하면 상황이 훨씬 더 좋아집니다 ( 안전함 ).

   1. 항상 NUL 채우기 데이터를 추가하기 전에 버퍼를.
   2. 매크로 상수를 사용하여 문자 버퍼를 [SIZE + 1]로 선언합니다.

예를 들면 다음과 같습니다.

#define   BUFSIZE   10
char      Buffer[BUFSIZE+1] = { 0x00 };  /* The compiler NUL-fills the rest */

다음과 같은 코드를 사용할 수 있습니다.

memset(Buffer,0x00,sizeof(Buffer));
strncpy(Buffer,BUFSIZE,"12345678901234567890");

비교적 안전하게. memset ()은 컴파일 타임에 Buffer를 초기화 했음에도 불구하고 strncpy () 앞에 나타나야합니다. 왜냐하면 함수가 호출되기 전에 다른 코드가 어떤 가비지 안에 있는지 알지 못하기 때문입니다. strncpy ()는 복사 된 데이터를 "1234567890"으로 자르고 NUL 종료 하지 않습니다 . 그러나 BUFSIZE가 아닌 sizeof (Buffer) 버퍼 전체를 이미 NUL로 채웠으므로 BUFSIZE를 사용하여 쓰기를 제한하는 한 NUL을 종료하는 최종 "범위 외"가 보장됩니다. sizeof (Buffer) 대신 상수.

버퍼 및 BUFSIZE는 snprintf ()에서도 잘 작동합니다.

memset(Buffer,0x00,sizeof(Buffer));
if(snprintf(Buffer,BUFIZE,"Data: %s","Too much data") > BUFSIZE) {
    /* Do some error-handling */
}   /* If using MFC, you need if(... < 0), instead */

snprintf ()가 특별히 BUFIZE-1 문자 만 쓰고이어서 NUL을 작성하더라도 안전하게 작동합니다. 그래서 우리는 Buffer 끝에 불필요한 NUL 바이트를 "낭비"합니다. 아주 적은 메모리 비용으로 버퍼 오버플로와 종료되지 않은 문자열 조건을 모두 방지합니다.

strcat () 및 strncat ()에 대한 내 호출은 더 강경합니다. 사용하지 마십시오. strcat ()을 안전하게 사용하는 것은 어렵고 strncat ()에 대한 API는 너무 직관적이지 않아이를 적절히 사용하는 데 필요한 노력이 모든 이점을 무효화합니다. 다음 드롭 인을 제안합니다.

#define strncat(target,source,bufsize) snprintf(target,source,"%s%s",target,source)

strcat () 드롭 인을 생성하고 싶지만 좋은 생각은 아닙니다.

#define strcat(target,source) snprintf(target,sizeof(target),"%s%s",target,source)

대상이 포인터 일 수 있기 때문입니다 (따라서 sizeof ()는 필요한 정보를 반환하지 않습니다). 코드에있는 strcat () 인스턴스에 대한 좋은 "보편적 인"솔루션이 없습니다.

"strFunc () 인식"프로그래머가 자주 접하는 문제는 strlen ()을 사용하여 버퍼 오버플로를 방지하려는 시도입니다. 내용이 NUL 종료가 보장되는 경우 괜찮습니다. 그렇지 않으면 보호하려는 "문제가있는"코드에 도달하기 전에 strlen () 자체가 버퍼 오버런 오류 (일반적으로 세분화 위반 또는 기타 코어 덤프 상황으로 이어짐)를 일으킬 수 있습니다.


-2

atoi는 스레드로부터 안전하지 않습니다. man 페이지의 권장 사항에 따라 대신 strtol을 사용합니다.


5
이것은 하나의 특정 구현에 적용되는 것처럼 들립니다. strtol()스레드로부터 안전하고 atoi()그렇지 않을 이유 가 없습니다.
David Thornley 2010-04-02

2
strtol 사용에 대한 권장 사항은 스레드 안전성과는 관련이 없지만 오류 처리와 관련이 있습니다. 어느 쪽에서 해당 정보를 얻었는지 확실하지 않습니다. 아래에서 권장 사항을 찾을 수 없습니다 man atoi(하지만 있어야합니다).
Lundin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.