[cpp] MmProbeAndLockPages/MmMapLockedPages 함수에 관한 고찰

@codemaru · June 26, 2015 · 5 min read

일반적으로 커널 모드 드라이버에서 사용자 영역의 주소를 고정시키기 위해서는 아래와 같은 함수 호출을 사용한다.

mdl = IoAllocateMdl(address, size, ...);  
MmProbeAndLockPages(mdl, UserMode/KernelMode, ...);  
kptr = MmMapLockedPages(mdl, UserMode/KernelMode, ...);  
  
// kptr을 통해서 유저 메모리 address에 접근할 수 있음  
  
MmUnmapLockedPages(kptr, mdl);  
MmUnlockPages(mdl);  
IoFreeMdl(mdl);  

MmProbeAndLockPages/MmMapLockedPages 두 함수 모두 인자에 KernelMode/UserMode가 들어간다. 그런데 그 친절한 MSDN 조차도 이 값을 어떻게 지정해야 하며, 그게 어떤 효과를 가져오는지에 대해서 자세히 설명을 해주지 않고 있다. 단지 MmMapLockedPages의 두 번째 인자는 드라이버라면 대체로 KernelMode를 지정하면 된다 정도의 코멘트만 있다. 심각한 문제는 설명이 이렇다보니 블루스크린이 발생하면 이 값을 막 바꿔주다 발생하지 않는 값이 있다면 아 이렇게 쓰는거구나 하면서 엉뚱하게 변경해 버리는 일이 생긴다는 점이다. 헐~ 대박… 여튼 그래서 각 함수의 KernelMode/UserMode의 의미에 대해서 살펴보는 시간을 가져볼까 한다.

MmProbeAndLockPages에 두번째 인자는 mdl이 가리키는 영역이 사용자 주소 공간인지 커널 공간인지를 판별하기 위해서 사용된다. UserMode를 넣게 되면 MmProbeAndLockPages 함수가 실질적인 동작을 하기 전에 mdl이 가리키는 영역이 사용자 영역을 벗어나는지 벗어나지 않는지를 점검하는 코드가 추가적으로 실행된다. 만약 사용자 접근 공간(일반적으로 4G 32비트 시스템에서 2G까지)을 벗어나는 영역을 포함하고 있으면 실질적인 동작을 수행하지 않고 ACCESS_VIOLATION 예외를 발생시킨다.

MmMapLockedPages 함수의 모드 파라미터는 MmProbeAndLockPages와는 다른 목적이다. 이 함수에서 KernelMode/UserMode가 결정하는 일은 최종적으로 생성될 kptr이 유저 영역에 매핑될지 커널 영역에 매핑될지 결정하는 역할을 한다. 즉, 이 파라미터는 mdl이 가리키는 메모리 공간과는 전혀 상관이 없다. 최종적으로 우리가 사용할 포인터를 어느 영역 주소로 정할 것인지를 지정하는 역할을 한다. 그러니 드라이버라면 응당 KernelMode를 쓰는 것이 일반적일 것이다.

여기서 벌어지는 BSOD의 관전 포인트는 MmProbeAndLockPages 함수로 커널 영역을 가리키는 쓰레기 포인터가 넘어간 경우에 발생한다. 이 경우에 KernelMode가 인자로 넘어갔다면 논페이지드 영역에서 잘못된 참조가 발생할 수 있고 그랬다면 BSOD로 이어진다. 반면에 쓰레기 포인터가 넘어오더라도 UserMode를 지정했다면 커널 영역을 가리키는 포인터이기 때문에 ACCESS_VIOLATION 예외가 발생하고 __try/__except로 예외를 잡아서 동작을 지속시킬 수 있다. 이 경우에 설명이 부실하다보니 쓰레기 포인터가 넘어온 걸 고쳐야 함에도 UserMode로 파라미터를 바꾸고는 아주 이상한 경우에 발생하는 커널 모드 문제를 해결했다며 커밋을 하는 어처구니 없는 경우가 발생한다. 뉴요커 프로그래머라면 이런 실수를 하지 않도록 주의하자~~~ @.@

의외로 이런 경우가 많다.

의외로 이런 경우가 많다.

@codemaru
돌아보니 좋은 날도 있었고, 나쁜 날도 있었다. 그런 나의 모든 소소한 일상과 배움을 기록한다. 여기에 기록된 모든 내용은 한 개인의 관점이고 의견이다. 내가 속한 조직과는 1도 상관이 없다.
(C) 2001 YoungJin Shin, 0일째 운영 중