How do I keep DEP from killing my JITted exception handler? How do I keep DEP from killing my JITted exception handler? windows windows

How do I keep DEP from killing my JITted exception handler?


The following code might help (which coms from my own compiler for stubbing interfaces:

function GetExecutableMem(Size: Integer): Pointer;  procedure RaiseOutofMemory;  begin    raise EOutOfResources.Create('UnitProxyGenerator.GetExecutableMem: Out of memory error.');  end;var  LastCommitTop: PChar;begin  // We round the memory needed up to 16 bytes which seems to be a cache line amound on the P4.  Size := (Size + $F) and (not $F);  //  Result := MemUsed;  Inc(MemUsed, Size);  // Do we need to commit some more memory?  if MemUsed > MemCommitTop then begin    // Do we need more mem than we reserved initially?    if MemUsed > MemTop then RaiseOutOfMemory;    // Try to commit the memory requested.    LastCommitTop := MemCommitTop;    MemCommitTop := PChar((Longword(MemUsed) + (SystemInfo.dwPageSize - 1)) and (not (SystemInfo.dwPageSize - 1)));    if not Assigned(VirtualAlloc(LastCommitTop, MemCommitTop - LastCommitTop, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) then RaiseOutOfMemory;  end;end;initialization  GetSystemInfo(SystemInfo);  MemBase := VirtualAlloc(nil, MemSize, MEM_RESERVE, PAGE_NOACCESS);  if MemBase = nil then Halt; // VERY BAD ...  MemUsed := MemBase;  MemCommitTop := MemBase;  MemTop := MemBase + MemSize;finalization  VirtualFree(MemBase, MemSize, MEM_DECOMMIT);  VirtualFree(MemBase, 0, MEM_RELEASE);end.

Please note the PAGE_EXECUTE_READWRITE in the VirtualAlloc call.

When process is run DEP enabled the following runs correctly:

type  TTestProc = procedure( out A: Integer ); stdcall;procedure Encode( var P: PByte; Code: array of Byte ); overload;var  i: Integer;begin  for i := 0 to High( Code ) do begin    P^ := Code[ i ];    Inc( P );  end;end;procedure Encode( var P: PByte; Code: Integer ); overload;begin  PInteger( P )^ := Code;  Inc( P, sizeof( Integer ) );end;procedure Encode( var P: PByte; Code: Pointer ); overload;begin  PPointer( P )^ := Code;  Inc( P, sizeof( Pointer ) );end;// returns address where exceptiuon handler will be.function EncodeTry( var P: PByte ): PByte;begin  Encode( P, [ $33, $C0, $55,$68 ] );             // xor eax,eax; push ebp; push @handle  Result := P;  Encode( P, nil );  Encode( P, [ $64, $FF, $30, $64, $89, $20 ] );  // push dword ptr fs:[eax]; mov fs:[eax],espend;procedure EncodePopTry( var P: PByte );begin  Encode( P, [ $33, $C0, $5A, $59, $59, $64, $89, $10 ] );  // xor eax,eax; pop edx; pop ecx; pop ecx; mov fs:[eax],edxend;function Delta( P, Q: PByte ): Integer;begin  Result := Integer( P ) - Integer( Q );end;function GetHandleFinally(): pointer;asm  lea eax, system.@HandleFinallyend;procedure TForm10.Button5Click( Sender: TObject );var  P, Q, R, S, T: PByte;  A:             Integer;begin  P := VirtualAlloc( nil, $10000, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE );  if not Assigned( P ) then Exit;  try    // ------------------------------------------------------------------------    // Equivalent    //    // A:=10;    // try    //   A:=20    //   PInteger(nil)^:=20    // finally    //   A:=30;    // end;    // A:=40;    //    // ------------------------------------------------------------------------    // Stack frame    Q := P;    Encode( Q, [ $55, $8B, $EC ] );                  // push ebp, mov ebp, esp    // A := 10;    Encode( Q, [ $8B, $45, $08, $C7, $00 ] );    Encode( Q, 10 );                                 // mov eax,[ebp+$08], mov [eax],<int32>    // try    R := EncodeTry( Q );    // TRY CODE !!!!    // A := 20;    Encode( Q, [ $8B, $45, $08, $C7, $00 ] );    Encode( Q, 20 );                                 // mov eax,[ebp+$08], mov [eax],<int32>    // REMOVE THIS AND NO EXCEPTION WILL OCCUR.    Encode( Q, [ $33, $C0, $C7, $00 ] );             // EXCEPTION: xor eax, eax, mov [eax], 20    Encode( Q, 20 );    // END OF REMOVE    // END OF TRY CODE    EncodePopTry( Q );    Encode( Q, [ $68 ] );                            // push @<afterfinally>    S := Q;    Encode( Q, nil );    // FINALLY CODE!!!!    T := Q;    // A := 30;    Encode( Q, [ $8B, $45, $08, $C7, $00 ] );    Encode( Q, 30 );                                 // mov eax,[ebp+$08], mov [eax],<int32>    // AFter finally    Encode( Q, [ $C3 ] );                            // ret    Encode( R, Q );                                  // Fixup try    // SEH handler    Encode( Q, [ $E9 ] );                            // jmp    Encode( Q, Delta( GetHandleFinally(), Q ) - sizeof( Pointer ) ); // <diff:i32>    Encode( Q, [ $E9 ] );                            // jmp    Encode( Q, Delta( T, Q ) - sizeof( Pointer ) );  // <diff:i32>    // After SEH frame    Encode( S, Q );    // A := 40;    Encode( Q, [ $8B, $45, $08, $C7, $00 ] );    Encode( Q, 40 );                             // mov eax,[ebp+$08], mov [eax],<int32>    // pop stack frame    Encode( Q, [ $5D, $C2, $04, $00 ] );         // pop ebp, ret 4    // ------------------------------------------------------------------------    // And.... execute    A := 0;    try      TTestProc( P )( A );    except      ;    end;    Caption := IntToStr( A )+'!1';    // Dofferent protection... execute    VirtualProtect( P, $10000, PAGE_EXECUTE_READ, nil );    A := 0;    try      TTestProc( P )( A );    except      ;    end;    Caption := IntToStr( A ) + '!2';  finally    // Cleanup    VirtualFree( P, $10000, MEM_RELEASE );  end;end;

It works on Windows 7 with both DEP disabled and enabled and seems to be a minimal piece of "JIT code" with a Delphi try-finally block in it. Could it be that it is a problem with a different / newer Windows platform?


I have deleted my other post and believe I realize what your problem probably is.

The issue lies in ntdll.RtlIsValidHandler which is validating your exception handler when its dispatching exceptions according to SAFESEH.

You need to avoid this by registering a Vectored Exception Handler and do your own exception dispatching so you wont have to worry about this behavior at all.

Edit:I believe your underlying issue is that the ExecuteDispatchEnable and ImageDispatchEnable are being set in the kernel's KPROCESS structure for some reason by DEP which is why you are having this problem to begin with. It might be possible to set these by calling NtSetInformationProcess, but given that this is not officially documented I cannot give good insight on how to make this call.