|
Managed Code역시 Access Violation(c0000005)는 발생한다. Managed 환경에서 Pointer 연산이 존재하지 않기 때문에 발생하지 않지 않나 라고 생각할 수도 있겠지만, Managed Code 역시 Intermediate code가 결국은 machine code로 변환되고 실행되기 때문에 register 에 들어있는 Address 가 invalid 한 경우는 흔하게 발생할 수 있다. 다음은 managed application이 AV로 인한 Crash가 발생한 대표적인 Callstack 이다. 0012ef30 792c3b86 c0000005 00000000 00000002 kernel32!RaiseException+0x53 0012ef70 792c3bce 791b4418 0012f104 0104d2c8 mscorwks!LinkFrameAndThrow+0x49 0012efc4 791dc474 00008004 00000000 011e63b0 mscorwks!CPFH_HandleManagedFault 0012efd8 793439ff 0012f070 00000003 0012f03c mscorwks!CallDescrWorker+0x30 0012f0cc 79343c36 0012eff0 0117bfc4 00ff8a6b mscorwks!CallDescrWithObjectArray+0x3bb 0012f1bc 00fe102c 0012f1c8 011dde14 0012f25c mscorwks!CStackBuilderSink::PrivateProcessMessage+0x16c WARNING: Frame IP not in any known module. Following frames may be wrong. 0012f274 799d16d1 00000000 00000000 799d1694 0xfe102c 0012f280 799d1694 011d4a6c 011a9a94 01196858 mscorlib_79990000+0x416d1 00000000 00000000 00000000 00000000 00000000 mscorlib_79990000+0x41694 Kernel32!RaiseException Function은 Argument를 통해서 AV가 발생했음을 여지 없이 보여주고 있다. 그러면, 어디에서 AV가 발생한 것일 까? AV는 managed exception은 아니기 때문에 Native Code에서 살펴보아야 할 것 같은 데.. 상위의 Callstack은 뭔가 Access Violation이 CLR에서 handling 된 것은 아닌지 의심하게 해준다. 예를 들어, mscorwks!CPFH_HandleManagedFault 와 같은 함수의 이름이 그렇다. 그러므로, Managed Debugging을 해본 사람이라면 그렇듯이 Managed Callstack을 확인해 볼 필요가 있다. 0:000> !clrstack Thread 0 ESP EIP 0x0012ef78 0x7c812aeb [FRAME: FaultingExceptionFrame] 0x0012f104 0x7c812aeb [FRAME: GCFrame] 0x0012f1fc 0x7c812aeb [FRAME: ECallMethodFrame] [DEFAULT] [hasThis] Object System.Runtime.Remoting.Messaging.StackBuilderSink.PrivateProcessMessage(Class System.Reflection.MethodBase,SZArray Object,Object,I4,Boolean,ByRef SZArray Object) 0x0012f220 0x799d19f3 [DEFAULT] [hasThis] Class System.Runtime.Remoting.Messaging.IMessage System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(Class System.Runtime.Remoting.Messaging.IMessage,I4,Boolean) . . . Managed Callstack은 Native callstack과는 다르게 ESP, EIP 정보와 MethodDesc 정보 그리고, Frame 정보가 존재한다. 여기서 Frame 이라는 자료구조가 특이하다. 이는 EE(CLR에서의 Execution Engine)가 Managed Thread라는 형태로 Stack walking을 할 때, 필요로 한 정보들, 예를 들어, registers, local variables 나 return address 등의 정보(Managed에서는 흔히 Activation Record라는 용어를 사용한다.)를 keeping 하는 것에 대한 일종의 자료구조 형태로 Frame을 이용하기도 한다. 상위 Managed Callstack을 보면, FaultingExceptionFrame 이라는 것이 존재하는 데, 이것은 http://blogs.msdn.com/joelpob/archive/2004/03/05/84738.aspx 문서에 언급한 것처럼, Hardware exceptions에 대한 Exception Handling정보를 포함하고 있음을 알 수 있는 데, 실제로 FaultingExceptionFrame을 Memory Dumping에서 살펴보면, Exception이 발생한 시점의 return address에 대한 Jitting 된 MethodDesc 정보를 포함하고 있다. 0:000> dd 0x0012ef78 l4 0012ef78 791b4418 0012f104 0104d2c8 041b011b 0:000> !ip2md 041b011b MethodDesc: 0x00ff8a70 Jitted by normal JIT Method Name : [DEFAULT] [hasThis] I4 IEHost.Execute.IEExecuteRemote.ExecuteAsAssembly(String,Class System.Security.Policy.Evidence,SZArray UI1,ValueClass System.Configuration.Assemblies.AssemblyHashAlgorithm) MethodTable 0xff8abc Module: 0x1d7e48 mdToken: 0x06000006 (c:\windows\assembly\gac\ieexecremote\1.0.5000.0__b03f5f7f11d50a3a\ieexecremote.dll) Flags : 0x20a0 Method VA : 0x041b0098 또한, Low callstack을 확인해보면, mscorwks!g_SavedExceptionInfo 라는 전역변수를 확인할 수 있는 데, 해당 전역변수는 Exception Record 나 Exception Context와 같은 정보를 가지고 있으므로 AV가 발생한 시점의 Context로 이동이 가능하다. 0:000> dds esp 0012eedc 00000002 0012eee0 c0000005 0012eee4 00000000 0012eee8 00000000 0012eeec 7c812aeb kernel32!RaiseException+0x53 0012eef0 00000002 0012eef4 00000000 0012eef8 00000000 0012eefc 00000080 0012ef00 0226b761 0012ef04 003a0043 0012ef08 0064005c 0012ef0c 0000213c 0012ef10 0006f748 0012ef14 77bf0017 msvcrt!_vsnwprintf+0x30 0012ef18 0006f728 0012ef1c 0200c7b4 0012ef20 0006fc4c 0012ef24 00000001 0012ef28 0230b459 0012ef2c 00007ef6 0012ef30 0012ef70 0012ef34 792c3b86 mscorwks!LinkFrameAndThrow+0x49 0012ef38 c0000005 0012ef3c 00000000 0012ef40 00000002 0012ef44 793ef084 mscorwks!g_SavedExceptionInfo+0x14 0012ef48 00000000 0012ef4c 011dbf88 0012ef50 0014eba8 0012ef54 00000000 0012ef58 0012ef48 0:000> x mscorwks!g_SavedExceptionInfo 793ef070 mscorwks!g_SavedExceptionInfo = <no type information> 0:000> dd 793ef070 793ef070 c0000005 00000000 00000000 041b011b <--- c0000005(AV), 041b011b=MethodDesc 793ef080 00000002 00000000 00000000 0001003f 793ef090 00000000 00000000 00000000 00000000 793ef0a0 00000000 00000000 ffff027f ffff4020 793ef0b0 ffffffff 799b14d4 05d8001b 0012f21c 793ef0c0 0001003f 00000000 00000000 00000000 <---- 1003f는 Context structure의 시작위치 793ef0d0 00000000 00000000 00000000 ffff027f 793ef0e0 ffff4020 ffffffff 799b14d4 05d8001b 0:000> .cxr 793ef0c0 eax=011ed86c ebx=0014eba8 ecx=00000000 edx=011ed86c esi=011dbf88 edi=00000000 eip=041b011b esp=0012ef9c ebp=0012efc4 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246 041b011b 8b01 mov eax,dword ptr [ecx] ds:0023:00000000=???????? 0:000> kb *** Stack trace for last set context - .thread/.cxr resets it ChildEBP RetAddr Args to Child WARNING: Frame IP not in any known module. Following frames may be wrong. 0012efc4 791dc474 00008004 00000000 011e63b0 0x41b011b 0012efd8 793439ff 0012f070 00000003 0012f03c mscorwks!CallDescrWorker+0x30 0012f0cc 79343c36 0012eff0 0117bfc4 00ff8a6b mscorwks!CallDescrWithObjectArray+0x3bb 0012f1bc 00fe102c 0012f1c8 011dde14 0012f25c mscorwks!CStackBuilderSink::PrivateProcessMessage+0x16c 0012f274 799d16d1 00000000 00000000 799d1694 0xfe102c 0012f280 799d1694 011d4a6c 011a9a94 01196858 mscorlib_79990000+0x416d1 00000000 00000000 00000000 00000000 00000000 mscorlib_79990000+0x41694 그러므로, 0x41b011b 는 AV가 발생한 시점의 return address이며, Native Callstack에서는 상위와 같이 보여진다. Access Violation은 왜 발생했을 까? MethodDesc 에 대한 Assembly code는 아래와 같이 확인한다. 0:000> !u 0x41b011b Will print '>>> ' at address: 0x041b011b Normal JIT generated code [DEFAULT] [hasThis] I4 IEHost.Execute.IEExecuteRemote.ExecuteAsAssembly(String,Class System.Security.Policy.Evidence,SZArray UI1,ValueClass System.Configuration.Assemblies.AssemblyHashAlgorithm) Begin 0x041b0098, size 0x159 041b0098 55 push ebp 041b0099 8bec mov ebp,esp 041b009b 83ec18 sub esp,18h 041b009e 57 push edi 041b009f 56 push esi 041b00a0 53 push ebx 041b00a1 c745f800000000 mov dword ptr [ebp-8],0 041b00a8 894dec mov dword ptr [ebp-14h],ecx 041b00ab 8bfa mov edi,edx 041b00ad 8b75ec mov esi,dword ptr [ebp-14h] 041b00b0 ff750c push dword ptr [ebp+0Ch] 041b00b3 ff7508 push dword ptr [ebp+8] 041b00b6 8bcf mov ecx,edi 041b00b8 33d2 xor edx,edx 041b00ba ff15144eba79 call dword ptr [mscorlib_79990000+0x214e14 (79ba4e14)] (System.Reflection.Assembly.LoadFrom) 041b00c0 8945e8 mov dword ptr [ebp-18h],eax 041b00c3 b93c8cff00 mov ecx,0FF8C3Ch 041b00c8 e84b1f87fc call 00a22018 041b00cd 8d5608 lea edx,[esi+8] 041b00d0 e8e32f82fc call 009d30b8 041b00d5 8b4608 mov eax,dword ptr [esi+8] 041b00d8 8d5004 lea edx,[eax+4] 041b00db e8f73082fc call 009d31d7 041b00e0 8b5608 mov edx,dword ptr [esi+8] 041b00e3 8b450c mov eax,dword ptr [ebp+0Ch] 041b00e6 8d5208 lea edx,[edx+8] 041b00e9 e8ca2f82fc call 009d30b8 041b00ee 8b4608 mov eax,dword ptr [esi+8] 041b00f1 8b5508 mov edx,dword ptr [ebp+8] 041b00f4 895010 mov dword ptr [eax+10h],edx 041b00f7 8b4608 mov eax,dword ptr [esi+8] 041b00fa c7400c02000000 mov dword ptr [eax+0Ch],2 041b0101 8b4de8 mov ecx,dword ptr [ebp-18h] ; ebp-18h는 LoadFrom의 ret object, Assembly class 041b0104 8b01 mov eax,dword ptr [ecx] 041b0106 ff5068 call dword ptr [eax+68h] ; Assembly class가 제공하는 Method 041b0109 8bf8 mov edi,eax ; AV 유발하는 edi는 eax+68h Method call의 ret value 041b010b b9e8dcc079 mov ecx,offset mscorlib_79990000+0x27dce8 (79c0dce8) 041b0110 e8c2300375 call mscorwks!COMClass::GetClassFromHandle (791e31d7) 041b0115 6a00 push 0 041b0117 8bcf mov ecx,edi ;<--- AV 유발하는 ecx는 edi 로 부터.. 041b0119 8bd0 mov edx,eax >>> 041b011b 8b01 mov eax,dword ptr [ecx] ;<-- AV, ecx가 Null 0:000> r Last set context: eax=011ed86c ebx=0014eba8 ecx=00000000 edx=011ed86c esi=011dbf88 edi=00000000 eip=041b011b esp=0012ef9c ebp=0012efc4 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246 041b011b 8b01 mov eax,dword ptr [ecx] ds:0023:00000000=???????? ecx 는 Null 이다. 이는 edi 로 부터 얻어진다. edi는 Method Call의 return value 인데, 이는 결국, System.Reflection.Assembly.LoadFrom 의 return value 인 Assembly class에서 제공하는 특정 method의 return value 로 추측할 수 있다. 문제는 Assembly class를 확인하기 위해서 @ebp-18 의 정보를 확인해 보면, 정상적으로 Assembly가 load되지 않았음을 알 수 있다. 0:000> !dumpassembly @ebp-18 Parent Domain: 0x00008004 <--- ????? Name: X/¹y ClassLoader: 0x00000000 <--- ????? Module Name 0x66893209 결국, System.Reflection.Assembly.LoadFrom 로 부터 얻어진 Assembly class가 문제되어 AV가 발생한 것으로 확인된다. 이는 추후에 NullReferenceException으로 다시 돌아온다. Native Code 거나 Managed Code거나 Object 의 Null Check는 필수!!.
|
카테고리
이글루링크
최근 등록된 덧글
그러세요
by 강세윤 at 12/15 오늘 많이 헤매다..알게 .. by youna at 12/14 글 잘 읽었습니다 . 전 .. by 위시 at 11/26 어렷다 by klhk at 11/09 dhjjgbem by kl at 11/09 17번부터 어떻게 접는지.. by tykim0131 at 10/28 ATL이나 MFC를 이용하.. by 김명신 at 09/24 복원되었군요.. 제 RSS.. by 강세윤 at 09/24 허걱, 하고 있는 것으로.. by 강세윤 at 09/15 RSS 주소 서비스는 안 .. by 정성태 at 09/15 이글루 파인더
| |||