Windows에서와 달리 Linux환경에서는 딱히 포인터의 유효성을 검증할 수 있는 system call이 없다. 이 포스팅은 Linux환경에서 이와 유사한 기능을 구현하기 위해 “정보의 바다”에서 찾은 내용들을 정리해 둔 것이다.
1. _etext를 이용하는 방법
첫번째 방법은 Define and use a pointer validation function 이라는 위키문서에서 가져온 것인데 컴파일러가 생성하는 text 영역의 시작점을 이용해서 포인터 값이 이를 침범 하는지 여부를 검사하고 유효성을 판단한다. 하지만 위 링크의 커멘트에도 나와 있듯이 시스템에 따라 동작하지 않을 수도 있으므로 reliable 한 구현이라 볼 수는 없다.
bool isValidPointer_ET(void *ptr) { extern const char _etext; return (ptr != nullptr) && ((const char*)ptr > &_etext); }
2. msync()를 이용하는 방법
Checking whether a pointer is valid in Linux라는 블로그 포스트에 소개된 방법으로 매핑된 메모리 공간을 동기화 할 때 쓰는 msync() 시스템 콜을 호출 하면서 유효하지 않은 page 시작 주소를 넘겨 주면 0이 아닌 음수 값을 반환하는 것을 이용하는 방법이다. (이 경우 errno에 ENOMEM이 설정된다)
#include <sys/mman.h> #include <unistd.h> bool isValidPointer_MS(void *ptr) { const size_t pageSize = sysconf(_SC_PAGESIZE); void *basePtr = (void *)((((size_t)ptr) / pageSize) * pageSize); return msync(basePtr, pageSize, MS_SYNC) == 0; }
3. mincore()를 이용하는 방법
Stackoverflow에 올려진 Testing pointers for validity (C/C++)라는 질문에 대한 답변에 있는 아이디어 중 하나인데 메모리 페이지의 swap 상태를 확인해서 반환해 주는 mincore()을 이용하는 방법이다. 해당 답변에는 다른 아이디어 들도 있으니 필요에 따라 참고.
#include <sys/mman.h> #include <unistd.h> bool isValidPointer_MC(void *ptr) { unsigned char vec = 0; const size_t pageSize = sysconf(_SC_PAGESIZE); void *basePtr = (void *)((((size_t)ptr) / pageSize) * pageSize); int ret = mincore(basePtr, pageSize, &vec); return (ret == 0 && ((vec & 0x1) == 0x1)); }
시험 결과와 결론
위의 함수들에 대해 Linux환경에서 전역 변수 포인터, 지역 변수 포인터, 널 포인터, 널 포인터는 아니지만 명백하게 무효한 포인터(0x04 같은), 동적 할당된 공간에 대한 유효성 여부는 잘 동작한다.
하지만, 이미 해제된 포인터나 할당되지 않은 heap 공간 내의 임의 주소에 대해서는 제대로 유효성 여부를 판단하지 못하고, 주소 범위가 유효 하다면 포인터 역시 유효 하다고 판단하는 오류가 세가지 구현 모두에 있다.
// 해제된 heap공간에 대한 유효성 여부 확인. 모두 실패함. unsigned int* dynamicVar = new unsigned int[100]; delete[] dynamicVar; EXPECT_FALSE(isValidPointer_ET(dynamicVar)); EXPECT_FALSE(isValidPointer_MS(dynamicVar)); EXPECT_FALSE(isValidPointer_MC(dynamicVar)); // 할당 되지 않은 Heap공간 내의 임의 포인터에 대한 유효성 확인. 모두 실패함. unsigned int* dynamicUnallocVar = dynamicVar + 100; EXPECT_FALSE(isValidPointer_ET(dynamicUnallocVar)); EXPECT_FALSE(isValidPointer_MS(dynamicUnallocVar)); EXPECT_FALSE(isValidPointer_MC(dynamicUnallocVar));
즉, 위의 구현 들은 주어진 포인터가 유효한 메모리 공간내에 속하는지는 확인할 수 있어도, 동적 할당 영역의 메모리 포인터가 실제 read/write 가능한 상태인지 여부는 정확히 반환 할 수 없다.
시험에 사용한 code는 여기에 붙여 둔다.