개발을 하다보면 해당 포인터가 유효한지 아닌지를 판별해야하는 경우가 많이 있습니다. 일반적으로 NULL 체크를 하면 대부분 해결되지만 랜덤한 메모리 주소에 접근 시 해당 메모리에 있는 값을 read/write 할 수 있는지 체크하기란 매우 까다롭습니다.


인터넷을 찾아보니 여러 블로그에

 - IsBadCodePtr

 - IsBadReadPtr

 - IsBadStringPtr

 - IsBadWritePtr

와 같은 함수를 쓰라고 나와있지만 이 함수들은 MSDN 설명을 보더라도 사용하면 안될 뿐더러

직접 사용해보니 잘못된 메모리 영역을 인자로 넘기면 Crash가 발생하더군요.


그러면 스킵하고 또 어떠한 방법이 있을까요?

가장 간단하게는 VirtualQuery 를 사용하는 방법입니다.


MEMORY_BASIC_INFORMATION 구조체 내부의 몇몇 값들을 확인해주면 됩니다.


State 값이 == MEM_COMMIT 인가?

Protect 값이 PAGE_READONLY, PAGE_READWIRTE, ... , PAGE_EXECUTE_WRITECOPY 인가?

이 때 Protect가 PAGE_GUARD나 PAGE_NOACCESS 같은 값을 가지면 안되겠죠.

결론은 VirtualQuery를 사용해 읽을 메모리인지 쓸 메모리인지 속성들을 확인해 유효성을 판단하는 것입니다.


잘못된 메모리를 넘겼을 경우엔 해당 속성들에 이상한 값이 채워져 있으므로 쉽게 판단할 수 있습니다.


하지만 VirtualQuery는 System call을 사용하는 함수입니다. 즉, 커널 내부로 들어가 해당 프로세스의 VAD 구조체를 따라가면서 해당 메모리에 맞는 속성을 찾아가는 함수입니다. 한 두 번이 아닌 수 만번 수십 만번 호출이 된다면 분명 미세하게나마 속도가 느려지는 것을 느낄 것 같습니다. 저 또한 그런 점을 약간 느껴 이를 개선하고자 했지만 딱히 방법이 안떠오르더군요.


Windows 8부터는 Bad Memory를 체크하는 함수들이 생겼습니다.

 - BadMemoryCallbackRoutine

 - GetMemoryErrorHandlingCapabilities

 - RegisterBadMemoryNotification

 - UnregisterBadMemoryNotification

아직 써보진 않았지만 콜백을 이용하는 것이므로 오버헤드도 발생안하고 깔끔하게 처리될 것 같습니다. 

하지만 실제 범용성(XP, Vista, 7, 8 .. )을 가져가면서 프로그램 개발을 해야하므로 위 함수들은 쓸 수가 없습니다.

( 계속 OS 새로운 버전을 내놓고 있는 Windows가 야속하기만 합니다 ㅜㅜ )


또 Windows kernel에서 사용할 수 있는 함수가 있습니다.

 - MmIsAddressValid

이 함수는 주어진 가상 주소에 대해 read나 write를 할 때 페이지 폴트가 발생할 것인지 아닌지를 판별하는 함수입니다.

하지만 MSDN에서는 이 함수 사용하는 것을 추천하진 않는다고 나와있네요.

또한 내부를 직접 뜯어보니 어떠한 연산을 하지만 User에서는 사용할 수 없는 값들이라서 그냥 이 함수도 무시..


이번엔 VirtualAlloc을 사용해볼까요?

VirtualAlloc을 사용해서 할당이 안되면 이미 할당되어 있는 메모리이고, 할당이되면 읽을 수 없는 메모리인걸로 판별하려했지만.. 이 역시 system call을 사용하고 VAD 구조체를 따라가고 등등.. 많은 작업을 합니다. 이도 fail..


아무리 생각해봐도 유저에서 오버헤드도 발생안하면서 메모리 유효성 체크할 수 있는 방법이 안떠오릅니다;; 쩝..

System call을 사용하고 싶지 않은데.. 흐엉..;

Posted by Ezbeat