Microsoft MVP성태의 닷넷 이야기
VC++: 94. MPX(Memory Protection Extensions) 테스트 [링크 복사], [링크+제목 복사]
조회: 11988
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

Visual C++ - MPX(Memory Protection Extensions) 테스트

Visual C++ Team Blog에 재미있는 글이 실렸군요. ^^

Visual Studio 2015 Update 1: New Experimental Feature - MPX
; http://blogs.msdn.com/b/vcblog/archive/2016/01/20/visual-studio-2015-update-1-new-experimental-feature-mpx.aspx

MPX기능은 Skylake CPU 모델 중에서도 지난 2015년 10월부터 출시되는 제품부터 지원한다고 인텔은 발표했습니다. 애석하게도 국내의 경우, CPU 재고 소진 상황으로 인해 2015년도에 Skylake가 탑재된 컴퓨터를 구매하신 경우에는 거의 MPX 기능이 없다고 보시면 됩니다. (저도 1월 중순이 되어서야 국내에서 구할 수 있었습니다. ^^)

MPX 기능은 별도의 CPU 명령어 셋이 제공되기 때문에 해당 명령어들을 출력하려면 컴파일러 수준에서 제공되어야 합니다. Visual C++의 경우 2015 Update 1이후부티 /d2MPX 컴파일러 옵션으로 이를 제어할 수 있게 되었는데요... 간단하게 테스트를 해볼까요? ^^

다음의 소스코드는 "Visual Studio 2015 Update 1: New Experimental Feature - MPX" 글에 실린 내용을 그대로 복사해 온 것입니다.

#include "stdafx.h"
#include <Windows.h>

const int OUTBUFSIZE = 42;
wchar_t out[OUTBUFSIZE];

void copyUpper(wchar_t *str, size_t size)
{
    __try
    {
        for (unsigned int i = 0; i < size; i++)
        {
            out[i] = towupper(str[i]);
        }
    }
    __except (GetExceptionCode() == STATUS_ARRAY_BOUNDS_EXCEEDED)
    {
        wprintf(L"Caught array bounds exceeded exception\n");
    }
}

int main()
{
    wchar_t str[] = L"the quick brown fox jumps over the lazy dog";
    memset(out, 0, OUTBUFSIZE);
    copyUpper(str, wcsnlen_s(str, 255));
    wprintf(L"%s\n", out);
    return 0;
}

소스 코드를 보시면 out 문자열 배열은 42개만 잡혀 있지만 str 문자열은 43이기 때문에 안정된 버퍼 영역을 지나 (wchar_t) 2바이트를 더 쓰게 되는 오류가 있습니다. 일반적으로 이런 프로그램이 작성된 경우 out 버퍼 다음에 어떤 영역이 있는지에 따라 문제의 심각성이 달라집니다. (그래서, 오류 잡기가 더 힘들죠~~~ ^^;)

위의 소스코드를 컴파일할 때 /d2MPX 옵션을 주면 Visual C++ (2015 Update 1) 컴파일러는 MPX 관련 CPU 코드를 삽입해 줍니다. 이는 디버깅 시에 Disassembly 창을 통해 다음과 같이 확인할 수 있습니다.

    __try
    {
        for (unsigned int i = 0; i < size; i++)
00007FF7EA9C1064  mov         dword ptr [rbp+4],0  
00007FF7EA9C106B  jmp         copyUpper+45h (07FF7EA9C1075h)  
00007FF7EA9C106D  mov         eax,dword ptr [rbp+4]  
00007FF7EA9C1070  inc         eax  
00007FF7EA9C1072  mov         dword ptr [rbp+4],eax  
00007FF7EA9C1075  mov         eax,dword ptr [rbp+4]  
00007FF7EA9C1078  cmp         rax,qword ptr [size]  
00007FF7EA9C107F  bnd jae     copyUpper+0C3h (07FF7EA9C10F3h)  
        {
            out[i] = towupper(str[i]);
00007FF7EA9C1082  mov         eax,dword ptr [rbp+4]  
00007FF7EA9C1085  mov         rcx,qword ptr [str]  
00007FF7EA9C108C  movzx       ecx,word ptr [rcx+rax*2]  
00007FF7EA9C1090  bnd call    qword ptr [__imp_towupper (07FF7EA9C5220h)]  
00007FF7EA9C1097  mov         ecx,dword ptr [rbp+4]  
00007FF7EA9C109A  shl         rcx,1  
00007FF7EA9C109D  lea         rdx,[out (07FF7EA9C7050h)]  
00007FF7EA9C10A4  bndmk       bnd0,qword ptr [rdx+53h]  
00007FF7EA9C10A9  bndmov      oword ptr [rbp+148h],bnd0  
00007FF7EA9C10B1  lea         rdx,[out (07FF7EA9C7050h)]  
00007FF7EA9C10B8  add         rdx,rcx  
00007FF7EA9C10BB  mov         qword ptr [rbp+1D8h],rdx  
00007FF7EA9C10C2  mov         rcx,qword ptr [rbp+1D8h]  
00007FF7EA9C10C9  inc         rcx  
00007FF7EA9C10CC  bndmov      bnd0,oword ptr [rbp+148h]  
00007FF7EA9C10D4  mov         rdx,qword ptr [rbp+1D8h]  
00007FF7EA9C10DB  bndcu       bnd0,rcx  
00007FF7EA9C10DF  bndcl       bnd0,rdx  
00007FF7EA9C10E3  mov         rcx,qword ptr [rbp+1D8h]  
00007FF7EA9C10EA  mov         word ptr [rcx],ax  
        }
00007FF7EA9C10ED  bnd jmp     copyUpper+3Dh (07FF7EA9C106Dh)  
    }
00007FF7EA9C10F3  jmp         copyUpper+102h (07FF7EA9C1132h)  
    __except (GetExceptionCode() == STATUS_ARRAY_BOUNDS_EXCEEDED)
    {
        wprintf(L"Caught array bounds exceeded exception\n");
00007FF7EA9C10F5  lea         rax,[string L"Caught array bounds "... (07FF7EA9C5370h)]  
00007FF7EA9C10FC  bndmk       bnd0,qword ptr [rax+4Fh]  
00007FF7EA9C1101  bndmov      oword ptr [rbp+178h],bnd0  
00007FF7EA9C1109  lea         rax,[string L"Caught array bounds "... (07FF7EA9C5370h)]  
00007FF7EA9C1110  bndmov      bnd0,oword ptr [rbp+178h]  
00007FF7EA9C1118  bndstx      qword ptr [rsp+rax],bnd0  
00007FF7EA9C111C  lea         rcx,[string L"Caught array bounds "... (07FF7EA9C5370h)]  
00007FF7EA9C1123  bndmov      bnd0,oword ptr [rbp+178h]  
00007FF7EA9C112B  bnd call    wprintf (07FF7EA9C1430h)  
00007FF7EA9C1131  nop  
    }

MPX 관련해서 128비트 길이를 가진 4개의 경계 레지스터(bounds registers)가 BND0 ~ BND3까지 제공되는데, 상위 경계 8바이트와 하위 경계 8바이트를 하나의 레지스터에서 담게 되는 구조입니다.

위의 명령어들 중에서 특히 bndmk를 거치면 MPX 관련 CPU 레지스터에 유효 주소 범위값이 설정됩니다.

mpx_test_sample_1.png

그리고 그 범위 밖의 메모리 주소를 접근하면 예외가 발생하게 되고 __except 핸들러 내부의 코드가 실행됩니다. (MPX가 지원되는 CPU에서만 __except 내로 진입합니다.)

(첨부한 파일은 위의 코드를 포함하는 간단한 Visual C++ 프로젝트입니다.)




좀 더 자세한 정보는 인텔이 배포하고 있는 문서를 보시면 됩니다.

Intel_MPX_EnablingGuide.pdf
; https://software.intel.com/sites/default/files/managed/9d/f6/Intel_MPX_EnablingGuide.pdf

물론 MPX 명령어가 지원되는 CPU들이 일반화되려면 시간이 한참 지나야 할 것입니다. 그래도 미리 이 명령어가 추가되도록 /d2MPX 옵션을 줘도 상관없는데요, 왜냐하면 MPX 지원이 안되는 CPU에서 이 명령어들이 실행되면 CPU 입장에서 그냥 NOP 명령어를 수행하는 것과 동일하게 처리하므로 결국 아무런 영향도 주지 않게 됩니다. 따라서, CPU 종류를 개발자 입장에서 신경쓸 필요도 없다는 이야기입니다.

단지, MPX가 지원되지 않는 컴퓨터에서 "Intel MPX Runtime Driver"는 설치될 수 있는데요. 이런 환경에서 MPX 추가된 응용 프로그램을 실행하면 다음과 같은 메시지를 동반하는 블루스크린을 볼 수 있습니다.

Your PC ran into a problem and needs to restart. We're just collecting some error info, and then we'll restart for you. (..% complete)

If you'd like to know more, you can search online later for this error: KMODE_EXCEPTION_NOT_HANDLED (MpxRuntime.sys)

이것 역시 그다지 흔한 환경은 아닐 것이기 때문에 문제가 될 수준은 아니겠지요! ^^

정리하면... 그냥 MPX는 인텔(과 컴파일러 회사들)이 지원해주는 공짜 점심(Free lunch)라고 보시면 됩니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 7/10/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 46  47  48  49  50  51  52  53  54  55  56  57  58  59  [60]  ...
NoWriterDateCnt.TitleFile(s)
12123정성태1/23/202011817웹: 39. Google Analytics - gtag 함수를 이용해 페이지 URL 수정 및 별도의 이벤트 생성 방법 [2]
12122정성태1/20/20208826.NET Framework: 879. C/C++의 UNREFERENCED_PARAMETER 매크로를 C#에서 우회하는 방법(IDE0060 - Remove unused parameter '...')파일 다운로드1
12121정성태1/20/20209337VS.NET IDE: 139. Visual Studio - Error List: "Could not find schema information for the ..."파일 다운로드1
12120정성태1/19/202010796.NET Framework: 878. C# DLL에서 Win32 C/C++처럼 dllexport 함수를 제공하는 방법 - 네 번째 이야기(IL 코드로 직접 구현)파일 다운로드1
12119정성태1/17/202010847디버깅 기술: 160. Windbg 확장 DLL 만들기 (3) - C#으로 만드는 방법
12118정성태1/17/202011464개발 환경 구성: 466. C# DLL에서 Win32 C/C++처럼 dllexport 함수를 제공하는 방법 - 세 번째 이야기 [1]
12117정성태1/15/202010491디버깅 기술: 159. C# - 디버깅 중인 프로세스를 강제로 다른 디버거에서 연결하는 방법파일 다운로드1
12116정성태1/15/202010975디버깅 기술: 158. Visual Studio로 디버깅 시 sos.dll 확장 명령어를 (비롯한 windbg의 다양한 기능을) 수행하는 방법
12115정성태1/14/202010725디버깅 기술: 157. C# - PEB.ProcessHeap을 이용해 디버깅 중인지 확인하는 방법파일 다운로드1
12114정성태1/13/202012561디버깅 기술: 156. C# - PDB 파일로부터 심벌(Symbol) 및 타입(Type) 정보 열거 [1]파일 다운로드3
12113정성태1/12/202013196오류 유형: 590. Visual C++ 빌드 오류 - fatal error LNK1104: cannot open file 'atls.lib' [1]
12112정성태1/12/20209806오류 유형: 589. PowerShell - 원격 Invoke-Command 실행 시 "WinRM cannot complete the operation" 오류 발생
12111정성태1/12/202013035디버깅 기술: 155. C# - KernelMemoryIO 드라이버를 이용해 실행 프로그램을 숨기는 방법(DKOM: Direct Kernel Object Modification) [16]파일 다운로드1
12110정성태1/11/202011601디버깅 기술: 154. Patch Guard로 인해 블루 스크린(BSOD)가 발생하는 사례 [5]파일 다운로드1
12109정성태1/10/20209533오류 유형: 588. Driver 프로젝트 빌드 오류 - Inf2Cat error -2: "Inf2Cat, signability test failed."
12108정성태1/10/20209577오류 유형: 587. Kernel Driver 시작 시 127(The specified procedure could not be found.) 오류 메시지 발생
12107정성태1/10/202010548.NET Framework: 877. C# - 프로세스의 모든 핸들을 열람 - 두 번째 이야기
12106정성태1/8/202011927VC++: 136. C++ - OSR Driver Loader와 같은 Legacy 커널 드라이버 설치 프로그램 제작 [1]
12105정성태1/8/202010599디버깅 기술: 153. C# - PEB를 조작해 로드된 DLL을 숨기는 방법
12104정성태1/7/202011297DDK: 9. 커널 메모리를 읽고 쓰는 NT Legacy driver와 C# 클라이언트 프로그램 [4]
12103정성태1/7/202013957DDK: 8. Visual Studio 2019 + WDK Legacy Driver 제작- Hello World 예제 [1]파일 다운로드2
12102정성태1/6/202011617디버깅 기술: 152. User 권한(Ring 3)의 프로그램에서 _ETHREAD 주소(및 커널 메모리를 읽을 수 있다면 _EPROCESS 주소) 구하는 방법
12101정성태1/5/202010995.NET Framework: 876. C# - PEB(Process Environment Block)를 통해 로드된 모듈 목록 열람
12100정성태1/3/20209035.NET Framework: 875. .NET 3.5 이하에서 IntPtr.Add 사용
12099정성태1/3/202011306디버깅 기술: 151. Windows 10 - Process Explorer로 확인한 Handle 정보를 windbg에서 조회 [1]
12098정성태1/2/202010918.NET Framework: 874. C# - 커널 구조체의 Offset 값을 하드 코딩하지 않고 사용하는 방법 [3]
... 46  47  48  49  50  51  52  53  54  55  56  57  58  59  [60]  ...