2012-08-31 3 views
6

데이터를 저장하는 데 사용되는 프로그램에서 고정 크기의 일부 이진 버퍼가 있습니다. 그리고 memcpy는 버퍼를 다른 것으로 복사하는 데 사용됩니다. 소스 버퍼가 목적지 버퍼보다 ​​클 수 있기 때문입니다. 버퍼 오버 플로우가 있는지 어떻게 알 수 있습니까?memcpy 버퍼 오버플로를 방지하는 방법은 무엇입니까?

+1

? 대상 버퍼 크기를 알고 있습니까? 그런 다음 memcpy (src, dst, sizeof (dst))와 같은 코드를 작성하십시오. – BSen

+0

소스 버퍼와 대상 버퍼의 크기를 비교하여 어느 것이 더 큰지 확인하십시오. – SingerOfTheFall

+1

@BSen''sizeof''는 단지 포인터의 크기를 줄 것입니다. – juanchopanza

답변

8

원본 버퍼에있는 데이터의 양과 대상 버퍼에서 사용 가능한 공간의 양을 알아야합니다.

원본 버퍼에서 복사 할 모든 데이터에 대해 대상 버퍼에 충분한 공간이 없으면 memcpy()을 호출하지 마십시오. 소스가 대상보다 클 경우 데이터를 자르려면 문제가 없는지 여부를 결정해야합니다.

잘 모르는 경우 코드를 다시 작성하여 충분한 공간이 있는지 확인하십시오. 그렇지 않으면 안전하지 않습니다.

원본 버퍼와 대상 버퍼가 겹칠 가능성이있는 경우 memcpy() 대신 memmove()을 사용해야합니다.

C++의 경우 처음에는 memcpy()을 사용하십시오. 이는 C++보다는 C 스타일 연산입니다.

+0

감사합니다. C++에서 메모리 복사본을 수행하는 적절한 방법은 무엇입니까? –

+1

@MichaelD : 데이터를'std :: vector <>'에 저장하고 단지'vector2 = vector1'을 사용하십시오. – MSalters

+0

어떻게 데이터를 벡터에 삽입 할 수 있습니까? push_back을 사용하여 바이트 단위로 날짜를 삽입 하시겠습니까? –

3

src 및 dest 버퍼 크기를 항상 알고 있어야합니다.

void *memcpy(void *dest, const void *src, size_t n); 

nsrc 또는 dest 크기보다 큰를 않을 것입니다.

0

예를 들어, 당신은 :

대상 4 바이트 크기

소스 5 바이트 크기

당신은 최대 4 바이트 대상 버퍼에 복사해야합니다 수 있습니다

size_t getCopySize(size_t sourceSize, size_t destSize) 
{ 
    return (destSize <= sourceSize ? destSize : sourceSize); 
} 
memcpy(destination, source, getCopySize(sizeof(source),sizeof(destination))); 

응용 프로그램을 기반으로 나머지 데이터가 나중에 복사되는지 확인하거나 일부 데이터를 무시할 수 있으면 건너 뛸 수 있습니다.

4

버퍼 오버플로가 있는지 어떻게 알 수 있습니까?

3 가지 또는 4 가지 선택 사항이 있다고 생각합니다.


첫 번째 선택은 memcpy에 대해 "안전한"기능을 제공하는 것입니다. 이것은 필자의 권한하에 코드에서 요구하는 것이며 정기적으로 감사를 실시합니다. 또한 모든 매개 변수의 유효성을 확인하고 모든 매개 변수를 지정해야합니다.

어설 션은 자체 디버깅 코드를 생성합니다. 나는 개발자들이 코드를 작성하기를 원한다. 나는 디버깅 시간 낭비하지 않기를 바란다. 그래서 나는 그들에게 디버그 코드를 작성하도록 요구한다. ASSERT는 또한 일을 문서화하기 때문에 문서를 자세히 검토 할 수 있습니다. 릴리즈 빌드에서 ASSERT는 프리 프로세서 매크로에 의해 제거됩니다.

errno_t safe_memcpy(void* dest, size_t dsize, void* src, size_t ssize, size_t cnt) 
{ 
    ASSERT(dest != NULL); 
    ASSERT(src != NULL); 
    ASSERT(dsize != 0); 
    ASSERT(ssize != 0); 
    ASSERT(cnt != 0); 

    // What was the point of this call? 
    if(cnt == 0) 
     retrn 0; 

    if(dest == NULL || src == NULL) 
     return EINVALID; 

    if(dsize == 0 || ssize == 0) 
     return EINVALID; 

    ASSERT(dsize <= RSIZE_MAX); 
    ASSERT(ssize <= RSIZE_MAX); 
    ASSERT(cnt <= RSIZE_MAX); 

    if(dsize > RSIZE_MAX || ssize > RSIZE_MAX || cnt > RSIZE_MAX) 
     return EINVALID; 

    size_t cc = min(min(dsize, ssize), cnt); 
    memmove(dest, src, cc); 

    if(cc != cnt) 
     return ETRUNCATE; 

    return 0; 
} 

당신의 safe_memcpy 수익률이 아닌 0, 다음 잘못된 매개 변수 또는 잠재적 인 버퍼 오버 플로우와 같은 오류가 발생했습니다 경우.


두 번째 방법은 C 표준에서 제공하는 "보다 안전한"기능을 사용하는 것입니다. C는 ISO/IEC TR 24731-1, Bounds Checking Interfaces을 통해보다 안전한 기능을 제공합니다. 플랫폼을 준수하면 gets_ssprintf_s으로 간단히 전화 할 수 있습니다. 일관된 동작 (항상 문자열이 NULL 종료 됨) 및 일관된 반환 값 (성공시 0 또는 errno_t)을 제공합니다.

errno_t err = memcpy_s(dest, dsize, src, cnt); 
... 

불행히도 gcc 및 glibc는 C 표준을 준수하지 않습니다. Ulrich Drepper (glibc 메인테이너 중 한 명)는 bounds checking interfaces를 "horribly inefficient BSD crap"라고 불렀고 결코 추가되지 않았습니다.


세 번째 방법은 플랫폼의 "안전한"인터페이스 (있는 경우)를 사용하는 것입니다. Windows에서는 ISO/IEC TR 24731-1, Bounds Checking Interfaces의 경우와 동일합니다. String Safe 라이브러리도 있습니다.

애플과 BSD에

, 당신은이 memcpy에 대한 "안전"기능이 없습니다. 그러나 strlcpy, strlcat 및 친구들과 같은 더 안전한 문자열 함수가 있습니다.


Linux에서는 네 번째로 FORTIFY_SOURCE를 사용합니다. FORTIFY_SOURCE는 memcpy, strcpygets과 같이 위험도가 높은 기능의 '안전한'변종을 사용합니다. 컴파일러는 대상 버퍼 크기를 추론 할 수있는 경우보다 안전한 변형을 사용합니다. 복사본이 대상 버퍼 크기를 초과하면 프로그램은 abort()을 호출합니다. 컴파일러가 대상 버퍼 크기를 추론 할 수없는 경우 "더 안전한"변형은 사용되지 않습니다.

테스트를 위해 FORTIFY_SOURCE를 사용하지 않으려면 -U_FORTIFY_SOURCE 또는 -D_FORTIFY_SOURCE=0으로 프로그램을 컴파일해야합니다.

관련 문제