source

표준 라이브러리에서 피해야 할 기능은 무엇입니까?

goodcode 2022. 8. 15. 21:11
반응형

표준 라이브러리에서 피해야 할 기능은 무엇입니까?

Stack Overflow에서 읽은 바로는 일부 C 함수는 "구식"이거나 "피해야 한다"고 합니다.이러한 기능의 예와 이유를 가르쳐 주시겠습니까?

이러한 기능에는 어떤 대안이 있습니까?

안전하게 사용할 수 있습니까? 좋은 방법이 있습니까?

사용되지 않는 기능
안전하지 않다
이러한 함수의 가장 좋은 예는 gets()입니다.이것은, 행선지 버퍼의 크기를 나타내는 방법이 없기 때문입니다.따라서 gets()를 사용하여 입력을 읽는 프로그램에는 버퍼 오버플로 취약성이 있습니다.같은 이유로 strcpy() 대신 strncpy()를 사용하고 strcat() 대신 strncpy()를 사용해야 합니다.

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

재신청자 없음
기타 예로는 gethostbyaddr()gethostbyname()이 있습니다.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를 사용해야 한다면 저는 개인적으로 strtol()이나 strtool() 등과 함께 fgetc() 또는 getchar()를 사용하여 수동으로 해석할 것입니다.왜냐하면 저는 vargs나 포맷 문자열의 광팬이 아니기 때문입니다.즉, 포맷 문자열을 직접 작성하는 한 [f]scanf()[f]printf() 등에는 문제가 없습니다.또한 임의의 포맷 문자열을 전달하거나 사용자 입력을 포맷 문자열로 사용하지 않고 <inttypes>에 정의된 포맷 매크로를 사용합니다.h > 를 누릅니다.(snprintf()sprintf() 대신 사용해야 하는데, 이는 형식 문자열이 아닌 수신처 버퍼의 크기를 지정하지 않은 것과 관련이 있습니다).또한 C++에서는 boost:: format이 varargs 없이 printf와 같은 포맷을 제공한다는 점도 지적해야 합니다.

절대 사용하지 않는 표준 라이브러리 기능:

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()Unix 시스템에서 오래된 문자열 형식을 처리하는 것이 유일한 목적이었고 표준 라이브러리에 포함된 것은 알려진 실수입니다.이 함수는 문자열이 늘 끝 없이 남아 있을 수 있고 프로그래머가 잘못 사용하는 경우가 많기 때문에 위험합니다.참고 자료:strlcpy와 strlcat은 안전하지 않은 으로 간주됩니까?자세한 설명은 다음과 같습니다.strcpy는 위험하고 대신 무엇을 사용해야 하는가?

주의해야 할 표준 라이브러리 기능:

assert.h

  • assert()오버헤드와 함께 제공되며 일반적으로 프로덕션 코드에 사용하지 마십시오.오류를 표시하지만 프로그램 전체를 종료할 필요는 없는 응용 프로그램별 오류 핸들러를 사용하는 것이 좋습니다.

signal.h

  • signal()참고 자료: MISRA-C: 2012 규칙 21.5, CERT C SIG32-C.

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()발신자 문자열을 변경하고 내부 상태 변수를 사용합니다. 따라서 멀티 스레드 환경에서는 안전하지 않을 수 있습니다.

여기에서는, 다음의 몇개의 회답에 의해,strncat()위에strcat(); 나는 그것을 추천한다.strncat()(그리고strncpy())도 피해야 합니다.올바르게 사용하기 어렵고 버그로 이어지는 문제가 있습니다.

  • 길이 파라미터strncat()는, 행선지 버퍼의 사이즈가 아니고, 행선지에 카피할 수 있는 최대 문자수에 관련하고 있습니다(정확히는 아닙니다만, 3번째 포인트를 참조).이렇게 하면strncat()특히 여러 항목이 대상에 연결될 경우, 사용해야 하는 것보다 더 사용하기 어렵습니다.
  • 결과가 잘렸는지 확인하는 것은 어려울 수 있다(중요할 수도 있고 중요하지 않을 수도 있다).
  • 하나씩 틀리기 쉽습니다.C99 표준에서는 다음과 같이 기술되어 있습니다.따라서 어레이에 포함될 수 있는 최대 글자 수는s1strlen(s1)+n+1"와 같은 콜의 경우strncat( s1, s2, n)

strncpy()또, 직관적으로 사용하려고 하면 버그가 발생할 수 있는 문제도 있습니다.이것은, 수신처가 null로 종료하는 것을 보증하는 것은 아닙니다.이 코너 케이스를 확실하게 처리하려면 , 이 코너 케이스를 드롭 해 주세요.'\0'(적어도 특정 상황에서는) 직접 버퍼의 마지막 위치에 있습니다.

오픈비 같은 걸 쓰는 게 좋을 것 같아요.SD의strlcat()그리고.strlcpy()(이러한 기능을 싫어하는 사람도 있습니다만, 보다 훨씬 사용하기 쉽다고 생각합니다.strncat()/strncpy()).

Todd Miller와 Theo de Raadt가 말한 문제점은 다음과 같습니다.strncat()그리고.strncpy():

다음과 같은 경우 몇 가지 문제가 발생합니다.strncpy()그리고.strncat()의 안전한 버전으로 사용됩니다.strcpy()그리고.strcat()두 함수 모두 NUL 종단 및 길이 매개 변수를 경험 많은 프로그래머도 혼동할 수 있는 서로 다른 방식으로 처리합니다.또, 잘라낸 타이밍을 검출하는 간단한 방법도 없습니다.이러한 모든 문제 중에서 길이 파라미터에 의한 혼란과 NUL 종료의 관련 문제가 가장 중요합니다.OpenB를 감사할 때잠재적인 보안 취약점에 대한 SD 소스 트리의 오남용이 만연함을 발견했습니다.strncpy()그리고.strncat()이 모든 것이 악용 가능한 보안 취약점을 초래한 것은 아니지만, 그들은 사용 규칙을 명확히 했습니다.strncpy()그리고.strncat()안전 스트링 조작에 대해서는 널리 오해되고 있습니다.

OpenBSD의 보안 감사 결과 이러한 기능을 가진 버그가 "난폭한" 것으로 나타났습니다.와는 달리gets()이러한 기능은 안전하게 사용할 수 있지만 실제로는 인터페이스가 혼란스럽고 의도적이지 않으며 올바르게 사용하기 어렵기 때문에 많은 문제가 있습니다.Microsoft가 분석을 실시한 것도 알고 있습니다(그들이 발표한 데이터의 양을 알 수 없습니다).그 결과, 「금기」의 사용이 금지(또는 적어도 매우 권장되지 않음)되고 있는 것은 절대적인 것은 아닐 수 있습니다.strncat()그리고.strncpy()(다른 기능 중에서도).

상세 정보가 있는 링크:

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

만약 그것이 그들이 의도한 것이라면 그들은 항상 문자열을 종료할 것이다.

함수의 "n" 버전은 문자열이 필드를 채우지 않는 경우에만 nul 터미네이터가 필요한 고정 길이 필드(예: 초기 파일 시스템의 디렉토리 엔트리)와 함께 사용하도록 작성되었습니다.이것이 함수에 이상한 부작용이 생기는 이유이기도 합니다.이러한 부작용은 치환용으로만 사용해도 무의미합니다.예를 들어 strncpy()를 사용합니다.

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

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

안전한 버전을 원한다면 스트링을 항상 종료하고 부작용이 없는 스트링 루틴(strlcpy, strlcat 등)을 얻거나 직접 작성하십시오.이러한 프로그램들은 문자열을 소리 없이 잘라낼 수 있기 때문에 안전하지 않다는 점에 유의하시기 바랍니다.실제 프로그램에서는 이것이 최선의 행동 방침이 되는 경우는 거의 없습니다.이것이 정상인 경우도 있지만, 치명적인 결과를 초래할 수 있는 상황(예: 의료 처방전 인쇄)도 많이 있습니다.

매우 사용하기 어렵다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;
    }
}

마이크로소프트의 금지된 API 목록도 확인하십시오.이러한 API(이미 여기에 기재되어 있는 API 포함)는 오용되어 보안 문제로 이어지는 경우가 많기 때문에 Microsoft 코드에서 금지되어 있습니다.

여러분은 이 모든 것에 동의하지 않을 수도 있지만, 모두 고려해 볼 가치가 있습니다.API의 오용으로 인해 다수의 보안 버그가 발생했을 때 목록에 API를 추가합니다.

아마 다시 추가할 가치가 있을 것이다.strncpy()의 범용 대체품이 아닙니다.strcpy()그 이름이 시사하는 바입니다.nul 터미네이터가 필요 없는 고정 길이 필드를 위해 설계되었습니다(원래 UNIX 디렉토리 엔트리와 함께 사용하도록 설계되었지만 암호화 키 필드 등에 유용합니다).

단, 사용하기 쉽다.strncat()을 대신해서strcpy():

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

(the.if일반적인 케이스에서는 테스트를 폐기할 수 있습니다.이 케이스에서는, 다음과 같은 것을 알 수 있습니다.dest_size0이 아닌 것은 확실합니다.

라고 주장하는 사람도 있을 것이다.strcpy그리고.strcat을 위해 피해야 한다strncpy그리고.strncat제 생각에 이것은 다소 주관적인 것 같습니다.

사용자 입력을 처리할 때 이러한 정보는 절대 피해야 합니다. 의심의 여지가 없습니다.

사용자로부터의 코드 「멀리」에서는, 버퍼가 충분히 길다고 판단했을 때에,strcpy그리고.strcat보다 효율적일 수 있습니다.n그들의 사촌에게 물려주는 것은 불필요할 수 있다.

피하다

  • strtok멀티스레드 프로그램의 경우 스레드 세이프가 아닙니다.
  • gets버퍼 오버플로를 일으킬 수 있기 때문에

NUL 종료된 문자열을 처리하는 거의 모든 함수는 안전하지 않을 수 있습니다.외부로부터 데이터를 수신하여 str*() 함수를 통해 조작하고 있는 경우, 대재앙에 대비할 수 있습니다.

strcat(), strncat(), strncat(), strcpy(), strncpy() 등 모든 string-copy/move 시나리오에서 몇 가지 단순한 휴리스틱이 적용되면 상황이 훨씬 좋아집니다(안전합니다).

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가 아닌 전체 버퍼(Buffer)를 이미 NUL로 채웠기 때문에 크기(Buffer)가 아닌 BUFSIZE 상수를 사용하여 쓰기를 제한한다면 NUL을 종료하는 최종적인 "범위 외"가 반드시 존재합니다.

snprintf()에 대해서도 버퍼와 BUFSIZE는 마찬가지로 정상적으로 동작합니다.

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은 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)

타겟이 포인터일 수 있기 때문입니다(target size of()는 필요한 정보를 반환하지 않습니다).당신의 코드에 있는 strcat() 인스턴스에 대한 적절한 "범용" 솔루션이 없습니다.

"strFunc()-aware" 프로그래머에서 자주 발생하는 문제는 strlen()을 사용하여 버퍼 오버플로우로부터 보호하려는 시도입니다.내용이 NUL로 종료되는 것이 보증되어 있으면 괜찮습니다.그렇지 않으면 strlen() 자체가 보호하려는 "문제 있는" 코드에 도달하기 전에 버퍼 오버런 오류를 일으킬 수 있습니다(일반적으로 세그먼트화 위반 또는 기타 코어 덤프 상황을 발생시킵니다).

atoi는 스레드 세이프가 아닙니다.대신 man 페이지의 추천에 따라 strtol을 사용합니다.

스프린트에 대해 잊지 마세요. 그것은 많은 문제의 원인입니다.이는 대체 snprintf의 구현이 다를 수 있기 때문에 코드 포터블이 되지 않을 수 있기 때문입니다.

  1. linux: http://linux.die.net/man/3/snprintf

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

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

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

일반적으로 다음 기능이 아닌 기능은 피해야 합니다.

  1. buffer overflow safe (여기서 이미 많은 함수가 언급되어 있습니다)

  2. 스레드 세이프/재진입하지 않음(예를 들어 strtok)

각 기능의 매뉴얼에서 safe, sync, async, 스레드, 버퍼, 버그 등의 키워드를 검색해야 합니다.

언급URL : https://stackoverflow.com/questions/2565727/which-functions-from-the-standard-library-must-should-be-avoided

반응형