Development Tip

유효성을위한 포인터 테스트 (C / C ++)

yourdevel 2020. 10. 6. 19:31
반응형

유효성을위한 포인터 테스트 (C / C ++)


주어진 포인터가 "유효"한지 (물론 프로그램 적으로) 결정하는 방법이 있습니까? NULL을 확인하는 것은 쉽지만 0x00001234와 같은 것은 어떻습니까? 이러한 종류의 포인터를 역 참조하려고하면 예외 / 충돌이 발생합니다.

크로스 플랫폼 방법이 선호되지만 플랫폼 별 (Windows 및 Linux의 경우)도 괜찮습니다.

설명을위한 업데이트 : 이 문제는 부실 / 해제 / 초기화되지 않은 포인터가 아닙니다. 대신 호출자 (문자열에 대한 포인터, 파일 핸들 등)로부터 포인터를 가져 오는 API를 구현하고 있습니다. 호출자는 잘못된 값을 포인터로 보낼 수 있습니다 (의도적으로 또는 실수로). 충돌을 어떻게 방지합니까?


설명을위한 업데이트 : 문제는 오래된 포인터, 해제되었거나 초기화되지 않은 포인터가 아닙니다. 대신 호출자 (문자열에 대한 포인터, 파일 핸들 등)로부터 포인터를 가져 오는 API를 구현하고 있습니다. 호출자는 잘못된 값을 포인터로 보낼 수 있습니다 (의도적으로 또는 실수로). 충돌을 어떻게 방지합니까?

그 수표를 만들 수 없습니다. 포인터가 "유효"한지 확인할 수있는 방법은 없습니다. 사람들이 포인터를받는 함수를 사용할 때 그 사람들이 자신이 무엇을하는지 안다는 것을 믿어야합니다. 포인터 값으로 0x4211을 전달하면 0x4211 주소를 가리키는 것을 신뢰해야합니다. 그리고 그들이 "우연히"물체에 부딪힌 경우, 무서운 운영 체제 기능 (IsValidPtr 등)을 사용하더라도 여전히 버그에 빠질 것이며 빠르게 실패하지 않을 것입니다.

이런 종류의 신호를 보내기 위해 널 포인터를 사용하고 라이브러리 사용자에게 실수로 잘못된 포인터를 전달하는 경향이있는 경우 포인터를 사용해서는 안된다고 알리십시오.


호출자가 잘못된 포인터를 전송하여 발생하는 크래시를 방지하는 것은 찾기 어려운 조용한 버그를 만드는 좋은 방법입니다.

API를 사용하는 프로그래머가 코드를 숨기지 않고 충돌시켜 가짜라는 명확한 메시지를받는 것이 더 낫지 않습니까?


Win32 / 64에는이를 수행하는 방법이 있습니다. 포인터 읽기를 시도하고 실패시 발생하는 결과 SEH 예외를 포착합니다. 던지지 않으면 유효한 포인터입니다.

이 방법의 문제는 포인터에서 데이터를 읽을 수 있는지 여부를 반환한다는 것입니다. 형식 안전성이나 기타 불변성을 보장하지 않습니다. 일반적으로이 방법은 "예, 지금 지나간 시간에 기억의 특정 위치를 읽을 수 있습니다"라고 말하는 것 외에는 거의 유용하지 않습니다.

간단히 말해서,하지 마십시오;)

Raymond Chen은이 주제에 대한 블로그 게시물 : http://blogs.msdn.com/oldnewthing/archive/2007/06/25/3507294.aspx


다음은 Linux에서 C 프로그램이 실행중인 메모리의 상태에 대해 성찰 할 수있는 세 가지 쉬운 방법과 질문이 일부 컨텍스트에서 적절한 정교한 답변을 갖는 이유입니다.

  1. getpagesize ()를 호출하고 포인터를 페이지 경계로 반올림 한 후 mincore ()를 호출하여 페이지가 유효한지 여부와 프로세스 작업 집합의 일부인지 확인할 수 있습니다. 여기에는 약간의 커널 리소스가 필요하므로 벤치마킹하고이 함수를 호출하는 것이 API에서 정말 적절한 지 결정해야합니다. API가 인터럽트를 처리하거나 직렬 포트에서 메모리로 읽는 경우 예측할 수없는 동작을 피하기 위해 이것을 호출하는 것이 적절합니다.
  2. 사용 가능한 / proc / self 디렉토리가 있는지 확인하기 위해 stat ()를 호출 한 후, 포인터가있는 영역에 대한 정보를 찾기 위해 / proc / self / maps를 열고 읽을 수 있습니다. 프로세스 정보 의사 파일 시스템 인 proc의 매뉴얼 페이지를 살펴보십시오. 분명히 이것은 상대적으로 비용이 많이 들지만 이진 검색을 사용하여 효율적으로 조회 할 수있는 배열로 구문 분석 결과를 캐싱하는 것을 피할 수 있습니다. / proc / self / smaps도 고려하십시오. API가 고성능 컴퓨팅을위한 것이라면 프로그램은 비 균일 메모리 아키텍처 인 numa의 man 페이지에 문서화 된 / proc / self / numa에 대해 알고 싶어 할 것입니다.
  3. get_mempolicy (MPOL_F_ADDR) 호출은 실행 스레드가 여러 개 있고 CPU 코어 및 소켓 리소스와 관련하여 비 균일 메모리에 대한 선호도를 갖도록 작업을 관리하는 고성능 컴퓨팅 API 작업에 적합합니다. 물론 이러한 API는 포인터가 유효한지 여부도 알려줍니다.

Microsoft Windows에는 Process Status API (NUMA API에도 있음)에 문서화 된 QueryWorkingSetEx 함수가 있습니다. 정교한 NUMA API 프로그래밍의 결과로서이 함수를 사용하면 간단한 "유효성 테스트 포인터 (C / C ++)"작업을 수행 할 수 있습니다. 따라서 최소 15 년 동안 사용되지 않을 가능성이 높습니다.


AFAIK 방법이 없습니다. 메모리를 해제 한 후 항상 포인터를 NULL로 설정하여 이러한 상황을 피해야합니다.


에 살펴보세요 질문을. 또한 스마트 포인터를 살펴보십시오 .


이 스레드의 답변에 대해서는 다음과 같습니다.

Windows의 경우 IsBadReadPtr (), IsBadWritePtr (), IsBadCodePtr (), IsBadStringPtr ().

내 조언은 그들로부터 멀리 떨어져 있다는 것입니다. 누군가 이미 이것을 게시했습니다 : http://blogs.msdn.com/oldnewthing/archive/2007/06/25/3507294.aspx

: 동일한 주제에 같은 저자 (나는 생각한다)에 의해 또 다른 게시물이 하나 http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx ( "IsBadXxxPtr 정말 CrashProgramRandomly 호출해야 ").

API 사용자가 잘못된 데이터를 보내면 충돌이 발생하도록합니다. 문제가 전달 된 데이터가 나중에까지 사용되지 않아 원인을 찾기 어렵게 만드는 경우 입력시 문자열 등이 기록되는 디버그 모드를 추가합니다. 그들이 나쁘다면 그것은 명백 할 것입니다 (그리고 아마도 충돌 할 것입니다). 자주 발생하는 경우 API를 프로세스 외부로 이동하여 기본 프로세스 대신 API 프로세스를 중단시키는 것이 좋습니다.


첫째, 고의로 충돌을 일으키려는 발신자로부터 자신을 보호하려고 노력하는 데 아무런 의미가 없습니다. 그들은 잘못된 포인터를 통해 접근을 시도함으로써 쉽게 이것을 할 수 있습니다. 다른 많은 방법이 있습니다. 메모리 나 스택을 덮어 쓸 수 있습니다. 이런 종류의 일로부터 보호해야하는 경우 통신을 위해 소켓이나 다른 IPC를 사용하여 별도의 프로세스에서 실행해야합니다.

우리는 파트너 / 고객 / 사용자가 기능을 확장 할 수있는 많은 소프트웨어를 작성합니다. 필연적으로 모든 버그가 우리에게 먼저보고되므로 문제가 플러그인 코드에 있음을 쉽게 보여줄 수 있으면 유용합니다. 또한 보안 문제가 있으며 일부 사용자는 다른 사용자보다 더 신뢰할 수 있습니다.

우리는 성능 / 처리량 요구 사항 및 신뢰성에 따라 다양한 방법을 사용합니다. 가장 선호하는 항목 :

  • 소켓을 사용하는 별도의 프로세스 (종종 데이터를 텍스트로 전달).

  • 공유 메모리를 사용하는 별도의 프로세스 (대량의 데이터를 전달할 경우).

  • 동일한 프로세스는 메시지 대기열을 통해 스레드를 분리합니다 (짧은 메시지가 자주 발생하는 경우).

  • 동일한 프로세스는 메모리 풀에서 할당 된 모든 전달 된 데이터 스레드를 분리합니다.

  • 직접 프로 시저 호출을 통한 동일한 프로세스-메모리 풀에서 할당 된 모든 전달 된 데이터.

우리는 타사 소프트웨어를 다룰 때, 특히 플러그인 / 라이브러리를 소스 코드가 아닌 바이너리로 제공 할 때 사용자가하려는 작업에 의지하지 않습니다.

메모리 풀의 사용은 대부분의 상황에서 매우 쉽고 비효율적 일 필요는 없습니다. 처음에 데이터를 할당하는 경우 할당 한 값에 대해 포인터를 확인하는 것은 간단합니다. 할당 된 길이를 저장하고 데이터 앞뒤에 "매직"값을 추가하여 유효한 데이터 유형과 데이터 오버런을 확인할 수도 있습니다.


나는 거의 동일한 입장에 있기 때문에 귀하의 질문에 많은 동정심을 가지고 있습니다. 나는 많은 응답이 말하는 것에 감사하고 정확합니다. 포인터 제공하는 루틴 은 유효한 포인터를 제공 해야 합니다. 내 경우에는, 그들이 포인터를 손상 수 있었다는 것을 거의 상상할 수있다 - 그러나이 경우 관리, 내 소프트웨어가 될 것이라고 비난을받을 것이다 충돌 및 ME :-(

내 요구 사항은 세분화 오류 후에도 계속하는 것이 아닙니다. 위험 할 수 있습니다. 고객이 나를 비난하는 대신 코드를 수정할 수 있도록 종료하기 전에 발생한 상황을 고객에게보고하고 싶습니다.

이것이 내가 그것을 (Windows에서) 찾은 방법입니다 : http://www.cplusplus.com/reference/clibrary/csignal/signal/

시놉시스를 제공하려면 :

#include <signal.h>

using namespace std;

void terminate(int param)
/// Function executed if a segmentation fault is encountered during the cast to an instance.
{
  cerr << "\nThe function received a corrupted reference - please check the user-supplied  dll.\n";
  cerr << "Terminating program...\n";
  exit(1);
}

...
void MyFunction()
{
    void (*previous_sigsegv_function)(int);
    previous_sigsegv_function = signal(SIGSEGV, terminate);

    <-- insert risky stuff here -->

    signal(SIGSEGV, previous_sigsegv_function);
}

이제이 나타납니다 (이 에러 메시지를 출력하고 프로그램을 종료) 나는 희망으로 행동하는 -하지만 누군가가 결함을 발견 할 수 있다면, 알려 주시기 바랍니다!


C ++에는 일반적인 경우로서 포인터의 유효성을 테스트 할 수있는 조항이 없습니다. NULL (0x00000000)이 나쁘다고 가정 할 수 있으며, 다양한 컴파일러와 라이브러리는 디버깅을 더 쉽게하기 위해 여기 저기에서 "특수 값"을 사용하는 것을 좋아합니다 (예를 들어 Visual Studio에서 포인터가 0xCECECECE로 표시되는 것을 본 경우 나는 뭔가 잘못했다) 그러나 진실은 포인터가 단지 메모리에 대한 인덱스이기 때문에 포인터가 "올바른"인덱스인지를 보는 것만으로는 거의 불가능하다는 것입니다.

가리키는 객체가 원하는 유형인지 확인하기 위해 dynamic_cast 및 RTTI로 수행 할 수있는 다양한 트릭이 있지만, 모두 처음부터 유효한 것을 가리켜 야합니다.

프로그램이 "유효하지 않은"포인터를 감지 할 수 있는지 확인하려면 내 조언은 다음과 같습니다. 선언 한 모든 포인터를 생성 즉시 NULL 또는 유효한 주소로 설정하고 가리키는 메모리를 해제 한 후 즉시 NULL로 설정합니다. 이 관행에 부지런한 경우 NULL을 확인하는 것만으로도 충분합니다.


이를 수행하는 이식 가능한 방법은 없으며 특정 플랫폼에 대해 수행하는 것은 어렵거나 불가능할 수 있습니다. 어떤 경우에도 그러한 검사에 의존하는 코드를 작성해서는 안됩니다. 처음부터 포인터가 잘못된 값을 차지하게해서는 안됩니다.


사용 전후에 포인터를 NULL로 설정하는 것은 좋은 기술입니다. 예를 들어 (문자열) 클래스 내에서 포인터를 관리하는 경우 C ++에서 쉽게 수행 할 수 있습니다.

class SomeClass
{
public:
    SomeClass();
    ~SomeClass();

    void SetText( const char *text);
    char *GetText() const { return MyText; }
    void Clear();

private:
    char * MyText;
};


SomeClass::SomeClass()
{
    MyText = NULL;
}


SomeClass::~SomeClass()
{
    Clear();
}

void SomeClass::Clear()
{
    if (MyText)
        free( MyText);

    MyText = NULL;
}



void SomeClass::Settext( const char *text)
{
    Clear();

    MyText = malloc( strlen(text));

    if (MyText)
        strcpy( MyText, text);
}

퍼블릭 API에서 임의의 포인터를 입력 매개 변수로 받아들이는 것은 좋은 정책이 아닙니다. 정수, 문자열 또는 구조체와 같은 "일반 데이터"유형을 갖는 것이 더 좋습니다 (물론 일반 데이터가 포함 된 고전적 구조체를 의미합니다. 공식적으로 모든 것이 구조체가 될 수 있음).

왜? 다른 사람들이 말했듯이 유효한 포인터를 받았는지 아니면 정크를 가리키는 포인터를 받았는지 알 수있는 표준 방법이 없기 때문입니다.

하지만 때로는 선택의 여지가 없습니다. API는 포인터를 받아야합니다.

이 경우 좋은 포인터를 전달하는 것은 호출자의 의무입니다. NULL은 값으로 허용 될 수 있지만 정크에 대한 포인터는 아닙니다.

어떤 식 으로든 다시 확인할 수 있습니까? 글쎄, 내가 그런 경우에 한 것은 포인터가 가리키는 유형에 대한 불변을 정의하고 얻을 때 (디버그 모드에서) 호출하는 것이 었습니다. 적어도 불변이 실패 (또는 충돌)하면 잘못된 값이 전달되었음을 알 수 있습니다.

// API that does not allow NULL
void PublicApiFunction1(Person* in_person)
{
  assert(in_person != NULL);
  assert(in_person->Invariant());

  // Actual code...
}

// API that allows NULL
void PublicApiFunction2(Person* in_person)
{
  assert(in_person == NULL || in_person->Invariant());

  // Actual code (must keep in mind that in_person may be NULL)
}

Unix에서는 다음과 같이 포인터 검사를 수행하고 EFAULT를 반환하는 커널 시스템 호출을 사용할 수 있어야합니다.

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdbool.h>

bool isPointerBad( void * p )
{
   int fh = open( p, 0, 0 );
   int e = errno;

   if ( -1 == fh && e == EFAULT )
   {
      printf( "bad pointer: %p\n", p );
      return true;
   }
   else if ( fh != -1 )
   {
      close( fh );
   }

   printf( "good pointer: %p\n", p );
   return false;
}

int main()
{
   int good = 4;
   isPointerBad( (void *)3 );
   isPointerBad( &good );
   isPointerBad( "/tmp/blah" );

   return 0;
}

반환 :

bad pointer: 0x3
good pointer: 0x7fff375fd49c
good pointer: 0x400793

open () [아마도 액세스]보다 더 나은 syscall이있을 것입니다. 이것이 실제 파일 생성 코드 경로로 이어질 가능성이 있고 후속 닫기 요구 사항이 있기 때문입니다.


다른 사람들이 말했듯이 잘못된 포인터를 안정적으로 감지 할 수 없습니다. 유효하지 않은 포인터가 취할 수있는 몇 가지 형식을 고려하십시오.

널 포인터가있을 수 있습니다. 그것은 당신이 쉽게 확인하고 뭔가를 할 수있는 것입니다.

유효한 메모리 외부에 대한 포인터를 가질 수 있습니다. 유효한 메모리를 구성하는 것은 시스템의 런타임 환경이 주소 공간을 설정하는 방법에 따라 다릅니다. Unix 시스템에서는 일반적으로 0에서 시작하여 많은 수의 메가 바이트로 이동하는 가상 주소 공간입니다. 임베디드 시스템에서는 매우 작을 수 있습니다. 어떤 경우에도 0에서 시작하지 않을 수 있습니다. 앱이 감독자 모드 또는 이와 동등한 모드에서 실행되는 경우 포인터가 실제 주소를 참조 할 수 있으며 실제 메모리로 백업되거나 백업되지 않을 수 있습니다.

데이터 세그먼트, bss, 스택 또는 힙 내부에서도 유효한 메모리 내부 어딘가에 대한 포인터를 가질 수 있지만 유효한 개체를 가리킬 수는 없습니다. 이것의 변형은 개체에 문제가 발생하기 전에 유효한 개체를 가리키는 데 사용되는 포인터입니다. 이 맥락에서 나쁜 것은 할당 해제, 메모리 손상 또는 포인터 손상을 포함합니다.

참조되는 항목에 대해 잘못된 정렬이있는 포인터와 같이 평면이없는 잘못된 포인터가있을 수 있습니다.

세그먼트 / 오프셋 기반 아키텍처 및 기타 이상한 포인터 구현을 고려하면 문제가 더욱 악화됩니다. 이런 종류의 일은 일반적으로 좋은 컴파일러와 현명한 유형의 사용에 의해 개발자에게 숨겨져 있지만 베일을 뚫고 운영 체제와 컴파일러 개발자를 능가하려는 경우 할 수 있지만 일반적인 방법은 없습니다. 발생할 수있는 모든 문제를 처리 할 수 ​​있습니다.

최선의 방법은 충돌을 허용하고 좋은 진단 정보를 제공하는 것입니다.


이 기사 MEM10-C. 포인터 유효성 검사 기능을 정의하고 사용하면 특히 Linux OS에서 어느 정도 검사를 수행 할 수 있습니다.


일반적으로 불가능합니다. 특히 불쾌한 경우가 있습니다.

struct Point2d {
    int x;
    int y;
};

struct Point3d {
    int x;
    int y;
    int z;
};

void dump(Point3 *p)
{
    printf("[%d %d %d]\n", p->x, p->y, p->z);
}

Point2d points[2] = { {0, 1}, {2, 3} };
Point3d *p3 = reinterpret_cast<Point3d *>(&points[0]);
dump(p3);

많은 플랫폼에서 다음과 같이 출력됩니다.

[0 1 2]

런타임 시스템이 메모리 비트를 잘못 해석하도록 강요하지만,이 경우 비트가 모두 이해되기 때문에 충돌하지 않습니다. 이것은 (와 C 스타일의 다형성 봐 언어의 설계의 일부입니다 struct inaddr, inaddr_in, inaddr_in6당신이 안정적으로 모든 플랫폼에 대해 보호 할 수 있도록).


위의 기사에서 얼마나 많은 오해의 소지가있는 정보를 읽을 수 있는지 믿을 수 없습니다.

그리고 Microsoft msdn 문서에서도 IsBadPtr은 금지되어 있다고 주장됩니다. 오 글쎄-나는 충돌보다는 작동하는 응용 프로그램을 선호합니다. 용어 작업이 잘못 작동하더라도 (최종 사용자가 응용 프로그램을 계속할 수있는 한).

인터넷 검색을 통해 Windows에 대한 유용한 예를 찾지 못했습니다. 32 비트 앱에 대한 솔루션을 찾았습니다.

http://www.codeproject.com/script/Content/ViewAssociatedFile.aspx?rzp=%2FKB%2Fsystem%2Fdetect-driver%2F%2FDetectDriverSrc.zip&zep=DetectDriverSrc%2FDetectDriver%2Fsrc%2FdrvCppLib%2Frtti.cpp&obid=58895&obtid=2&ovid = 2

하지만 64 비트 앱도 지원해야하므로이 솔루션이 효과가 없었습니다.

하지만 와인의 소스 코드를 모아서 64 비트 앱에서도 작동하는 비슷한 종류의 코드를 만들었습니다. 여기에 코드를 첨부하세요.

#include <typeinfo.h>   

typedef void (*v_table_ptr)();   

typedef struct _cpp_object   
{   
    v_table_ptr*    vtable;   
} cpp_object;   



#ifndef _WIN64
typedef struct _rtti_object_locator
{
    unsigned int signature;
    int base_class_offset;
    unsigned int flags;
    const type_info *type_descriptor;
    //const rtti_object_hierarchy *type_hierarchy;
} rtti_object_locator;
#else

typedef struct
{
    unsigned int signature;
    int base_class_offset;
    unsigned int flags;
    unsigned int type_descriptor;
    unsigned int type_hierarchy;
    unsigned int object_locator;
} rtti_object_locator;  

#endif

/* Get type info from an object (internal) */  
static const rtti_object_locator* RTTI_GetObjectLocator(void* inptr)  
{   
    cpp_object* cppobj = (cpp_object*) inptr;  
    const rtti_object_locator* obj_locator = 0;   

    if (!IsBadReadPtr(cppobj, sizeof(void*)) &&   
        !IsBadReadPtr(cppobj->vtable - 1, sizeof(void*)) &&   
        !IsBadReadPtr((void*)cppobj->vtable[-1], sizeof(rtti_object_locator)))  
    {  
        obj_locator = (rtti_object_locator*) cppobj->vtable[-1];  
    }  

    return obj_locator;  
}  

그리고 다음 코드는 포인터가 유효한지 여부를 감지 할 수 있으므로 NULL 검사를 추가해야합니다.

    CTest* t = new CTest();
    //t = (CTest*) 0;
    //t = (CTest*) 0x12345678;

    const rtti_object_locator* ptr = RTTI_GetObjectLocator(t);  

#ifdef _WIN64
    char *base = ptr->signature == 0 ? (char*)RtlPcToFileHeader((void*)ptr, (void**)&base) : (char*)ptr - ptr->object_locator;
    const type_info *td = (const type_info*)(base + ptr->type_descriptor);
#else
    const type_info *td = ptr->type_descriptor;
#endif
    const char* n =td->name();

이것은 포인터에서 클래스 이름을 가져옵니다-귀하의 요구에 충분해야한다고 생각합니다.

내가 여전히 두려워하는 한 가지는 포인터 검사의 성능입니다. 위의 코드 스 니펫에는 이미 3-4 개의 API 호출이 수행되고 있습니다. 시간이 중요한 애플리케이션에서는 과도 할 수 있습니다.

It would be good if someone could measure overhead of pointer checking compared for example to C#/managed c++ calls.


Indeed, something could be done under specific occasion: for example if you want to check whether a string pointer string is valid, using write(fd, buf, szie) syscall can help you do the magic: let fd be a file descriptor of temporary file you create for test, and buf pointing to the string you are tesing, if the pointer is invalid write() would return -1 and errno set to EFAULT which indicating that buf is outside your accessible address space.


IsBadReadPtr(), IsBadWritePtr(), IsBadCodePtr(), IsBadStringPtr() for Windows.
These take time proportional to the length of the block, so for sanity check I just check the starting address.


I have seen various libraries use some method to check for unreferenced memory and such. I believe they simply "override" the memory allocation and deallocation methods (malloc/free), which has some logic that keeps track of the pointers. I suppose this is overkill for your use case, but it would be one way to do it.


Technically you can override operator new (and delete) and collect information about all allocated memory, so you can have a method to check if heap memory is valid. but:

  1. you still need a way to check if pointer is allocated on stack ()

  2. you will need to define what is 'valid' pointer:

a) memory on that address is allocated

b) memory at that address is start address of object (e.g. address not in the middle of huge array)

c) memory at that address is start address of object of expected type

Bottom line: approach in question is not C++ way, you need to define some rules which ensure that function receives valid pointers.


There is no way to make that check in C++. What should you do if other code passes you an invalid pointer? You should crash. Why? Check out this link: http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx


Addendum to the accpeted answer(s):

Assume that your pointer could hold only three values -- 0, 1 and -1 where 1 signifies a valid pointer, -1 an invalid one and 0 another invalid one. What is the probability that your pointer is NULL, all values being equally likely? 1/3. Now, take the valid case out, so for every invalid case, you have a 50:50 ratio to catch all errors. Looks good right? Scale this for a 4-byte pointer. There are 2^32 or 4294967294 possible values. Of these, only ONE value is correct, one is NULL, and you are still left with 4294967292 other invalid cases. Recalculate: you have a test for 1 out of (4294967292+ 1) invalid cases. A probability of 2.xe-10 or 0 for most practical purposes. Such is the futility of the NULL check.


You know, a new driver (at least on Linux) that is capable of this probably wouldn't be that hard to write.

On the other hand, it would be folly to build your programs like this. Unless you have some really specific and single use for such a thing, I wouldn't recommend it. If you built a large application loaded with constant pointer validity checks it would likely be horrendously slow.


you should avoid these methods because they do not work. blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx – JaredPar Feb 15 '09 at 16:02

If they don't work - next windows update will fix it ? If they don't work on concept level - function will be probably removed from windows api completely.

MSDN documentation claim that they are banned, and reason for this is probably flaw of further design of application (e.g. generally you should not eat invalid pointers silently - if you're in charge of design of whole application of course), and performance/time of pointer checking.

But you should not claim that they does not work because of some blog. In my test application I've verified that they do work.


these links may be helpful

_CrtIsValidPointer Verifies that a specified memory range is valid for reading and writing (debug version only). http://msdn.microsoft.com/en-us/library/0w1ekd5e.aspx

_CrtCheckMemory Confirms the integrity of the memory blocks allocated in the debug heap (debug version only). http://msdn.microsoft.com/en-us/library/e73x0s4b.aspx


Following does work in Windows (somebody suggested it before):

 static void copy(void * target, const void* source, int size)
 {
     __try
     {
         CopyMemory(target, source, size);
     }
     __except(EXCEPTION_EXECUTE_HANDLER)
     {
         doSomething(--whatever--);
     }
 }

The function has to be static, standalone or static method of some class. To test on read-only, copy data in the local buffer. To test on write without modifying contents, write them over. You can test first/last addresses only. If pointer is invalid, control will be passed to 'doSomething', and then outside the brackets. Just do not use anything requiring destructors, like CString.


On Windows I use this code:

void * G_pPointer = NULL;
const char * G_szPointerName = NULL;
void CheckPointerIternal()
{
    char cTest = *((char *)G_pPointer);
}
bool CheckPointerIternalExt()
{
    bool bRet = false;

    __try
    {
        CheckPointerIternal();
        bRet = true;
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
    }

    return  bRet;
}
void CheckPointer(void * A_pPointer, const char * A_szPointerName)
{
    G_pPointer = A_pPointer;
    G_szPointerName = A_szPointerName;
    if (!CheckPointerIternalExt())
        throw std::runtime_error("Invalid pointer " + std::string(G_szPointerName) + "!");
}

Usage:

unsigned long * pTest = (unsigned long *) 0x12345;
CheckPointer(pTest, "pTest"); //throws exception

참고URL : https://stackoverflow.com/questions/551069/testing-pointers-for-validity-c-c

반응형