summaryrefslogtreecommitdiff
path: root/src/vm/validator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/validator.cpp')
-rw-r--r--src/vm/validator.cpp946
1 files changed, 946 insertions, 0 deletions
diff --git a/src/vm/validator.cpp b/src/vm/validator.cpp
new file mode 100644
index 0000000000..54f6ecdb2b
--- /dev/null
+++ b/src/vm/validator.cpp
@@ -0,0 +1,946 @@
+// 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;
+ {
+ // <REVISIT>
+ // 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.
+ // </REVISIT>
+ 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<BYTE*>(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<VerifyFunc> 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
+ // <REVISIT> This validation is not performed on non-manifest modules. </REVISIT>
+ 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);
+}
+
+