// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
/*
*
* Purpose: Provide IValidate implementation.
* IValidate is used to validate PE stub, Metadata and IL.
*
*/
#include "common.h"
#include "corerror.h"
#include "vererror.h"
#include "ivalidator.h"
#include "securityattributes.h"
#include "corhost.h"
#include "verifier.hpp"
#include "pedecoder.h"
#include "comcallablewrapper.h"
#include "../dlls/mscorrc/resource.h"
#include "posterror.h"
#include "comcallablewrapper.h"
#include "eeconfig.h"
#include "corhost.h"
#include "security.h"
#include "appdomain.inl"
typedef void (*VerifyErrorHandler)(void* pThis, HRESULT hrError, struct VerErrorStruct* pError);
// Declare global variables
#define DECLARE_DATA
#include "veropcodes.hpp"
#undef DECLARE_DATA
class CValidator
{
public:
CValidator(IVEHandler *veh) : m_veh(veh)
{
LIMITED_METHOD_CONTRACT;
}
HRESULT VerifyAllMethodsForClass(Module *pModule, mdTypeDef cl, ValidateWorkerArgs* pArgs);
HRESULT VerifyAllGlobalFunctions(Module *pModule, ValidateWorkerArgs* pArgs);
HRESULT VerifyAssembly(Assembly *pAssembly, ValidateWorkerArgs* pArgs);
HRESULT VerifyModule(Module* pModule, ValidateWorkerArgs* pArgs);
HRESULT ReportError(HRESULT hr, ValidateWorkerArgs* pArgs, mdToken tok=0);
HRESULT VerifyMethod(COR_ILMETHOD_DECODER* pILHeader, IVEHandler* pVEHandler, WORD wFlags, ValidateWorkerArgs* pArgs);
HRESULT VerifyExportedType(
Module * pModule,
mdToken tkExportedType,
ValidateWorkerArgs * pArgs);
void HandleError(HRESULT hrError, struct VerErrorStruct* pError);
private:
IVEHandler *m_veh;
ValidateWorkerArgs* m_pArgs;
}; // class CValidator
HRESULT CValidator::ReportError(HRESULT hr, ValidateWorkerArgs* pArgs, mdToken tok /* = 0 */)
{
CONTRACTL {
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
} CONTRACTL_END;
if (m_veh == NULL)
return hr;
HRESULT hr2 = E_FAIL;
BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return COR_E_STACKOVERFLOW);
VEContext vec;
memset(&vec, 0, sizeof(VEContext));
if (tok != 0)
{
vec.flags = VER_ERR_TOKEN;
vec.Token = tok;
}
hr2 = Verifier::ReportError(m_veh, hr, &vec, pArgs);
END_SO_INTOLERANT_CODE;
return hr2;
} // CValidator::ReportError
// Separate method since EX_TRY uses _alloca and is in a loop below.
COR_ILMETHOD* GetILHeader(MethodDesc *pMD)
{
STANDARD_VM_CONTRACT;
COR_ILMETHOD *pILHeader = NULL;
EX_TRY
{
pILHeader = pMD->GetILHeader();
}
EX_CATCH
{
}
EX_END_CATCH(SwallowAllExceptions);
return pILHeader;
}
HRESULT CValidator::VerifyAllMethodsForClass(Module *pModule, mdTypeDef cl, ValidateWorkerArgs* pArgs)
{
STANDARD_VM_CONTRACT;
HRESULT hr = S_OK;
MethodTable *pMT = NULL;
// In the case of COR_GLOBAL_PARENT_TOKEN (i.e. global functions), it is guaranteed
// that the module has a method table or our caller will have skipped this step.
TypeHandle th;
{
//
// Although there's no assert to disable here, we need to improve OOM reliability here. We are ignoring the HRESULT from the loader here.
// That could cause an OOM failure to be disguised as something else. OOM's
// need to be handled or propagated up to the caller.
//
CONTRACT_VIOLATION(0);
EX_TRY {
th = ClassLoader::LoadTypeDefOrRefThrowing(pModule, cl,
ClassLoader::ReturnNullIfNotFound,
ClassLoader::PermitUninstDefOrRef);
}
EX_CATCH_HRESULT(hr);
if (FAILED(hr)) {
if ((hr==COR_E_TYPELOAD) || (hr==VER_E_TYPELOAD)) {
hr = ReportError(hr, pArgs,cl);
} else {
hr = ReportError(hr, pArgs);
}
goto Exit;
}
}
pMT = th.GetMethodTable();
if (pMT == NULL)
{
hr = ReportError(VER_E_TYPELOAD, pArgs, cl);
goto Exit;
}
g_fVerifierOff = false;
{
// Verify all methods in class - excluding inherited methods
MethodTable::MethodIterator it(pMT);
for (; it.IsValid(); it.Next())
{
pArgs->pMethodDesc = it.GetMethodDesc();
bool fVerifyTransparentMethod = true;
if (pArgs->fTransparentMethodsOnly)
{
MethodSecurityDescriptor msd(pArgs->pMethodDesc);
fVerifyTransparentMethod = !msd.IsCritical();
}
if (pArgs->pMethodDesc &&
pArgs->pMethodDesc->GetMethodTable() == pMT &&
pArgs->pMethodDesc->IsIL() &&
!pArgs->pMethodDesc->IsAbstract() &&
!pArgs->pMethodDesc->IsUnboxingStub() &&
fVerifyTransparentMethod)
{
COR_ILMETHOD* pILHeader = GetILHeader(pArgs->pMethodDesc);
if (pILHeader != NULL)
{
COR_ILMETHOD_DECODER::DecoderStatus status;
COR_ILMETHOD_DECODER ILHeader(pILHeader,
pArgs->pMethodDesc->GetMDImport(), &status);
if (status == COR_ILMETHOD_DECODER::SUCCESS)
{
hr = VerifyMethod(&ILHeader, m_veh, VER_FORCE_VERIFY, pArgs);
if (hr == VER_E_INTERNAL) // this probably means peverify.dll was missing
{
goto Exit;
}
}
else if (status == COR_ILMETHOD_DECODER::VERIFICATION_ERROR)
{
hr = COR_E_VERIFICATION;
}
else if (status == COR_ILMETHOD_DECODER::FORMAT_ERROR)
{
hr = COR_E_BADIMAGEFORMAT;
}
else
{
_ASSERTE(!"Unhandled status from COR_ILMETHOD_DECODER");
}
}
else
{
hr = COR_E_BADIMAGEFORMAT;
}
if (FAILED(hr))
hr = ReportError(hr, pArgs);
if (FAILED(hr))
goto Exit;
}
// We should ideally have an API to yield to the host,
// but this is not critical for Whidbey.
if (CLRTaskHosted())
ClrSleepEx(0, FALSE);
}
}
Exit:
pArgs->pMethodDesc = NULL;
return hr;
} // CValidator::VerifyAllMethodsForClass
//---------------------------------------------------------------------------------------
//
void
MethodDescAndCorILMethodDecoderToCorInfoMethodInfo(
MethodDesc * ftn,
COR_ILMETHOD_DECODER * ILHeader,
CORINFO_METHOD_INFO * pMethodInfo)
{
STANDARD_VM_CONTRACT;
pMethodInfo->ftn = CORINFO_METHOD_HANDLE(ftn);
pMethodInfo->scope = CORINFO_MODULE_HANDLE(ftn->GetModule());
pMethodInfo->ILCode = const_cast(ILHeader->Code);
pMethodInfo->ILCodeSize = ILHeader->GetCodeSize();
pMethodInfo->maxStack = ILHeader->GetMaxStack();
pMethodInfo->EHcount = ILHeader->EHCount();
pMethodInfo->options =
(CorInfoOptions)
(((ILHeader->GetFlags() & CorILMethod_InitLocals) ? CORINFO_OPT_INIT_LOCALS : 0) |
(ftn->AcquiresInstMethodTableFromThis() ? CORINFO_GENERICS_CTXT_FROM_THIS : 0) |
(ftn->RequiresInstMethodTableArg() ? CORINFO_GENERICS_CTXT_FROM_METHODTABLE : 0) |
(ftn->RequiresInstMethodDescArg() ? CORINFO_GENERICS_CTXT_FROM_METHODDESC : 0));
PCCOR_SIGNATURE pSigToConvert;
DWORD cbSigToConvert;
ftn->GetSig(&pSigToConvert, &cbSigToConvert);
CONSISTENCY_CHECK(NULL != pSigToConvert);
// fetch the method signature
CEEInfo::ConvToJitSig(
pSigToConvert,
cbSigToConvert,
pMethodInfo->scope,
mdTokenNil,
&pMethodInfo->args,
ftn,
false);
//@GENERICS:
// Shared generic methods and shared methods on generic structs take an extra argument representing their instantiation
if (ftn->RequiresInstArg())
pMethodInfo->args.callConv = (CorInfoCallConv) (pMethodInfo->args.callConv | CORINFO_CALLCONV_PARAMTYPE);
// method attributes and signature are consistant
_ASSERTE(!!ftn->IsStatic() == ((pMethodInfo->args.callConv & CORINFO_CALLCONV_HASTHIS) == 0));
// And its local variables
CEEInfo::ConvToJitSig(
ILHeader->LocalVarSig,
ILHeader->cbLocalVarSig,
pMethodInfo->scope,
mdTokenNil,
&pMethodInfo->locals,
ftn,
true);
} // MethodDescAndCorILMethodDecoderToCorInfoMethodInfo
//---------------------------------------------------------------------------------------
//
void PEVerifyErrorHandler(void* pThis, HRESULT hrError, struct VerErrorStruct* pError)
{
WRAPPER_NO_CONTRACT;
STATIC_CONTRACT_SO_TOLERANT;
((CValidator*)pThis)->HandleError(hrError, pError);
}
void CValidator::HandleError(HRESULT hrError, struct VerErrorStruct* pError)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
SO_TOLERANT;
}
CONTRACTL_END;
BEGIN_SO_INTOLERANT_CODE(GetThread());
_ASSERTE(sizeof(VEContext) == sizeof(struct VerErrorStruct));
Verifier::ReportError(m_veh, hrError, (VEContext*)pError, m_pArgs);
END_SO_INTOLERANT_CODE;
}
typedef void (__stdcall* VerifyFunc)(ICorJitInfo* pJitInfo, CORINFO_METHOD_INFO* pMethodInfo, VerifyErrorHandler pErrorHandler, void* pThis);
static void VerifyMethodHelper(VerifyFunc pVerFunc, CEEJitInfo* pJI, CORINFO_METHOD_INFO* pMethodInfo, void* pThis)
{
// Helper method to allow us to use SO_TOLERANT_CODE macro
STATIC_CONTRACT_SO_INTOLERANT;
WRAPPER_NO_CONTRACT;
BEGIN_SO_TOLERANT_CODE(GetThread());
// Verify the method
pVerFunc(pJI, pMethodInfo, PEVerifyErrorHandler, pThis);
END_SO_TOLERANT_CODE;
}
static Volatile g_pVerFunc = NULL;
HRESULT CValidator::VerifyMethod(COR_ILMETHOD_DECODER* pILHeader, IVEHandler* pVEHandler, WORD wFlags, ValidateWorkerArgs* pArgs)
{
STANDARD_VM_CONTRACT;
HRESULT hr = S_OK;
EX_TRY
{
// Find the DLL entrypoint
m_pArgs = pArgs;
if (g_pVerFunc.Load() == NULL)
{
HINSTANCE hJit64 = NULL;
if (SUCCEEDED(g_pCLRRuntime->LoadLibrary(W("peverify.dll"), &hJit64)))
{
typedef void (__stdcall* psxsPeVerifyStartup) (CoreClrCallbacks);
psxsPeVerifyStartup sxsPeVerifyStartup = (psxsPeVerifyStartup) GetProcAddress(hJit64, "sxsPeVerifyStartup");
if(sxsPeVerifyStartup)
{
CoreClrCallbacks cccallbacks = GetClrCallbacks();
(*sxsPeVerifyStartup) (cccallbacks);
g_pVerFunc = (VerifyFunc)GetProcAddress(hJit64, "VerifyMethod");
}
}
}
if(!g_pVerFunc)
{
_ASSERTE(!"Failed to load peverify.dll or find VerifyMethod proc address");
hr = VER_E_INTERNAL;
}
else
{
Thread *pThread = GetThread();
if (pThread->IsAbortRequested())
{
pThread->HandleThreadAbort();
}
// Prepare the args
MethodDesc* ftn = pArgs->pMethodDesc;
CEEJitInfo ji(pArgs->pMethodDesc, pILHeader, NULL, true /* verify only */);
CORINFO_METHOD_INFO methodInfo;
MethodDescAndCorILMethodDecoderToCorInfoMethodInfo(ftn, pILHeader, &methodInfo);
// Verify the method
VerifyMethodHelper(g_pVerFunc, &ji, &methodInfo, this);
}
}
EX_CATCH
{
// Catch and report any errors that peverify.dll lets fall through (ideally that should never happen)
hr = GET_EXCEPTION()->GetHR();
hr = ReportError(hr, pArgs);
}
EX_END_CATCH(RethrowTerminalExceptions)
return hr;
} // CValidator::VerifyMethod
// Helper function to verify the global functions
HRESULT CValidator::VerifyAllGlobalFunctions(Module *pModule, ValidateWorkerArgs* pArgs)
{
STANDARD_VM_CONTRACT;
HRESULT hr = S_OK;
// Is there anything worth verifying?
if (pModule->GetGlobalMethodTable())
hr = VerifyAllMethodsForClass(pModule, COR_GLOBAL_PARENT_TOKEN, pArgs);
return hr;
} // CValidator::VerifyAllGlobalFunctions
HRESULT CValidator::VerifyModule(Module* pModule, ValidateWorkerArgs* pArgs)
{
STANDARD_VM_CONTRACT;
// Get a count of all the classdefs and enumerate them.
HRESULT hr = S_OK;
IMDInternalImport * pMDI = NULL;
if (pModule == NULL)
{
IfFailGo(VER_E_BAD_MD);
}
pMDI = pModule->GetMDImport();
if (pMDI == NULL)
{
IfFailGo(VER_E_BAD_MD);
}
// First verify all global functions - if there are any
IfFailGoto(
VerifyAllGlobalFunctions(pModule, pArgs),
ErrExit_SkipReportError);
{
HENUMTypeDefInternalHolder hTypeDefEnum(pMDI);
IfFailGo(hTypeDefEnum.EnumTypeDefInitNoThrow());
// Verify all TypeDefs
mdTypeDef tkTypeDef;
while (pMDI->EnumTypeDefNext(&hTypeDefEnum, &tkTypeDef))
{
IfFailGoto(
VerifyAllMethodsForClass(pModule, tkTypeDef, pArgs),
ErrExit_SkipReportError);
}
}
{
HENUMInternalHolder hExportedTypeEnum(pMDI);
IfFailGo(hExportedTypeEnum.EnumInitNoThrow(
mdtExportedType,
mdTokenNil));
// Verify all ExportedTypes
mdToken tkExportedType;
while (pMDI->EnumNext(&hExportedTypeEnum, &tkExportedType))
{
IfFailGoto(
VerifyExportedType(pModule, tkExportedType, pArgs),
ErrExit_SkipReportError);
}
}
ErrExit:
if (FAILED(hr))
{
hr = ReportError(hr, pArgs);
}
ErrExit_SkipReportError:
return hr;
} // CValidator::VerifyModule
HRESULT CValidator::VerifyAssembly(Assembly *pAssembly, ValidateWorkerArgs* pArgs)
{
STANDARD_VM_CONTRACT;
HRESULT hr;
_ASSERTE(pAssembly->GetManifestImport());
// Verify the module containing the manifest. There is no
// FileRefence so will no show up in the list.
hr = VerifyModule(pAssembly->GetManifestModule(), pArgs);
if (FAILED(hr))
goto Exit;
{
IMDInternalImport* pManifestImport = pAssembly->GetManifestImport();
HENUMInternalHolder hEnum(pManifestImport);
mdToken mdFile;
hr = hEnum.EnumInitNoThrow(mdtFile, mdTokenNil);
if (FAILED(hr))
{
hr = ReportError(hr, pArgs);
goto Exit;
}
while(pManifestImport->EnumNext(&hEnum, &mdFile))
{
DomainFile* pModule = pAssembly->GetManifestModule()->LoadModule(GetAppDomain(), mdFile, FALSE);
if (pModule != NULL)
{
hr = VerifyModule(pModule->GetModule(), pArgs);
if (FAILED(hr))
goto Exit;
}
}
}
Exit:
return hr;
} // CValidator::VerifyAssembly
HRESULT
CValidator::VerifyExportedType(
Module * pModule,
mdToken tkExportedType,
ValidateWorkerArgs * pArgs)
{
STANDARD_VM_CONTRACT;
HRESULT hr;
TypeHandle th;
NameHandle nameHandle(pModule, tkExportedType);
LPCSTR szNamespace;
LPCSTR szName;
IfFailGo(pModule->GetMDImport()->GetExportedTypeProps(
tkExportedType,
&szNamespace,
&szName,
NULL, // tkImplementation
NULL, // tkTypeDefId
NULL)); // dwExportedTypeFlags
nameHandle.SetName(szNamespace, szName);
EX_TRY
{
th = pModule->GetClassLoader()->LoadTypeHandleThrowing(
&nameHandle,
CLASS_LOADED,
pModule);
hr = S_OK;
}
EX_CATCH
{
hr = GET_EXCEPTION()->GetHR();
}
EX_END_CATCH(SwallowAllExceptions);
IfFailGo(hr);
if (th.GetMethodTable() == NULL)
{
IfFailGo(VER_E_TYPELOAD);
}
ErrExit:
if (FAILED(hr))
{
hr = ReportError(hr, pArgs, tkExportedType);
}
return hr;
} // CValidator::VerifyExportedType
static void ValidateWorker(LPVOID /* ValidateWorker_Args */ ptr)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
} CONTRACTL_END;
ValidateWorkerArgs *args = (ValidateWorkerArgs *) ptr;
AppDomain *pDomain = GetThread()->GetDomain();
StackSString ssFile(args->wszFileName);
StackSString ssFileDir;
StackSString ssDirectory;
// Fill ssDirectory with just drive of the file (e.g. 'C:')
SplitPath(ssFile, &ssDirectory, &ssFileDir, NULL, NULL);
// Now apped directory from the file name (incl. leading and trailing '/' or '\')
ssDirectory.Append(ssFileDir);
{
// Set up the domain to resolve all dependency assemblies for introspection
struct _gc {
OBJECTREF orAppDomain;
STRINGREF refDirectory;
} gc;
ZeroMemory(&gc, sizeof(gc));
GCPROTECT_BEGIN(gc);
gc.orAppDomain = pDomain->GetExposedObject();
if (!ssDirectory.IsEmpty())
{
gc.refDirectory = StringObject::NewString(ssDirectory);
}
MethodDescCallSite meth(METHOD__APP_DOMAIN__ENABLE_RESOLVE_ASSEMBLIES_FOR_INTROSPECTION, &gc.orAppDomain);
ARG_SLOT args[2] =
{
ObjToArgSlot(gc.orAppDomain),
ObjToArgSlot(gc.refDirectory)
};
meth.Call(args);
GCPROTECT_END();
}
GCX_PREEMP();
Assembly *pAssembly;
if (args->wszFileName)
{
// Load the primary assembly for introspection
AssemblySpec spec;
spec.SetCodeBase(args->wszFileName);
spec.SetIntrospectionOnly(TRUE);
pAssembly = spec.LoadAssembly(FILE_LOADED);
}
else
{
// TODO: This is a workaround to get SQLCLR running.
// Our loader requires that a parent assembly is specified in order to load an
// assembly from byte array. But here we do not know the parent.
PEAssemblyHolder pFile(PEAssembly::OpenMemory(SystemDomain::System()->SystemFile(),
args->pe, args->size, TRUE));
pAssembly = pDomain->LoadAssembly(NULL, pFile, FILE_LOADED);
}
// Verify the assembly
args->hr = args->val->VerifyAssembly(pAssembly, args);
}
static HRESULT ValidateHelper(
IVEHandler *veh,
IUnknown *pAppDomain,
DWORD ulAppDomainId,
BOOL UseId,
unsigned long ulFlags,
unsigned long ulMaxError,
unsigned long token,
__in_z LPWSTR fileName,
BYTE *pe,
unsigned long ulSize)
{
CONTRACTL {
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
SO_TOLERANT;
} CONTRACTL_END;
Thread *pThread = GetThread();
if (pe == NULL)
return E_POINTER;
HRESULT hr = S_OK;
BEGIN_SO_INTOLERANT_CODE_NOTHROW(pThread, return COR_E_STACKOVERFLOW);
ADID pDomain;
ValidateWorkerArgs args;
CValidator val(veh);
AppDomainFromIDHolder ad;
BOOL Chk = FALSE;
BOOL UnloadDomain = FALSE;
GCX_COOP();
EX_TRY {
PEDecoder pev(pe, (COUNT_T)ulSize);
args.wszFileName = fileName;
args.fVerbose = (ulFlags & VALIDATOR_EXTRA_VERBOSE) ? true : false;
args.fShowSourceLines = (ulFlags & VALIDATOR_SHOW_SOURCE_LINES) ? true : false;
args.fTransparentMethodsOnly = (ulFlags & VALIDATOR_TRANSPARENT_ONLY) ? true : false;
args.val = &val;
args.pe = pe;
args.size = ulSize;
if((ulFlags & VALIDATOR_NOCHECK_PEFORMAT) == 0)
{
// Verify the PE header / native stubs first
// This validation is not performed on non-manifest modules.
Chk = ((ulFlags & VALIDATOR_CHECK_ILONLY) != 0) ? (BOOL) pev.CheckILOnlyFormat() :
(BOOL) pev.CheckILFormat();
if (!Chk)
{
hr = val.ReportError(VER_E_BAD_PE, &args);
if (FAILED(hr))
goto End;
}
}
if((ulFlags & VALIDATOR_CHECK_PEFORMAT_ONLY) != 0)
goto End;
if (fileName)
{
AppDomain* pAD = AppDomain::CreateDomainContext(fileName);
UnloadDomain = TRUE;
pAD->SetPassiveDomain();
pDomain=pAD->GetId();
}
else if (UseId)
{
pDomain = (ADID)ulAppDomainId;
}
else
{
SystemDomain::LockHolder lh;
ComCallWrapper* pWrap = GetCCWFromIUnknown(pAppDomain, FALSE);
if (pWrap == NULL)
{
hr = COR_E_APPDOMAINUNLOADED;
goto End;
}
pDomain = pWrap->GetDomainID();
}
if (FAILED(hr))
{
hr = val.ReportError(hr, &args);
goto End;
}
ad.Assign(pDomain, TRUE);
if (ad.IsUnloaded())
COMPlusThrow(kAppDomainUnloadedException);
if (ad->IsIllegalVerificationDomain())
COMPlusThrow(kFileLoadException, IDS_LOADINTROSPECTION_DISALLOWED);
ad->SetVerificationDomain();
ad.Release();
args.val = &val;
// We need a file path here. This is to do a fusion bind, and also
// to make sure we can find any modules in the assembly. We assume
// that the path points to the same place the bytes came from, which is true
// with PEVerify, but perhaps not with other clients.
if (pDomain != pThread->GetDomain()->GetId())
{
pThread->DoADCallBack(
pDomain, ValidateWorker, &args);
}
else
{
ValidateWorker(&args);
}
if (FAILED(args.hr))
hr = val.ReportError(args.hr, &args);
// Only Unload the domain if we created it.
if (UnloadDomain)
AppDomain::UnloadById(pDomain,TRUE);
End:;
}
EX_CATCH
{
hr = GET_EXCEPTION()->GetHR();
hr = val.ReportError(hr, &args);
}
EX_END_CATCH(RethrowSOExceptions)
END_SO_INTOLERANT_CODE;
return hr;
}
void GetFormattingErrorMsg(__out_ecount(ulMaxLength) __out_z LPWSTR msg, unsigned int ulMaxLength)
{
CONTRACTL {
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(ulMaxLength >= 30);
} CONTRACTL_END;
EX_TRY
{
SString s;
s.LoadResource(CCompRC::Debugging, IDS_VER_E_FORMATTING);
wcsncpy_s(msg, ulMaxLength, s.GetUnicode(), _TRUNCATE);
}
EX_CATCH
{
wcscpy_s(msg, ulMaxLength, W("Error loading resource string"));
}
EX_END_CATCH(SwallowAllExceptions)
}
static HRESULT FormatEventInfoHelper(
HRESULT hVECode,
VEContext Context,
__out_ecount(ulMaxLength) __out_z LPWSTR msg,
unsigned int ulMaxLength,
SAFEARRAY *psa)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(ulMaxLength >= 30);
SO_TOLERANT;
} CONTRACTL_END;
BEGIN_SO_INTOLERANT_CODE(GetThread());
VerError err;
memcpy(&err, &Context, sizeof(VerError));
ValidateWorkerArgs argsDefault;
ValidateWorkerArgs* pArgs = &argsDefault;
// We passed a pointer to the ValidateWorkerArgs object through
// the SAFEARRAY casted as a UINT because there was no room left in the
// interface to pass information through it.
{
UINT dim;
LONG l;
#ifdef _WIN64
VARTYPE vt;
#endif // _WIN64
VARIANT var;
if(!psa) {
goto lDone;
}
dim = SafeArrayGetDim(psa);
if (dim != 1) {
_ASSERTE(!"There should be one element in the SafeArray");
goto lDone;
}
if (FAILED(SafeArrayGetLBound(psa, 1, &l))) {
_ASSERTE(false);
goto lDone;
}
if (l != 0) {
_ASSERTE(!"expected the lower bound to be zero");
goto lDone;
}
if (FAILED(SafeArrayGetUBound(psa, 1, &l))) {
_ASSERTE(false);
goto lDone;
}
if (l != 0) {
_ASSERTE(!"expected the upper bound to be zero");
goto lDone;
}
#ifdef _WIN64
// This check fails on Win2K when it should pass
SafeArrayGetVartype(psa, &vt);
if(vt != VT_VARIANT) {
_ASSERTE(!"expected the ElementType to be a VT_VARIANT");
goto lDone;
}
#endif // _WIN64
l = 0;
SafeArrayGetElement(psa, &l, &var);
#ifdef _WIN64
if (V_VT(&var) != VT_UI8) { // We expect the VarType to be a VT_UI8 (VT_UI8 is not supported on Windows 2000)
_ASSERTE(false);
goto lDone;
}
pArgs = (ValidateWorkerArgs*)(size_t)V_UI8(&var);
#else
// We don't check that the type is V_UINT here because that check fails on Win2K when it should pass
pArgs = (ValidateWorkerArgs*)(size_t)V_UINT(&var);
#endif
}
lDone: ;
EX_TRY
{
Verifier::GetErrorMsg(hVECode, err, msg, ulMaxLength, pArgs);
}
EX_CATCH
{
GetFormattingErrorMsg(msg, ulMaxLength);
}
EX_END_CATCH(SwallowAllExceptions)
END_SO_INTOLERANT_CODE;
return S_OK;
}
HRESULT CorValidator::Validate(
IVEHandler *veh,
IUnknown *pAppDomain,
unsigned long ulFlags,
unsigned long ulMaxError,
unsigned long token,
__in_z LPWSTR fileName,
BYTE *pe,
unsigned long ulSize)
{
WRAPPER_NO_CONTRACT;
STATIC_CONTRACT_SO_TOLERANT;
return ValidateHelper(veh, pAppDomain, 0, FALSE, ulFlags, ulMaxError,
token, fileName, pe, ulSize);
}
HRESULT CLRValidator::Validate(
IVEHandler *veh,
unsigned long ulAppDomainId,
unsigned long ulFlags,
unsigned long ulMaxError,
unsigned long token,
__in_z LPWSTR fileName,
BYTE *pe,
unsigned long ulSize)
{
WRAPPER_NO_CONTRACT;
STATIC_CONTRACT_SO_TOLERANT;
return ValidateHelper(veh, NULL, ulAppDomainId, TRUE, ulFlags, ulMaxError,
token, fileName, pe, ulSize);
}
HRESULT CorValidator::FormatEventInfo(
HRESULT hVECode,
VEContext Context,
__out_ecount(ulMaxLength) LPWSTR msg,
unsigned long ulMaxLength,
SAFEARRAY *psa)
{
WRAPPER_NO_CONTRACT;
return FormatEventInfoHelper(hVECode, Context, msg, ulMaxLength, psa);
}
HRESULT CLRValidator::FormatEventInfo(
HRESULT hVECode,
VEContext Context,
__out_ecount(ulMaxLength) LPWSTR msg,
unsigned long ulMaxLength,
SAFEARRAY *psa)
{
WRAPPER_NO_CONTRACT;
STATIC_CONTRACT_SO_TOLERANT;
return FormatEventInfoHelper(hVECode, Context, msg, ulMaxLength, psa);
}