diff options
Diffstat (limited to 'src/md/compiler/mdvalidator.cpp')
-rw-r--r-- | src/md/compiler/mdvalidator.cpp | 7739 |
1 files changed, 7739 insertions, 0 deletions
diff --git a/src/md/compiler/mdvalidator.cpp b/src/md/compiler/mdvalidator.cpp new file mode 100644 index 0000000000..adcfd51eb3 --- /dev/null +++ b/src/md/compiler/mdvalidator.cpp @@ -0,0 +1,7739 @@ +// 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. +//***************************************************************************** +// MDValidator.cpp +// + +// +// Implementation for the MetaData validator. +// Only supported for full mscorwks version. +// +//***************************************************************************** +#include "stdafx.h" + +#ifdef FEATURE_METADATA_VALIDATOR + +#include "regmeta.h" +#include "importhelper.h" +#include "pedecoder.h" +#include "stgio.h" +#include "corhost.h" +#ifdef FEATURE_FUSION +#include "fusion.h" +#endif +#include "sstring.h" +#include "nsutilpriv.h" +#include "holder.h" +#include "vererror.h" + +#include "mdsighelper.h" + +#ifdef DACCESS_COMPILE +#error Dac should be using standalone version of metadata, not Wks version. +#endif + +//----------------------------------------------------------------------------- +// Application specific debug macro. +#define IfBreakGo(EXPR) \ +do {if ((EXPR) != S_OK) IfFailGo(VLDTR_E_INTERRUPTED); } while (0) + +//----------------------------------------------------------------------------- + +//#define CACHE_IMPLMAP_VALIDATION_RESULT +#ifdef CACHE_IMPLMAP_VALIDATION_RESULT +// To avoid multiple validation of the same thing: +struct ValidationResult +{ + mdToken tok; + HRESULT hr; +}; +ValidationResult* g_rValidated=NULL; // allocated in ValidateMetaData +unsigned g_nValidated=0; +#endif + +//----------------------------------------------------------------------------- + +#define BASE_OBJECT_CLASSNAME "Object" +#define BASE_NAMESPACE "System" +#define BASE_VTYPE_CLASSNAME "ValueType" +#define BASE_ENUM_CLASSNAME "Enum" +#define BASE_VALUE_FIELDNAME "value__" +#define BASE_CTOR_NAME ".ctor" +#define BASE_CCTOR_NAME ".cctor" +#define BASE_MCDELEGATE_CLASSNAME "MulticastDelegate" + +#define SYSTEM_OBJECT_TOSTRING_METHODNAME "ToString" +#define SYSTEM_OBJECT_GETHASHCODE_METHODNAME "GetHashCode" +#define SYSTEM_OBJECT_EQUALS_METHODNAME "Equals" + +// string ToString() +static const BYTE g_sigSystemObject_ToString[] = +{ + IMAGE_CEE_CS_CALLCONV_HASTHIS, // 0x20 + 0, // 0x00 ... Param Count + ELEMENT_TYPE_STRING // 0x0e ... Return Type - string +}; + +// int GetHashCode() +static const BYTE g_sigSystemObject_GetHashCode[] = +{ + IMAGE_CEE_CS_CALLCONV_HASTHIS, // 0x20 + 0, // 0x00 ... Param Count + ELEMENT_TYPE_I4 // 0x08 ... Return Type - I4 +}; + +// bool Equals(object) +static const BYTE g_sigSystemObject_Equals[] = +{ + IMAGE_CEE_CS_CALLCONV_HASTHIS, // 0x20 + 1, // 0x01 ... Param Count + ELEMENT_TYPE_BOOLEAN, // 0x02 ... Return Type - bool + ELEMENT_TYPE_OBJECT // 0x1c ... Param #1 - object +}; + +// as defined in src\vm\vars.hpp +#define MAX_CLASSNAME_LENGTH 1024 +//----------------------------------------------------------------------------- +// Class names used in long form signatures (namespace is always "System") +unsigned g_NumSigLongForms = 19; +static const LPCSTR g_SigLongFormName[] = { + "String", + "______", // "Object", <REVISIT_TODO>// uncomment when EE handles ELEMENT_TYPE_OBJECT</REVISIT_TODO> + "Boolean", + "Char", + "Byte", + "SByte", + "UInt16", + "Int16", + "UInt32", + "Int32", + "UInt64", + "Int64", + "Single", + "Double", + "SysInt", // Review this. + "SysUInt", // Review this. + "SingleResult", + "Void", + "IntPtr" +}; + +// <REVISIT_TODO>: Why are these global variables?</REVISIT_TODO> +mdToken g_tkEntryPoint; +bool g_fValidatingMscorlib; +bool g_fIsDLL; + +//----------------------------------------------------------------------------- + +static HRESULT _FindClassLayout( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdTypeDef tkParent, // [IN] the parent that ClassLayout is associated with + RID *clRid, // [OUT] rid for the ClassLayout. + RID rid); // [IN] rid to be ignored. + +static HRESULT _FindFieldLayout( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdFieldDef tkParent, // [IN] the parent that FieldLayout is associated with + RID *flRid, // [OUT] rid for the FieldLayout record. + RID rid); // [IN] rid to be ignored. + +static BOOL _IsValidLocale(LPCUTF8 szLocale, + BOOL fIsV2Assembly); + + +#define REPORT_ERROR0(_VECode) \ + IfFailGo(_ValidateErrorHelper(_VECode, veCtxt)) +#define REPORT_ERROR1(_VECode, _Arg0) \ + IfFailGo(_ValidateErrorHelper(_VECode, veCtxt, _Arg0)) +#define REPORT_ERROR2(_VECode, _Arg0, _Arg1) \ + IfFailGo(_ValidateErrorHelper(_VECode, veCtxt, _Arg0, _Arg1)) +#define REPORT_ERROR3(_VECode, _Arg0, _Arg1, _Arg2) \ + IfFailGo(_ValidateErrorHelper(_VECode, veCtxt, _Arg0, _Arg1, _Arg2)) + +//***************************************************************************** +// Returns true if ixPtrTbl and ixParTbl are a valid parent-child combination +// in the pointer table scheme. +//***************************************************************************** +static inline bool IsTblPtr(ULONG ixPtrTbl, ULONG ixParTbl) +{ + if ((ixPtrTbl == TBL_Field && ixParTbl == TBL_TypeDef) || + (ixPtrTbl == TBL_Method && ixParTbl == TBL_TypeDef) || + (ixPtrTbl == TBL_Param && ixParTbl == TBL_Method) || + (ixPtrTbl == TBL_Property && ixParTbl == TBL_PropertyMap) || + (ixPtrTbl == TBL_Event && ixParTbl == TBL_EventMap)) + { + return true; + } + return false; +} // IsTblPtr() + +//***************************************************************************** +// This inline function is used to set the return hr value for the Validate +// functions to one of VLDTR_S_WRN, VLDTR_S_ERR or VLDTR_S_WRNERR based on +// the current hr value and the new success code. +// The general algorithm for error codes from the validation functions is: +// if (no warnings or errors found) +// return S_OK or S_FALSE +// else if (warnings found) +// return VLDTR_S_WRN +// else if (errors found) +// return VLDTR_S_ERR +// else if (warnings and errors found) +// return VLDTR_S_WRNERR +//***************************************************************************** +static inline void SetVldtrCode(HRESULT *phr, HRESULT successcode) +{ + _ASSERTE(successcode == S_OK || successcode == S_FALSE ||successcode == VLDTR_S_WRN || + successcode == VLDTR_S_ERR || successcode == VLDTR_S_WRNERR); + _ASSERTE(*phr == S_OK || *phr == VLDTR_S_WRN || *phr == VLDTR_S_ERR || + *phr == VLDTR_S_WRNERR); + if (successcode == S_OK || successcode == S_FALSE ||*phr == VLDTR_S_WRNERR) + return; + else if (*phr == S_OK || *phr == S_FALSE) + *phr = successcode; + else if (*phr != successcode) + *phr = VLDTR_S_WRNERR; +} // SetVldtrCode() + +//***************************************************************************** +// Initialize the Validator related structures in RegMeta. +//***************************************************************************** +HRESULT RegMeta::ValidatorInit( // S_OK or error. + DWORD dwModuleType, // [IN] Specifies whether the module is a PE file or an obj. + IUnknown *pUnk) // [IN] Validation error handler. +{ + HRESULT hr = S_OK; // Return value. + + BEGIN_ENTRYPOINT_NOTHROW; + + int i = 0; // Index into the function pointer table. + + // Initialize the array of function pointers to the validation function on + // each table. +#undef MiniMdTable +#define MiniMdTable(x) m_ValidateRecordFunctionTable[i++] = &RegMeta::Validate##x; + MiniMdTables() + + // Verify that the ModuleType passed in is a valid one. + if (dwModuleType < ValidatorModuleTypeMin || + dwModuleType > ValidatorModuleTypeMax) + { + IfFailGo(E_INVALIDARG); + } + + // Verify that the interface passed in supports IID_IVEHandler. + IfFailGo(pUnk->QueryInterface(IID_IVEHandler, (void **)&m_pVEHandler)); + + // Set the ModuleType class member. Do this last, this is used in + // ValidateMetaData to see if the validator is correctly initialized. + m_ModuleType = (CorValidatorModuleType)dwModuleType; +ErrExit: + END_ENTRYPOINT_NOTHROW; + + return hr; +} // HRESULT RegMeta::ValidatorInit() + + +//***************************************************************************** +// Public implementation for code:IMetaDataValidate::ValidateMetaData +// +// Validate the entire MetaData. Here is the basic algorithm. +// for each table +// for each record +// { +// Do generic validation - validate that the offsets into the blob +// pool are good, validate that all the rids are within range, +// validate that token encodings are consistent. +// } +// if (problems found in generic validation) +// return; +// for each table +// for each record +// Do semantic validation. +//****************************************************************************** +HRESULT RegMeta::ValidateMetaData() +{ + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + CMiniMdRW * pMiniMd = &(m_pStgdb->m_MiniMd); + HRESULT hrSave = S_OK; // Saved hr from generic validation. + ULONG ulCount; // Count of records in the current table. + ULONG i; // Index to iterate over the tables. + ULONG j; // Index to iterate over the records in a given table. + IHostTaskManager * pHostTaskManager = NULL; + +#ifdef CACHE_IMPLMAP_VALIDATION_RESULT + ULONG rValidatedSize=0; // Size of g_rValidated array +#endif + + // Verify that the validator is initialized correctly + if (m_ModuleType == ValidatorModuleTypeInvalid) + { + _ASSERTE(!"Validator not initialized, initialize with ValidatorInit()."); + IfFailGo(VLDTR_E_NOTINIT); + } + + // First do a validation pass to do some basic structural checks based on + // the Meta-Meta data. This'll validate all the offsets into the pools, + // rid value and coded token ranges. + for (i = 0; i < pMiniMd->GetCountTables(); i++) + { + ulCount = pMiniMd->GetCountRecs(i); + +#ifdef CACHE_IMPLMAP_VALIDATION_RESULT + switch(i) + { + case TBL_ImplMap: + rValidatedSize += ulCount; + default: + ; + } +#endif + for (j = 1; j <= ulCount; j++) + { + IfFailGo(ValidateRecord(i, j)); + SetVldtrCode(&hrSave, hr); + } + } + // Validate that the size of the Ptr tables matches with the corresponding + // real tables. + + // Do not do semantic validation if structural validation failed. + if (hrSave != S_OK) + { + hr = hrSave; + goto ErrExit; + } + + // Verify the entry point (if any) + ::g_tkEntryPoint = 0; + ::g_fIsDLL = false; + if(m_pStgdb && m_pStgdb->m_pImage) + { + NewHolder<PEDecoder> pe; + + EX_TRY + { + // We need to use different PEDecoder constructors based on the type of data we give it. + // We use the one with a 'bool' as the second argument when dealing with a mapped file, + // and we use the one that takes a COUNT_T as the second argument when dealing with a + // flat file. + + if (m_pStgdb->m_pStgIO->GetMemoryMappedType() == MTYPE_IMAGE) + pe = new (nothrow) PEDecoder(m_pStgdb->m_pImage, false); + else + pe = new (nothrow) PEDecoder(m_pStgdb->m_pImage, (COUNT_T)(m_pStgdb->m_dwImageSize)); + + hr = S_OK; + } + EX_CATCH + { + hr = COR_E_BADIMAGEFORMAT; + } + EX_END_CATCH(SwallowAllExceptions) + + if (SUCCEEDED(hr) && pe == NULL) + IfFailGo(E_OUTOFMEMORY); + + if(FAILED(hr) || !pe->CheckFormat()) + { + VEContext veCtxt; // Context structure. + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = 0; + veCtxt.uOffset = 0; + REPORT_ERROR0(COR_E_BADIMAGEFORMAT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if(!pe->IsILOnly()) + { + VEContext veCtxt; // Context structure. + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = 0; + veCtxt.uOffset = 0; + REPORT_ERROR0(VER_E_BAD_PE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if((pe->GetCorHeader()->Flags & COMIMAGE_FLAGS_NATIVE_ENTRYPOINT) == 0) + g_tkEntryPoint = pe->GetEntryPointToken(); + g_fIsDLL = pe->IsDll() ? true : false; + + if(g_tkEntryPoint) + { + RID rid = RidFromToken(g_tkEntryPoint); + RID maxrid = 0; + switch(TypeFromToken(g_tkEntryPoint)) + { + case mdtMethodDef: maxrid = pMiniMd->getCountMethods(); break; + case mdtFile: maxrid = pMiniMd->getCountFiles(); break; + default: break; + } + if((rid == 0)||(rid > maxrid)) + { + VEContext veCtxt; // Context structure. + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = g_tkEntryPoint; + veCtxt.uOffset = 0; + REPORT_ERROR0(VLDTR_E_EP_BADTOKEN); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else if(!g_fIsDLL) // exe must have an entry point + { + VEContext veCtxt; // Context structure. + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = g_tkEntryPoint; + veCtxt.uOffset = 0; + REPORT_ERROR0(VLDTR_E_EP_BADTOKEN); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + g_fValidatingMscorlib = false; + if(pMiniMd->GetCountRecs(TBL_Assembly)) + { + AssemblyRec *pRecord; + IfFailGo(pMiniMd->GetAssemblyRecord(1, &pRecord)); + LPCSTR szName; + IfFailGo(pMiniMd->getNameOfAssembly(pRecord, &szName)); + g_fValidatingMscorlib = (0 == SString::_stricmp(szName,"mscorlib")); + } + // Verify there are no circular class hierarchies. + + // Do per record semantic validation on the MetaData. The function + // pointers to the per record validation are stored in the table by the + // ValidatorInit() function. + +#ifdef CACHE_IMPLMAP_VALIDATION_RESULT + g_rValidated = NULL; + ::g_nValidated = 0; + if (rValidatedSize) + { + g_rValidated = new(nothrow) ValidationResult[rValidatedSize]; + IfNullGo(g_rValidated); + } +#endif + pHostTaskManager = CorHost2::GetHostTaskManager(); + +#ifdef Sleep +#undef Sleep +#endif + //DWORD cBegin=0,cEnd=0; + for (i = 0; i < pMiniMd->GetCountTables(); i++) + { + ulCount = pMiniMd->GetCountRecs(i); + //cBegin = GetTickCount(); + for (j = 1; j <= ulCount; j++) + { + IfFailGo((this->*m_ValidateRecordFunctionTable[i])(j)); + SetVldtrCode(&hrSave, hr); + if(pHostTaskManager) + { + // SwitchToTask forces the current thread to give up quantum, while a host can decide what + // to do with Sleep if the current thread has not run out of quantum yet. + ClrSleepEx(0, FALSE); + } + } + //cEnd = GetTickCount(); + //printf("Table %d, recs: %d, time: %d\n",i,ulCount,(cEnd-cBegin)); + } + hr = hrSave; +ErrExit: + +#ifdef CACHE_IMPLMAP_VALIDATION_RESULT + if(g_rValidated) delete [] g_rValidated; +#endif + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateMetaData() + +//***************************************************************************** +// Validate the Module record. +//***************************************************************************** +HRESULT RegMeta::ValidateModule(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + ModuleRec *pRecord; // Module record. + VEContext veCtxt; // Context structure. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + LPCSTR szName; + GUID GuidOfModule; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the Module record. + veCtxt.Token = TokenFromRid(rid, mdtModule); + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetModuleRecord(rid, &pRecord)); + + // There can only be one Module record. + if (rid > 1) + { + REPORT_ERROR0(VLDTR_E_MOD_MULTI); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Verify the name + IfFailGo(pMiniMd->getNameOfModule(pRecord, &szName)); + if(szName && *szName) + { + ULONG L = (ULONG)strlen(szName); + if(L >= MAX_CLASSNAME_LENGTH) + { + // Name too long + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(strchr(szName,':') || strchr(szName,'\\')) + { + REPORT_ERROR0(VLDTR_E_MOD_NAMEFULLQLFD); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else + { + REPORT_ERROR0(VLDTR_E_MOD_NONAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Verify that the MVID is valid. + IfFailGo(pMiniMd->getMvidOfModule(pRecord, &GuidOfModule)); + if (GuidOfModule == GUID_NULL) + { + REPORT_ERROR0(VLDTR_E_MOD_NULLMVID); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateModule() + +//***************************************************************************** +// Validate the given TypeRef. +//***************************************************************************** +HRESULT RegMeta::ValidateTypeRef(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + TypeRefRec *pRecord; // TypeRef record. + mdToken tkRes; // Resolution scope. + LPCSTR szNamespace; // TypeRef Namespace. + LPCSTR szName; // TypeRef Name. + mdTypeRef tkTypeRef; // Duplicate TypeRef. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + // Get the TypeRef record. + veCtxt.Token = TokenFromRid(rid, mdtTypeRef); + veCtxt.uOffset = 0; + + IfFailGo(pMiniMd->GetTypeRefRecord(rid, &pRecord)); + + // Check name is not NULL. + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pRecord, &szNamespace)); + IfFailGo(pMiniMd->getNameOfTypeRef(pRecord, &szName)); + if (!*szName) + { + REPORT_ERROR0(VLDTR_E_TR_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + RID ridScope; + // Look for a Duplicate, this function reports only one duplicate. + tkRes = pMiniMd->getResolutionScopeOfTypeRef(pRecord); + hr = ImportHelper::FindTypeRefByName(pMiniMd, tkRes, szNamespace, szName, &tkTypeRef, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_TR_DUP, tkTypeRef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + ULONG L = (ULONG)(strlen(szName)+strlen(szNamespace)); + if(L >= MAX_CLASSNAME_LENGTH) + { + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + ridScope = RidFromToken(tkRes); + if(ridScope) + { + bool badscope = true; + //check if valid scope + switch(TypeFromToken(tkRes)) + { + case mdtAssemblyRef: + case mdtModuleRef: + case mdtModule: + case mdtTypeRef: + badscope = !IsValidToken(tkRes); + break; + default: + break; + } + if(badscope) + { + REPORT_ERROR1(VLDTR_E_TR_BADSCOPE, tkTypeRef); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + else + { + // check if there is a ExportedType + //hr = ImportHelper::FindExportedType(pMiniMd, szNamespace, szName, tkImpl, &tkExportedType, rid); + } + // Check if there is TypeDef with the same name + if(!ridScope) + { + if((TypeFromToken(tkRes) != mdtTypeRef) && + (S_OK == ImportHelper::FindTypeDefByName(pMiniMd, szNamespace, szName, mdTokenNil,&tkTypeRef, 0))) + { + REPORT_ERROR1(VLDTR_E_TR_HASTYPEDEF, tkTypeRef); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + } + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateTypeRef() + +//***************************************************************************** +// Validate the given TypeDef. +//***************************************************************************** +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +HRESULT RegMeta::ValidateTypeDef(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + TypeDefRec *pRecord; // TypeDef record. + TypeDefRec *pExtendsRec = 0; // TypeDef record for the parent class. + mdTypeDef tkTypeDef; // Duplicate TypeDef token. + DWORD dwFlags; // TypeDef flags. + DWORD dwExtendsFlags; // TypeDef flags of the parent class. + LPCSTR szName; // TypeDef Name. + LPCSTR szNameSpace; // TypeDef NameSpace. + LPCSTR szExtName = NULL; // Parent Name. + LPCSTR szExtNameSpace = NULL; // Parent NameSpace. + CQuickBytes qb; // QuickBytes for flexible allocation. + mdToken tkExtends; // TypeDef of the parent class. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + mdToken tkEncloser=mdTokenNil; // Encloser, if any + BOOL bIsEnum,bExtendsEnum,bExtendsVType,bIsVType,bExtendsObject,bIsObject,bExtendsMCDelegate; + BOOL bHasMethods=FALSE, bHasFields=FALSE; + + BEGIN_ENTRYPOINT_NOTHROW; + + // Skip validating m_tdModule class. + if (rid == RidFromToken(m_tdModule)) + goto ErrExit; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the TypeDef record. + veCtxt.Token = TokenFromRid(rid, mdtTypeDef); + veCtxt.uOffset = 0; + + IfFailGo(pMiniMd->GetTypeDefRecord(rid, &pRecord)); + + // Do checks for name validity.. + IfFailGo(pMiniMd->getNameOfTypeDef(pRecord, &szName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pRecord, &szNameSpace)); + if (!*szName) + { + // TypeDef Name is null. + REPORT_ERROR0(VLDTR_E_TD_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (!IsDeletedName(szName)) + { + RID iRecord; + IfFailGo(pMiniMd->FindNestedClassHelper(TokenFromRid(rid, mdtTypeDef), &iRecord)); + + if (InvalidRid(iRecord)) + { + tkEncloser = mdTokenNil; + } + else + { + NestedClassRec *pNestedClassRec; + IfFailGo(pMiniMd->GetNestedClassRecord(iRecord, &pNestedClassRec)); + tkEncloser = pMiniMd->getEnclosingClassOfNestedClass(pNestedClassRec); + } + + // Check for duplicates based on Name/NameSpace. Do not do Dup checks + // on deleted records. + hr = ImportHelper::FindTypeDefByName(pMiniMd, szNameSpace, szName, tkEncloser, + &tkTypeDef, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_TD_DUPNAME, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + ULONG L = (ULONG)(strlen(szName)+strlen(szNameSpace)); + if(L >= MAX_CLASSNAME_LENGTH) + { + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // Get the flag value for the TypeDef. + dwFlags = pMiniMd->getFlagsOfTypeDef(pRecord); + // Do semantic checks on the flags. + // RTSpecialName bit must be set on Deleted records. + if (IsDeletedName(szName)) + { + if(!IsTdRTSpecialName(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_TD_DLTNORTSPCL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + hr = hrSave; + goto ErrExit; + } + + // If RTSpecialName bit is set, the record must be a Deleted record. + if (IsTdRTSpecialName(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_TD_RTSPCLNOTDLT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + if(!IsTdSpecialName(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_TD_RTSPCLNOTSPCL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // Check if flag value is valid + { + DWORD dwInvalidMask, dwExtraBits; + dwInvalidMask = (DWORD)~(tdVisibilityMask | tdLayoutMask | tdClassSemanticsMask | + tdAbstract | tdSealed | tdSpecialName | tdImport | tdSerializable | tdWindowsRuntime | + tdStringFormatMask | tdBeforeFieldInit | tdReservedMask); + // check for extra bits + dwExtraBits = dwFlags & dwInvalidMask; + if (dwExtraBits == 0) + { + // if no extra bits, check layout + dwExtraBits = dwFlags & tdLayoutMask; + if (dwExtraBits != tdLayoutMask) + { + // layout OK, check string format + dwExtraBits = dwFlags & tdStringFormatMask; + if (dwExtraBits != tdStringFormatMask) + dwExtraBits = 0; + } + } + if (dwExtraBits != 0) + { + REPORT_ERROR1(VLDTR_E_TD_EXTRAFLAGS, dwExtraBits); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + + // Generic types may be specified to have only AutoLayout or SequentialLayout (never ExplicitLayout). + if (IsTdExplicitLayout(dwFlags)) + { + HENUMInternal hEnumTyPars; + ULONG ulTypeDefArity; + hr = pMiniMd->FindGenericParamHelper(TokenFromRid(rid, mdtTypeDef), &hEnumTyPars); + if (SUCCEEDED(hr)) + { + IfFailGo(HENUMInternal::GetCount(&hEnumTyPars,&ulTypeDefArity)); + HENUMInternal::ClearEnum(&hEnumTyPars); + if (ulTypeDefArity != 0) + { + REPORT_ERROR0(VLDTR_E_TD_GENERICHASEXPLAYOUT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + + } + + // Get the parent of the TypeDef. + tkExtends = pMiniMd->getExtendsOfTypeDef(pRecord); + + // Check if TypeDef extends itself + if (tkExtends == veCtxt.Token) + { + REPORT_ERROR0(VLDTR_E_TD_EXTENDSITSELF); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Check if TypeDef extends one of its children + if (RidFromToken(tkExtends)&&(TypeFromToken(tkExtends)==mdtTypeDef)) + { + TypeDefRec *pRec; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkExtends), &pRec)); + mdToken tkExtends2 = pMiniMd->getExtendsOfTypeDef(pRec); + if( tkExtends2 == veCtxt.Token) + { + REPORT_ERROR0(VLDTR_E_TD_EXTENDSCHILD); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + + + if (IsNilToken(tkEncloser) == IsTdNested(dwFlags)) + { + REPORT_ERROR0(IsNilToken(tkEncloser) ? VLDTR_E_TD_NESTEDNOENCL : VLDTR_E_TD_ENCLNOTNESTED); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + bIsObject = bIsEnum = bIsVType = FALSE; + if(0 == strcmp(szNameSpace,BASE_NAMESPACE)) + { + bIsObject = (0 == strcmp(szName,BASE_OBJECT_CLASSNAME)); + if(!bIsObject) + { + bIsEnum = (0 == strcmp(szName,BASE_ENUM_CLASSNAME)); + if(!bIsEnum) + { + bIsVType = (0 == strcmp(szName,BASE_VTYPE_CLASSNAME)); + } + } + } + + if (IsNilToken(tkExtends)) + { + // If the parent token is nil, the class must be marked Interface, + // unless it's the System.Object class. + if ( !(bIsObject || IsTdInterface(dwFlags))) + { + REPORT_ERROR0(VLDTR_E_TD_NOTIFACEOBJEXTNULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + szExtName = ""; + szExtNameSpace = ""; + } + else + { + + // If tkExtends is a TypeSpec, extract the generic type and continue + if (TypeFromToken(tkExtends) == mdtTypeSpec) + { + //@GENERICSVER: TODO first validate the spec + + TypeSpecRec *pRec; + IfFailGo(pMiniMd->GetTypeSpecRecord(RidFromToken(tkExtends), &pRec)); + PCCOR_SIGNATURE pSig; + ULONG cSig; + + IfFailGo(pMiniMd->getSignatureOfTypeSpec(pRec, &pSig, &cSig)); + + switch(CorSigUncompressElementType(pSig)) + { + default: + { + REPORT_ERROR1(VLDTR_E_TD_EXTBADTYPESPEC, tkExtends); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + szExtName = ""; + szExtNameSpace = ""; + break; + } + case ELEMENT_TYPE_GENERICINST: + { + switch(CorSigUncompressElementType(pSig)) + { + default: + { + REPORT_ERROR1(VLDTR_E_TD_EXTBADTYPESPEC, tkExtends); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + szExtName = ""; + szExtNameSpace = ""; + break; + } + case ELEMENT_TYPE_VALUETYPE: + case ELEMENT_TYPE_CLASS: + { + tkExtends = CorSigUncompressToken(pSig); + break; + } + } + } + } + } + + // If tkExtends is a TypeRef try to resolve it to a corresponding + // TypeDef. If it resolves successfully, issue a warning. It means + // that the Ref to Def optimization didn't happen successfully. + if (TypeFromToken(tkExtends) == mdtTypeRef) + { + TypeRefRec *pTypeRefRec; + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkExtends), &pTypeRefRec)); + + IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szExtName)); + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szExtNameSpace)); + + BOOL fLookForDef = TRUE; + mdToken tkResScope = pMiniMd->getResolutionScopeOfTypeRef(pTypeRefRec); + if (TypeFromToken(tkResScope) == mdtAssemblyRef) + { // We will look for the TypeDef of the same name, only if the AssemblyRef has the same name as AssemblyDef + fLookForDef = FALSE; + RID ridResScope = RidFromToken(tkResScope); + if ((ridResScope > 0) && (ridResScope <= pMiniMd->GetCountRecs(TBL_AssemblyRef))) + { + if (pMiniMd->GetCountRecs(TBL_Assembly) > 0) + { + AssemblyRefRec * pAsmRefRec; + IfFailGo(pMiniMd->GetAssemblyRefRecord(ridResScope, &pAsmRefRec)); + AssemblyRec *pAsmRec; + IfFailGo(pMiniMd->GetAssemblyRecord(1, &pAsmRec)); + if ((pAsmRec != NULL) && (pAsmRefRec != NULL)) + { + LPCUTF8 szAsmName; + IfFailGo(pMiniMd->getNameOfAssembly(pAsmRec, &szAsmName)); + LPCUTF8 szAsmRefName; + IfFailGo(pMiniMd->getNameOfAssemblyRef(pAsmRefRec, &szAsmRefName)); + if ((szAsmName != NULL) && (szAsmRefName != NULL)) + fLookForDef = (strcmp(szAsmName,szAsmRefName) == 0); + } + } + } + } + + if (fLookForDef) + { + mdTypeDef tkResTd; + + if (ImportHelper::FindTypeDefByName(pMiniMd, + szExtNameSpace, + szExtName, + tkResScope, + &tkResTd) == S_OK) + { + // Ref to Def optimization is not expected to happen for Obj files. + /* + if (m_ModuleType != ValidatorModuleTypeObj) + { + REPORT_ERROR2(VLDTR_E_TD_EXTTRRES, tkExtends, tkResTd); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + */ + + // Set tkExtends to the new TypeDef, so we can continue + // with the validation. + tkExtends = tkResTd; + } + } + } + + // Continue validation, even for the case where TypeRef got resolved + // to a corresponding TypeDef in the same Module. + if (TypeFromToken(tkExtends) == mdtTypeDef) + { + // Extends must not be sealed. + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkExtends), &pExtendsRec)); + dwExtendsFlags = pMiniMd->getFlagsOfTypeDef(pExtendsRec); + IfFailGo(pMiniMd->getNameOfTypeDef(pExtendsRec, &szExtName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pExtendsRec, &szExtNameSpace)); + if (IsTdSealed(dwExtendsFlags)) + { + REPORT_ERROR1(VLDTR_E_TD_EXTENDSSEALED, tkExtends); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if (IsTdInterface(dwExtendsFlags)) + { + REPORT_ERROR1(VLDTR_E_TD_EXTENDSIFACE, tkExtends); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else if(TypeFromToken(tkExtends) == mdtTypeSpec) + { + //If we got here, the instantiated generic type is itself a type spec, which is illegal + REPORT_ERROR1(VLDTR_E_TD_EXTBADTYPESPEC, tkExtends); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + szExtName = ""; + szExtNameSpace = ""; + + } + // If the parent token is non-null, the class must not be System.Object. + if (bIsObject) + { + REPORT_ERROR1(VLDTR_E_TD_OBJEXTENDSNONNULL, tkExtends); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + bExtendsObject = bExtendsEnum = bExtendsVType = bExtendsMCDelegate = FALSE; + if(0 == strcmp(szExtNameSpace,BASE_NAMESPACE)) + { + bExtendsObject = (0 == strcmp(szExtName,BASE_OBJECT_CLASSNAME)); + if(!bExtendsObject) + { + bExtendsEnum = (0 == strcmp(szExtName,BASE_ENUM_CLASSNAME)); + if(!bExtendsEnum) + { + bExtendsVType = (0 == strcmp(szExtName,BASE_VTYPE_CLASSNAME)); + if(!bExtendsVType) + { + bExtendsMCDelegate = (0 == strcmp(szExtName,BASE_MCDELEGATE_CLASSNAME)); + } + } + } + } + + // System.ValueType must extend System.Object + if(bIsVType && !bExtendsObject) + { + REPORT_ERROR0(VLDTR_E_TD_SYSVTNOTEXTOBJ); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Validate rules for interface. Some of the VOS rules are verified as + // part of the validation for the corresponding Methods, fields etc. + if (IsTdInterface(dwFlags)) + { + // Interface type must be marked abstract. + if (!IsTdAbstract(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_TD_IFACENOTABS); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Interface must not be sealed + if(IsTdSealed(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_TD_IFACESEALED); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Interface must have parent Nil token. + if (!IsNilToken(tkExtends)) + { + REPORT_ERROR1(VLDTR_E_TD_IFACEPARNOTNIL, tkExtends); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + //Interface must have only static fields -- checked in ValidateField + //Interface must have only public fields -- checked in ValidateField + //Interface must have only abstract or static methods -- checked in ValidateMethod + //Interface must have only public methods -- checked in ValidateMethod + + // Interface must have GUID + /* + if (*pGuid == GUID_NULL) + { + REPORT_ERROR0(VLDTR_E_TD_IFACEGUIDNULL); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + */ + } + + + // Class must have valid method and field lists + { + ULONG ridStart,ridEnd; + ridStart = pMiniMd->getMethodListOfTypeDef(pRecord); + ridEnd = pMiniMd->getCountMethods() + 1; + if(ridStart > ridEnd) + { + REPORT_ERROR0(VLDTR_E_TD_BADMETHODLST); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + IfFailGo(pMiniMd->getEndMethodListOfTypeDef(rid, &ridEnd)); + bHasMethods = (ridStart && (ridStart < ridEnd)); + } + + ridStart = pMiniMd->getFieldListOfTypeDef(pRecord); + ridEnd = pMiniMd->getCountFields() + 1; + if(ridStart > ridEnd) + { + REPORT_ERROR0(VLDTR_E_TD_BADFIELDLST); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + IfFailGo(pMiniMd->getEndFieldListOfTypeDef(rid, &ridEnd)); + bHasFields = (ridStart && (ridStart < ridEnd)); + } + } + + // Validate rules for System.Enum + if(bIsEnum) + { + if(!IsTdClass(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_TD_SYSENUMNOTCLASS); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(!bExtendsVType) + { + REPORT_ERROR0(VLDTR_E_TD_SYSENUMNOTEXTVTYPE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else + { + if(bExtendsVType || bExtendsEnum) + { + // ValueTypes and Enums must be sealed + if(!IsTdSealed(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_TD_VTNOTSEAL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Value class must have fields or size + if(!bHasFields) + { + ULONG ulClassSize = 0; + ClassLayoutRec *pRec; + RID ridClassLayout; + IfFailGo(pMiniMd->FindClassLayoutHelper(TokenFromRid(rid, mdtTypeDef), &ridClassLayout)); + + if (!InvalidRid(ridClassLayout)) + { + IfFailGo(pMiniMd->GetClassLayoutRecord(RidFromToken(ridClassLayout), &pRec)); + ulClassSize = pMiniMd->getClassSizeOfClassLayout(pRec); + } + if(ulClassSize == 0) + { + REPORT_ERROR0(VLDTR_E_TD_VTNOSIZE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + else if(bExtendsMCDelegate) + { + // Delegates must be sealed + if(!IsTdSealed(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_TD_VTNOTSEAL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + + // Enum-related checks + if (bExtendsEnum) + { + { + PCCOR_SIGNATURE pValueSig = NULL; + ULONG cbValueSig = 0; + mdFieldDef tkValueField=0, tkField, tkValue__Field = 0; + ULONG ridStart,ridEnd,index; + FieldRec *pFieldRecord; // Field record. + DWORD dwRecordFlags, dwTally, dwValueFlags, dwValue__Flags = 0; + RID ridField,ridValue=0,ridValue__ = 0; + + ridStart = pMiniMd->getFieldListOfTypeDef(pRecord); + IfFailGo(pMiniMd->getEndFieldListOfTypeDef(rid, &ridEnd)); + // check the instance (value__) field(s) + dwTally = 0; + for (index = ridStart; index < ridEnd; index++ ) + { + IfFailGo(pMiniMd->GetFieldRid(index, &ridField)); + IfFailGo(pMiniMd->GetFieldRecord(ridField, &pFieldRecord)); + dwRecordFlags = pFieldRecord->GetFlags(); + if(!IsFdStatic(dwRecordFlags)) + { + dwTally++; + if(ridValue == 0) + { + ridValue = ridField; + tkValueField = TokenFromRid(ridField, mdtFieldDef); + IfFailGo(pMiniMd->getSignatureOfField(pFieldRecord, &pValueSig, &cbValueSig)); + dwValueFlags = dwRecordFlags; + } + } + LPCSTR szFieldName; + IfFailGo(pMiniMd->getNameOfField(pFieldRecord, &szFieldName)); + if(!strcmp(szFieldName, BASE_VALUE_FIELDNAME)) + { + ridValue__ = ridField; + dwValue__Flags = dwRecordFlags; + tkValue__Field = TokenFromRid(ridField, mdtFieldDef); + } + } + // Enum must have one (and only one) inst.field + if(dwTally == 0) + { + REPORT_ERROR0(VLDTR_E_TD_ENUMNOINSTFLD); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if(dwTally > 1) + { + REPORT_ERROR0(VLDTR_E_TD_ENUMMULINSTFLD); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // inst.field name must be "value__" (CLS) + if(ridValue__ == 0) + { + REPORT_ERROR0(VLDTR_E_TD_ENUMNOVALUE); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + else + { + // if "value__" field is present ... + // ... it must be 1st instance field + if(ridValue__ != ridValue) + { + REPORT_ERROR1(VLDTR_E_TD_ENUMVALNOT1ST, tkValue__Field); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // ... it must not be static + if(IsFdStatic(dwValue__Flags)) + { + REPORT_ERROR1(VLDTR_E_TD_ENUMVALSTATIC, tkValue__Field); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // ... it must be fdRTSpecialName + if(!IsFdRTSpecialName(dwValue__Flags)) + { + REPORT_ERROR1(VLDTR_E_TD_ENUMVALNOTSN, tkValueField); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // ... its type must be integral + if(cbValueSig && pValueSig) + { + //ULONG ulCurByte = CorSigUncompressedDataSize(pValueSig); + //CorSigUncompressData(pValueSig); + //ULONG ulElemSize,ulElementType; + //ulCurByte += (ulElemSize = CorSigUncompressedDataSize(pValueSig)); + //ulElementType = CorSigUncompressData(pValueSig); + //switch (ulElementType) + BYTE* pB = (BYTE*)pValueSig; + pB++; // skip the calling convention + while((*pB == ELEMENT_TYPE_CMOD_OPT)|| + (*pB == ELEMENT_TYPE_CMOD_REQD)) + { + mdToken tok; + pB++; // move from E_T_... to compressed token + pB += CorSigUncompressToken((PCOR_SIGNATURE)pB,&tok); + } + switch(*pB) + { + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_U: + case ELEMENT_TYPE_I: + case ELEMENT_TYPE_R4: + case ELEMENT_TYPE_R8: + break; + default: + REPORT_ERROR1(VLDTR_E_TD_ENUMFLDBADTYPE, tkValue__Field); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + // check all the fields + dwTally = 0; + for (index = ridStart; index < ridEnd; index++ ) + { + IfFailGo(pMiniMd->GetFieldRid(index, &ridField)); + if(ridField == ridValue) continue; + IfFailGo(pMiniMd->GetFieldRecord(ridField, &pFieldRecord)); + LPCSTR szFieldName; + IfFailGo(pMiniMd->getNameOfField(pFieldRecord, &szFieldName)); + if(IsFdRTSpecialName(pFieldRecord->GetFlags()) + && IsDeletedName(szFieldName)) continue; + dwTally++; + tkField = TokenFromRid(ridField, mdtFieldDef); + if(!IsFdStatic(pFieldRecord->GetFlags())) + { + REPORT_ERROR1(VLDTR_E_TD_ENUMFLDNOTST, tkField); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(!IsFdLiteral(pFieldRecord->GetFlags())) + { + REPORT_ERROR1(VLDTR_E_TD_ENUMFLDNOTLIT, tkField); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + /* + IfFailGo(pMiniMd->getSignatureOfField(pFieldRecord, &pvSigTmp, &cbSig)); + if(!(pvSigTmp && (cbSig==cbValueSig) &&(memcmp(pvSigTmp,pValueSig,cbSig)==0))) + { + REPORT_ERROR1(VLDTR_E_TD_ENUMFLDSIGMISMATCH, tkField); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + */ + } + if(dwTally == 0) + { + REPORT_ERROR0(VLDTR_E_TD_ENUMNOLITFLDS); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + // Enum must have no methods + if (bHasMethods) + { + REPORT_ERROR0(VLDTR_E_TD_ENUMHASMETHODS); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Enum must implement no interfaces + { + ULONG ridStart = 1; + ULONG ridEnd = pMiniMd->getCountInterfaceImpls() + 1; + ULONG index; + for (index = ridStart; index < ridEnd; index ++ ) + { + InterfaceImplRec *pInterfaceImplRecord; + IfFailGo(pMiniMd->GetInterfaceImplRecord(index, &pInterfaceImplRecord)); + if (veCtxt.Token == pMiniMd->getClassOfInterfaceImpl(pInterfaceImplRecord)) + { + REPORT_ERROR0(VLDTR_E_TD_ENUMIMPLIFACE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + } + } + // Enum must have no properties + { + ULONG ridStart = 1; + ULONG ridEnd = pMiniMd->getCountPropertys() + 1; + ULONG index; + mdToken tkClass; + for (index = ridStart; index < ridEnd; index ++ ) + { + IfFailGo(pMiniMd->FindParentOfPropertyHelper(index | mdtProperty, &tkClass)); + if (veCtxt.Token == tkClass) + { + REPORT_ERROR0(VLDTR_E_TD_ENUMHASPROP); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + } + } + // Enum must have no events + { + ULONG ridStart = 1; + ULONG ridEnd = pMiniMd->getCountEvents() + 1; + ULONG index; + mdToken tkClass; + for (index = ridStart; index < ridEnd; index ++ ) + { + IfFailGo(pMiniMd->FindParentOfEventHelper(index | mdtEvent, &tkClass)); + if (veCtxt.Token == tkClass) + { + REPORT_ERROR0(VLDTR_E_TD_ENUMHASEVENT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + } + } + } // end if(bExtendsEnum) + // Class having security must be marked tdHasSecurity and vice versa + { + ULONG ridStart = 1; + ULONG ridEnd = pMiniMd->getCountDeclSecuritys() + 1; + ULONG index; + BOOL bHasSecurity = FALSE; + for (index = ridStart; index < ridEnd; index ++ ) + { + DeclSecurityRec *pDeclSecurityRecord; + IfFailGo(pMiniMd->GetDeclSecurityRecord(index, &pDeclSecurityRecord)); + if (veCtxt.Token == pMiniMd->getParentOfDeclSecurity(pDeclSecurityRecord)) + { + bHasSecurity = TRUE; + break; + } + } + if (!bHasSecurity) // No records, check for CA "SuppressUnmanagedCodeSecurityAttribute" + { + bHasSecurity = (S_OK == ImportHelper::GetCustomAttributeByName(pMiniMd, veCtxt.Token, + "System.Security.SuppressUnmanagedCodeSecurityAttribute", NULL, NULL)); + } + if(bHasSecurity != (IsTdHasSecurity(pRecord->GetFlags())!=0)) + { + REPORT_ERROR0(bHasSecurity ? VLDTR_E_TD_SECURNOTMARKED : VLDTR_E_TD_MARKEDNOSECUR); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateTypeDef() +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +//***************************************************************************** +// Validate the given FieldPtr. +//***************************************************************************** +HRESULT RegMeta::ValidateFieldPtr(RID rid) +{ + return S_OK; +} // RegMeta::ValidateFieldPtr() + + +//***************************************************************************** +// Validate the given Field. +//***************************************************************************** +HRESULT RegMeta::ValidateField(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + FieldRec *pRecord; // Field record. + mdTypeDef tkTypeDef; // Parent TypeDef token. + mdFieldDef tkFieldDef; // Duplicate FieldDef token. + LPCSTR szName; // FieldDef name. + PCCOR_SIGNATURE pbSig; // FieldDef signature. + ULONG cbSig; // Signature size in bytes. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + BOOL bIsValueField; + BOOL bIsGlobalField = FALSE; + BOOL bHasValidRVA = FALSE; + DWORD dwInvalidFlags; + DWORD dwFlags; + RID tempRid; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the FieldDef record. + veCtxt.Token = TokenFromRid(rid, mdtFieldDef); + veCtxt.uOffset = 0; + + IfFailGo(pMiniMd->GetFieldRecord(rid, &pRecord)); + + // Do checks for name validity. + IfFailGo(pMiniMd->getNameOfField(pRecord, &szName)); + if (!*szName) + { + // Field name is NULL. + REPORT_ERROR0(VLDTR_E_FD_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + if(!strcmp(szName,COR_DELETED_NAME_A)) goto ErrExit; + ULONG L = (ULONG)strlen(szName); + if(L >= MAX_CLASSNAME_LENGTH) + { + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + bIsValueField = (strcmp(szName,BASE_VALUE_FIELDNAME)==0); + // If field is RTSpecialName, its name must be 'value__' and vice versa + if((IsFdRTSpecialName(pRecord->GetFlags())!=0) != bIsValueField) + { + REPORT_ERROR1(bIsValueField ? VLDTR_E_TD_ENUMVALNOTSN : VLDTR_E_FD_NOTVALUERTSN, veCtxt.Token); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate flags + dwFlags = pRecord->GetFlags(); + dwInvalidFlags = ~(fdFieldAccessMask | fdStatic | fdInitOnly | fdLiteral | fdNotSerialized | fdSpecialName + | fdPinvokeImpl | fdReservedMask); + if(dwFlags & dwInvalidFlags) + { + REPORT_ERROR1(VLDTR_E_TD_EXTRAFLAGS, dwFlags & dwInvalidFlags); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate access + if((dwFlags & fdFieldAccessMask) == fdFieldAccessMask) + { + REPORT_ERROR0(VLDTR_E_FMD_BADACCESSFLAG); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Literal : Static, !InitOnly + if(IsFdLiteral(dwFlags)) + { + if(IsFdInitOnly(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FD_INITONLYANDLITERAL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(!IsFdStatic(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FD_LITERALNOTSTATIC); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(!IsFdHasDefault(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FD_LITERALNODEFAULT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // RTSpecialName => SpecialName + if(IsFdRTSpecialName(dwFlags) && !IsFdSpecialName(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FMD_RTSNNOTSN); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate Field signature. + IfFailGo(pMiniMd->getSignatureOfField(pRecord, &pbSig, &cbSig)); + IfFailGo(ValidateFieldSig(TokenFromRid(rid, mdtFieldDef), pbSig, cbSig)); + if (hr != S_OK) + SetVldtrCode(&hrSave, hr); + + // Validate Field RVA + if(IsFdHasFieldRVA(dwFlags)) + { + ULONG iFieldRVARid; + IfFailGo(pMiniMd->FindFieldRVAHelper(TokenFromRid(rid, mdtFieldDef), &iFieldRVARid)); + if((iFieldRVARid==0) || (iFieldRVARid > pMiniMd->getCountFieldRVAs())) + { + REPORT_ERROR0(VLDTR_E_FD_RVAHASNORVA); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + /* + FieldRVARec *pRVARec; + IfFailGo(pMiniMd->GetFieldRVARecord(iFieldRVARid, &pRVARec)); + if(pRVARec->GetRVA() == 0) + { + REPORT_ERROR0(VLDTR_E_FD_RVAHASZERORVA); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + */ + bHasValidRVA = TRUE; + } + } + + // Get the parent of the Field. + IfFailGo(pMiniMd->FindParentOfFieldHelper(TokenFromRid(rid, mdtFieldDef), &tkTypeDef)); + // Validate that the parent is not nil. + if (IsNilToken(tkTypeDef)) + { + REPORT_ERROR0(VLDTR_E_FD_PARNIL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (RidFromToken(tkTypeDef) != RidFromToken(m_tdModule)) + { + if(IsValidToken(tkTypeDef) && (TypeFromToken(tkTypeDef) == mdtTypeDef)) + { + TypeDefRec *pParentRec; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkTypeDef), &pParentRec)); + // If the name is "value__" ... + if(bIsValueField) + { + // parent must be Enum + mdToken tkExtends = pMiniMd->getExtendsOfTypeDef(pParentRec); + RID ridExtends = RidFromToken(tkExtends); + LPCSTR szExtName="",szExtNameSpace=""; + if(ridExtends) + { + if(TypeFromToken(tkExtends) == mdtTypeRef) + { + TypeRefRec *pExtRec; + IfFailGo(pMiniMd->GetTypeRefRecord(ridExtends, &pExtRec)); + IfFailGo(pMiniMd->getNameOfTypeRef(pExtRec, &szExtName)); + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pExtRec, &szExtNameSpace)); + } + else if(TypeFromToken(tkExtends) == mdtTypeDef) + { + TypeDefRec *pExtRec; + IfFailGo(pMiniMd->GetTypeDefRecord(ridExtends, &pExtRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pExtRec, &szExtName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pExtRec, &szExtNameSpace)); + } + } + if(strcmp(szExtName,BASE_ENUM_CLASSNAME) || strcmp(szExtNameSpace,BASE_NAMESPACE)) + { + REPORT_ERROR0(VLDTR_E_FD_VALUEPARNOTENUM); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // field must be instance - checked in ValidateTypeDef + // must be no other instance fields - checked in ValidateTypeDef + // must be first field - checked in ValidateTypeDef + // must be RTSpecialName -- checked in ValidateTypeDef + } + if(IsTdInterface(pMiniMd->getFlagsOfTypeDef(pParentRec))) + { + // Fields in interface are not CLS compliant + REPORT_ERROR0(VLDTR_E_FD_FLDINIFACE); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + + // If field is not static, verify parent is not interface. + if(!IsFdStatic(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FD_INSTINIFACE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + // If field is not public, verify parent is not interface. + if(!IsFdPublic(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FD_NOTPUBINIFACE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + } // end if Valid and TypeDef + else + { + REPORT_ERROR1(VLDTR_E_FD_BADPARENT, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else // i.e. if (RidFromToken(tkTypeDef) == RidFromToken(m_tdModule)) + { + bIsGlobalField = TRUE; + // Globals are not CLS-compliant + REPORT_ERROR0(VLDTR_E_FMD_GLOBALITEM); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + // Validate global field: + // Must be static + if(!IsFdStatic(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FMD_GLOBALNOTSTATIC); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Must have a non-zero RVA + /* + if(!bHasValidRVA) + { + REPORT_ERROR0(VLDTR_E_FD_GLOBALNORVA); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + */ + } + + // Check for duplicates, except global fields with PrivateScope. + if (*szName && cbSig && !IsFdPrivateScope(dwFlags)) + { + hr = ImportHelper::FindField(pMiniMd, tkTypeDef, szName, pbSig, cbSig, &tkFieldDef, rid); + if (hr == S_OK) + { + if(!IsFdPrivateScope(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_FD_DUP, tkFieldDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else hr = S_OK; + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + { + hr = S_OK; + } + else + { + IfFailGo(hr); + } + } + // Field having security must be marked fdHasSecurity and vice versa + { + ULONG ridStart = 1; + ULONG ridEnd = pMiniMd->getCountDeclSecuritys() + 1; + ULONG index; + BOOL bHasSecurity = FALSE; + for (index = ridStart; index < ridEnd; index ++ ) + { + DeclSecurityRec *pDeclSecurityRecord; + IfFailGo(pMiniMd->GetDeclSecurityRecord(index, &pDeclSecurityRecord)); + if ( veCtxt.Token == pMiniMd->getParentOfDeclSecurity(pDeclSecurityRecord)) + { + bHasSecurity = TRUE; + break; + } + } + if(!bHasSecurity) // No records, check for CA "SuppressUnmanagedCodeSecurityAttribute" + { + bHasSecurity = (S_OK == ImportHelper::GetCustomAttributeByName(pMiniMd, veCtxt.Token, + "System.Security.SuppressUnmanagedCodeSecurityAttribute", NULL, NULL)); + } + if(bHasSecurity) + { + REPORT_ERROR0(VLDTR_E_FMD_SECURNOTMARKED); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // Field having marshaling must be marked fdHasFieldMarshal and vice versa + IfFailGo(pMiniMd->FindFieldMarshalHelper(veCtxt.Token, &tempRid)); + if (InvalidRid(tempRid) == (IsFdHasFieldMarshal(dwFlags) != 0)) + { + REPORT_ERROR0(IsFdHasFieldMarshal(dwFlags)? VLDTR_E_FD_MARKEDNOMARSHAL : VLDTR_E_FD_MARSHALNOTMARKED); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Field having const value must be marked fdHasDefault and vice versa + IfFailGo(pMiniMd->FindConstantHelper(veCtxt.Token, &tempRid)); + if(InvalidRid(tempRid) == (IsFdHasDefault(dwFlags) != 0)) + { + REPORT_ERROR0(IsFdHasDefault(dwFlags)? VLDTR_E_FD_MARKEDNODEFLT : VLDTR_E_FD_DEFLTNOTMARKED); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Check the field's impl.map + { + ULONG iRecord; + IfFailGo(pMiniMd->FindImplMapHelper(veCtxt.Token, &iRecord)); + if(IsFdPinvokeImpl(dwFlags)) + { + // must be static + if(!IsFdStatic(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FMD_PINVOKENOTSTATIC); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // must have ImplMap + if (InvalidRid(iRecord)) + { + REPORT_ERROR0(VLDTR_E_FMD_MARKEDNOPINVOKE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else + { + // must have no ImplMap + if (!InvalidRid(iRecord)) + { + REPORT_ERROR0(VLDTR_E_FMD_PINVOKENOTMARKED); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + if (!InvalidRid(iRecord)) + { + hr = ValidateImplMap(iRecord); + if(hr != S_OK) + { + REPORT_ERROR0(VLDTR_E_FMD_BADIMPLMAP); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateField() + +//***************************************************************************** +// Validate the given MethodPtr. +//***************************************************************************** +HRESULT RegMeta::ValidateMethodPtr(RID rid) +{ + return S_OK; +} // RegMeta::ValidateMethodPtr() + + +//***************************************************************************** +// Validate the given Method. +//***************************************************************************** +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +HRESULT RegMeta::ValidateMethod(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + MethodRec *pRecord = NULL; // Method record. + mdTypeDef tkTypeDef; // Parent TypeDef token. + mdMethodDef tkMethodDef; // Duplicate MethodDef token. + LPCSTR szName; // MethodDef name. + DWORD dwFlags = 0; // Method flags. + DWORD dwImplFlags = 0; // Method impl.flags. + PCCOR_SIGNATURE pbSig; // MethodDef signature. + ULONG cbSig; // Signature size in bytes. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + BOOL bIsCtor=FALSE; + BOOL bIsCctor=FALSE; + BOOL bIsGlobal=FALSE; + BOOL bIsParentImport = FALSE; + BOOL bIsGeneric = FALSE; + unsigned retType; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the MethodDef record. + veCtxt.Token = TokenFromRid(rid, mdtMethodDef); + veCtxt.uOffset = 0; + + IfFailGo(pMiniMd->GetMethodRecord(rid, &pRecord)); + + // Do checks for name validity. + IfFailGo(pMiniMd->getNameOfMethod(pRecord, &szName)); + if (!*szName) + { + // Method name is NULL. + REPORT_ERROR0(VLDTR_E_MD_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + if(!strcmp(szName,COR_DELETED_NAME_A)) goto ErrExit; + bIsCtor = (0 == strcmp(szName,BASE_CTOR_NAME)); + bIsCctor = (0 == strcmp(szName,BASE_CCTOR_NAME)); + ULONG L = (ULONG)strlen(szName); + if(L >= MAX_CLASSNAME_LENGTH) + { + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // Get the parent, flags and signature of the Method. + IfFailGo(pMiniMd->FindParentOfMethodHelper(TokenFromRid(rid, mdtMethodDef), &tkTypeDef)); + dwFlags = pMiniMd->getFlagsOfMethod(pRecord); + dwImplFlags = pMiniMd->getImplFlagsOfMethod(pRecord); + IfFailGo(pMiniMd->getSignatureOfMethod(pRecord, &pbSig, &cbSig)); + + // Check for duplicates. + if (*szName && cbSig && !IsNilToken(tkTypeDef) && !IsMdPrivateScope(dwFlags)) + { + hr = ImportHelper::FindMethod(pMiniMd, tkTypeDef, szName, pbSig, cbSig, &tkMethodDef, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_MD_DUP, tkMethodDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + } + + // No further error checking for VtblGap methods. + if (IsVtblGapName(szName)) + { + hr = hrSave; + goto ErrExit; + } + + // Validate Method signature. + IfFailGo(ValidateMethodSig(TokenFromRid(rid, mdtMethodDef), pbSig, cbSig, + dwFlags)); + if (hr != S_OK) + SetVldtrCode(&hrSave, hr); + + // Validate that the parent is not nil. + if (IsNilToken(tkTypeDef)) + { + REPORT_ERROR0(VLDTR_E_MD_PARNIL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (RidFromToken(tkTypeDef) != RidFromToken(m_tdModule)) + { + if(TypeFromToken(tkTypeDef) == mdtTypeDef) + { + TypeDefRec *pTDRec; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkTypeDef), &pTDRec)); + DWORD dwTDFlags = pTDRec->GetFlags(); + LPCSTR szTDName; + IfFailGo(pMiniMd->getNameOfTypeDef(pTDRec, &szTDName)); + LPCSTR szTDNameSpace; + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTDRec, &szTDNameSpace)); + BOOL fIsTdValue=FALSE, fIsTdEnum=FALSE; + mdToken tkExtends = pMiniMd->getExtendsOfTypeDef(pTDRec); + + if(0 == strcmp(szTDNameSpace,BASE_NAMESPACE)) + { + fIsTdEnum = (0 == strcmp(szTDName,BASE_ENUM_CLASSNAME)); + if(!fIsTdEnum) + { + fIsTdValue = (0 == strcmp(szTDName,BASE_VTYPE_CLASSNAME)); + } + } + if(fIsTdEnum || fIsTdValue) + { + fIsTdEnum = fIsTdValue = FALSE; // System.Enum and System.ValueType themselves are classes + } + else if(RidFromToken(tkExtends)) + { + if(TypeFromToken(tkExtends) == mdtTypeDef) + { + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkExtends), &pTDRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pTDRec, &szTDName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTDRec, &szTDNameSpace)); + } + else if(TypeFromToken(tkExtends) == mdtTypeSpec) + { + fIsTdEnum = fIsTdValue = FALSE; // a type extending a spec cannot be an enum or value type + // the assignments are redundant, but clear. + } + else + { + TypeRefRec *pTRRec; + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkExtends), &pTRRec)); + IfFailGo(pMiniMd->getNameOfTypeRef(pTRRec, &szTDName)); + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTRRec, &szTDNameSpace)); + } + + if(0 == strcmp(szTDNameSpace,BASE_NAMESPACE)) + { + fIsTdEnum = (0 == strcmp(szTDName,BASE_ENUM_CLASSNAME)); + if(!fIsTdEnum) + { + fIsTdValue = (0 == strcmp(szTDName,BASE_VTYPE_CLASSNAME)); + } + else fIsTdValue = FALSE; + } + } + + // If Method is abstract, verify parent is abstract. + if(IsMdAbstract(dwFlags) && !IsTdAbstract(dwTDFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_ABSTPARNOTABST, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // If parent is import, method must have zero RVA, otherwise it depends... + if(IsTdImport(dwTDFlags)) bIsParentImport = TRUE; + if(IsTdInterface(dwTDFlags)) + { + if(!IsMdStatic(dwFlags)) + { + // No non-abstract instance methods in interface. + if(!IsMdAbstract(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_NOTSTATABSTININTF, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // No non-public instance methods in interface. + if(!IsMdPublic(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_NOTPUBININTF, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // If Method is constructor, verify parent is not interface. + if(bIsCtor) + { + REPORT_ERROR1(VLDTR_E_MD_CTORININTF, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + }//end if(interface) + if((fIsTdValue || fIsTdEnum) && IsMiSynchronized(dwImplFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_SYNCMETHODINVTYPE, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(bIsCtor) + { + // .ctor must be instance + if(IsMdStatic(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_CTORSTATIC, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + }//end if .ctor + else if(bIsCctor) + { + // .cctor must be static + if(!IsMdStatic(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_CCTORNOTSTATIC, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // ..cctor must have default callconv + IfFailGo(pMiniMd->getSignatureOfMethod(pRecord, &pbSig, &cbSig)); + if(IMAGE_CEE_CS_CALLCONV_DEFAULT != CorSigUncompressData(pbSig)) + { + REPORT_ERROR0(VLDTR_E_MD_CCTORCALLCONV); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // .cctor must have no arguments + if(0 != CorSigUncompressData(pbSig)) + { + REPORT_ERROR0(VLDTR_E_MD_CCTORHASARGS); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + + }//end if .cctor + if(bIsCtor || bIsCctor) + { + // .ctor, .cctor must be SpecialName and RTSpecialName + if(!(IsMdSpecialName(dwFlags) && IsMdRTSpecialName(dwFlags))) + { + REPORT_ERROR1(VLDTR_E_MD_CTORNOTSNRTSN, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } +#ifdef NO_SUCH_CHECKS_NEEDED_SPEC_TO_BE_UODATED + // .ctor, .cctor must not be virtual + if(IsMdVirtual(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_CTORVIRT, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // .ctor, .cctor must not be abstract + if(IsMdAbstract(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_CTORABST, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // .ctor, .cctor must not be PInvoke + if(IsMdPinvokeImpl(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_CTORPINVOKE, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // .ctor,.cctor must have RVA!=0 + if(pRecord->GetRVA()==0) + { + REPORT_ERROR0(VLDTR_E_MD_CTORZERORVA); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } +#endif + }//end if .ctor or .cctor + }// end if(parent == TypeDef) + }// end if not Module + else // i.e. if (RidFromToken(tkTypeDef) == RidFromToken(m_tdModule)) + { + bIsGlobal = TRUE; + // Globals are not CLS-compliant + REPORT_ERROR0(VLDTR_E_FMD_GLOBALITEM); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + // Validate global method: + // Must be static + if(!IsMdStatic(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FMD_GLOBALNOTSTATIC); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Must not be abstract or virtual + if(IsMdAbstract(dwFlags) || IsMdVirtual(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_MD_GLOBALABSTORVIRT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Must be not .ctor or .cctor + if(bIsCtor) + { + REPORT_ERROR0(VLDTR_E_MD_GLOBALCTORCCTOR); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } //end if Module + + // Signature specifics: .ctor, .cctor, entrypoint + if(bIsCtor || bIsCctor) + { + // .ctor, .cctor must return void + IfFailGo(pMiniMd->getSignatureOfMethod(pRecord, &pbSig, &cbSig)); + CorSigUncompressData(pbSig); // get call conv out of the way + CorSigUncompressData(pbSig); // get num args out of the way + while (((retType=CorSigUncompressData(pbSig)) == ELEMENT_TYPE_CMOD_OPT) + || (retType == ELEMENT_TYPE_CMOD_REQD)) CorSigUncompressToken(pbSig); + if(retType != ELEMENT_TYPE_VOID) + { + REPORT_ERROR0(VLDTR_E_MD_CTORNOTVOID); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + if(g_tkEntryPoint == veCtxt.Token) + { + ULONG ulCallConv; + // EP must be static + if(!IsMdStatic(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_EP_INSTANCE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // EP can't belong to generic class or nested in generic class + mdToken tkTypeDefCur; + for(tkTypeDefCur = tkTypeDef; tkTypeDefCur != mdTokenNil;) + { + HENUMInternal hEnumTyPars; + ULONG ulTypeDefArity = 0; + hr = pMiniMd->FindGenericParamHelper(tkTypeDefCur, &hEnumTyPars); + if (SUCCEEDED(hr)) + { + IfFailGo(HENUMInternal::GetCount(&hEnumTyPars,&ulTypeDefArity)); + HENUMInternal::ClearEnum(&hEnumTyPars); + if (ulTypeDefArity != 0) + { + REPORT_ERROR0(VLDTR_E_EP_GENERIC_TYPE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + if(ulTypeDefArity == 0) + { + // This class is not generic, how about the encloser? + RID iRecord; + IfFailGo(pMiniMd->FindNestedClassHelper(tkTypeDefCur, &iRecord)); + + if (InvalidRid(iRecord)) + { + tkTypeDefCur = mdTokenNil; + } + else + { + NestedClassRec *pNestedClassRec; + IfFailGo(pMiniMd->GetNestedClassRecord(iRecord, &pNestedClassRec)); + tkTypeDefCur = pMiniMd->getEnclosingClassOfNestedClass(pNestedClassRec); + } + } + else + tkTypeDefCur = mdTokenNil; + } + + // EP must have a predetermined signature (different for DLL and EXE + IfFailGo(pMiniMd->getSignatureOfMethod(pRecord, &pbSig, &cbSig)); + ulCallConv = CorSigUncompressData(pbSig); // get call conv out of the way + // EP can't be generic + if (ulCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + // Skip the arity + CorSigUncompressData(pbSig); + REPORT_ERROR0(VLDTR_E_EP_GENERIC_METHOD); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // EP must have 0 or 1 argument + unsigned nArgs = CorSigUncompressData(pbSig); + if(g_fIsDLL) + { + if(nArgs != 3) + { + REPORT_ERROR1(VLDTR_E_EP_TOOMANYARGS, 3); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + //EP must return I4 + while (((retType=CorSigUncompressData(pbSig)) == ELEMENT_TYPE_CMOD_OPT) + || (retType == ELEMENT_TYPE_CMOD_REQD)) CorSigUncompressToken(pbSig); + + if(retType != ELEMENT_TYPE_I4) + { + REPORT_ERROR0(VLDTR_E_EP_BADRET); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Arguments must be VOID*, U4, VOID* + if(nArgs) + { + unsigned jj; + bool badarg; + for(jj=0; jj<nArgs;jj++) + { + while (((retType=CorSigUncompressData(pbSig)) == ELEMENT_TYPE_CMOD_OPT) + || (retType == ELEMENT_TYPE_CMOD_REQD)) CorSigUncompressToken(pbSig); + + switch(jj) + { + case 0: + case 2: + badarg = (retType != ELEMENT_TYPE_PTR) + ||(CorSigUncompressData(pbSig) != ELEMENT_TYPE_VOID); + break; + + case 1: + badarg = (retType != ELEMENT_TYPE_U4); + break; + + default: + badarg = true; + } + if(badarg) + { + REPORT_ERROR1(VLDTR_E_EP_BADARG, jj+1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + } + else + { + if(nArgs > 1) + { + REPORT_ERROR1(VLDTR_E_EP_TOOMANYARGS, 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + //EP must return VOID, I4 or U4 + while (((retType=CorSigUncompressData(pbSig)) == ELEMENT_TYPE_CMOD_OPT) + || (retType == ELEMENT_TYPE_CMOD_REQD)) CorSigUncompressToken(pbSig); + + if((retType != ELEMENT_TYPE_VOID)&&(retType != ELEMENT_TYPE_I4)&&(retType != ELEMENT_TYPE_U4)) + { + REPORT_ERROR0(VLDTR_E_EP_BADRET); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Argument (if any) must be vector of strings + if(nArgs) + { + while (((retType=CorSigUncompressData(pbSig)) == ELEMENT_TYPE_CMOD_OPT) + || (retType == ELEMENT_TYPE_CMOD_REQD)) CorSigUncompressToken(pbSig); + + if((retType != ELEMENT_TYPE_SZARRAY)||(CorSigUncompressData(pbSig) != ELEMENT_TYPE_STRING)) + { + REPORT_ERROR1(VLDTR_E_EP_BADARG, 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } // end if(IsDll)--else + } // end if (IsEntryPoint) + + + // Check method RVA + if(pRecord->GetRVA()==0) + { + if(!(IsMdPinvokeImpl(dwFlags) || IsMdAbstract(dwFlags) + || IsMiRuntime(dwImplFlags) || IsMiInternalCall(dwImplFlags) + || bIsParentImport)) + { + REPORT_ERROR0(VLDTR_E_MD_ZERORVA); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else + { + if(m_pStgdb && m_pStgdb->m_pImage) + { + NewHolder<PEDecoder> pe; + + EX_TRY + { + // We need to use different PEDecoder constructors based on the type of data we give it. + // We use the one with a 'bool' as the second argument when dealing with a mapped file, + // and we use the one that takes a COUNT_T as the second argument when dealing with a + // flat file. + + if (m_pStgdb->m_pStgIO->GetMemoryMappedType() == MTYPE_IMAGE) + pe = new (nothrow) PEDecoder(m_pStgdb->m_pImage, false); + else + pe = new (nothrow) PEDecoder(m_pStgdb->m_pImage, (COUNT_T)(m_pStgdb->m_dwImageSize)); + + } + EX_CATCH + { + hr = COR_E_BADIMAGEFORMAT; + } + EX_END_CATCH(SwallowAllExceptions) + + IfFailGo(hr); + IfNullGo(pe); + + if (!pe->CheckRva(pRecord->GetRVA())) + { + REPORT_ERROR1(VLDTR_E_MD_BADRVA, pRecord->GetRVA()); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + if(IsMiManaged(dwImplFlags) && (IsMiIL(dwImplFlags) || IsMiOPTIL(dwImplFlags))) + { + HRESULT hrTemp = S_OK; + // validate locals signature token + EX_TRY + { + COR_ILMETHOD_DECODER method((COR_ILMETHOD*) pe->GetRvaData(pRecord->GetRVA())); + if (method.LocalVarSigTok) + { + if((TypeFromToken(method.GetLocalVarSigTok()) != mdtSignature) || + (!IsValidToken(method.GetLocalVarSigTok())) || (RidFromToken(method.GetLocalVarSigTok())==0)) + { + hrTemp = _ValidateErrorHelper(VLDTR_E_MD_BADLOCALSIGTOK, veCtxt, method.GetLocalVarSigTok()); + if (SUCCEEDED(hrTemp)) + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + EX_CATCH + { + hrTemp = _ValidateErrorHelper(VLDTR_E_MD_BADHEADER, veCtxt); + if (SUCCEEDED(hrTemp)) + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + EX_END_CATCH(SwallowAllExceptions) + + IfFailGo(hrTemp); + } + } + } + + if(IsMdAbstract(dwFlags) || bIsParentImport + || IsMiRuntime(dwImplFlags) || IsMiInternalCall(dwImplFlags)) + { + REPORT_ERROR0(VLDTR_E_MD_ZERORVA); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // Check the method flags + // Validate access + if((dwFlags & mdMemberAccessMask) == mdMemberAccessMask) + { + REPORT_ERROR0(VLDTR_E_FMD_BADACCESSFLAG); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Final/NewSlot must be virtual + if((IsMdFinal(dwFlags)||IsMdNewSlot(dwFlags)||IsMdCheckAccessOnOverride(dwFlags)) + && !IsMdVirtual(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_MD_FINNOTVIRT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Static can't be final or virtual + if(IsMdStatic(dwFlags)) + { + if(IsMdFinal(dwFlags) || IsMdVirtual(dwFlags) || IsMdNewSlot(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_MD_STATANDFINORVIRT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else // non-static can't be an entry point + { + if(g_tkEntryPoint == veCtxt.Token) + { + REPORT_ERROR0(VLDTR_E_EP_INSTANCE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + if(IsMdAbstract(dwFlags)) + { + // Can't be both abstract and final + if(IsMdFinal(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_MD_ABSTANDFINAL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // If abstract, must be not miForwardRef, not Pinvoke, and must be virtual + if(IsMiForwardRef(dwImplFlags)) + { + REPORT_ERROR0(VLDTR_E_MD_ABSTANDIMPL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(IsMdPinvokeImpl(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_MD_ABSTANDPINVOKE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(!IsMdVirtual(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_MD_ABSTNOTVIRT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // If PrivateScope, must have RVA!=0 + if(IsMdPrivateScope(dwFlags) && (pRecord->GetRVA() ==0)) + { + REPORT_ERROR0(VLDTR_E_MD_PRIVSCOPENORVA); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // RTSpecialName => SpecialName + if(IsMdRTSpecialName(dwFlags) && !IsMdSpecialName(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FMD_RTSNNOTSN); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Method having security must be marked mdHasSecurity and vice versa + { + ULONG ridStart = 1; + ULONG ridEnd = pMiniMd->getCountDeclSecuritys() + 1; + ULONG index; + BOOL bHasSecurity = FALSE; + for (index = ridStart; index < ridEnd; index ++ ) + { + DeclSecurityRec *pDeclSecurityRecord; + IfFailGo(pMiniMd->GetDeclSecurityRecord(index, &pDeclSecurityRecord)); + if ( veCtxt.Token == pMiniMd->getParentOfDeclSecurity(pDeclSecurityRecord)) + { + bHasSecurity = TRUE; + break; + } + } + if(!bHasSecurity) // No records, check for CA "SuppressUnmanagedCodeSecurityAttribute" + { + bHasSecurity = (S_OK == ImportHelper::GetCustomAttributeByName(pMiniMd, veCtxt.Token, + "System.Security.SuppressUnmanagedCodeSecurityAttribute", NULL, NULL)); + } + if(bHasSecurity != (IsMdHasSecurity(dwFlags)!=0)) + { + REPORT_ERROR0(bHasSecurity ? VLDTR_E_FMD_SECURNOTMARKED : VLDTR_E_FMD_MARKEDNOSECUR); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // Validate method semantics + { + MethodSemanticsRec *pRec; + ULONG ridEnd; + ULONG index; + unsigned uTally = 0; + mdToken tkEventProp; + ULONG iCount; + DWORD dwSemantic; + // get the range of method rids given a typedef + ridEnd = pMiniMd->getCountMethodSemantics(); + + for (index = 1; index <= ridEnd; index++ ) + { + IfFailGo(pMiniMd->GetMethodSemanticsRecord(index, &pRec)); + if ( pMiniMd->getMethodOfMethodSemantics(pRec) == veCtxt.Token ) + { + uTally++; + if(uTally > 1) + { + REPORT_ERROR0(VLDTR_E_MD_MULTIPLESEMANTICS); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + tkEventProp = pMiniMd->getAssociationOfMethodSemantics(pRec); + if((TypeFromToken(tkEventProp) == mdtEvent)||(TypeFromToken(tkEventProp) == mdtProperty)) + { + iCount = (TypeFromToken(tkEventProp) == mdtEvent) ? pMiniMd->getCountEvents() : + pMiniMd->getCountPropertys(); + if(RidFromToken(tkEventProp) > iCount) + { + REPORT_ERROR1(VLDTR_E_MD_SEMANTICSNOTEXIST, tkEventProp); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + else + { + REPORT_ERROR1(VLDTR_E_MD_INVALIDSEMANTICS, tkEventProp); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + // One and only one semantics flag must be set + iCount = 0; + dwSemantic = pRec->GetSemantic(); + if(IsMsSetter(dwSemantic)) iCount++; + if(IsMsGetter(dwSemantic)) iCount++; + if(IsMsOther(dwSemantic)) iCount++; + if(IsMsAddOn(dwSemantic)) iCount++; + if(IsMsRemoveOn(dwSemantic)) iCount++; + if(IsMsFire(dwSemantic)) iCount++; + if(iCount != 1) + { + REPORT_ERROR1(iCount ? VLDTR_E_MD_MULTSEMANTICFLAGS : VLDTR_E_MD_NOSEMANTICFLAGS, tkEventProp); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + }// end for(index) + } + // Check the method's impl.map + { + RID iRecord; + IfFailGo(pMiniMd->FindImplMapHelper(veCtxt.Token, &iRecord)); + if(IsMdPinvokeImpl(dwFlags)) + { + // must be static + if(!IsMdStatic(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_FMD_PINVOKENOTSTATIC); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // must have either ImplMap or RVA == 0 + if (InvalidRid(iRecord)) + { + if(pRecord->GetRVA()==0) + { + REPORT_ERROR0(VLDTR_E_FMD_MARKEDNOPINVOKE); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else + { + if(pRecord->GetRVA()!=0) + { + // C++ emits ImplMaps for IJW methods, + // with resolution=ModuleRef with name "" + ImplMapRec *pIMRecord; + mdToken tkModuleRef; + IfFailGo(pMiniMd->GetImplMapRecord(iRecord, &pIMRecord)); + tkModuleRef = pMiniMd->getImportScopeOfImplMap(pIMRecord); + if((TypeFromToken(tkModuleRef) == mdtModuleRef) && (!IsNilToken(tkModuleRef))) + { + ModuleRefRec *pMRRecord; // ModuleRef record. + LPCUTF8 szMRName; // ModuleRef name. + // Get the ModuleRef record. + IfFailGo(pMiniMd->GetModuleRefRecord(RidFromToken(tkModuleRef), &pMRRecord)); + // Check ModuleRef name is "". + IfFailGo(pMiniMd->getNameOfModuleRef(pMRRecord, &szMRName)); + if (*szMRName) + { + REPORT_ERROR0(VLDTR_E_MD_RVAANDIMPLMAP); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + else + { + hr = ValidateImplMap(iRecord); + if(hr != S_OK) + { + REPORT_ERROR0(VLDTR_E_FMD_BADIMPLMAP); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + } + + } + else + { + // must have no ImplMap + if (!InvalidRid(iRecord)) + { + REPORT_ERROR0(VLDTR_E_FMD_PINVOKENOTMARKED); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + // Validate params + { + ULONG ridStart = pMiniMd->getParamListOfMethod(pRecord); + ULONG ridEnd; + IfFailGo(pMiniMd->getEndParamListOfMethod(rid, &ridEnd)); + ParamRec* pRec; + ULONG cbSigT; + PCCOR_SIGNATURE typePtr; + IfFailGo(pMiniMd->getSignatureOfMethod(pRecord, &typePtr, &cbSigT)); + unsigned callConv = CorSigUncompressData(typePtr); // get the calling convention out of the way + unsigned numTyArgs = 0; + if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + bIsGeneric = TRUE; + numTyArgs = CorSigUncompressData(typePtr); + } + + unsigned numArgs = CorSigUncompressData(typePtr); + USHORT usPrevSeq = 0; + + for(ULONG ridP = ridStart; ridP < ridEnd; ridP++) + { + RID tempRid; + IfFailGo(pMiniMd->GetParamRecord(ridP, &pRec)); + // Sequence order must be ascending + if(ridP > ridStart) + { + if(pRec->GetSequence() <= usPrevSeq) + { + REPORT_ERROR2(VLDTR_E_MD_PARAMOUTOFSEQ, ridP-ridStart,pRec->GetSequence()); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + usPrevSeq = pRec->GetSequence(); + // Sequence value must not exceed num of arguments + if(usPrevSeq > numArgs) + { + REPORT_ERROR2(VLDTR_E_MD_PARASEQTOOBIG, ridP-ridStart,usPrevSeq); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Param having marshaling must be marked pdHasFieldMarshal and vice versa + IfFailGo(pMiniMd->FindFieldMarshalHelper(TokenFromRid(ridP,mdtParamDef), &tempRid)); + if (InvalidRid(tempRid) == (IsPdHasFieldMarshal(pRec->GetFlags()) != 0)) + { + REPORT_ERROR1(IsPdHasFieldMarshal(pRec->GetFlags()) ? VLDTR_E_MD_PARMMARKEDNOMARSHAL + : VLDTR_E_MD_PARMMARSHALNOTMARKED, ridP-ridStart); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Param having const value must be marked pdHasDefault and vice versa + IfFailGo(pMiniMd->FindConstantHelper(TokenFromRid(ridP,mdtParamDef), &tempRid)); + if (InvalidRid(tempRid) == (IsPdHasDefault(pRec->GetFlags()) != 0)) + { + REPORT_ERROR1(IsPdHasDefault(pRec->GetFlags()) ? VLDTR_E_MD_PARMMARKEDNODEFLT + : VLDTR_E_MD_PARMDEFLTNOTMARKED, ridP-ridStart); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + + // Generic Method related checks + if (bIsGeneric) + { + if (bIsCctor) + { + REPORT_ERROR0(VLDTR_E_MD_GENERIC_CCTOR); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + if (bIsCtor) + { + REPORT_ERROR0(VLDTR_E_MD_GENERIC_CTOR); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + if (bIsParentImport) + { + REPORT_ERROR0(VLDTR_E_MD_GENERIC_IMPORT); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateMethod() +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +//***************************************************************************** +// Validate the given ParamPtr. +//***************************************************************************** +HRESULT RegMeta::ValidateParamPtr(RID rid) +{ + return S_OK; +} // RegMeta::ValidateParamPtr() + +//***************************************************************************** +// Validate the given Param. +//***************************************************************************** +HRESULT RegMeta::ValidateParam(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + ParamRec *pRecord; // Param record + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + LPCSTR szName; // Param name. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the InterfaceImpl record. + veCtxt.Token = TokenFromRid(rid, mdtParamDef); + veCtxt.uOffset = 0; + + DWORD dwBadFlags = 0; + DWORD dwFlags = 0; + IfFailGo(pMiniMd->GetParamRecord(rid, &pRecord)); + // Name, if any, must not exceed MAX_CLASSNAME_LENGTH + IfFailGo(pMiniMd->getNameOfParam(pRecord, &szName)); + ULONG L = (ULONG)strlen(szName); + if(L >= MAX_CLASSNAME_LENGTH) + { + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Flags must be as defined in CorHdr.h + dwBadFlags = ~(pdIn | pdOut | pdOptional | pdHasDefault | pdHasFieldMarshal); + dwFlags = pRecord->GetFlags(); + if(dwFlags & dwBadFlags) + { + REPORT_ERROR1(VLDTR_E_PD_BADFLAGS, dwFlags); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateParam() + +//***************************************************************************** +// Helper function for ValidateInterfaceImpl +//***************************************************************************** +int IsMethodImplementedByClass(CMiniMdRW *pMiniMd, + mdToken tkMethod, + LPCUTF8 szName, + PCCOR_SIGNATURE pSig, + ULONG cbSig, + mdToken tkClass) +{ + HRESULT hr; + int numImpl = 0; + if(TypeFromToken(tkMethod) == mdtMethodDef) + { + if(TypeFromToken(tkClass) == mdtTypeSpec) + { + // We are trying to find out if an interface method is implemented in the generic class tkClass. + // Simple signature comparison doesn't work here, because "int Method()" in the interface might + // be implemented by "T Type.Method()" in the generic type. + // Therefore we assume it is implemented. Atlernatively we could implement better signature + // comparison which would match T with any other type, etc. + numImpl = 1; + } + else if(TypeFromToken(tkClass) == mdtTypeDef) + { + TypeDefRec *pClass; + IfFailRet(pMiniMd->GetTypeDefRecord(RidFromToken(tkClass), &pClass)); + RID ridClsStart = pMiniMd->getMethodListOfTypeDef(pClass); + RID ridClsEnd; + IfFailRet(pMiniMd->getEndMethodListOfTypeDef(RidFromToken(tkClass), &ridClsEnd)); + mdMethodDef tkFoundMethod = 0; + DWORD dwFoundMethodFlags = 0; + // Check among methods + hr = ImportHelper::FindMethod(pMiniMd, tkClass, szName, pSig, cbSig, &tkFoundMethod, 0); + if(SUCCEEDED(hr)) + { + MethodRec * pMethod; + IfFailRet(pMiniMd->GetMethodRecord(RidFromToken(tkFoundMethod), &pMethod)); + if(pMethod) + { + dwFoundMethodFlags = pMiniMd->getFlagsOfMethod(pMethod); + if(IsMdVirtual(dwFoundMethodFlags)) //&&!IsMdNewSlot(dwFoundMethodFlags)) + numImpl = 1; + } + } + if (numImpl==0) //if(hr == CLDB_E_RECORD_NOTFOUND) + { // Check among MethodImpls + RID ridImpl; + for(RID idxCls = ridClsStart; idxCls < ridClsEnd; idxCls++) + { + RID ridCls; + IfFailRet(pMiniMd->GetMethodRid(idxCls, &ridCls)); + + hr = ImportHelper::FindMethodImpl(pMiniMd,tkClass,TokenFromRid(ridCls,mdtMethodDef), + tkMethod,&ridImpl); + if(hr != CLDB_E_RECORD_NOTFOUND) + { + if(SUCCEEDED(hr)) numImpl++; + break; + } + } + if(numImpl == 0) + { + // Check if parent class implements this method + mdToken tkParent = pMiniMd->getExtendsOfTypeDef(pClass); + if(RidFromToken(tkParent)) + numImpl = IsMethodImplementedByClass(pMiniMd,tkMethod,szName,pSig,cbSig,tkParent); + } + } + } + else if (TypeFromToken(tkClass) == mdtTypeRef) + { + TypeRefRec *pRecord; // TypeRef record. + LPCSTR szTRNamespace; // TypeRef Namespace. + LPCSTR szTRName; // TypeRef Name. + + // Get the TypeRef record. + IfFailRet(pMiniMd->GetTypeRefRecord(RidFromToken(tkClass), &pRecord)); + + // Check name is not NULL. + IfFailRet(pMiniMd->getNamespaceOfTypeRef(pRecord, &szTRNamespace)); + IfFailRet(pMiniMd->getNameOfTypeRef(pRecord, &szTRName)); + + mdToken tkRefScope = pMiniMd->getResolutionScopeOfTypeRef(pRecord); + if (tkRefScope == TokenFromRid(1, mdtModule)) + { + // if the typeref is referring to a type in this module then + // we should check the type definition it is referring to + mdTypeDef tkTypeDef; + hr = ImportHelper::FindTypeDefByName(pMiniMd, szTRNamespace, szTRName, tkRefScope, &tkTypeDef); + if (SUCCEEDED(hr)) + numImpl = IsMethodImplementedByClass(pMiniMd, tkMethod, szName, pSig, cbSig, tkTypeDef); + } + else if ((strcmp(szTRNamespace, BASE_NAMESPACE) == 0) && + ((strcmp(szTRName, BASE_OBJECT_CLASSNAME) == 0) || + (strcmp(szTRName, BASE_VTYPE_CLASSNAME) == 0) || + (strcmp(szTRName, BASE_ENUM_CLASSNAME) == 0))) + { + if (((strcmp(szName, SYSTEM_OBJECT_TOSTRING_METHODNAME) == 0) && + (cbSig == _countof(g_sigSystemObject_ToString)) && + (memcmp(pSig, g_sigSystemObject_ToString, cbSig) == 0)) || + ((strcmp(szName, SYSTEM_OBJECT_GETHASHCODE_METHODNAME) == 0) && + (cbSig == _countof(g_sigSystemObject_GetHashCode)) && + (memcmp(pSig, g_sigSystemObject_GetHashCode, cbSig) == 0)) || + ((strcmp(szName, SYSTEM_OBJECT_EQUALS_METHODNAME) == 0) && + (cbSig == _countof(g_sigSystemObject_Equals)) && + (memcmp(pSig, g_sigSystemObject_Equals, cbSig) == 0))) + { + numImpl = 1; // Method signature matches one of System.Object's virtual methods + } + else + { + numImpl = 0; // These classes (System.Object, System.ValueType and System.Enum) don't implement any other virtual methods + } + } + else + { + numImpl = -1; // The method is defined in another module, we cannot verify it (no external modules are loaded) + } + } + } + return numImpl; +} + +//***************************************************************************** +// Validate the given InterfaceImpl. +//***************************************************************************** +//@todo GENERICS: complete logic for type specs +// - for now, we just allow them, but we should be checking more properties +HRESULT RegMeta::ValidateInterfaceImpl(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + InterfaceImplRec *pRecord; // InterfaceImpl record. + mdTypeDef tkClass; // Class implementing the interface. + mdToken tkInterface; // TypeDef for the interface. + mdInterfaceImpl tkInterfaceImpl; // Duplicate InterfaceImpl. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + BOOL fCheckTheMethods=TRUE; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the InterfaceImpl record. + veCtxt.Token = TokenFromRid(rid, mdtInterfaceImpl); + veCtxt.uOffset = 0; + + IfFailGo(pMiniMd->GetInterfaceImplRecord(rid, &pRecord)); + + // Get implementing Class and the TypeDef for the interface. + tkClass = pMiniMd->getClassOfInterfaceImpl(pRecord); + + // No validation needs to be done on deleted records. + if (IsNilToken(tkClass)) + goto ErrExit; + + tkInterface = pMiniMd->getInterfaceOfInterfaceImpl(pRecord); + + // Validate that the Class is TypeDef. + if((!IsValidToken(tkClass))||(TypeFromToken(tkClass) != mdtTypeDef)/*&&(TypeFromToken(tkClass) != mdtTypeRef)*/) + { + REPORT_ERROR1(VLDTR_E_IFACE_BADIMPL, tkClass); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + fCheckTheMethods = FALSE; + } + // Validate that the Interface is TypeDef or TypeRef or TypeSpec + if((!IsValidToken(tkInterface))||(TypeFromToken(tkInterface) != mdtTypeDef)&&(TypeFromToken(tkInterface) != mdtTypeRef) + &&(TypeFromToken(tkInterface) != mdtTypeSpec)) + { + REPORT_ERROR1(VLDTR_E_IFACE_BADIFACE, tkInterface); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + fCheckTheMethods = FALSE; + } + // Validate that Interface is marked tdInterface. + else if(TypeFromToken(tkInterface) == mdtTypeDef) + { + TypeDefRec *pTDRec; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkInterface), &pTDRec)); + if(!IsTdInterface(pTDRec->GetFlags())) + { + REPORT_ERROR1(VLDTR_E_IFACE_NOTIFACE, tkInterface); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + } + + // Look for duplicates. + hr = ImportHelper::FindInterfaceImpl(pMiniMd, tkClass, tkInterface, + &tkInterfaceImpl, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_IFACE_DUP, tkInterfaceImpl); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + + // Validate that the Class (if not interface or abstract) implements all the methods of Interface + if((TypeFromToken(tkInterface) == mdtTypeDef) && fCheckTheMethods && (tkInterface != tkClass)) + { + TypeDefRec *pClass; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkClass), &pClass)); + if(!(IsTdAbstract(pClass->GetFlags()) + ||IsTdImport(pClass->GetFlags()) + ||IsTdInterface(pClass->GetFlags()))) + { + TypeDefRec *pInterface; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkInterface), &pInterface)); + RID ridIntStart = pMiniMd->getMethodListOfTypeDef(pInterface); + RID ridIntEnd; + IfFailGo(pMiniMd->getEndMethodListOfTypeDef(RidFromToken(tkInterface), &ridIntEnd)); + MethodRec* pIntMethod; + for(RID idxInt = ridIntStart; idxInt < ridIntEnd; idxInt++) + { + RID ridInt; + IfFailGo(pMiniMd->GetMethodRid(idxInt, &ridInt)); + IfFailGo(pMiniMd->GetMethodRecord(ridInt, &pIntMethod)); + const char* szName; + IfFailGo(pMiniMd->getNameOfMethod(pIntMethod, &szName)); + if(!IsMdStatic(pIntMethod->GetFlags()) + && !IsDeletedName(szName) + && !IsVtblGapName(szName)) + { + ULONG cbSig; + PCCOR_SIGNATURE pSig; + IfFailGo(pMiniMd->getSignatureOfMethod(pIntMethod, &pSig, &cbSig)); + if(cbSig) + { + int num = IsMethodImplementedByClass(pMiniMd,TokenFromRid(ridInt,mdtMethodDef),szName,pSig,cbSig,tkClass); + if(num == 0) + { // Error: method not implemented + REPORT_ERROR3(VLDTR_E_IFACE_METHNOTIMPL, tkClass, tkInterface, TokenFromRid(ridInt,mdtMethodDef)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(num == -1) + { + // Traced to a TypeRef, which might implement the method, give warning + REPORT_ERROR3(VLDTR_E_IFACE_METHNOTIMPLTHISMOD, tkClass, tkInterface, TokenFromRid(ridInt,mdtMethodDef)); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + if(num > 1) + { // Error: multiple method implementation + REPORT_ERROR3(VLDTR_E_IFACE_METHMULTIMPL, tkClass, tkInterface, TokenFromRid(ridInt,mdtMethodDef)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + } + } + } + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateInterfaceImpl() + +//***************************************************************************** +// Validate the given GenericParam. +//***************************************************************************** +HRESULT RegMeta::ValidateGenericParam(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + GenericParamRec *pRecord; // GenericParam record. + LPCSTR szName; // GenericParam name field. + mdToken tkOwner; // GenericParam owner field. + ULONG ulNumber; // GenericParam number field. + DWORD dwFlags; // GenericParam flags field + + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the GenericParam record. + veCtxt.Token = TokenFromRid(rid, mdtGenericParam); + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetGenericParamRecord(rid, &pRecord)); + + // 1. GenericParam may contain zero or more rows. + // (Nothing to check.) + + tkOwner = pMiniMd->getOwnerOfGenericParam(pRecord); + // 2. Owner must be a valid token and a type def or method def + // (Already checked by ValidateRecord) + + // CLR tolerates Nil owners, ECMA does not + if(IsNilToken(tkOwner)) + { + REPORT_ERROR0(VLDTR_E_GP_OWNERNIL); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + + //3. Every generic type shall own one row in the Generic Param table for each of its type parameters. [ERROR] + // (Nothing to check, as the arity of a generic type is, by definition, the number of generic param entries). + + //4. Every generic method shall own one row in the Generic Param table for each of its type parameters. [ERROR] + // (This is checked in ValidateMethodSig, error VLDTR_E_MD_GPMISMATCH). + + ulNumber = pMiniMd->getNumberOfGenericParam(pRecord); + + // 5. Flags must be valid + { + DWORD dwInvalidMask, dwExtraBits; + + dwFlags = pMiniMd->getFlagsOfGenericParam(pRecord); + + + // check for extra bits + dwInvalidMask = (DWORD)~(gpVarianceMask|gpSpecialConstraintMask); + dwExtraBits = dwFlags & dwInvalidMask; + if(dwExtraBits) + { + //@GENERICS: we could use a custom error, + // but this is one is already used in more than one context. + REPORT_ERROR1(VLDTR_E_TD_EXTRAFLAGS, dwExtraBits); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + //Check Variance + { + DWORD dwVariance = dwFlags & gpVarianceMask; + switch (dwVariance) + { + case gpNonVariant: + // always ok + break; + case gpCovariant: + case gpContravariant: + if (TypeFromToken(tkOwner)==mdtTypeDef) + { + if (IsNilToken(tkOwner)) + break; + TypeDefRec *pTypeDefRec; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkOwner), &pTypeDefRec)); + // co-contra variance only legal on interfaces and delegates + // If owner is not an interface and does not extend MultiCastDelegate, report an error + if(!IsTdInterface(pTypeDefRec->GetFlags())) + { + // Get the parent of the TypeDef. + mdToken tkExtends = pMiniMd->getExtendsOfTypeDef(pTypeDefRec); + LPCSTR szExtName = NULL; // Parent Name. + LPCSTR szExtNameSpace = NULL; // Parent NameSpace. + BOOL bExtendsMCDelegate = FALSE; + + // Determine if the parent is MCDelegate + if (TypeFromToken(tkExtends) != mdtTypeSpec) + { + if (TypeFromToken(tkExtends) == mdtTypeRef) + { + TypeRefRec *pExtTypeRefRec; + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkExtends), &pExtTypeRefRec)); + mdToken tkResScope = pMiniMd->getResolutionScopeOfTypeRef(pExtTypeRefRec); + if (RidFromToken(tkResScope) && (TypeFromToken(tkResScope) == mdtAssemblyRef)) + { + AssemblyRefRec * pARRec; + IfFailGo(pMiniMd->GetAssemblyRefRecord(RidFromToken(tkResScope), &pARRec)); + LPCSTR szAssemblyRefName; + IfFailGo(pMiniMd->getNameOfAssemblyRef(pARRec, &szAssemblyRefName)); + if ((0 == SString::_stricmp("mscorlib", szAssemblyRefName)) || (0 == SString::_stricmp("System.Runtime", szAssemblyRefName))) + // otherwise don't even bother extracting the name + { + IfFailGo(pMiniMd->getNameOfTypeRef(pExtTypeRefRec, &szExtName)); + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pExtTypeRefRec, &szExtNameSpace)); + } + } + } + else if (TypeFromToken(tkExtends) == mdtTypeDef) + { + if (g_fValidatingMscorlib) // otherwise don't even bother extracting the name + { + TypeDefRec * pExtTypeRefRec; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkExtends), &pExtTypeRefRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pExtTypeRefRec, &szExtName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pExtTypeRefRec, &szExtNameSpace)); + } + } + + bExtendsMCDelegate = + szExtNameSpace && szExtName && + (0 == strcmp(szExtNameSpace,BASE_NAMESPACE)) && + (0 == strcmp(szExtName,BASE_MCDELEGATE_CLASSNAME)); + } + + // Report any error + if (!bExtendsMCDelegate) + { + REPORT_ERROR1(VLDTR_E_GP_UNEXPECTED_OWNER_FOR_VARIANT_VAR,tkOwner); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + else + { + // co-contra variance never legal on MVARs + REPORT_ERROR0(VLDTR_E_GP_ILLEGAL_VARIANT_MVAR); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + break; + default: + REPORT_ERROR1(VLDTR_E_GP_ILLEGAL_VARIANCE_FLAGS,dwFlags); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + } + + // Check special constraints + { + DWORD dwSpecialConstraints = dwFlags & gpSpecialConstraintMask; + // It is illegal go declare both gpNotNullableValueTypeConstraint + // and gpReferenceTypeConstraint, but gpDefaultConstructorConstraint + // is legal with either (or neither). + if ((dwSpecialConstraints & (gpReferenceTypeConstraint | gpNotNullableValueTypeConstraint)) == + (gpReferenceTypeConstraint | gpNotNullableValueTypeConstraint)) + { + REPORT_ERROR1(VLDTR_E_GP_REFANDVALUETYPE,dwFlags); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + + // 6. Number shall have a value >=0 and < number of type parameters in owner type or method. + // 7. Successive rows of the GenericParam table that are owned by the same method (sic) (owner?) shall + // be ordered by increasing Number value; there can be no gaps in the Number sequence. + { + if(ulNumber>0) + { if(rid==1) + { + REPORT_ERROR0(VLDTR_E_GP_NONSEQ_BY_NUMBER); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + GenericParamRec *pPredRecord; + IfFailGo(pMiniMd->GetGenericParamRecord(rid-1, &pPredRecord)); + mdToken tkPredOwner = pMiniMd->getOwnerOfGenericParam(pPredRecord); + ULONG ulPredNumber = pMiniMd->getNumberOfGenericParam(pPredRecord); + if (tkPredOwner != tkOwner) + { + REPORT_ERROR0(VLDTR_E_GP_NONSEQ_BY_OWNER); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if (ulPredNumber != ulNumber-1) + { + REPORT_ERROR0(VLDTR_E_GP_NONSEQ_BY_NUMBER); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + } + + // 8. Name must be non-null and not too long + IfFailGo(pMiniMd->getNameOfGenericParam(pRecord, &szName)); + if (!*szName) + { + // name is NULL. + REPORT_ERROR0(VLDTR_E_GP_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + if(!strcmp(szName,COR_DELETED_NAME_A)) goto ErrExit; //@GENERICS: do we allow parameters to be deleted? + ULONG L = (ULONG)strlen(szName); + if(L >= MAX_CLASSNAME_LENGTH) + { + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + +#ifdef THIS_RULE_IS_DISABLED_BECAUSE_CSHARP_EMITS_DUP_NAMES_AND_DOESNT_WANT_TO_STOP + // 9. There shall be no duplicates based upon Owner and Name + if (szName) + { + mdGenericParam tkDupGenericParam; + hr = ImportHelper::FindGenericParamByOwner(pMiniMd, tkOwner, szName, NULL, &tkDupGenericParam, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_GP_DUPNAME, tkDupGenericParam); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + } +#endif + + // 10. There shall be no duplicates based upon Owner and Number + { + mdGenericParam tkDupGenericParam; + hr = ImportHelper::FindGenericParamByOwner(pMiniMd, tkOwner, NULL, &ulNumber, &tkDupGenericParam, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_GP_DUPNUMBER, tkDupGenericParam); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + } + + hr = hrSave; + +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateGenericParam() + +//***************************************************************************** +// Validate the given MemberRef. +//***************************************************************************** +HRESULT RegMeta::ValidateMemberRef(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + MemberRefRec *pRecord; // MemberRef record. + mdMemberRef tkMemberRef; // Duplicate MemberRef. + mdToken tkClass; // MemberRef parent. + LPCSTR szName; // MemberRef name. + PCCOR_SIGNATURE pbSig; // MemberRef signature. + PCCOR_SIGNATURE pbSigTmp; // Temp copy of pbSig, so that can be changed. + ULONG cbSig; // Size of sig in bytes. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the MemberRef record. + veCtxt.Token = TokenFromRid(rid, mdtMemberRef); + veCtxt.uOffset = 0; + + IfFailGo(pMiniMd->GetMemberRefRecord(rid, &pRecord)); + + // Do checks for name validity. + IfFailGo(pMiniMd->getNameOfMemberRef(pRecord, &szName)); + if (!*szName) + { + REPORT_ERROR0(VLDTR_E_MR_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + if (IsVtblGapName(szName)) + { + REPORT_ERROR0(VLDTR_E_MR_VTBLNAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (IsDeletedName(szName)) + { + REPORT_ERROR0(VLDTR_E_MR_DELNAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + ULONG L = (ULONG)strlen(szName); + if(L >= MAX_CLASSNAME_LENGTH) + { + // Name too long + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // MemberRef parent should never be nil in a PE file. + tkClass = pMiniMd->getClassOfMemberRef(pRecord); + if (m_ModuleType == ValidatorModuleTypePE && IsNilToken(tkClass)) + { + REPORT_ERROR0(VLDTR_E_MR_PARNIL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Verify that the signature is a valid signature as per signature spec. + IfFailGo(pMiniMd->getSignatureOfMemberRef(pRecord, &pbSig, &cbSig)); + + // Do some semantic checks based on the signature. + if (hr == S_OK) + { + ULONG ulCallingConv; + ULONG ulArgCount; + ULONG ulTyArgCount = 0; + ULONG ulCurByte = 0; + + // Extract calling convention. + pbSigTmp = pbSig; + ulCurByte += CorSigUncompressedDataSize(pbSigTmp); + ulCallingConv = CorSigUncompressData(pbSigTmp); + + // Get the type argument count + if (ulCallingConv & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + ulCurByte += CorSigUncompressedDataSize(pbSigTmp); + ulTyArgCount = CorSigUncompressData(pbSigTmp); + } + + // Get the argument count. + ulCurByte += CorSigUncompressedDataSize(pbSigTmp); + ulArgCount = CorSigUncompressData(pbSigTmp); + + // Calling convention must be one of IMAGE_CEE_CS_CALLCONV_DEFAULT, + // IMAGE_CEE_CS_CALLCONV_VARARG or IMAGE_CEE_CS_CALLCONV_FIELD. + if (!isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_DEFAULT) && + !isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_VARARG) && + !isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_FIELD)) + { + REPORT_ERROR1(VLDTR_E_MR_BADCALLINGCONV, ulCallingConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // [CLS] Calling convention must not be VARARG + if(isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_VARARG)) + { + REPORT_ERROR0(VLDTR_E_MR_VARARGCALLINGCONV); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + + // If the parent is a MethodDef... + if (TypeFromToken(tkClass) == mdtMethodDef) + { + if (RidFromToken(tkClass) != 0) + { + // The MethodDef must be the same name and the fixed part of the + // vararg signature must be the same. + MethodRec *pMethodRecord; // Method Record. + LPCSTR szMethodName; // Method name. + PCCOR_SIGNATURE pbMethodSig; // Method signature. + ULONG cbMethodSig; // Size in bytes of signature. + + // Get Method record, name and signature. + IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tkClass), &pMethodRecord)); + IfFailGo(pMiniMd->getNameOfMethod(pMethodRecord, &szMethodName)); + IfFailGo(pMiniMd->getSignatureOfMethod(pMethodRecord, &pbMethodSig, &cbMethodSig)); + + // Verify that the name of the Method is the same as the MemberRef. + if (strcmp(szName, szMethodName)) + { + REPORT_ERROR1(VLDTR_E_MR_NAMEDIFF, tkClass); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + if (isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_VARARG)) + { // It's VARARG calling convention + CQuickBytes qbFixedSig; // Quick bytes to hold the fixed part of the variable signature. + ULONG cbFixedSig; // Size in bytes of the fixed part. + + // Get the fixed part of the vararg signature of the MemberRef. + hr = _GetFixedSigOfVarArg(pbSig, cbSig, &qbFixedSig, &cbFixedSig); + if (FAILED(hr) || cbFixedSig != cbMethodSig || + memcmp(pbMethodSig, qbFixedSig.Ptr(), cbFixedSig)) + { + UnifiedAssemblySigComparer uasc(*this); + MDSigComparer sc(MDSigParser(pbMethodSig, cbMethodSig), + MDSigParser((PCCOR_SIGNATURE)qbFixedSig.Ptr(), cbFixedSig), + uasc); + + hr = sc.CompareMethodSignature(); + if (FAILED(hr)) + { + hr = S_OK; + REPORT_ERROR1(VLDTR_E_MR_SIGDIFF, tkClass); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + else + { // It's not VARARG calling convention - a MemberRef is referencing MethodDef (part of + // NoPIA) + UnifiedAssemblySigComparer uasc(*this); + MDSigComparer sc(MDSigParser(pbMethodSig, cbMethodSig), + MDSigParser(pbSig, cbSig), + uasc); + + // Compare signatures + hr = sc.CompareMethodSignature(); + if (FAILED(hr)) + { + hr = S_OK; + REPORT_ERROR1(VLDTR_E_MR_SIGDIFF, tkClass); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + } + + // There should be no duplicate MemberRefs. + if (*szName && pbSig && cbSig) + { + hr = ImportHelper::FindMemberRef(pMiniMd, tkClass, szName, pbSig, + cbSig, &tkMemberRef, rid, + ImportHelper::CreateHash); // Optimize for multiple calls + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_MR_DUP, tkMemberRef); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + { + hr = S_OK; + } + else + { + IfFailGo(hr); + } + } + + if (!isCallConv(ulCallingConv, IMAGE_CEE_CS_CALLCONV_FIELD)) + { + hr = ValidateMethodSig(veCtxt.Token,pbSig, cbSig,0); + SetVldtrCode(&hrSave,hr); + } + } + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateMemberRef() + +//***************************************************************************** +// Validate the given Constant. +//***************************************************************************** +HRESULT RegMeta::ValidateConstant(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + ConstantRec *pRecord; // Constant record. + mdToken tkParent; // Constant parent. + const VOID* pbBlob; // Constant value blob ptr + DWORD cbBlob; // Constant value blob size + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Get the MemberRef record. + veCtxt.Token = rid; + veCtxt.uOffset = 0; + + ULONG maxrid = 0; + ULONG typ = 0; + IfFailGo(pMiniMd->GetConstantRecord(rid, &pRecord)); + IfFailGo(pMiniMd->getValueOfConstant(pRecord, (const BYTE **)&pbBlob, &cbBlob)); + switch(pRecord->GetType()) + { + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_R4: + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_R8: + if(pbBlob == NULL) + { + REPORT_ERROR1(VLDTR_E_CN_BLOBNULL, pRecord->GetType()); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + case ELEMENT_TYPE_STRING: + break; + + case ELEMENT_TYPE_CLASS: + if(GET_UNALIGNED_32(pbBlob) != 0) + { + REPORT_ERROR1(VLDTR_E_CN_BLOBNOTNULL, pRecord->GetType()); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + break; + + default: + REPORT_ERROR1(VLDTR_E_CN_BADTYPE, pRecord->GetType()); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + tkParent = pMiniMd->getParentOfConstant(pRecord); + typ = TypeFromToken(tkParent); + switch(typ) + { + case mdtFieldDef: + maxrid = pMiniMd->getCountFields(); + break; + case mdtParamDef: + maxrid = pMiniMd->getCountParams(); + break; + case mdtProperty: + maxrid = pMiniMd->getCountPropertys(); + break; + } + switch(typ) + { + case mdtFieldDef: + case mdtParamDef: + case mdtProperty: + { + ULONG rid_p = RidFromToken(tkParent); + if((0==rid_p)||(rid_p > maxrid)) + { + REPORT_ERROR1(VLDTR_E_CN_PARENTRANGE, tkParent); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + break; + } + + default: + REPORT_ERROR1(VLDTR_E_CN_PARENTTYPE, tkParent); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + break; + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateConstant() + +//***************************************************************************** +// Validate the given CustomAttribute. +//***************************************************************************** +HRESULT RegMeta::ValidateCustomAttribute(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + CustomAttributeRec *pRecord; + IfFailGo(pMiniMd->GetCustomAttributeRecord(rid, &pRecord)); + + memset(&veCtxt, 0, sizeof(VEContext)); + + veCtxt.Token = TokenFromRid(rid,mdtCustomAttribute); + veCtxt.uOffset = 0; + + if (pRecord != NULL) + { + mdToken tkOwner = pMiniMd->getParentOfCustomAttribute(pRecord); + if(RidFromToken(tkOwner)) + { // if 0, it's deleted CA, don't pay attention + mdToken tkCAType = pMiniMd->getTypeOfCustomAttribute(pRecord); + DWORD cbValue=0; + const BYTE *pbValue; + IfFailGo(pMiniMd->getValueOfCustomAttribute(pRecord, &pbValue, &cbValue)); + if((TypeFromToken(tkOwner)==mdtCustomAttribute)||(!IsValidToken(tkOwner))) + { + REPORT_ERROR1(VLDTR_E_CA_BADPARENT, tkOwner); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(((TypeFromToken(tkCAType)!=mdtMethodDef)&&(TypeFromToken(tkCAType)!=mdtMemberRef)) + ||(!IsValidToken(tkCAType))||(RidFromToken(tkCAType)==0)) + { + REPORT_ERROR1(VLDTR_E_CA_BADTYPE, tkCAType); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { //i.e. Type is valid MethodDef or MemberRef + LPCUTF8 szName; + PCCOR_SIGNATURE pSig=NULL; + DWORD cbSig=0; + DWORD dwFlags=0; + if (TypeFromToken(tkCAType) == mdtMethodDef) + { + MethodRec *pTypeRec; + IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tkCAType), &pTypeRec)); + IfFailGo(pMiniMd->getNameOfMethod(pTypeRec, &szName)); + IfFailGo(pMiniMd->getSignatureOfMethod(pTypeRec, &pSig, &cbSig)); + dwFlags = pTypeRec->GetFlags(); + } + else // it can be only MemberRef, otherwise we wouldn't be here + { + MemberRefRec *pTypeRec; + IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkCAType), &pTypeRec)); + IfFailGo(pMiniMd->getNameOfMemberRef(pTypeRec, &szName)); + IfFailGo(pMiniMd->getSignatureOfMemberRef(pTypeRec, &pSig, &cbSig)); + } + if (strcmp(szName, ".ctor") != 0) + { + REPORT_ERROR1(VLDTR_E_CA_NOTCTOR, tkCAType); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if ((cbSig > 0) && (pSig != NULL)) + { + if(FAILED(ValidateMethodSig(tkCAType, pSig,cbSig,dwFlags)) + || (!((*pSig) & IMAGE_CEE_CS_CALLCONV_HASTHIS))) + { + REPORT_ERROR1(VLDTR_E_CA_BADSIG, tkCAType); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { // sig seems to be OK + if ((pbValue != NULL) && (cbValue > 0)) + { + // Check if prolog is OK + WORD pW = *((UNALIGNED WORD*)pbValue); + if(pW != 0x0001) + { + REPORT_ERROR1(VLDTR_E_CA_BADPROLOG, pW); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Check if blob corresponds to the signature + } + } + + } + else + { + REPORT_ERROR1(VLDTR_E_CA_NOSIG, tkCAType); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } // end if bad Type - else + } // end if RidFromToken(tkOwner) + } // end if pRecord + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateCustomAttribute() + +//***************************************************************************** +// Validate the given FieldMarshal. +//***************************************************************************** +HRESULT RegMeta::ValidateFieldMarshal(RID rid) +{ + return S_OK; +} // RegMeta::ValidateFieldMarshal() + +//***************************************************************************** +// Validate the given DeclSecurity. +//***************************************************************************** +HRESULT RegMeta::ValidateDeclSecurity(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + DeclSecurityRec *pRecord; + mdToken tkOwner; // Owner of the decl security + DWORD dwAction; // action flags + BOOL bIsValidOwner = FALSE; + + BEGIN_ENTRYPOINT_NOTHROW; + + IfFailGo(pMiniMd->GetDeclSecurityRecord(rid, &pRecord)); + + memset(&veCtxt, 0, sizeof(VEContext)); + + veCtxt.Token = TokenFromRid(rid,mdtPermission); + veCtxt.uOffset = 0; + + // Must have a valid owner + tkOwner = pMiniMd->getParentOfDeclSecurity(pRecord); + if(RidFromToken(tkOwner)==0) goto ErrExit; // deleted record, no need to validate + switch(TypeFromToken(tkOwner)) + { + case mdtModule: + case mdtAssembly: + case mdtTypeDef: + case mdtMethodDef: + case mdtFieldDef: + case mdtInterfaceImpl: + bIsValidOwner = IsValidToken(tkOwner); + break; + default: + break; + } + if(!bIsValidOwner) + { + REPORT_ERROR1(VLDTR_E_DS_BADOWNER, tkOwner); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Must have one and only one flag set + dwAction = pRecord->GetAction() & dclActionMask; + if(dwAction > dclMaximumValue) // the flags are 0,1,2,3,...,dclMaximumValue + { + REPORT_ERROR1(VLDTR_E_DS_BADFLAGS, dwAction); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // If field has DeclSecurity, verify its parent is not an interface.-- checked in ValidateField + // If method has DeclSecurity, verify its parent is not an interface.-- checked in ValidateMethod + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateDeclSecurity() + +//***************************************************************************** +// Validate the given ClassLayout. +//***************************************************************************** +HRESULT RegMeta::ValidateClassLayout(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + ClassLayoutRec *pRecord; // ClassLayout record. + TypeDefRec *pTypeDefRec; // Parent TypeDef record. + DWORD dwPackingSize; // Packing size. + mdTypeDef tkParent; // Parent TypeDef token. + DWORD dwTypeDefFlags; // Parent TypeDef flags. + RID clRid; // Duplicate ClassLayout rid. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + memset(&veCtxt, 0, sizeof(VEContext)); + + BEGIN_ENTRYPOINT_NOTHROW; + + // Extract the record. + veCtxt.Token = rid; + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetClassLayoutRecord(rid, &pRecord)); + + // Get the parent, if parent is nil its a deleted record. Skip validation. + tkParent = pMiniMd->getParentOfClassLayout(pRecord); + if (IsNilToken(tkParent)) + goto ErrExit; + + // Parent should not have AutoLayout set on it. + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkParent), &pTypeDefRec)); + dwTypeDefFlags = pMiniMd->getFlagsOfTypeDef(pTypeDefRec); + if (IsTdAutoLayout(dwTypeDefFlags)) + { + REPORT_ERROR1(VLDTR_E_CL_TDAUTO, tkParent); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Parent must not be an Interface + if(IsTdInterface(dwTypeDefFlags)) + { + REPORT_ERROR1(VLDTR_E_CL_TDINTF, tkParent); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate the PackingSize. + dwPackingSize = pMiniMd->getPackingSizeOfClassLayout(pRecord); + if((dwPackingSize > 128)||((dwPackingSize & (dwPackingSize-1)) !=0 )) + { + REPORT_ERROR2(VLDTR_E_CL_BADPCKSZ, tkParent, dwPackingSize); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate that there are no duplicates. + hr = _FindClassLayout(pMiniMd, tkParent, &clRid, rid); + if (hr == S_OK) + { + REPORT_ERROR2(VLDTR_E_CL_DUP, tkParent, clRid); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateClassLayout() + +//***************************************************************************** +// Validate the given FieldLayout. +//***************************************************************************** +HRESULT RegMeta::ValidateFieldLayout(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + FieldLayoutRec *pRecord; // FieldLayout record. + mdFieldDef tkField; // Field token. + ULONG ulOffset; // Field offset. + FieldRec *pFieldRec; // Field record. + TypeDefRec *pTypeDefRec; // Parent TypeDef record. + mdTypeDef tkTypeDef; // Parent TypeDef token. + RID clRid; // Corresponding ClassLayout token. + RID flRid = 0; // Duplicate FieldLayout rid. + DWORD dwTypeDefFlags; // Parent TypeDef flags. + DWORD dwFieldFlags; // Field flags. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Extract the record. + veCtxt.Token = rid; + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetFieldLayoutRecord(rid, &pRecord)); + + // Get the field, if it's nil it's a deleted record, so just skip it. + tkField = pMiniMd->getFieldOfFieldLayout(pRecord); + if (IsNilToken(tkField)) + goto ErrExit; + + // Validate the Offset value. + ulOffset = pMiniMd->getOffSetOfFieldLayout(pRecord); + if (ulOffset == ULONG_MAX) + { + REPORT_ERROR2(VLDTR_E_FL_BADOFFSET, tkField, ulOffset); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Get the parent of the Field. + IfFailGo(pMiniMd->FindParentOfFieldHelper(tkField, &tkTypeDef)); + // Validate that the parent is not nil. + if (IsNilToken(tkTypeDef)) + { + REPORT_ERROR1(VLDTR_E_FL_TDNIL, tkField); + SetVldtrCode(&hr, hrSave); + goto ErrExit; + } + + // Validate that there exists a ClassLayout record associated with + // this TypeDef. + IfFailGo(pMiniMd->FindClassLayoutHelper(tkTypeDef, &clRid)); + if (InvalidRid(rid)) + { + REPORT_ERROR2(VLDTR_E_FL_NOCL, tkField, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate that ExplicitLayout is set on the TypeDef flags. + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkTypeDef), &pTypeDefRec)); + dwTypeDefFlags = pMiniMd->getFlagsOfTypeDef(pTypeDefRec); + if (IsTdAutoLayout(dwTypeDefFlags)) + { + REPORT_ERROR2(VLDTR_E_FL_TDNOTEXPLCT, tkField, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Extract Field record. + IfFailGo(pMiniMd->GetFieldRecord(RidFromToken(tkField), &pFieldRec)); + // Validate that the field is non-static. + dwFieldFlags = pMiniMd->getFlagsOfField(pFieldRec); + if (IsFdStatic(dwFieldFlags)) + { + REPORT_ERROR1(VLDTR_E_FL_FLDSTATIC, tkField); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Look for duplicates. + hr = _FindFieldLayout(pMiniMd, tkField, &flRid, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_FL_DUP, flRid); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateFieldLayout() + +//***************************************************************************** +// Validate the given StandAloneSig. +//***************************************************************************** +HRESULT RegMeta::ValidateStandAloneSig(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + StandAloneSigRec *pRecord; // FieldLayout record. + PCCOR_SIGNATURE pbSig; // Signature. + ULONG cbSig; // Size in bytes of the signature. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + ULONG ulCurByte = 0; // Current index into the signature. + ULONG ulCallConv; // Calling convention. + ULONG ulArgCount; // Count of arguments. + ULONG ulTyArgCount = 0; // Count of type arguments. + ULONG i; // Looping index. + ULONG ulNSentinels = 0; // Number of sentinels in the signature + BOOL bNoVoidAllowed=TRUE; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + + // Extract the record. + veCtxt.Token = TokenFromRid(rid,mdtSignature); + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetStandAloneSigRecord(rid, &pRecord)); + IfFailGo(pMiniMd->getSignatureOfStandAloneSig(pRecord, &pbSig, &cbSig)); + + // Validate the signature is well-formed with respect to the compression + // scheme. If this fails, no further validation needs to be done. + if ( (hr = ValidateSigCompression(veCtxt.Token, pbSig, cbSig)) != S_OK) + goto ErrExit; + + //_ASSERTE((rid != 0x2c2)&&(rid!=0x2c8)&&(rid!=0x2c9)&&(rid!=0x2d6)&&(rid!=0x38b)); + // Validate the calling convention. + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulCallConv = CorSigUncompressData(pbSig); + i = ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK; + if(i == IMAGE_CEE_CS_CALLCONV_FIELD) // <REVISIT_TODO>it's a temporary bypass (VB bug)</REVISIT_TODO> + ulArgCount = 1; + else + { + if(i != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) // then it is function sig for calli + { + if((i >= IMAGE_CEE_CS_CALLCONV_FIELD) + ||((ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS) + &&(!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)))) + { + REPORT_ERROR1(VLDTR_E_MD_BADCALLINGCONV, ulCallConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + bNoVoidAllowed = FALSE; + } + // Is there any sig left for arguments? + _ASSERTE(ulCurByte <= cbSig); + if (cbSig == ulCurByte) + { + REPORT_ERROR1(VLDTR_E_MD_NOARGCNT, ulCurByte+1); + SetVldtrCode(&hr, hrSave); + goto ErrExit; + } + + // Get the type argument count. + if (ulCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulTyArgCount = CorSigUncompressData(pbSig); + } + + // Get the argument count. + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulArgCount = CorSigUncompressData(pbSig); + } + // Validate the the arguments. + if(ulArgCount) + { + for(i=1; ulCurByte < cbSig; i++) + { + hr = ValidateOneArg(veCtxt.Token, pbSig, cbSig, &ulCurByte,&ulNSentinels,bNoVoidAllowed); + if (hr != S_OK) + { + if(hr == VLDTR_E_SIG_MISSARG) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSARG, i); + } + SetVldtrCode(&hr, hrSave); + hrSave = hr; + break; + } + bNoVoidAllowed = TRUE; // whatever it was for the 1st arg, it must be TRUE for the rest + } + if((ulNSentinels != 0) && (!isCallConv(ulCallConv, IMAGE_CEE_CS_CALLCONV_VARARG ))) + { + REPORT_ERROR0(VLDTR_E_SIG_SENTMUSTVARARG); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(ulNSentinels > 1) + { + REPORT_ERROR0(VLDTR_E_SIG_MULTSENTINELS); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateStandAloneSig() + +//***************************************************************************** +// Validate the given EventMap. +//***************************************************************************** +HRESULT RegMeta::ValidateEventMap(RID rid) +{ + return S_OK; +} // RegMeta::ValidateEventMap() + +//***************************************************************************** +// Validate the given EventPtr. +//***************************************************************************** +HRESULT RegMeta::ValidateEventPtr(RID rid) +{ + return S_OK; +} // RegMeta::ValidateEventPtr() + +//***************************************************************************** +// Validate the given Event. +//***************************************************************************** +HRESULT RegMeta::ValidateEvent(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + mdToken tkClass; // Declaring TypeDef + mdToken tkEventType; // Event Type (TypeDef/TypeRef) + EventRec *pRecord; + HENUMInternal hEnum; + + BEGIN_ENTRYPOINT_NOTHROW; + + IfFailGo(pMiniMd->GetEventRecord(rid, &pRecord)); + + memset(&veCtxt, 0, sizeof(VEContext)); + memset(&hEnum, 0, sizeof(HENUMInternal)); + veCtxt.Token = TokenFromRid(rid,mdtEvent); + veCtxt.uOffset = 0; + + // The scope must be a valid TypeDef + if (FAILED(pMiniMd->FindParentOfEventHelper(veCtxt.Token, &tkClass)) || + (TypeFromToken(tkClass) != mdtTypeDef) || + !IsValidToken(tkClass)) + { + REPORT_ERROR1(VLDTR_E_EV_BADSCOPE, tkClass); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + tkClass = 0; + } + // Must have name + { + LPCUTF8 szName; + IfFailGo(pMiniMd->getNameOfEvent(pRecord, &szName)); + + if (*szName == 0) + { + REPORT_ERROR0(VLDTR_E_EV_NONAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + if (strcmp(szName, COR_DELETED_NAME_A) == 0) + goto ErrExit; + if (tkClass != 0) // Must be no duplicates + { + RID ridEventMap; + EventMapRec *pEventMapRec; + EventRec *pRec; + ULONG ridStart; + ULONG ridEnd; + ULONG i; + + IfFailGo(pMiniMd->FindEventMapFor(RidFromToken(tkClass), &ridEventMap)); + if (!InvalidRid(ridEventMap)) + { + IfFailGo(pMiniMd->GetEventMapRecord(ridEventMap, &pEventMapRec)); + ridStart = pMiniMd->getEventListOfEventMap(pEventMapRec); + IfFailGo(pMiniMd->getEndEventListOfEventMap(ridEventMap, &ridEnd)); + + for (i = ridStart; i < ridEnd; i++) + { + if (i == rid) + continue; + IfFailGo(pMiniMd->GetEventRecord(i, &pRec)); + + LPCSTR szEventName; + IfFailGo(pMiniMd->getNameOfEvent(pRec, &szEventName)); + if (strcmp(szName, szEventName) != 0) + continue; + + REPORT_ERROR1(VLDTR_E_EV_DUP, TokenFromRid(i, mdtEvent)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + } + }// end of name block + // EventType must be Nil or valid TypeDef, TypeRef or TypeSpec representing an instantiated generic type + tkEventType = pMiniMd->getEventTypeOfEvent(pRecord); + if (!IsNilToken(tkEventType)) + { + if(IsValidToken(tkEventType) && + ((TypeFromToken(tkEventType)==mdtTypeDef)|| + (TypeFromToken(tkEventType)==mdtTypeRef)|| + (TypeFromToken(tkEventType)==mdtTypeSpec))) + { + // TypeSpecs can be many things, we only handle instantiated generic types currently. + if (TypeFromToken(tkEventType)==mdtTypeSpec) + { + TypeSpecRec *pRec; + IfFailGo(pMiniMd->GetTypeSpecRecord(RidFromToken(tkEventType), &pRec)); + PCCOR_SIGNATURE pSig; + ULONG cSig; + + IfFailGo(pMiniMd->getSignatureOfTypeSpec(pRec, &pSig, &cSig)); + + if (CorSigUncompressElementType(pSig) == ELEMENT_TYPE_GENERICINST && + CorSigUncompressElementType(pSig) == ELEMENT_TYPE_CLASS) + { + // Just update the event type token variable and fall through to the validation code below (it doesn't care + // whether the type is generic or not). + tkEventType = CorSigUncompressToken(pSig); + } + else + { + REPORT_ERROR1(VLDTR_E_EV_BADEVTYPE, tkEventType); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // EventType must not be Interface or ValueType + if(TypeFromToken(tkEventType)==mdtTypeDef) // can't say anything about TypeRef: no flags available! + { + TypeDefRec *pTypeDefRecord; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkEventType), &pTypeDefRecord)); + DWORD dwFlags = pTypeDefRecord->GetFlags(); + if(!IsTdClass(dwFlags)) + { + REPORT_ERROR2(VLDTR_E_EV_EVTYPENOTCLASS, tkEventType, dwFlags); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + else + { + REPORT_ERROR1(VLDTR_E_EV_BADEVTYPE, tkEventType); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // Validate related methods + { + MethodSemanticsRec *pSemantics; + RID ridCur; + ULONG ulSemantics; + mdMethodDef tkMethod; + bool bHasAddOn = false; + bool bHasRemoveOn = false; + + IfFailGo( pMiniMd->FindMethodSemanticsHelper(veCtxt.Token, &hEnum) ); + while (HENUMInternal::EnumNext(&hEnum, (mdToken *)&ridCur)) + { + IfFailGo(pMiniMd->GetMethodSemanticsRecord(ridCur, &pSemantics)); + ulSemantics = pMiniMd->getSemanticOfMethodSemantics(pSemantics); + tkMethod = TokenFromRid( pMiniMd->getMethodOfMethodSemantics(pSemantics), mdtMethodDef ); + // Semantics must be Setter, Getter or Other + switch (ulSemantics) + { + case msAddOn: + bHasAddOn = true; + break; + case msRemoveOn: + bHasRemoveOn = true; + break; + case msFire: + case msOther: + break; + default: + REPORT_ERROR2(VLDTR_E_EV_BADSEMANTICS, tkMethod,ulSemantics); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Method must be valid + if(!IsValidToken(tkMethod)) + { + REPORT_ERROR1(VLDTR_E_EV_BADMETHOD, tkMethod); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + // Method's parent must be the same + mdToken tkTypeDef; + IfFailGo(pMiniMd->FindParentOfMethodHelper(tkMethod, &tkTypeDef)); + if(tkTypeDef != tkClass) + { + REPORT_ERROR2(VLDTR_E_EV_ALIENMETHOD, tkMethod,tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } // end loop over methods + // AddOn and RemoveOn are a must + if(!bHasAddOn) + { + REPORT_ERROR0(VLDTR_E_EV_NOADDON); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(!bHasRemoveOn) + { + REPORT_ERROR0(VLDTR_E_EV_NOREMOVEON); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + }// end of related method validation block + + hr = hrSave; +ErrExit: + HENUMInternal::ClearEnum(&hEnum); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateEvent() + + +//***************************************************************************** +// Validate the given PropertyMap. +//***************************************************************************** +HRESULT RegMeta::ValidatePropertyMap(RID rid) +{ + return S_OK; +} // RegMeta::ValidatePropertyMap(0 + +//***************************************************************************** +// Validate the given PropertyPtr. +//***************************************************************************** +HRESULT RegMeta::ValidatePropertyPtr(RID rid) +{ + return S_OK; +} // RegMeta::ValidatePropertyPtr() + +//***************************************************************************** +// Validate the given Property. +//***************************************************************************** +HRESULT RegMeta::ValidateProperty(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + mdToken tkClass = mdTokenNil; // Declaring TypeDef + PropertyRec *pRecord; + HENUMInternal hEnum; + RID tempRid; + + BEGIN_ENTRYPOINT_NOTHROW; + + IfFailGo(pMiniMd->GetPropertyRecord(rid, &pRecord)); + + memset(&veCtxt, 0, sizeof(VEContext)); + memset(&hEnum, 0, sizeof(HENUMInternal)); + veCtxt.Token = TokenFromRid(rid,mdtProperty); + veCtxt.uOffset = 0; + // The scope must be a valid TypeDef + IfFailGo(pMiniMd->FindParentOfPropertyHelper( veCtxt.Token, &tkClass)); + if ((TypeFromToken(tkClass) != mdtTypeDef) || + !IsValidToken(tkClass) || + IsNilToken(tkClass)) + { + REPORT_ERROR1(VLDTR_E_PR_BADSCOPE, tkClass); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Must have name and signature + { + ULONG cbSig; + PCCOR_SIGNATURE pvSig; + IfFailGo(pMiniMd->getTypeOfProperty(pRecord, &pvSig, &cbSig)); + + LPCUTF8 szName; + IfFailGo(pMiniMd->getNameOfProperty(pRecord, &szName)); + ULONG ulNameLen = (szName != NULL) ? (ULONG)strlen(szName) : 0; + + if (ulNameLen == 0) + { + REPORT_ERROR0(VLDTR_E_PR_NONAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + if(strcmp(szName, COR_DELETED_NAME_A) == 0) + goto ErrExit; + } + if (cbSig == 0) + { + REPORT_ERROR0(VLDTR_E_PR_NOSIG); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Must be no duplicates + if ((ulNameLen != 0) && (cbSig != 0)) + { + RID ridPropertyMap; + PropertyMapRec *pPropertyMapRec; + PropertyRec *pRec; + ULONG ridStart; + ULONG ridEnd; + ULONG i; + ULONG cbSig1; + PCCOR_SIGNATURE pvSig1; + + IfFailGo(pMiniMd->FindPropertyMapFor(RidFromToken(tkClass), &ridPropertyMap)); + if (!InvalidRid(ridPropertyMap) ) + { + IfFailGo(pMiniMd->GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec)); + ridStart = pMiniMd->getPropertyListOfPropertyMap(pPropertyMapRec); + IfFailGo(pMiniMd->getEndPropertyListOfPropertyMap(ridPropertyMap, &ridEnd)); + + for (i = ridStart; i < ridEnd; i++) + { + if (i == rid) + continue; + IfFailGo(pMiniMd->GetPropertyRecord(i, &pRec)); + IfFailGo(pMiniMd->getTypeOfProperty(pRec, &pvSig1, &cbSig1)); + + if (cbSig != cbSig1) + continue; + if (memcmp(pvSig,pvSig1,cbSig) != 0) + continue; + + LPCSTR szPropertyName; + IfFailGo(pMiniMd->getNameOfProperty(pRec, &szPropertyName)); + if (strcmp(szName, szPropertyName) != 0) + continue; + + REPORT_ERROR1(VLDTR_E_PR_DUP, TokenFromRid(i,mdtProperty)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + // Validate the signature + if ((pvSig != NULL) && (cbSig != 0)) + { + ULONG ulCurByte = 0; // Current index into the signature. + ULONG ulCallConv; // Calling convention. + ULONG ulArgCount; + ULONG i; + ULONG ulNSentinels = 0; + + // Validate the calling convention. + ulCurByte += CorSigUncompressedDataSize(pvSig); + ulCallConv = CorSigUncompressData(pvSig); + if (!isCallConv(ulCallConv, IMAGE_CEE_CS_CALLCONV_PROPERTY )) + { + REPORT_ERROR1(VLDTR_E_PR_BADCALLINGCONV, ulCallConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Get the argument count. + ulCurByte += CorSigUncompressedDataSize(pvSig); + ulArgCount = CorSigUncompressData(pvSig); + + // Validate the arguments. + for (i = 0; i < ulArgCount; i++) + { + hr = ValidateOneArg(veCtxt.Token, pvSig, cbSig, &ulCurByte,&ulNSentinels,(i>0)); + if (hr != S_OK) + { + if (hr == VLDTR_E_SIG_MISSARG) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSARG, i+1); + } + SetVldtrCode(&hr, hrSave); + break; + } + } + }//end if(pvSig && cbSig) + }// end of name/signature block + + // Marked HasDefault <=> has default value + IfFailGo(pMiniMd->FindConstantHelper(veCtxt.Token, &tempRid)); + if (InvalidRid(tempRid) == IsPrHasDefault(pRecord->GetPropFlags())) + { + REPORT_ERROR0(IsPrHasDefault(pRecord->GetPropFlags())? VLDTR_E_PR_MARKEDNODEFLT : VLDTR_E_PR_DEFLTNOTMARKED); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate related methods + { + MethodSemanticsRec *pSemantics; + RID ridCur; + ULONG ulSemantics; + mdMethodDef tkMethod; + + IfFailGo( pMiniMd->FindMethodSemanticsHelper(veCtxt.Token, &hEnum) ); + while (HENUMInternal::EnumNext(&hEnum, (mdToken *) &ridCur)) + { + IfFailGo(pMiniMd->GetMethodSemanticsRecord(ridCur, &pSemantics)); + ulSemantics = pMiniMd->getSemanticOfMethodSemantics(pSemantics); + tkMethod = TokenFromRid( pMiniMd->getMethodOfMethodSemantics(pSemantics), mdtMethodDef ); + // Semantics must be Setter, Getter or Other + switch (ulSemantics) + { + case msSetter: + case msGetter: + case msOther: + break; + default: + REPORT_ERROR2(VLDTR_E_PR_BADSEMANTICS, tkMethod, ulSemantics); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Method must be valid + if(!IsValidToken(tkMethod)) + { + REPORT_ERROR1(VLDTR_E_PR_BADMETHOD, tkMethod); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + // Method's parent must be the same + mdToken tkTypeDef; + IfFailGo(pMiniMd->FindParentOfMethodHelper(tkMethod, &tkTypeDef)); + if(tkTypeDef != tkClass) + { + REPORT_ERROR2(VLDTR_E_PR_ALIENMETHOD, tkMethod, tkTypeDef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } // end loop over methods + }// end of related method validation block + + hr = hrSave; +ErrExit: + HENUMInternal::ClearEnum(&hEnum); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateProperty() + +//***************************************************************************** +// Validate the given MethodSemantics. +//***************************************************************************** +HRESULT RegMeta::ValidateMethodSemantics(RID rid) +{ + return S_OK; +} // RegMeta::ValidateMethodSemantics() + +//***************************************************************************** +// Validate the given MethodImpl. +//***************************************************************************** +HRESULT RegMeta::ValidateMethodImpl(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + MethodImplRec* pRecord; + MethodImplRec* pRec; + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + mdToken tkClass; // Declaring TypeDef + mdToken tkBody; // Implementing method (MethodDef or MemberRef) + mdToken tkDecl; // Implemented method (MethodDef or MemberRef) + unsigned iCount; + unsigned index; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = TokenFromRid(rid, mdtMethodImpl); + veCtxt.uOffset = 0; + + PCCOR_SIGNATURE pbBodySig = NULL; + PCCOR_SIGNATURE pbDeclSig = NULL; + + IfFailGo(pMiniMd->GetMethodImplRecord(rid, &pRecord)); + tkClass = pMiniMd->getClassOfMethodImpl(pRecord); + // Class must be valid + if(!IsValidToken(tkClass) || (TypeFromToken(tkClass) != mdtTypeDef)) + { + REPORT_ERROR1(VLDTR_E_MI_BADCLASS, tkClass); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { // ... and not an Interface + TypeDefRec *pTypeDefRecord; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkClass), &pTypeDefRecord)); + if(IsTdInterface(pTypeDefRecord->GetFlags())) + { + REPORT_ERROR1(VLDTR_E_MI_CLASSISINTF, tkClass); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // Decl must be valid MethodDef or MemberRef + tkDecl = pMiniMd->getMethodDeclarationOfMethodImpl(pRecord); + if(!(IsValidToken(tkDecl) && + ((TypeFromToken(tkDecl) == mdtMethodDef) || (TypeFromToken(tkDecl) == mdtMemberRef)))) + { + REPORT_ERROR1(VLDTR_E_MI_BADDECL, tkDecl); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Body must be valid MethodDef or MemberRef + tkBody = pMiniMd->getMethodBodyOfMethodImpl(pRecord); + if(!(IsValidToken(tkBody) && + ((TypeFromToken(tkBody) == mdtMethodDef) || (TypeFromToken(tkBody) == mdtMemberRef)))) + { + REPORT_ERROR1(VLDTR_E_MI_BADBODY, tkBody); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // No duplicates based on (tkClass,tkDecl) + iCount = pMiniMd->getCountMethodImpls(); + for(index = rid+1; index <= iCount; index++) + { + IfFailGo(pMiniMd->GetMethodImplRecord(index, &pRec)); + if((tkClass == pMiniMd->getClassOfMethodImpl(pRec)) && + (tkDecl == pMiniMd->getMethodDeclarationOfMethodImpl(pRec))) + { + REPORT_ERROR1(VLDTR_E_MI_DUP, index); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + mdToken tkBodyParent; + ULONG cbBodySig; + + if(TypeFromToken(tkBody) == mdtMethodDef) + { + MethodRec *pBodyRec; + IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tkBody), &pBodyRec)); + IfFailGo(pMiniMd->getSignatureOfMethod(pBodyRec, &pbBodySig, &cbBodySig)); + IfFailGo(pMiniMd->FindParentOfMethodHelper(tkBody, &tkBodyParent)); + // Body must not be static + if(IsMdStatic(pBodyRec->GetFlags())) + { + REPORT_ERROR1(VLDTR_E_MI_BODYSTATIC, tkBody); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else if(TypeFromToken(tkBody) == mdtMemberRef) + { + MemberRefRec *pBodyRec; + IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkBody), &pBodyRec)); + tkBodyParent = pMiniMd->getClassOfMemberRef(pBodyRec); + IfFailGo(pMiniMd->getSignatureOfMemberRef(pBodyRec, &pbBodySig, &cbBodySig)); + } + // Body must belong to the same class + if(tkBodyParent != tkClass) + { + REPORT_ERROR1(VLDTR_E_MI_ALIENBODY, tkBodyParent); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + mdToken tkDeclParent; + ULONG cbDeclSig; + + if(TypeFromToken(tkDecl) == mdtMethodDef) + { + MethodRec *pDeclRec; + IfFailGo(pMiniMd->GetMethodRecord(RidFromToken(tkDecl), &pDeclRec)); + IfFailGo(pMiniMd->getSignatureOfMethod(pDeclRec, &pbDeclSig, &cbDeclSig)); + IfFailGo(pMiniMd->FindParentOfMethodHelper(tkDecl, &tkDeclParent)); + // Decl must be virtual + if(!IsMdVirtual(pDeclRec->GetFlags())) + { + REPORT_ERROR1(VLDTR_E_MI_DECLNOTVIRT, tkDecl); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Decl must not be final + if(IsMdFinal(pDeclRec->GetFlags())) + { + REPORT_ERROR1(VLDTR_E_MI_DECLFINAL, tkDecl); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Decl must not be private + if(IsMdPrivate(pDeclRec->GetFlags()) && IsMdCheckAccessOnOverride(pDeclRec->GetFlags())) + { + REPORT_ERROR1(VLDTR_E_MI_DECLPRIV, tkDecl); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else if(TypeFromToken(tkDecl) == mdtMemberRef) + { + MemberRefRec *pDeclRec; + IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkDecl), &pDeclRec)); + tkDeclParent = pMiniMd->getClassOfMemberRef(pDeclRec); + IfFailGo(pMiniMd->getSignatureOfMemberRef(pDeclRec, &pbDeclSig, &cbDeclSig)); + } + + // Compare the signatures as best we can, delegating some comparisons to the loader. + if (*pbBodySig & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + // decl's callconv must be generic + if (*pbDeclSig & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + // and the arities must match + ULONG ulBodyArity = CorSigUncompressData(++pbBodySig); + ULONG ulDeclArity = CorSigUncompressData(++pbDeclSig); + if(ulBodyArity != ulDeclArity) + { + REPORT_ERROR3(VLDTR_E_MI_ARITYMISMATCH,tkDecl,ulDeclArity,ulBodyArity); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else + { + REPORT_ERROR1(VLDTR_E_MI_DECLNOTGENERIC,tkDecl); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // delegate precise signature checking to the loader, + // as this requires signature comparison modulo substitution + } + else if (*pbDeclSig & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + REPORT_ERROR1(VLDTR_E_MI_IMPLNOTGENERIC,tkDecl); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (TypeFromToken(tkDeclParent) == mdtTypeSpec) + { + // do nothing for now... + // delegate precise signature checking to the loader, + // as this requires signature comparison modulo substitution + } + // Signatures must match (except call conv) + else if((cbDeclSig != cbBodySig)||(memcmp(pbDeclSig+1,pbBodySig+1,cbDeclSig-1))) + { + //@GENERICSVER: todo: + /* + //@TODO: Fix to have peverify resolve assemblies + // through the runtime. At that point, use this method instead + // of the current compare + + // @TODO: check for other bad memcmp sig comparisons in peverify + + // Can't use memcmp because there may be two AssemblyRefs + // in this scope, pointing to the same assembly, etc.). + if (!MetaSig::CompareMethodSigs(pbDeclSig, + cbDeclSig, + Module* pModule1, + pbBodySig, + cbDeclSig, + Module* pModule2)) + */ + UnifiedAssemblySigComparer uasc(*this); + MDSigComparer sc(MDSigParser(pbDeclSig, cbDeclSig), + MDSigParser(pbBodySig, cbBodySig), + uasc); + + hr = sc.CompareMethodSignature(); + + if (FAILED(hr)) + { + REPORT_ERROR2(VLDTR_E_MI_SIGMISMATCH,tkDecl,tkBody); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateMethodImpl() + +//***************************************************************************** +// Validate the given ModuleRef. +//***************************************************************************** +HRESULT RegMeta::ValidateModuleRef(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + ModuleRefRec *pRecord; // ModuleRef record. + LPCUTF8 szName; // ModuleRef name. + mdModuleRef tkModuleRef; // Duplicate ModuleRef. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + // Get the ModuleRef record. + veCtxt.Token = TokenFromRid(rid, mdtModuleRef); + veCtxt.uOffset = 0; + + IfFailGo(pMiniMd->GetModuleRefRecord(rid, &pRecord)); + + // C++ emits IJW methods with ImplMaps + // which have resolution=ModuleRef with empty name + IfFailGo(pMiniMd->getNameOfModuleRef(pRecord, &szName)); + if (*szName) + { + // Look for a Duplicate, this function reports only one duplicate. + hr = ImportHelper::FindModuleRef(pMiniMd, szName, &tkModuleRef, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_MODREF_DUP, tkModuleRef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + } + else + hrSave = S_FALSE; + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateModuleRef() + +//***************************************************************************** +// Validate the given TypeSpec. +//***************************************************************************** +//@todo GENERICS: reject duplicate specs? +HRESULT RegMeta::ValidateTypeSpec(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + TypeSpecRec *pRecord; // TypeSpec record. + PCCOR_SIGNATURE pbSig; // Signature. + ULONG cbSig; // Size in bytes of the signature. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + ULONG ulCurByte = 0; // Current index into the signature. + ULONG ulNSentinels = 0; // Number of sentinels in the signature + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + // Extract the record. + veCtxt.Token = TokenFromRid(rid,mdtTypeSpec); + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetTypeSpecRecord(rid, &pRecord)); + IfFailGo(pMiniMd->getSignatureOfTypeSpec(pRecord, &pbSig, &cbSig)); + + // Validate the signature is well-formed with respect to the compression + // scheme. If this fails, no further validation needs to be done. + if ( (hr = ValidateSigCompression(veCtxt.Token, pbSig, cbSig)) != S_OK) + goto ErrExit; + + hr = ValidateOneArg(veCtxt.Token, pbSig, cbSig, &ulCurByte,&ulNSentinels,FALSE); + if (hr != S_OK) + { + if(hr == VLDTR_E_SIG_MISSARG) + { + REPORT_ERROR0(VLDTR_E_TS_EMPTY); + } + SetVldtrCode(&hr, hrSave); + hrSave = hr; + } + if(ulNSentinels != 0) + { + REPORT_ERROR0(VLDTR_E_TS_HASSENTINALS); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateTypeSpec() + +//***************************************************************************** +// This function validates the given Field signature. This function works +// with Field signature for both the MemberRef and FieldDef. +//***************************************************************************** +HRESULT RegMeta::ValidateMethodSpecSig( + mdMethodSpec tk, // [IN] Token whose signature needs to be validated. + PCCOR_SIGNATURE pbSig, // [IN] Signature. + ULONG cbSig, // [IN] Size in bytes of the signature. + ULONG *pArity) // [Out] Arity of the instantiation +{ + ULONG ulCurByte = 0; // Current index into the signature. + ULONG ulCallConv; // Calling convention. + ULONG ulArity; // Arity of instantiation. + ULONG ulArgCnt; + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + _ASSERTE(TypeFromToken(tk) == mdtMethodSpec); + + veCtxt.Token = tk; + veCtxt.uOffset = 0; + + // Validate the calling convention. + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulCallConv = CorSigUncompressData(pbSig); + if (!isCallConv(ulCallConv, IMAGE_CEE_CS_CALLCONV_GENERICINST)) + { + REPORT_ERROR1(VLDTR_E_MS_BADCALLINGCONV, ulCallConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + if (cbSig == ulCurByte) + { + REPORT_ERROR1(VLDTR_E_MS_MISSARITY, ulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulArity = CorSigUncompressData(pbSig); + + if (ulArity == 0) + { + REPORT_ERROR1(VLDTR_E_MS_ARITYZERO, ulCurByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + ulArgCnt = ulArity; + + if(pArity != NULL) + { + *pArity = ulArity; + } + + // Validate and consume the arguments. + while(ulArgCnt--) + { + + PCCOR_SIGNATURE pbTypeArg = pbSig; + ULONG ulTypeArgByte = ulCurByte; + + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, &ulCurByte, NULL, TRUE)); + if (hr != S_OK) + { + if(hr == VLDTR_E_SIG_MISSARG) + { + REPORT_ERROR1(VLDTR_E_MS_MISSARG, ulArity-ulArgCnt); + } + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // reject byref-like args + switch (CorSigUncompressData(pbTypeArg)) + { + case ELEMENT_TYPE_TYPEDBYREF: + case ELEMENT_TYPE_BYREF: + { + REPORT_ERROR1(VLDTR_E_MS_BYREFINST, ulTypeArgByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + default: + break; + } + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateMethodSpecSig() + + +//***************************************************************************** +// Validate the given MethodSpec. +//***************************************************************************** +HRESULT RegMeta::ValidateMethodSpec(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + MethodSpecRec *pRecord; // MethodSpec record. + mdToken tkMethod; // Method field (a MethodDefOrRef) + PCCOR_SIGNATURE pInstantiation; // MethodSpec instantiation (a signature) + ULONG cbInstantiation; // Size of instantiation. + ULONG ulInstantiationArity; // Arity of the Instantiation + + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + // Get the GenericParamConstraint record. + veCtxt.Token = TokenFromRid(rid, mdtMethodSpec); + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetMethodSpecRecord(rid, &pRecord)); + + // 1. The MethodSpec table may contain zero or more rows. + // (Nothing to check.) + + // Implicit (missing from spec): Method is not nil [ERROR] + tkMethod = pMiniMd->getMethodOfMethodSpec(pRecord); + if(IsNilToken(tkMethod)) + { + REPORT_ERROR0(VLDTR_E_MS_METHODNIL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Implicit in ValidateRecord: Method is a valid mdMethodDefOrRef. + + // 2. One or more rows may refer to the same row in the MethodDef or MethodRef table. + // (There may be more multiple instantions of the same generic method) + // (nothing to check!) + + // 3. "The signature stored at Instantiation shall be a valid instantiation of the signature of the generic method stored at Method. [ERROR] + { + IfFailGo(pMiniMd->getInstantiationOfMethodSpec(pRecord, &pInstantiation, &cbInstantiation)); + IfFailGo(ValidateMethodSpecSig(TokenFromRid(rid, mdtMethodSpec), pInstantiation, cbInstantiation,&ulInstantiationArity)); + if (hr != S_OK) + SetVldtrCode(&hrSave, hr); + } + + IfFailGo(pMiniMd->getInstantiationOfMethodSpec(pRecord, &pInstantiation, &cbInstantiation)); + // 4. There shall be no duplicate rows based upon Method and Instantiation [ERROR] + { + mdMethodSpec tkDupMethodSpec; + hr = ImportHelper::FindMethodSpecByMethodAndInstantiation(pMiniMd, tkMethod, pInstantiation, cbInstantiation, &tkDupMethodSpec, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_MS_DUP, tkDupMethodSpec); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + } + + // check the method is generic and that the arity of the instantiation is correct + { + PCCOR_SIGNATURE pbGenericMethodSig; + ULONG cbGenericMethodSig; + + if(TypeFromToken(tkMethod) == mdtMethodDef) + { + MethodRec *pMethodRec; + IfFailGo(m_pStgdb->m_MiniMd.GetMethodRecord(RidFromToken(tkMethod), &pMethodRec)); + IfFailGo(pMiniMd->getSignatureOfMethod(pMethodRec, &pbGenericMethodSig, &cbGenericMethodSig)); + } + else + { + _ASSERTE(TypeFromToken(tkMethod) == mdtMemberRef); + MemberRefRec *pMethodRefRec; + IfFailGo(pMiniMd->GetMemberRefRecord(RidFromToken(tkMethod), &pMethodRefRec)); + IfFailGo(pMiniMd->getSignatureOfMemberRef(pMethodRefRec, &pbGenericMethodSig, &cbGenericMethodSig)); + } + + if (*pbGenericMethodSig & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + ULONG ulGenericArity = CorSigUncompressData(++pbGenericMethodSig); + if(ulGenericArity != ulInstantiationArity) + { + REPORT_ERROR2(VLDTR_E_MS_ARITYMISMATCH,ulGenericArity,ulInstantiationArity); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else + { + REPORT_ERROR1(VLDTR_E_MS_METHODNOTGENERIC, tkMethod); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + hr = hrSave; + +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateMethodSpec() + + +//***************************************************************************** +// Validate the given GenericParamConstraint. +//***************************************************************************** +HRESULT RegMeta::ValidateGenericParamConstraint(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd of the scope. + GenericParamConstraintRec *pRecord; // GenericParamConstraint record. + mdGenericParam tkOwner; // GenericParamConstraint owner field. + mdToken tkConstraint; // GenericParamConstraint constraint field. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + // Get the GenericParamConstraint record. + veCtxt.Token = TokenFromRid(rid, mdtGenericParamConstraint); + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetGenericParamConstraintRecord(rid, &pRecord)); + + // 1. GenericParamConstraint may contain zero or more rows. + // (Nothing to check.) + + // 2. Each row shall have one, and only one, owner row in the GenericParamTable [ERROR] + // (Nothing to check except owner not nil) + tkOwner = pMiniMd->getOwnerOfGenericParamConstraint(pRecord); + if(IsNilToken(tkOwner)) + { + REPORT_ERROR0(VLDTR_E_GPC_OWNERNIL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // 3. Each row in the GenericParam table shall own a separate row in the GenericParamConstraint table for each constraint that type parameter has [ERROR] + // (Nothing to check) + + // 4.All of the rows in the GenericParamConstraint table that are owned by a given row in the GenericParamTable + // shall form a contiguous range of rows [ERROR] + //@NOTE: this check is (iterated over all rows) is quadratic in the (typically small) number of constraints + { + RID curRid = rid; + GenericParamConstraintRec *pCurRecord; + mdGenericParam tkCurOwner = tkOwner; + // find the first preceding row with a distinct owner + while (curRid > 1 && tkCurOwner == tkOwner) + { + curRid--; + IfFailGo(pMiniMd->GetGenericParamConstraintRecord(curRid, &pCurRecord)); + tkCurOwner = pMiniMd->getOwnerOfGenericParamConstraint(pCurRecord); + }; + // reject this row if there is some row preceding the current row with this owner + while (curRid > 1) + { + curRid--; + IfFailGo(pMiniMd->GetGenericParamConstraintRecord(curRid, &pCurRecord)); + tkCurOwner = pMiniMd->getOwnerOfGenericParamConstraint(pCurRecord); + if (tkCurOwner == tkOwner) + { + REPORT_ERROR1(VLDTR_E_GPC_NONCONTIGUOUS,tkOwner); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + }; + } + + // 5. "At most one class constraint per GenericParam" --- no longer required. + // 6. "Zero or more interface constraints per GenericParam" --- no longer required. + + tkConstraint = pMiniMd->getConstraintOfGenericParamConstraint(pRecord); + // 7. There shall be no duplicates based upon Owner and Constraint + { + mdGenericParamConstraint tkDupGenericParamConstraint; + hr = ImportHelper::FindGenericParamConstraintByOwnerAndConstraint(pMiniMd, tkOwner, tkConstraint, &tkDupGenericParamConstraint, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_GPC_DUP, tkDupGenericParamConstraint); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + } + + hr = hrSave; + +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateGenericParamConstraint() + +//***************************************************************************** +// Validate the given ImplMap. +//***************************************************************************** +HRESULT RegMeta::ValidateImplMap(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + ImplMapRec *pRecord; + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + HRESULT hrModuleRef=S_OK; + mdToken tkModuleRef; + mdToken tkMember; + USHORT usFlags; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); +#ifdef CACHE_IMPLMAP_VALIDATION_RESULT + for(unsigned jjj=0; jjj<g_nValidated; jjj++) + { + if(g_rValidated[jjj].tok == (rid | 0x51000000)) return g_rValidated[jjj].hr; + } +#endif + veCtxt.Token = rid; + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetImplMapRecord(rid, &pRecord)); + if(pRecord == NULL) IfFailGo(E_FAIL); + // ImplMap must have ModuleRef + tkModuleRef = pMiniMd->getImportScopeOfImplMap(pRecord); + if((TypeFromToken(tkModuleRef) != mdtModuleRef) || IsNilToken(tkModuleRef) + || FAILED(hrModuleRef= ValidateModuleRef(RidFromToken(tkModuleRef)))) + { + REPORT_ERROR1(VLDTR_E_IMAP_BADMODREF, tkModuleRef); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // ImplMap must belong to FieldDef or MethodDef + tkMember = pMiniMd->getMemberForwardedOfImplMap(pRecord); + if((TypeFromToken(tkMember) != mdtFieldDef) && (TypeFromToken(tkMember) != mdtMethodDef)) + { + REPORT_ERROR1(VLDTR_E_IMAP_BADMEMBER, tkMember); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // ImplMap must have import name, unless ModuleRef has no name + // (special case for C++ IJW methods) + if(hrModuleRef != S_FALSE) + { + LPCSTR szName; // Import name. + IfFailGo(pMiniMd->getImportNameOfImplMap(pRecord, &szName)); + if((szName==NULL)||(*szName == 0)) + { + REPORT_ERROR0(VLDTR_E_IMAP_BADIMPORTNAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + // ImplMap must have valid flags: + // one value of pmCharSetMask - always so, no check needed (values: 0,2,4,6, mask=6) + // one value of pmCallConvMask... + // ...and it's not pmCallConvThiscall + usFlags = pRecord->GetMappingFlags() & pmCallConvMask; + if((usFlags < pmCallConvWinapi)||(usFlags > pmCallConvFastcall)) + { + REPORT_ERROR1(VLDTR_E_IMAP_BADCALLCONV, usFlags); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } +ErrExit: + +#ifdef CACHE_IMPLMAP_VALIDATION_RESULT + g_rValidated[g_nValidated].tok = rid | 0x51000000; + g_rValidated[g_nValidated].hr = hrSave; + g_nValidated++; +#endif + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateImplMap() + +//***************************************************************************** +// Validate the given FieldRVA. +//***************************************************************************** +HRESULT RegMeta::ValidateFieldRVA(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + FieldRVARec *pRecord; + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + mdToken tkField; + ULONG ulRVA; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = rid; + veCtxt.uOffset = 0; + IfFailGo(pMiniMd->GetFieldRVARecord(rid, &pRecord)); + ulRVA = pRecord->GetRVA(); + tkField = pMiniMd->getFieldOfFieldRVA(pRecord); + /* + if(ulRVA == 0) + { + REPORT_ERROR1(VLDTR_E_FRVA_ZERORVA, tkField); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + */ + if((0==RidFromToken(tkField))||(TypeFromToken(tkField) != mdtFieldDef)||(!IsValidToken(tkField))) + { + REPORT_ERROR2(VLDTR_E_FRVA_BADFIELD, tkField, ulRVA); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + { + RID N = pMiniMd->getCountFieldRVAs(); + RID tmp; + FieldRVARec* pRecTmp; + for(tmp = rid+1; tmp <= N; tmp++) + { + IfFailGo(pMiniMd->GetFieldRVARecord(tmp, &pRecTmp)); + if(tkField == pMiniMd->getFieldOfFieldRVA(pRecTmp)) + { + REPORT_ERROR2(VLDTR_E_FRVA_DUPFIELD, tkField, tmp); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateFieldRVA() + +//***************************************************************************** +// Validate the given ENCLog. +//***************************************************************************** +HRESULT RegMeta::ValidateENCLog(RID rid) +{ + return S_OK; +} // RegMeta::ValidateENCLog() + +//***************************************************************************** +// Validate the given ENCMap. +//***************************************************************************** +HRESULT RegMeta::ValidateENCMap(RID rid) +{ + return S_OK; +} // RegMeta::ValidateENCMap() + +//***************************************************************************** +// Validate the given Assembly. +//***************************************************************************** +HRESULT RegMeta::ValidateAssembly(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + AssemblyRec *pRecord; // Assembly record. + CorAssemblyFlags dwFlags; // Assembly flags. + LPCSTR szName; // Assembly Name. + VEContext veCtxt; // Context structure. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + BOOL invalidAssemblyFlags; // Whether the CorAssemblyFlags are valid. + BOOL fIsV2Assembly = FALSE; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + // Get the Assembly record. + veCtxt.Token = TokenFromRid(rid, mdtAssembly); + veCtxt.uOffset = 0; + + IfFailGo(pMiniMd->GetAssemblyRecord(rid, &pRecord)); + + // There can only be one Assembly record. + if (rid > 1) + { + REPORT_ERROR0(VLDTR_E_AS_MULTI); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Do checks for name validity.. + IfFailGo(pMiniMd->getNameOfAssembly(pRecord, &szName)); + if (!*szName) + { + // Assembly Name is null. + REPORT_ERROR0(VLDTR_E_AS_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + unsigned L = (unsigned)strlen(szName); + if((*szName==' ')||strchr(szName,':') || strchr(szName,'\\') || strchr(szName, '/') + || strchr(szName, ',') || strchr(szName, '\n') || strchr(szName, '\r') + || ((L > 4)&&((!SString::_stricmp(&szName[L-4],".exe"))||(!SString::_stricmp(&szName[L-4],".dll"))))) + { + //Assembly name has path and/or extension + REPORT_ERROR0(VLDTR_E_AS_BADNAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // Get the flags value for the Assembly. + dwFlags = (CorAssemblyFlags) pMiniMd->getFlagsOfAssembly(pRecord); + + // Validate the flags + invalidAssemblyFlags = dwFlags & (~(afPublicKey | afRetargetable | afPA_FullMask | afEnableJITcompileTracking | afDisableJITcompileOptimizer | afContentType_Mask)); + + // Validate we only set a legal processor architecture flags + // The processor architecture flags were introduced in CLR v2.0. + // Note that METAMODEL_MINOR_VER_V2_0 is 0. GCC points out the comparison + // is useless, so that part is commented out. + fIsV2Assembly = (m_pStgdb->m_MiniMd.m_Schema.m_major >= METAMODEL_MAJOR_VER_V2_0 + /* && m_pStgdb->m_MiniMd.m_Schema.m_minor >= METAMODEL_MINOR_VER_V2_0*/); + if (fIsV2Assembly) + { + if ((dwFlags & afPA_Mask) > afPA_AMD64 && !IsAfPA_NoPlatform(dwFlags)) + invalidAssemblyFlags = true; + } + else { + if ((dwFlags & afPA_Mask) != 0) + invalidAssemblyFlags = true; + } + + if (!IsAfContentType_Default(dwFlags) && !IsAfContentType_WindowsRuntime(dwFlags)) + { // Unknown ContentType value + invalidAssemblyFlags = true; + } + + if (invalidAssemblyFlags) + { + REPORT_ERROR1(VLDTR_E_AS_BADFLAGS, dwFlags); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate hash algorithm ID + switch(pRecord->GetHashAlgId()) + { + case CALG_MD2: + case CALG_MD4: + case CALG_MD5: + case CALG_SHA: + //case CALG_SHA1: // same as CALG_SHA + case CALG_MAC: + case CALG_SSL3_SHAMD5: + case CALG_HMAC: + case 0: + break; + default: + REPORT_ERROR1(VLDTR_E_AS_HASHALGID, pRecord->GetHashAlgId()); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + // Validate locale + { + LPCSTR szLocale; + IfFailGo(pMiniMd->getLocaleOfAssembly(pRecord, &szLocale)); + if(!_IsValidLocale(szLocale, fIsV2Assembly)) + { + REPORT_ERROR0(VLDTR_E_AS_BADLOCALE); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateAssembly() + +//***************************************************************************** +// Validate the given AssemblyProcessor. +//***************************************************************************** +HRESULT RegMeta::ValidateAssemblyProcessor(RID rid) +{ + return S_OK; +} // RegMeta::ValidateAssemblyProcessor() + +//***************************************************************************** +// Validate the given AssemblyOS. +//***************************************************************************** +HRESULT RegMeta::ValidateAssemblyOS(RID rid) +{ + return S_OK; +} // RegMeta::ValidateAssemblyOS() + +//***************************************************************************** +// Validate the given AssemblyRef. +//***************************************************************************** +HRESULT RegMeta::ValidateAssemblyRef(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + AssemblyRefRec *pRecord; // Assembly record. + LPCSTR szName; // AssemblyRef Name. + VEContext veCtxt; // Context structure. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = TokenFromRid(rid, mdtAssemblyRef); + veCtxt.uOffset = 0; + + // Get the AssemblyRef record. + IfFailGo(pMiniMd->GetAssemblyRefRecord(rid, &pRecord)); + + // Do checks for name and alias validity. + IfFailGo(pMiniMd->getNameOfAssemblyRef(pRecord, &szName)); + if (!*szName) + { + // AssemblyRef Name is null. + REPORT_ERROR0(VLDTR_E_AR_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + unsigned L = (unsigned)strlen(szName); + if((*szName==' ')||strchr(szName,':') || strchr(szName,'\\') || strchr(szName, '/') + || strchr(szName, ',') || strchr(szName, '\n') || strchr(szName, '\r') + || ((L > 4)&&((!SString::_stricmp(&szName[L-4],".exe"))||(!SString::_stricmp(&szName[L-4],".dll"))))) + { + //Assembly name has path and/or extension + REPORT_ERROR0(VLDTR_E_AS_BADNAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // Validate locale + { + LPCSTR szLocale; + IfFailGo(pMiniMd->getLocaleOfAssemblyRef(pRecord, &szLocale)); + BOOL fIsV2Assembly = (m_pStgdb->m_MiniMd.m_Schema.m_major >= METAMODEL_MAJOR_VER_V2_0 + /* && m_pStgdb->m_MiniMd.m_Schema.m_minor >= METAMODEL_MINOR_VER_V2_0*/); + if(!_IsValidLocale(szLocale, fIsV2Assembly)) + { + REPORT_ERROR0(VLDTR_E_AS_BADLOCALE); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateAssemblyRef() + +//***************************************************************************** +// Validate the given AssemblyRefProcessor. +//***************************************************************************** +HRESULT RegMeta::ValidateAssemblyRefProcessor(RID rid) +{ + return S_OK; +} // RegMeta::ValidateAssemblyRefProcessor() + +//***************************************************************************** +// Validate the given AssemblyRefOS. +//***************************************************************************** +HRESULT RegMeta::ValidateAssemblyRefOS(RID rid) +{ + return S_OK; +} // RegMeta::ValidateAssemblyRefOS() + +//***************************************************************************** +// Validate the given File. +//***************************************************************************** +HRESULT RegMeta::ValidateFile(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + FileRec *pRecord; // File record. + mdFile tkFile; // Duplicate File token. + LPCSTR szName; // File Name. + VEContext veCtxt; // Context structure. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = TokenFromRid(rid, mdtFile); + veCtxt.uOffset = 0; + + // Get the File record. + IfFailGo(pMiniMd->GetFileRecord(rid, &pRecord)); + + // Do checks for name validity. + IfFailGo(pMiniMd->getNameOfFile(pRecord, &szName)); + if (!*szName) + { + // File Name is null. + REPORT_ERROR0(VLDTR_E_FILE_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + ULONG L = (ULONG)strlen(szName); + if(L >= MAX_PATH_FNAME) + { + // Name too long + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_PATH_FNAME-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Check for duplicates based on Name. + hr = ImportHelper::FindFile(pMiniMd, szName, &tkFile, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_FILE_DUP, tkFile); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + + // File name must not be fully qualified. + if(strchr(szName,':') || strchr(szName,'\\') || strchr(szName,'/')) + { + REPORT_ERROR0(VLDTR_E_FILE_NAMEFULLQLFD); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // File name must not be one of system names. + char *sysname[6]={"con","aux","lpt","prn","null","com"}; + char *syssymbol = "0123456789$:"; + for(unsigned i=0; i<6; i++) + { + L = (ULONG)strlen(sysname[i]); + if(!SString::_strnicmp(szName,sysname[i],L)) + { + if((szName[L]==0)|| strchr(syssymbol,szName[L])) + { + REPORT_ERROR0(VLDTR_E_FILE_SYSNAME); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + } + } + } + + if (pRecord->GetFlags() & (~0x00000003)) + { + REPORT_ERROR1(VLDTR_E_FILE_BADFLAGS, pRecord->GetFlags()); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate hash value + { + const BYTE *pbHashValue = NULL; + ULONG cbHashValue; + IfFailGo(m_pStgdb->m_MiniMd.getHashValueOfFile(pRecord, &pbHashValue, &cbHashValue)); + if ((pbHashValue == NULL) || (cbHashValue == 0)) + { + REPORT_ERROR0(VLDTR_E_FILE_NULLHASH); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // Validate that the name is not the same as the file containing + // the manifest. + + // File name must be a valid file name. + + // Each ModuleRef in the assembly must have a corresponding File table entry. + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateFile() + +//***************************************************************************** +// Validate the given ExportedType. +//***************************************************************************** +HRESULT RegMeta::ValidateExportedType(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + ExportedTypeRec *pRecord; // ExportedType record. + mdExportedType tkExportedType; // Duplicate ExportedType. + mdToken tkImpl; // Implementation token + mdToken tkTypeDef; // TypeDef token + + LPCSTR szName; // ExportedType Name. + LPCSTR szNamespace; // ExportedType Namespace. + VEContext veCtxt; // Context structure. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = TokenFromRid(rid, mdtExportedType); + veCtxt.uOffset = 0; + + // Get the ExportedType record. + IfFailGo(pMiniMd->GetExportedTypeRecord(rid, &pRecord)); + + tkImpl = pMiniMd->getImplementationOfExportedType(pRecord); + + tkTypeDef = pRecord->GetTypeDefId(); + if ((TypeFromToken(tkImpl) == mdtFile) && IsNilToken(tkTypeDef)) + { // Report 'No TypeDefId' warning only for types exported from other modules (do not report it for + // type forwarders) + REPORT_ERROR0(VLDTR_E_CT_NOTYPEDEFID); + SetVldtrCode(&hrSave, VLDTR_S_WRN); + } + + // Do checks for name validity. + IfFailGo(pMiniMd->getTypeNameOfExportedType(pRecord, &szName)); + IfFailGo(pMiniMd->getTypeNamespaceOfExportedType(pRecord, &szNamespace)); + if (!*szName) + { + // ExportedType Name is null. + REPORT_ERROR0(VLDTR_E_CT_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + if(!strcmp(szName,COR_DELETED_NAME_A)) goto ErrExit; + ULONG L = (ULONG)(strlen(szName)+strlen(szNamespace)); + if(L >= MAX_CLASSNAME_LENGTH) + { + // Name too long + REPORT_ERROR2(VLDTR_E_TD_NAMETOOLONG, L, (ULONG)(MAX_CLASSNAME_LENGTH-1)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Check for duplicates based on Name and Enclosing ExportedType. + hr = ImportHelper::FindExportedType(pMiniMd, szNamespace, szName, tkImpl, &tkExportedType, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_CT_DUP, tkExportedType); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + // Check for duplicate TypeDef based on Name/NameSpace - only for top-level ExportedTypes. + if(TypeFromToken(tkImpl)==mdtFile) + { + mdToken tkTypeDef2; + hr = ImportHelper::FindTypeDefByName(pMiniMd, szNamespace, szName, mdTypeDefNil, + &tkTypeDef2, 0); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_CT_DUPTDNAME, tkTypeDef2); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + } + } + // Check if flag value is valid + { + DWORD dwFlags = pRecord->GetFlags(); + DWORD dwInvalidMask, dwExtraBits; + dwInvalidMask = (DWORD)~(tdVisibilityMask | tdLayoutMask | tdClassSemanticsMask | + tdAbstract | tdSealed | tdSpecialName | tdImport | tdSerializable | tdForwarder | + tdStringFormatMask | tdBeforeFieldInit | tdReservedMask); + // check for extra bits + dwExtraBits = dwFlags & dwInvalidMask; + if(!dwExtraBits) + { + // if no extra bits, check layout + dwExtraBits = dwFlags & tdLayoutMask; + if(dwExtraBits != tdLayoutMask) + { + // layout OK, check string format + dwExtraBits = dwFlags & tdStringFormatMask; + if(dwExtraBits != tdStringFormatMask) dwExtraBits = 0; + } + } + if(dwExtraBits) + { + REPORT_ERROR1(VLDTR_E_TD_EXTRAFLAGS, dwExtraBits); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + if(IsNilToken(tkImpl) + || ((TypeFromToken(tkImpl) != mdtFile)&&(TypeFromToken(tkImpl) != mdtExportedType)&&(TypeFromToken(tkImpl) != mdtAssemblyRef)) + || (!IsValidToken(tkImpl))) + { + REPORT_ERROR1(VLDTR_E_CT_BADIMPL, tkImpl); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateExportedType() + +//***************************************************************************** +// Validate the given ManifestResource. +//***************************************************************************** +HRESULT RegMeta::ValidateManifestResource(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + ManifestResourceRec *pRecord; // ManifestResource record. + LPCSTR szName; // ManifestResource Name. + DWORD dwFlags; // ManifestResource flags. + mdManifestResource tkmar; // Duplicate ManifestResource. + VEContext veCtxt; // Context structure. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + mdToken tkImplementation; + BOOL bIsValidImplementation = TRUE; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = TokenFromRid(rid, mdtManifestResource); + veCtxt.uOffset = 0; + + // Get the ManifestResource record. + IfFailGo(pMiniMd->GetManifestResourceRecord(rid, &pRecord)); + + // Do checks for name validity. + IfFailGo(pMiniMd->getNameOfManifestResource(pRecord, &szName)); + if (!*szName) + { + // ManifestResource Name is null. + REPORT_ERROR0(VLDTR_E_MAR_NAMENULL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + // Check for duplicates based on Name. + hr = ImportHelper::FindManifestResource(pMiniMd, szName, &tkmar, rid); + if (hr == S_OK) + { + REPORT_ERROR1(VLDTR_E_MAR_DUP, tkmar); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else if (hr == CLDB_E_RECORD_NOTFOUND) + hr = S_OK; + else + IfFailGo(hr); + } + + // Get the flags of the ManifestResource. + dwFlags = pMiniMd->getFlagsOfManifestResource(pRecord); + if(dwFlags &(~0x00000003)) + { + REPORT_ERROR1(VLDTR_E_MAR_BADFLAGS, dwFlags); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Visibility of ManifestResource flags must either be public or private. + if (!IsMrPublic(dwFlags) && !IsMrPrivate(dwFlags)) + { + REPORT_ERROR0(VLDTR_E_MAR_NOTPUBPRIV); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Implementation must be Nil or valid AssemblyRef or File + tkImplementation = pMiniMd->getImplementationOfManifestResource(pRecord); + if(!IsNilToken(tkImplementation)) + { + switch(TypeFromToken(tkImplementation)) + { + case mdtAssemblyRef: + bIsValidImplementation = IsValidToken(tkImplementation); + break; + case mdtFile: + if((bIsValidImplementation = IsValidToken(tkImplementation))) + { // if file not PE, offset must be 0 + FileRec *pFR; + IfFailGo(pMiniMd->GetFileRecord(RidFromToken(tkImplementation), &pFR)); + if(IsFfContainsNoMetaData(pFR->GetFlags()) + && pRecord->GetOffset()) + { + REPORT_ERROR1(VLDTR_E_MAR_BADOFFSET, tkImplementation); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + break; + default: + bIsValidImplementation = FALSE; + } + } + if(!bIsValidImplementation) + { + REPORT_ERROR1(VLDTR_E_MAR_BADIMPL, tkImplementation); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate the Offset into the PE file. + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateManifestResource() + +//***************************************************************************** +// Validate the given NestedClass. +//***************************************************************************** +HRESULT RegMeta::ValidateNestedClass(RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); // MiniMd for the scope. + NestedClassRec *pRecord; // NestedClass record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save the current state. + VEContext veCtxt; // Context structure. + mdToken tkNested; + mdToken tkEncloser; + + BEGIN_ENTRYPOINT_NOTHROW; + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = rid; + veCtxt.uOffset = 0; + + // Get the NestedClass record. + IfFailGo(pMiniMd->GetNestedClassRecord(rid, &pRecord)); + tkNested = pMiniMd->getNestedClassOfNestedClass(pRecord); + tkEncloser = pMiniMd->getEnclosingClassOfNestedClass(pRecord); + + // Nested must be valid TypeDef + if((TypeFromToken(tkNested) != mdtTypeDef) || !IsValidToken(tkNested)) + { + REPORT_ERROR1(VLDTR_E_NC_BADNESTED, tkNested); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Encloser must be valid TypeDef + if((TypeFromToken(tkEncloser) != mdtTypeDef) || !IsValidToken(tkEncloser)) + { + REPORT_ERROR1(VLDTR_E_NC_BADENCLOSER, tkEncloser); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + // Check for duplicates + { + RID N = pMiniMd->getCountNestedClasss(); + RID tmp; + NestedClassRec* pRecTmp; + mdToken tkEncloserTmp; + for(tmp = rid+1; tmp <= N; tmp++) + { + IfFailGo(pMiniMd->GetNestedClassRecord(tmp, &pRecTmp)); + if(tkNested == pMiniMd->getNestedClassOfNestedClass(pRecTmp)) + { + if(tkEncloser == (tkEncloserTmp = pMiniMd->getEnclosingClassOfNestedClass(pRecTmp))) + { + REPORT_ERROR1(VLDTR_E_NC_DUP, tmp); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + else + { + REPORT_ERROR3(VLDTR_E_NC_DUPENCLOSER, tkNested, tkEncloser, tkEncloserTmp); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateLocalVariable() + +//***************************************************************************** +// Given a Table ID and a Row ID, validate all the columns contain meaningful +// values given the column definitions. Validate that the offsets into the +// different pools are valid, the rids are within range and the coded tokens +// are valid. Every failure here is considered an error. +//***************************************************************************** +HRESULT RegMeta::ValidateRecord(ULONG ixTbl, RID rid) +{ + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save the current state. + ULONG ulCount; // Count of records in the table. + ULONG ulRawColVal; // Raw value of the column. + void *pRow; // Row with the data. + CMiniTableDef *pTbl; // Table definition. + CMiniColDef *pCol; // Column definition. + const CCodedTokenDef *pCdTkn; // Coded token definition. + ULONG ix; // Index into the array of coded tokens. + + BEGIN_ENTRYPOINT_NOTHROW; + + // Get the table definition. + pTbl = &pMiniMd->m_TableDefs[ixTbl]; + + // Get the row. We may assume that the Row pointer we get back from + // this call is correct since we do the verification on the Record + // pools for each table during the open sequence. The only place + // this is not valid is for Dynamic IL and we don't do this + // verification in that case since we go through IMetaData* APIs + // in that case and it should all be consistent. + IfFailGo(m_pStgdb->m_MiniMd.getRow(ixTbl, rid, &pRow)); + + for (ULONG ixCol = 0; ixCol < pTbl->m_cCols; ixCol++) + { + // Get the column definition. + pCol = &pTbl->m_pColDefs[ixCol]; + + // Get the raw value stored in the column. getIX currently doesn't + // handle byte sized fields, but there are some BYTE fields in the + // MetaData. So using the conditional to access BYTE fields. + if (pCol->m_cbColumn == 1) + ulRawColVal = pMiniMd->getI1(pRow, *pCol); + else + ulRawColVal = pMiniMd->getIX(pRow, *pCol); + + // Do some basic checks on the non-absurdity of the value stored in the + // column. + if (IsRidType(pCol->m_Type)) + { + // Verify that the RID is within range. + _ASSERTE(pCol->m_Type < pMiniMd->GetCountTables()); + ulCount = pMiniMd->GetCountRecs(pCol->m_Type); + // For records storing rids to pointer tables, the stored value may + // be one beyond the last record. + if (IsTblPtr(pCol->m_Type, ixTbl)) + ulCount++; + if (ulRawColVal > ulCount) + { + VEContext veCtxt; + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = 0; + veCtxt.uOffset = 0; + REPORT_ERROR3(VLDTR_E_RID_OUTOFRANGE, ixTbl, ixCol, rid); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else if (IsCodedTokenType(pCol->m_Type)) + { + // Verify that the Coded token and rid are valid. + pCdTkn = &g_CodedTokens[pCol->m_Type - iCodedToken]; + ix = ulRawColVal & ~(-1 << CMiniMdRW::m_cb[pCdTkn->m_cTokens]); + if (ix >= pCdTkn->m_cTokens) + { + VEContext veCtxt; + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = 0; + veCtxt.uOffset = 0; + REPORT_ERROR3(VLDTR_E_CDTKN_OUTOFRANGE, ixTbl, ixCol, rid); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + ulCount = pMiniMd->GetCountRecs(TypeFromToken(pCdTkn->m_pTokens[ix]) >> 24); + if ( (ulRawColVal >> CMiniMdRW::m_cb[pCdTkn->m_cTokens]) > ulCount) + { + VEContext veCtxt; + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = 0; + veCtxt.uOffset = 0; + REPORT_ERROR3(VLDTR_E_CDRID_OUTOFRANGE, ixTbl, ixCol, rid); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + else if (IsHeapType(pCol->m_Type)) + { + // Verify that the offsets for the Heap type fields are valid offsets + // into the heaps. + switch (pCol->m_Type) + { + case iSTRING: + if (!pMiniMd->m_StringHeap.IsValidIndex(ulRawColVal)) + { + VEContext veCtxt; + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = 0; + veCtxt.uOffset = 0; + REPORT_ERROR3(VLDTR_E_STRING_INVALID, ixTbl, ixCol, rid); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + break; + case iGUID: + if (ulRawColVal == 0) + { // GUID value 0 is valid value, though it's invalid GUID heap index + break; + } + if (!pMiniMd->m_GuidHeap.IsValidIndex(ulRawColVal)) + { + VEContext veCtxt; + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = 0; + veCtxt.uOffset = 0; + REPORT_ERROR3(VLDTR_E_GUID_INVALID, ixTbl, ixCol, rid); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + break; + case iBLOB: + if (! pMiniMd->m_BlobHeap.IsValidIndex(ulRawColVal)) + { + VEContext veCtxt; + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = 0; + veCtxt.uOffset = 0; + REPORT_ERROR3(VLDTR_E_BLOB_INVALID, ixTbl, ixCol, rid); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + break; + default: + _ASSERTE(!"Invalid heap type encountered!"); + } + } + else + { + // Not much checking that can be done on the fixed type in a generic sense. + _ASSERTE (IsFixedType(pCol->m_Type)); + } + hr = hrSave; + } +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateRecord() + +//***************************************************************************** +// This function validates that the given Method signature is consistent as per +// the compression scheme. +//***************************************************************************** +HRESULT RegMeta::ValidateSigCompression( + mdToken tk, // [IN] Token whose signature needs to be validated. + PCCOR_SIGNATURE pbSig, // [IN] Signature. + ULONG cbSig) // [IN] Size in bytes of the signature. +{ + VEContext veCtxt; // Context record. + ULONG ulCurByte = 0; // Current index into the signature. + ULONG ulSize; // Size of uncompressed data at each point. + HRESULT hr = S_OK; // Value returned. + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = tk; + veCtxt.uOffset = 0; + + // Check for NULL signature. + if (!cbSig) + { + REPORT_ERROR0(VLDTR_E_SIGNULL); + SetVldtrCode(&hr, VLDTR_S_ERR); + goto ErrExit; + } + + // Walk through the signature. At each point make sure there is enough + // room left in the signature based on the encoding in the current byte. + while (cbSig - ulCurByte) + { + _ASSERTE(ulCurByte <= cbSig); + // Get next chunk of uncompressed data size. + if ((ulSize = CorSigUncompressedDataSize(pbSig)) > (cbSig - ulCurByte)) + { + REPORT_ERROR1(VLDTR_E_SIGNODATA, ulCurByte+1); + SetVldtrCode(&hr, VLDTR_S_ERR); + goto ErrExit; + } + // Go past this chunk. + ulCurByte += ulSize; + CorSigUncompressData(pbSig); + } +ErrExit: + + return hr; +} // RegMeta::ValidateSigCompression() + +//***************************************************************************** +// This function validates one argument given an offset into the signature +// where the argument begins. This function assumes that the signature is well +// formed as far as the compression scheme is concerned. +//***************************************************************************** +//@GENERICS: todo: reject uninstantiated generic types used as types. +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +HRESULT RegMeta::ValidateOneArg( + mdToken tk, // [IN] Token whose signature is being processed. + PCCOR_SIGNATURE &pbSig, // [IN] Pointer to the beginning of argument. + ULONG cbSig, // [IN] Size in bytes of the full signature. + ULONG *pulCurByte, // [IN/OUT] Current offset into the signature.. + ULONG *pulNSentinels, // [IN/OUT] Number of sentinels + BOOL bNoVoidAllowed) // [IN] Flag indicating whether "void" is disallowed for this arg +{ + ULONG ulElementType; // Current element type being processed. + ULONG ulElemSize; // Size of the element type. + mdToken token; // Embedded token. + ULONG ulArgCnt; // Argument count for function pointer. + ULONG ulRank; // Rank of the array. + ULONG ulSizes; // Count of sized dimensions of the array. + ULONG ulLbnds; // Count of lower bounds of the array. + ULONG ulTkSize; // Token size. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + BOOL bRepeat = TRUE; // MODOPT and MODREQ belong to the arg after them + BOOL bByRefForbidden = FALSE;// ByRef is not allowed for fields + + BEGIN_ENTRYPOINT_NOTHROW; + + switch(TypeFromToken(tk)) + { + case mdtFieldDef: + bByRefForbidden = TRUE; + break; + case mdtName: + tk = TokenFromRid(RidFromToken(tk),mdtFieldDef); + // Field type can be a FNPTR with a sig containing ByRefs. + // So we change the token type not to be mdtFieldDef and thus allow ByRefs, + // but the token needs to be restored to its original type for reporting + break; + } + + _ASSERTE (pulCurByte); + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = tk; + veCtxt.uOffset = 0; + + while(bRepeat) + { + bRepeat = FALSE; + // Validate that the argument is not missing. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + hr = VLDTR_E_SIG_MISSARG; + goto ErrExit; + } + + // Get the element type. + *pulCurByte += (ulElemSize = CorSigUncompressedDataSize(pbSig)); + ulElementType = CorSigUncompressData(pbSig); + + // Walk past all the modifier types. + while (ulElementType & ELEMENT_TYPE_MODIFIER) + { + _ASSERTE(*pulCurByte <= cbSig); + if(ulElementType == ELEMENT_TYPE_SENTINEL) + { + if(pulNSentinels) *pulNSentinels+=1; + if(TypeFromToken(tk) == mdtMethodDef) + { + REPORT_ERROR0(VLDTR_E_SIG_SENTINMETHODDEF); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if (cbSig == *pulCurByte) + { + REPORT_ERROR0(VLDTR_E_SIG_LASTSENTINEL); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + goto ErrExit; + } + } + if (cbSig == *pulCurByte) + { + REPORT_ERROR2(VLDTR_E_SIG_MISSELTYPE, ulElementType, *pulCurByte + 1); + SetVldtrCode(&hr, hrSave); + goto ErrExit; + } + *pulCurByte += (ulElemSize = CorSigUncompressedDataSize(pbSig)); + ulElementType = CorSigUncompressData(pbSig); + } + + switch (ulElementType) + { + case ELEMENT_TYPE_VOID: + if(bNoVoidAllowed) + { + IfBreakGo(m_pVEHandler->VEHandler(VLDTR_E_SIG_BADVOID, veCtxt, 0)); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_R4: + case ELEMENT_TYPE_R8: + case ELEMENT_TYPE_STRING: + case ELEMENT_TYPE_OBJECT: + case ELEMENT_TYPE_TYPEDBYREF: + case ELEMENT_TYPE_U: + case ELEMENT_TYPE_I: + break; + case ELEMENT_TYPE_BYREF: //fallthru + if(bByRefForbidden) + { + IfBreakGo(m_pVEHandler->VEHandler(VLDTR_E_SIG_BYREFINFIELD, veCtxt, 0)); + SetVldtrCode(&hr, hrSave); + } + case ELEMENT_TYPE_PTR: + // Validate the referenced type. + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte,pulNSentinels,FALSE)); + if (hr != S_OK) + SetVldtrCode(&hrSave, hr); + break; + case ELEMENT_TYPE_PINNED: + case ELEMENT_TYPE_SZARRAY: + // Validate the referenced type. + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte,pulNSentinels,TRUE)); + if (hr != S_OK) + SetVldtrCode(&hrSave, hr); + break; + case ELEMENT_TYPE_VALUETYPE: //fallthru + case ELEMENT_TYPE_CLASS: + case ELEMENT_TYPE_CMOD_OPT: + case ELEMENT_TYPE_CMOD_REQD: + // See if the token is missing. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSTKN, ulElementType); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // See if the token is a valid token. + ulTkSize = CorSigUncompressedDataSize(pbSig); + token = CorSigUncompressToken(pbSig); + if (!IsValidToken(token)) + { + REPORT_ERROR2(VLDTR_E_SIG_TKNBAD, token, *pulCurByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + *pulCurByte += ulTkSize; + break; + } + *pulCurByte += ulTkSize; + if ((ulElementType == ELEMENT_TYPE_CLASS) || (ulElementType == ELEMENT_TYPE_VALUETYPE)) + { + // Check for long-form encoding + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + LPCSTR szName = ""; // token's Name. + LPCSTR szNameSpace = ""; // token's NameSpace. + + + // Check for TypeDef or TypeRef + // To prevent cycles in metadata, token must not be a TypeSpec. + if ((TypeFromToken(token) != mdtTypeRef) && (TypeFromToken(token) != mdtTypeDef)) + { + REPORT_ERROR2(VLDTR_E_SIG_BADTOKTYPE, token, *pulCurByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + if (TypeFromToken(token) == mdtTypeRef) + { + TypeRefRec *pTokenRec; + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(token), &pTokenRec)); + mdToken tkResScope = pMiniMd->getResolutionScopeOfTypeRef(pTokenRec); + if (RidFromToken(tkResScope) && (TypeFromToken(tkResScope) == mdtAssemblyRef)) + { + AssemblyRefRec * pARRec; + IfFailGo(pMiniMd->GetAssemblyRefRecord(RidFromToken(tkResScope), &pARRec)); + LPCSTR szAssemblyRefName; + IfFailGo(pMiniMd->getNameOfAssemblyRef(pARRec, &szAssemblyRefName)); + if((0 == SString::_stricmp("mscorlib", szAssemblyRefName)) || (0 == SString::_stricmp("System.Runtime", szAssemblyRefName))) + { + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTokenRec, &szNameSpace)); + IfFailGo(pMiniMd->getNameOfTypeRef(pTokenRec, &szName)); + } + } + } + else if (TypeFromToken(token) == mdtTypeDef) + { + TypeDefRec *pTokenRec; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(token), &pTokenRec)); + if(g_fValidatingMscorlib) // otherwise don't even bother checking the name + { + IfFailGo(pMiniMd->getNameOfTypeDef(pTokenRec, &szName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pTokenRec, &szNameSpace)); + } + // while at it, check if token is indeed a class (valuetype) + BOOL bValueType = FALSE; + if(!IsTdInterface(pTokenRec->GetFlags())) + { + mdToken tkExtends = pMiniMd->getExtendsOfTypeDef(pTokenRec); + if(RidFromToken(tkExtends)) + { + LPCSTR szExtName = ""; // parent's Name. + LPCSTR szExtNameSpace = ""; // parent's NameSpace. + if(TypeFromToken(tkExtends)==mdtTypeRef) + { + TypeRefRec *pExtRec; + IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(tkExtends), &pExtRec)); + mdToken tkResScope = pMiniMd->getResolutionScopeOfTypeRef(pExtRec); + if(RidFromToken(tkResScope) && (TypeFromToken(tkResScope)==mdtAssemblyRef)) + { + AssemblyRefRec *pARRec; + IfFailGo(pMiniMd->GetAssemblyRefRecord(RidFromToken(tkResScope), &pARRec)); + LPCSTR szAssemblyRefName; + IfFailGo(pMiniMd->getNameOfAssemblyRef(pARRec, &szAssemblyRefName)); + if((0 == SString::_stricmp("mscorlib", szAssemblyRefName)) || (0 == SString::_stricmp("System.Runtime", szAssemblyRefName))) + { + IfFailGo(pMiniMd->getNamespaceOfTypeRef(pExtRec, &szExtNameSpace)); + IfFailGo(pMiniMd->getNameOfTypeRef(pExtRec, &szExtName)); + } + } + } + else if(TypeFromToken(tkExtends)==mdtTypeDef) + { + if(g_fValidatingMscorlib) // otherwise don't even bother checking the name + { + TypeDefRec *pExtRec; + IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(tkExtends), &pExtRec)); + IfFailGo(pMiniMd->getNameOfTypeDef(pExtRec, &szExtName)); + IfFailGo(pMiniMd->getNamespaceOfTypeDef(pExtRec, &szExtNameSpace)); + } + } + if(0 == strcmp(szExtNameSpace,BASE_NAMESPACE)) + { + if(0==strcmp(szExtName,BASE_ENUM_CLASSNAME)) bValueType = TRUE; + else if(0==strcmp(szExtName,BASE_VTYPE_CLASSNAME)) + { + bValueType = (strcmp(szNameSpace,BASE_NAMESPACE) || + strcmp(szName,BASE_ENUM_CLASSNAME)); + } + } + } + } + if(bValueType != (ulElementType == ELEMENT_TYPE_VALUETYPE)) + { + REPORT_ERROR2(VLDTR_E_SIG_TOKTYPEMISMATCH, token, *pulCurByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + } + if(0 == strcmp(szNameSpace,BASE_NAMESPACE)) + { + for(unsigned jjj = 0; jjj < g_NumSigLongForms; jjj++) + { + if(0 == strcmp(szName,g_SigLongFormName[jjj])) + { + REPORT_ERROR2(VLDTR_E_SIG_LONGFORM, token, *pulCurByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + } + } + } + else // i.e. if(ELEMENT_TYPE_CMOD_OPT || ELEMENT_TYPE_CMOD_REQD) + bRepeat = TRUE; // go on validating, we're not done with this arg + break; + + case ELEMENT_TYPE_FNPTR: + // Validate that calling convention is present. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSFPTR, *pulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // Consume calling convention. + *pulCurByte += CorSigUncompressedDataSize(pbSig); + CorSigUncompressData(pbSig); + + // Validate that argument count is present. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSFPTRARGCNT, *pulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // Consume argument count. + *pulCurByte += CorSigUncompressedDataSize(pbSig); + ulArgCnt = CorSigUncompressData(pbSig); + + // Checking the signature, ByRefs OK + if(bByRefForbidden) + tk = TokenFromRid(RidFromToken(tk),mdtName); + + // Validate and consume return type. + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte,NULL,FALSE)); + if (hr != S_OK) + { + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + + // Validate and consume the arguments. + while(ulArgCnt--) + { + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte,NULL,TRUE)); + if (hr != S_OK) + { + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + } + break; + + case ELEMENT_TYPE_ARRAY: + // Validate and consume the base type. + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte,pulNSentinels,TRUE)); + + // Validate that the rank is present. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSRANK, *pulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // Consume the rank. + *pulCurByte += CorSigUncompressedDataSize(pbSig); + ulRank = CorSigUncompressData(pbSig); + + // Process the sizes. + if (ulRank) + { + // Validate that the count of sized-dimensions is specified. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSNSIZE, *pulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // Consume the count of sized dimensions. + *pulCurByte += CorSigUncompressedDataSize(pbSig); + ulSizes = CorSigUncompressData(pbSig); + + // Loop over the sizes. + while (ulSizes--) + { + // Validate the current size. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSSIZE, *pulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // Consume the current size. + *pulCurByte += CorSigUncompressedDataSize(pbSig); + CorSigUncompressData(pbSig); + } + + // Validate that the count of lower bounds is specified. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSNLBND, *pulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // Consume the count of lower bound. + *pulCurByte += CorSigUncompressedDataSize(pbSig); + ulLbnds = CorSigUncompressData(pbSig); + + // Loop over the lower bounds. + while (ulLbnds--) + { + // Validate the current lower bound. + _ASSERTE(*pulCurByte <= cbSig); + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSLBND, *pulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + // Consume the current size. + *pulCurByte += CorSigUncompressedDataSize(pbSig); + CorSigUncompressData(pbSig); + } + } + break; + + case ELEMENT_TYPE_VAR: + case ELEMENT_TYPE_MVAR: + // Consume index. + *pulCurByte += CorSigUncompressedDataSize(pbSig); + CorSigUncompressData(pbSig); + break; + + case ELEMENT_TYPE_GENERICINST: + { + PCCOR_SIGNATURE pbGenericTypeSig = pbSig; + BOOL fCheckArity = FALSE; + ULONG ulGenericArity = 0; + + // Validate and consume the type constructor + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte, NULL, TRUE)); + + // Extract its arity + { + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + switch(CorSigUncompressElementType(pbGenericTypeSig)) + { + case ELEMENT_TYPE_VALUETYPE: + case ELEMENT_TYPE_CLASS: + { + mdToken tkGenericType = CorSigUncompressToken(pbGenericTypeSig); + if (TypeFromToken(tkGenericType) == mdtTypeDef) + { + HENUMInternal hEnumTyPars; + hr = pMiniMd->FindGenericParamHelper(tkGenericType, &hEnumTyPars); + if (SUCCEEDED(hr)) + { + IfFailGo(HENUMInternal::GetCount(&hEnumTyPars,&ulGenericArity)); + HENUMInternal::ClearEnum(&hEnumTyPars); + fCheckArity = TRUE; + } + ; + } + // for a mdtTypeRef, don't check anything until load time + break; + } + default: + break; + } + + } + + // Consume argument count. + if (cbSig == *pulCurByte) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSARITY, *pulCurByte + 1); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + + *pulCurByte += CorSigUncompressedDataSize(pbSig); + ulArgCnt = CorSigUncompressData(pbSig); + + if (ulArgCnt == 0) + { + REPORT_ERROR1(VLDTR_E_SIG_ARITYZERO,*pulCurByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + if (fCheckArity && ulArgCnt != ulGenericArity) + { + REPORT_ERROR3(VLDTR_E_SIG_ARITYMISMATCH,ulGenericArity,ulArgCnt,*pulCurByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate and consume the arguments. + while(ulArgCnt--) + { + PCCOR_SIGNATURE pbTypeArg = pbSig; + ULONG ulTypeArgByte = *pulCurByte; + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, pulCurByte, NULL, TRUE)); + if (hr != S_OK) + { + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + + // reject byref-like args + switch (CorSigUncompressData(pbTypeArg)) + { + case ELEMENT_TYPE_TYPEDBYREF: + case ELEMENT_TYPE_BYREF: + { + REPORT_ERROR1(VLDTR_E_SIG_BYREFINST, ulTypeArgByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } + default: + break; + } + } + + break; + } + + + case ELEMENT_TYPE_SENTINEL: // this case never works because all modifiers are skipped before switch + if(TypeFromToken(tk) == mdtMethodDef) + { + REPORT_ERROR0(VLDTR_E_SIG_SENTINMETHODDEF); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + break; + default: + REPORT_ERROR2(VLDTR_E_SIG_BADELTYPE, ulElementType, *pulCurByte - ulElemSize); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + break; + } // switch (ulElementType) + } // end while(bRepeat) + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateOneArg() +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +//***************************************************************************** +// This function validates the given Method signature. This function works +// with Method signature for both the MemberRef and MethodDef. +//***************************************************************************** +HRESULT RegMeta::ValidateMethodSig( + mdToken tk, // [IN] Token whose signature needs to be validated. + PCCOR_SIGNATURE pbSig, // [IN] Signature. + ULONG cbSig, // [IN] Size in bytes of the signature. + DWORD dwFlags) // [IN] Method flags. +{ + ULONG ulCurByte = 0; // Current index into the signature. + ULONG ulCallConv; // Calling convention. + ULONG ulArgCount; // Count of arguments. + ULONG ulTyArgCount; // Count of type arguments. + ULONG i; // Looping index. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + ULONG ulNSentinels = 0; + + BEGIN_ENTRYPOINT_NOTHROW; + + _ASSERTE(TypeFromToken(tk) == mdtMethodDef || + TypeFromToken(tk) == mdtMemberRef); + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = tk; + veCtxt.uOffset = 0; + + // Validate the signature is well-formed with respect to the compression + // scheme. If this fails, no further validation needs to be done. + if ((hr = ValidateSigCompression(tk, pbSig, cbSig)) != S_OK) + goto ErrExit; + + // Validate the calling convention. + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulCallConv = CorSigUncompressData(pbSig); + + i = ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK; + if ((i != IMAGE_CEE_CS_CALLCONV_DEFAULT)&&( i != IMAGE_CEE_CS_CALLCONV_VARARG) + || (ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)) + { + REPORT_ERROR1(VLDTR_E_MD_BADCALLINGCONV, ulCallConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + if (TypeFromToken(tk) == mdtMethodDef) // MemberRefs have no flags available + { + // If HASTHIS is set on the calling convention, the method should not be static. + if ((ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) && + IsMdStatic(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_THISSTATIC, ulCallConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // If HASTHIS is not set on the calling convention, the method should be static. + if (!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) && + !IsMdStatic(dwFlags)) + { + REPORT_ERROR1(VLDTR_E_MD_NOTTHISNOTSTATIC, ulCallConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + + // Get the type argument count. + if (ulCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + if (i != IMAGE_CEE_CS_CALLCONV_DEFAULT) + { + REPORT_ERROR1(VLDTR_E_MD_GENERIC_BADCALLCONV, ulCallConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + if (cbSig == ulCurByte) + { + REPORT_ERROR1(VLDTR_E_MD_MISSARITY, ulCurByte+1); + SetVldtrCode(&hr, hrSave); + goto ErrExit; + } + + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulTyArgCount = CorSigUncompressData(pbSig); + + if (ulTyArgCount == 0) + { + REPORT_ERROR1(VLDTR_E_MD_ARITYZERO, ulCurByte); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // If this is a def, check the arity against the number of generic params + if (TypeFromToken(tk) == mdtMethodDef) + { + CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); + ULONG ulGenericParamCount; + HENUMInternal hEnumTyPars; + hr = pMiniMd->FindGenericParamHelper(tk, &hEnumTyPars); + if (SUCCEEDED(hr)) + { + IfFailGo(HENUMInternal::GetCount(&hEnumTyPars,&ulGenericParamCount)); + HENUMInternal::ClearEnum(&hEnumTyPars); + if (ulTyArgCount != ulGenericParamCount) + { + REPORT_ERROR2(VLDTR_E_MD_GPMISMATCH,ulTyArgCount,ulGenericParamCount); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + } + } + } + + + // Is there any sig left for arguments? + _ASSERTE(ulCurByte <= cbSig); + if (cbSig == ulCurByte) + { + REPORT_ERROR1(VLDTR_E_MD_NOARGCNT, ulCurByte+1); + SetVldtrCode(&hr, hrSave); + goto ErrExit; + } + + // Get the argument count. + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulArgCount = CorSigUncompressData(pbSig); + + // Validate the return type and the arguments. +// for (i = 0; i < (ulArgCount + 1); i++) + for(i=1; ulCurByte < cbSig; i++) + { + hr = ValidateOneArg(tk, pbSig, cbSig, &ulCurByte,&ulNSentinels,(i > 1)); + if (hr != S_OK) + { + if(hr == VLDTR_E_SIG_MISSARG) + { + REPORT_ERROR1(VLDTR_E_SIG_MISSARG, i); + } + SetVldtrCode(&hr, hrSave); + hrSave = hr; + break; + } + } + if((ulNSentinels != 0) && (!isCallConv(ulCallConv, IMAGE_CEE_CS_CALLCONV_VARARG ))) + { + REPORT_ERROR0(VLDTR_E_SIG_SENTMUSTVARARG); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + if(ulNSentinels > 1) + { + REPORT_ERROR0(VLDTR_E_SIG_MULTSENTINELS); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateMethodSig() + +//***************************************************************************** +// This function validates the given Field signature. This function works +// with Field signature for both the MemberRef and FieldDef. +//***************************************************************************** +HRESULT RegMeta::ValidateFieldSig( + mdToken tk, // [IN] Token whose signature needs to be validated. + PCCOR_SIGNATURE pbSig, // [IN] Signature. + ULONG cbSig) // [IN] Size in bytes of the signature. +{ + ULONG ulCurByte = 0; // Current index into the signature. + ULONG ulCallConv; // Calling convention. + VEContext veCtxt; // Context record. + HRESULT hr = S_OK; // Value returned. + HRESULT hrSave = S_OK; // Save state. + + BEGIN_ENTRYPOINT_NOTHROW; + + _ASSERTE(TypeFromToken(tk) == mdtFieldDef || + TypeFromToken(tk) == mdtMemberRef); + + memset(&veCtxt, 0, sizeof(VEContext)); + veCtxt.Token = tk; + veCtxt.uOffset = 0; + + // Validate the calling convention. + ulCurByte += CorSigUncompressedDataSize(pbSig); + ulCallConv = CorSigUncompressData(pbSig); + if (!isCallConv(ulCallConv, IMAGE_CEE_CS_CALLCONV_FIELD )) + { + REPORT_ERROR1(VLDTR_E_FD_BADCALLINGCONV, ulCallConv); + SetVldtrCode(&hrSave, VLDTR_S_ERR); + } + + // Validate the field. + IfFailGo(ValidateOneArg(tk, pbSig, cbSig, &ulCurByte,NULL,TRUE)); + SetVldtrCode(&hrSave, hr); + + hr = hrSave; +ErrExit: + ; + END_ENTRYPOINT_NOTHROW; + + return hr; +} // RegMeta::ValidateFieldSig() + +//***************************************************************************** +// This is a utility function to allocate a one-dimensional zero-based safe +// array of variants. +//***************************************************************************** +static HRESULT _AllocSafeVariantArrayVector( // Return status. + VARIANT *rVar, // [IN] Variant array. + int cElem, // [IN] Size of the array. + SAFEARRAY **ppArray) // [OUT] Double pointer to SAFEARRAY. +{ + HRESULT hr = S_OK; + LONG i; + + _ASSERTE(rVar && cElem && ppArray); + + IfNullGo(*ppArray = SafeArrayCreateVector(VT_VARIANT, 0, cElem)); + for (i = 0; i < cElem; i++) + IfFailGo(SafeArrayPutElement(*ppArray, &i, &rVar[i])); +ErrExit: + return hr; +} // _AllocSafeVariantArrayVector() + +//***************************************************************************** +// Helper function for reporting error with no arguments +//***************************************************************************** +HRESULT RegMeta::_ValidateErrorHelper( + HRESULT VECode, + VEContext Context) +{ + HRESULT hr = S_OK; + + // + // MDValidator does not zero out the Context. Fix it here. This fix relies + // on the fact that MDValidator just uses the token and offset field of the + // context. + // + + if (Context.Token != 0) { + Context.flags = VER_ERR_TOKEN; + } + + IfBreakGo(m_pVEHandler->VEHandler(VECode, Context, NULL)); +ErrExit: + + return hr; +} // _ValidateErrorHelper() + +//***************************************************************************** +// Helper function for reporting error with 1 argument +//***************************************************************************** +HRESULT RegMeta::_ValidateErrorHelper( + HRESULT VECode, + VEContext Context, + ULONG ulVal1) +{ + HRESULT hr = S_OK; + SAFEARRAY *psa = 0; // The SAFEARRAY. + VARIANT rVar[1]; // The VARIANT array + + if (Context.Token != 0) { + Context.flags = VER_ERR_TOKEN; + } + + V_VT(&rVar[0]) = VT_UI4; + V_UI4(&rVar[0]) = ulVal1; + IfFailGo(_AllocSafeVariantArrayVector(rVar, 1, &psa)); + IfBreakGo(m_pVEHandler->VEHandler(VECode, Context, psa)); + +ErrExit: + if (psa) + { + HRESULT hrSave = SafeArrayDestroy(psa); + if (FAILED(hrSave)) + hr = hrSave; + } + return hr; +} // _ValidateErrorHelper() + +//***************************************************************************** +// Helper function for reporting error with 2 arguments +//***************************************************************************** +HRESULT RegMeta::_ValidateErrorHelper( + HRESULT VECode, + VEContext Context, + ULONG ulVal1, + ULONG ulVal2) +{ + HRESULT hr = S_OK; + SAFEARRAY *psa = 0; // The SAFEARRAY. + VARIANT rVar[2]; // The VARIANT array + + if (Context.Token != 0) { + Context.flags = VER_ERR_TOKEN; + } + + V_VT(&rVar[0]) = VT_UI4; + V_UI4(&rVar[0]) = ulVal1; + V_VT(&rVar[1]) = VT_UI4; + V_UI4(&rVar[1]) = ulVal2; + + IfFailGo(_AllocSafeVariantArrayVector(rVar, 2, &psa)); + IfBreakGo(m_pVEHandler->VEHandler(VECode, Context, psa)); + +ErrExit: + if (psa) + { + HRESULT hrSave = SafeArrayDestroy(psa); + if (FAILED(hrSave)) + hr = hrSave; + } + return hr; +} // _ValidateErrorHelper() + +//***************************************************************************** +// Helper function for reporting error with 3 arguments +//***************************************************************************** +HRESULT RegMeta::_ValidateErrorHelper( + HRESULT VECode, + VEContext Context, + ULONG ulVal1, + ULONG ulVal2, + ULONG ulVal3) +{ + HRESULT hr = S_OK; + SAFEARRAY *psa = 0; // The SAFEARRAY. + VARIANT rVar[3]; // The VARIANT array + + if (Context.Token != 0) { + Context.flags = VER_ERR_TOKEN; + } + + V_VT(&rVar[0]) = VT_UI4; + V_UI4(&rVar[0]) = ulVal1; + V_VT(&rVar[1]) = VT_UI4; + V_UI4(&rVar[1]) = ulVal2; + V_VT(&rVar[2]) = VT_UI4; + V_UI4(&rVar[2]) = ulVal3; + + IfFailGo(_AllocSafeVariantArrayVector(rVar, 3, &psa)); + IfBreakGo(m_pVEHandler->VEHandler(VECode, Context, psa)); + +ErrExit: + if (psa) + { + HRESULT hrSave = SafeArrayDestroy(psa); + if (FAILED(hrSave)) + hr = hrSave; + } + return hr; +} + +//***************************************************************************** +// Helper function to see if there is a duplicate record for ClassLayout. +//***************************************************************************** +static HRESULT _FindClassLayout( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdTypeDef tkParent, // [IN] the parent that ClassLayout is associated with + RID *pclRid, // [OUT] rid for the ClassLayout. + RID rid) // [IN] rid to be ignored. +{ + HRESULT hr; + ULONG cClassLayoutRecs; + ClassLayoutRec *pRecord; + mdTypeDef tkParTmp; + ULONG i; + + _ASSERTE(pMiniMd && pclRid && rid); + _ASSERTE(TypeFromToken(tkParent) == mdtTypeDef && RidFromToken(tkParent)); + + cClassLayoutRecs = pMiniMd->getCountClassLayouts(); + + for (i = 1; i <= cClassLayoutRecs; i++) + { + // Ignore the rid to be ignored! + if (rid == i) + continue; + + IfFailRet(pMiniMd->GetClassLayoutRecord(i, &pRecord)); + tkParTmp = pMiniMd->getParentOfClassLayout(pRecord); + if (tkParTmp == tkParent) + { + *pclRid = i; + return S_OK; + } + } + return CLDB_E_RECORD_NOTFOUND; +} // _FindClassLayout() + +//***************************************************************************** +// Helper function to see if there is a duplicate for FieldLayout. +//***************************************************************************** +static HRESULT _FindFieldLayout( + CMiniMdRW *pMiniMd, // [IN] the minimd to lookup + mdFieldDef tkParent, // [IN] the parent that FieldLayout is associated with + RID *pflRid, // [OUT] rid for the FieldLayout record. + RID rid) // [IN] rid to be ignored. +{ + HRESULT hr; + ULONG cFieldLayoutRecs; + FieldLayoutRec *pRecord; + mdFieldDef tkField; + ULONG i; + + _ASSERTE(pMiniMd && pflRid && rid); + _ASSERTE(TypeFromToken(tkParent) == mdtFieldDef && RidFromToken(tkParent)); + + cFieldLayoutRecs = pMiniMd->getCountFieldLayouts(); + + for (i = 1; i <= cFieldLayoutRecs; i++) + { + // Ignore the rid to be ignored! + if (rid == i) + continue; + + IfFailRet(pMiniMd->GetFieldLayoutRecord(i, &pRecord)); + tkField = pMiniMd->getFieldOfFieldLayout(pRecord); + if (tkField == tkParent) + { + *pflRid = i; + return S_OK; + } + } + return CLDB_E_RECORD_NOTFOUND; +} // _FindFieldLayout() + +//***************************************************************************** +//***************************************************************************** +HRESULT +MDSigComparer::CompareMethodSignature() +{ + HRESULT hr = S_OK; + + EX_TRY + { + hr = _CompareMethodSignature(); + } + EX_CATCH + { + hr = E_FAIL; + } + EX_END_CATCH(SwallowAllExceptions) + + return hr; +} + +//***************************************************************************** +//***************************************************************************** +HRESULT +MDSigComparer::_CompareMethodSignature() +{ + HRESULT hr; + + // Test equivalency of method signature header + ULONG cArgs; + IfFailRet(_CompareMethodSignatureHeader(cArgs)); + + // Iterate for cArgs + 1 to include the return type + for (ULONG i = 0; i < cArgs + 1; i++) + { + IfFailRet(_CompareExactlyOne()); + } + + return S_OK; +} + +//***************************************************************************** +//***************************************************************************** +HRESULT +MDSigComparer::_CompareExactlyOne() +{ + HRESULT hr; + + CorElementType typ1, typ2; + IfFailRet(m_sig1.GetElemType(&typ1)); + IfFailRet(m_sig2.GetElemType(&typ2)); + + if (typ1 != typ2) + { + return E_FAIL; + } + + CorElementType typ = typ1; + if (!CorIsPrimitiveType((CorElementType)typ)) + { + switch (typ) + { + default: + { + // _ASSERT(!"Illegal or unimplement type in COM+ sig."); + return META_E_BAD_SIGNATURE; + break; + } + case ELEMENT_TYPE_VAR: + case ELEMENT_TYPE_MVAR: + { + IfFailRet(_CompareData(NULL)); // Skip variable number + break; + } + case ELEMENT_TYPE_OBJECT: + case ELEMENT_TYPE_STRING: + case ELEMENT_TYPE_TYPEDBYREF: + { + break; + } + + case ELEMENT_TYPE_BYREF: // fallthru + case ELEMENT_TYPE_PTR: + case ELEMENT_TYPE_PINNED: + case ELEMENT_TYPE_SZARRAY: + { + IfFailRet(_CompareExactlyOne()); // Compare referenced type + break; + } + + case ELEMENT_TYPE_VALUETYPE: // fallthru + case ELEMENT_TYPE_CLASS: + { + mdToken tok1, tok2; + IfFailRet(m_sig1.GetToken(&tok1)); + IfFailRet(m_sig2.GetToken(&tok2)); + IfFailRet(m_comparer.CompareToken(tok1, tok2)); + break; + } + + case ELEMENT_TYPE_FNPTR: + { + IfFailRet(_CompareMethodSignature()); + break; + } + + case ELEMENT_TYPE_ARRAY: + { + IfFailRet(_CompareExactlyOne()); // Compare element type + + ULONG rank; + IfFailRet(_CompareData(&rank)); // Compare & get rank + + if (rank) + { + ULONG nsizes; + IfFailRet(_CompareData(&nsizes)); // Compare & get # of sizes + while (nsizes--) + { + IfFailRet(_CompareData(NULL)); // Compare size + } + + ULONG nlbounds; + IfFailRet(_CompareData(&nlbounds)); // Compare & get # of lower bounds + while (nlbounds--) + { + IfFailRet(_CompareData(NULL)); // Compare lower bounds + } + } + + break; + } + + case ELEMENT_TYPE_SENTINEL: + { + // Should be unreachable since GetElem strips it + break; + } + + case ELEMENT_TYPE_INTERNAL: + { + // Shouldn't ever get this since it is internal to the runtime, + // but just in case we know how to compare and skip these. + PVOID val1 = *((PVOID *)m_sig1.m_ptr); + PVOID val2 = *((PVOID *)m_sig2.m_ptr); + + if (val1 != val2) + { + return E_FAIL; + } + + m_sig1.SkipBytes(sizeof(void*)); + m_sig2.SkipBytes(sizeof(void*)); + break; + } + + case ELEMENT_TYPE_GENERICINST: + { + IfFailRet(_CompareExactlyOne()); // Compare generic type + ULONG argCnt; + IfFailRet(_CompareData(&argCnt)); // Compare & get number of parameters + _ASSERTE(argCnt > 0); + while (argCnt--) + { + IfFailRet(_CompareExactlyOne()); // Compare the parameters + } + break; + } + } + } + + return S_OK; +} + +//***************************************************************************** +//***************************************************************************** +HRESULT +MDSigComparer::_CompareData( + ULONG *pulData) +{ + ULONG cbCompressedData1, cbCompressedData2; + ULONG ulData1, ulData2; + + cbCompressedData1 = CorSigUncompressData(m_sig1.m_ptr, &ulData1); + cbCompressedData2 = CorSigUncompressData(m_sig2.m_ptr, &ulData2); + + if ((cbCompressedData1 == ((ULONG)(-1))) || + (cbCompressedData2 == ((ULONG)(-1))) || + (cbCompressedData1 != cbCompressedData2) || + (ulData1 != ulData2)) + { + return E_FAIL; + } + + m_sig1.SkipBytes(cbCompressedData1); + m_sig2.SkipBytes(cbCompressedData2); + + // Out data + if (pulData) + *pulData = ulData1; + + return S_OK; +} + +//***************************************************************************** +//***************************************************************************** +HRESULT +MDSigComparer::_CompareMethodSignatureHeader( + ULONG &cArgs) +{ + HRESULT hr; + + // Get calling convention information, but only use it to get type param information. + ULONG uCallConv1, uCallConv2; + IfFailRet(m_sig1.GetData(&uCallConv1)); + IfFailRet(m_sig2.GetData(&uCallConv2)); + + // Check type parameter information + ULONG uTypeParamCount1 = 0; + ULONG uTypeParamCount2 = 0; + + if (uCallConv1 & IMAGE_CEE_CS_CALLCONV_GENERIC) + IfFailRet(m_sig1.GetData(&uTypeParamCount1)); + + if (uCallConv2 & IMAGE_CEE_CS_CALLCONV_GENERIC) + IfFailRet(m_sig2.GetData(&uTypeParamCount2)); + + if (uTypeParamCount1 != uTypeParamCount2) + { + return E_FAIL; + } + + // Get arg count + ULONG cArgs1, cArgs2; + IfFailRet(m_sig1.GetData(&cArgs1)); + IfFailRet(m_sig2.GetData(&cArgs2)); + + if (cArgs1 != cArgs2) + { + return E_FAIL; + } + + // Out parameter + cArgs = cArgs1; + + return S_OK; +} + +//***************************************************************************** +//***************************************************************************** + +#ifdef FEATURE_FUSION +HRESULT +UnifiedAssemblySigComparer::_CreateIAssemblyNameFromAssemblyRef( + mdToken tkAsmRef, + IAssemblyName **ppAsmName) +{ + HRESULT hr; + + void const * pvPublicKey; + ULONG cbPublicKey; + ULONG cchName; + ASSEMBLYMETADATA amd; + void const * pvHashValue; + ULONG cbHashValue; + DWORD dwFlags; + + ZeroMemory(&amd, sizeof(amd)); + + IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef, + NULL, + NULL, + NULL, + 0, + &cchName, + &amd, + NULL, + NULL, + NULL)); + + StackSString ssName; + StackSString ssLocale; + amd.szLocale = ssLocale.OpenUnicodeBuffer(amd.cbLocale); + + IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef, + &pvPublicKey, + &cbPublicKey, + ssName.OpenUnicodeBuffer(cchName), + cchName, + &cchName, + &amd, + &pvHashValue, + &cbHashValue, + &dwFlags)); + + ssName.CloseBuffer(); + ssLocale.CloseBuffer(); + + IAssemblyName *pAsmName = NULL; + + IfFailRet(CreateAssemblyNameObject(&pAsmName, + ssName.GetUnicode(), + CANOF_SET_DEFAULT_VALUES, + NULL)); + + // Set the public key token + IfFailRet(pAsmName->SetProperty(ASM_NAME_PUBLIC_KEY_TOKEN, + (LPVOID)pvPublicKey, + cbPublicKey)); + + // Set the culture + if (amd.cbLocale == 0 || amd.szLocale == NULL) + { + IfFailRet(pAsmName->SetProperty(ASM_NAME_CULTURE, + W("Neutral"), + sizeof(W("Neutral")))); + } + else + { + IfFailRet(pAsmName->SetProperty(ASM_NAME_CULTURE, + amd.szLocale, + amd.cbLocale)); + } + + // Set the major version + IfFailRet(pAsmName->SetProperty(ASM_NAME_MAJOR_VERSION, + &amd.usMajorVersion, + sizeof(amd.usMajorVersion))); + + // Set the minor version + IfFailRet(pAsmName->SetProperty(ASM_NAME_MINOR_VERSION, + &amd.usMinorVersion, + sizeof(amd.usMinorVersion))); + + // Set the build number + IfFailRet(pAsmName->SetProperty(ASM_NAME_BUILD_NUMBER, + &amd.usBuildNumber, + sizeof(amd.usBuildNumber))); + + // Set the revision number + IfFailRet(pAsmName->SetProperty(ASM_NAME_REVISION_NUMBER, + &amd.usRevisionNumber, + sizeof(amd.usRevisionNumber))); + + *ppAsmName = pAsmName; + + return S_OK; +} + +//***************************************************************************** +// Define holder to release IAssemblyName on exception. +//***************************************************************************** +void UnifiedAssemblySigComparer_IAssemblyNameRelease(IAssemblyName *value) +{ + if (value != NULL) + { + value->Release(); + } +} + +typedef Holder<IAssemblyName*, + DoNothing<IAssemblyName*>, + &UnifiedAssemblySigComparer_IAssemblyNameRelease, + NULL> UnifiedAssemblySigComparer_IAssemblyNameHolder; + +#endif // FEATURE_FUSION + +#ifndef FEATURE_FUSION +HRESULT UnifiedAssemblySigComparer::_CompareAssemblies(mdToken tkAsmRef1,mdToken tkAsmRef2, BOOL* pfEquivalent) +{ + + HRESULT hr; + void const * pvPublicKey1; + ULONG cbPublicKey1; + ULONG cchName1; + ASSEMBLYMETADATA amd1; + void const * pvHashValue; + ULONG cbHashValue; + DWORD dwFlags1; + + void const * pvPublicKey2; + ULONG cbPublicKey2; + ULONG cchName2; + ASSEMBLYMETADATA amd2; + DWORD dwFlags2; + + + ZeroMemory(&amd1, sizeof(amd1)); + ZeroMemory(&amd2, sizeof(amd2)); + + IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef1, + NULL, + NULL, + NULL, + 0, + &cchName1, + &amd1, + NULL, + NULL, + NULL)); + + StackSString ssName1; + StackSString ssLocale1; + amd1.szLocale = ssLocale1.OpenUnicodeBuffer(amd1.cbLocale); + + IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef1, + &pvPublicKey1, + &cbPublicKey1, + ssName1.OpenUnicodeBuffer(cchName1), + cchName1, + &cchName1, + &amd1, + &pvHashValue, + &cbHashValue, + &dwFlags1)); + + ssName1.CloseBuffer(); + ssLocale1.CloseBuffer(); + + IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef2, + NULL, + NULL, + NULL, + 0, + &cchName2, + &amd2, + NULL, + NULL, + NULL)); + + StackSString ssName2; + StackSString ssLocale2; + amd2.szLocale = ssLocale2.OpenUnicodeBuffer(amd2.cbLocale); + + IfFailRet(m_pRegMeta->GetAssemblyRefProps(tkAsmRef2, + &pvPublicKey2, + &cbPublicKey2, + ssName2.OpenUnicodeBuffer(cchName2), + cchName2, + &cchName2, + &amd2, + &pvHashValue, + &cbHashValue, + &dwFlags2)); + + ssName2.CloseBuffer(); + ssLocale2.CloseBuffer(); + + StackSString sMscorlib(W("mscorlib")); + + + if(ssName1.CompareCaseInsensitive(sMscorlib)==0 && + ssName2.CompareCaseInsensitive(sMscorlib)==0 ) + { + *pfEquivalent=TRUE; + return S_OK; + } + + *pfEquivalent=FALSE; + + if (ssName1.CompareCaseInsensitive(ssName2)!=0) + return S_OK; + if (ssLocale1.CompareCaseInsensitive(ssLocale2)!=0) + return S_OK; + if(cbPublicKey1!=cbPublicKey2) + return S_OK; + if(memcmp(pvPublicKey1,pvPublicKey2,cbPublicKey1)!=0) + return S_OK; + if(dwFlags1!=dwFlags2) + return S_OK; + if(amd1.usMajorVersion!=amd2.usMajorVersion) + return S_OK; + if(amd1.usMinorVersion!=amd2.usMinorVersion) + return S_OK; + if(amd1.usBuildNumber!=amd2.usBuildNumber) + return S_OK; + if(amd1.usRevisionNumber!=amd2.usRevisionNumber) + return S_OK; + + *pfEquivalent=TRUE; + return S_OK; + +}; +#endif // FEATURE_FUSION + +//***************************************************************************** +//***************************************************************************** +HRESULT +UnifiedAssemblySigComparer::_CreateTypeNameFromTypeRef( + mdToken tkTypeRef, + SString &ssName, + mdToken &tkParent) +{ + HRESULT hr; + + // Get the parent token as well as the name, and return. + ULONG cchTypeRef; + IfFailRet(m_pRegMeta->GetTypeRefProps(tkTypeRef, NULL, NULL, 0, &cchTypeRef)); + IfFailRet(m_pRegMeta->GetTypeRefProps(tkTypeRef, &tkParent, ssName.OpenUnicodeBuffer(cchTypeRef), cchTypeRef, NULL)); + ssName.CloseBuffer(); + + return S_OK; +} + +//***************************************************************************** +//***************************************************************************** +HRESULT +UnifiedAssemblySigComparer::_CreateFullyQualifiedTypeNameFromTypeRef( + mdToken tkTypeRef, + SString &ssFullName, + mdToken &tkParent) +{ + HRESULT hr; + + StackSString ssBuf; + StackSString ssName; + mdToken tok = tkTypeRef; + BOOL fFirstLoop = TRUE; + + // Loop stops at first non-typeref parent token. + do + { + // Get the name for this token, as well as the parent token value. + IfFailRet(_CreateTypeNameFromTypeRef(tok, ssName, tok)); + + // If this is the first time through the loop, just assign values. + if (fFirstLoop) + { + ssFullName = ssName; + fFirstLoop = FALSE; + } + // If this isn't the first time through, make nested type name + else + { + ns::MakeNestedTypeName(ssBuf, ssName, ssFullName); + ssFullName = ssBuf; + } + } while (TypeFromToken(tok) == mdtTypeRef); + + // Assign non-typeref token parent + tkParent = tok; + + return S_OK; +} + + + +//***************************************************************************** +//***************************************************************************** +HRESULT +UnifiedAssemblySigComparer::CompareToken( + const mdToken &tok1, + const mdToken &tok2) +{ + HRESULT hr; + + // Check binary equality + if (tok1 == tok2) + { + return S_OK; + } + + // Currently only want to do extra checking on TypeRefs + if (TypeFromToken(tok1) != mdtTypeRef || TypeFromToken(tok2) != mdtTypeRef) + { + return E_FAIL; + } + + // Get the fully qualified type names as well as the non-typeref parents. + mdToken tkParent1, tkParent2; + StackSString ssName1, ssName2; + + IfFailRet(_CreateFullyQualifiedTypeNameFromTypeRef(tok1, ssName1, tkParent1)); + IfFailRet(_CreateFullyQualifiedTypeNameFromTypeRef(tok2, ssName2, tkParent2)); + + // Currently only want to do extra checking if the parent tokens are AssemblyRefs + if (TypeFromToken(tkParent1) != mdtAssemblyRef || TypeFromToken(tkParent2) != mdtAssemblyRef) + { + return E_FAIL; + } + + // If the type names are not equal, no need to check the assembly refs for unification since + // we know the types couldn't possibly match. + if (!ssName1.Equals(ssName2)) + { + return E_FAIL; + } + BOOL fEquivalent; + +#ifdef FEATURE_FUSION //move into _CompareAssemblies + IAssemblyName *pAsmName1 = NULL; + IfFailRet(_CreateIAssemblyNameFromAssemblyRef(tkParent1, &pAsmName1)); + UnifiedAssemblySigComparer_IAssemblyNameHolder anh1(pAsmName1); + + IAssemblyName *pAsmName2 = NULL; + IfFailRet(_CreateIAssemblyNameFromAssemblyRef(tkParent2, &pAsmName2)); + UnifiedAssemblySigComparer_IAssemblyNameHolder anh2(pAsmName2); + + DWORD cchDisplayName = 0; + + StackSString ssDisplayName1; + pAsmName1->GetDisplayName(NULL, &cchDisplayName, NULL); + IfFailRet(pAsmName1->GetDisplayName(ssDisplayName1.OpenUnicodeBuffer(cchDisplayName), &cchDisplayName, NULL)); + ssDisplayName1.CloseBuffer(); + + StackSString ssDisplayName2; + pAsmName2->GetDisplayName(NULL, &cchDisplayName, NULL); + IfFailRet(pAsmName2->GetDisplayName(ssDisplayName2.OpenUnicodeBuffer(cchDisplayName), &cchDisplayName, NULL)); + ssDisplayName2.CloseBuffer(); + + AssemblyComparisonResult res; + IfFailRet(CompareAssemblyIdentity(ssDisplayName1.GetUnicode(), + TRUE, + ssDisplayName2.GetUnicode(), + TRUE, + &fEquivalent, + &res)); +#else + // no redirects supported + IfFailRet(_CompareAssemblies(tkParent1,tkParent2,&fEquivalent)); +#endif + + if (!fEquivalent) + { + return E_FAIL; + } + + return S_OK; +} + + +//***************************************************************************** +// Helper function to validate a locale. +//***************************************************************************** +static const char* const g_szValidLocale_V1[] = { +"ar","ar-SA","ar-IQ","ar-EG","ar-LY","ar-DZ","ar-MA","ar-TN","ar-OM","ar-YE","ar-SY","ar-JO","ar-LB","ar-KW","ar-AE","ar-BH","ar-QA", +"bg","bg-BG", +"ca","ca-ES", +"zh-CHS","zh-TW","zh-CN","zh-HK","zh-SG","zh-MO","zh-CHT", +"cs","cs-CZ", +"da","da-DK", +"de","de-DE","de-CH","de-AT","de-LU","de-LI", +"el","el-GR", +"en","en-US","en-GB","en-AU","en-CA","en-NZ","en-IE","en-ZA","en-JM","en-CB","en-BZ","en-TT","en-ZW","en-PH", +"es","es-ES-Ts","es-MX","es-ES","es-GT","es-CR","es-PA","es-DO","es-VE","es-CO","es-PE","es-AR","es-EC","es-CL", +"es-UY","es-PY","es-BO","es-SV","es-HN","es-NI","es-PR", +"fi","fi-FI", +"fr","fr-FR","fr-BE","fr-CA","fr-CH","fr-LU","fr-MC", +"he","he-IL", +"hu","hu-HU", +"is","is-IS", +"it","it-IT","it-CH", +"ja","ja-JP", +"ko","ko-KR", +"nl","nl-NL","nl-BE", +"no", +"nb-NO", +"nn-NO", +"pl","pl-PL", +"pt","pt-BR","pt-PT", +"ro","ro-RO", +"ru","ru-RU", +"hr","hr-HR", +"sk","sk-SK", +"sq","sq-AL", +"sv","sv-SE","sv-FI", +"th","th-TH", +"tr","tr-TR", +"ur","ur-PK", +"id","id-ID", +"uk","uk-UA", +"be","be-BY", +"sl","sl-SI", +"et","et-EE", +"lv","lv-LV", +"lt","lt-LT", +"fa","fa-IR", +"vi","vi-VN", +"hy","hy-AM", +"az", +"eu","eu-ES", +"mk","mk-MK", +"af","af-ZA", +"ka","ka-GE", +"fo","fo-FO", +"hi","hi-IN", +"ms","ms-MY","ms-BN", +"kk","kk-KZ", +"ky","ky-KZ", +"sw","sw-KE", +"uz", +"tt","tt-RU", +"pa","pa-IN", +"gu","gu-IN", +"ta","ta-IN", +"te","te-IN", +"kn","kn-IN", +"mr","mr-IN", +"sa","sa-IN", +"mn","mn-MN", +"gl","gl-ES", +"kok","kok-IN", +"syr","syr-SY", +"div" +}; + +static const char* const g_szValidLocale_V2[] = { + "bn", "bn-IN", + "bs-Latn-BA", "bs-Cyrl-BA", + "hr-BA", + "fil", "fil-PH", + "fy", "fy-NL", + "iu-Latn-CA", + "ga", "ga-IE", + "ky-KG", + "lb", "lb-LU", + "ml", "ml-IN", + "mt", "mt-MT", + "mi", "mi-NZ", + "arn", "arn-CL", + "moh", "moh-CA", + "ne", "ne-NP", + "ps", "ps-AF", + "quz", "quz-BO", "quz-EC", "quz-PE", + "rm", "rm-CH", + "smn", "smn-FI", + "smj" , "smj-SE", "smj-NO", + "se", "se-NO", "se-SE", "se-FI", + "sms", "sms-FI", + "sma", "sma-NO", "sma-SE", + "sr-Latn-BA", "sr-Cyrl-BA", + "nso", "nso-ZA" +}; + +// Pre-vista specific cultures (renamed on Vista) +static const char* const g_szValidLocale_PreVista[] = { + "div-MV", + "sr-SP-Latn", "sr-SP-Cyrl", + "az-AZ-Latn", "az-AZ-Cyrl", + "uz-UZ-Latn", "uz-UZ-Cyrl", +}; + +// Vista only specific cultures (renamed and freshly introduced) +static const char * const g_szValidLocale_Vista[] = { + "dv-MV", + "sr-Latn-CS", "sr-Cyrl-CS", + "az-Latn-AZ", "az-Cyrl-AZ", + "uz-Latn-UZ", "uz-Cyrl-UZ", + "zh-Hant", "zh-Hans", + "gsw", "gsw-FR", + "am", "am-ET", + "as", "as-IN", + "ba", "ba-RU", + "br", "br-FR", + "en-IN", + "kl", "kl-GL", + "iu-Cans-CA", + "km", "km-KH", + "lo", "lo-LA", + "dsb", "dsb-DE", + "mn-Mong-CN", + "oc", "oc-FR", + "or", "or-IN" +}; + +static BOOL FindInArray(LPCUTF8 szLocale, const char * const *cultureArr, const int nCultures) +{ + for (int i = 0; i < nCultures; i++) + { + if(!SString::_stricmp(szLocale, cultureArr[i])) + return TRUE; + } + return FALSE; +} + +#define LENGTH_OF(x) (sizeof(x) / sizeof(x[0])) + +// For Everett assemblies, only the preVista cultures are valid even if running on Vista. +static BOOL _IsValidLocale(LPCUTF8 szLocale, + BOOL fIsV2Assembly) +{ + if (szLocale && *szLocale) + { + // Locales valid for Everett and Whidbey + if (FindInArray(szLocale, g_szValidLocale_V1, LENGTH_OF(g_szValidLocale_V1))) + return TRUE; + + // Locales valid for Whidbey assemblies only + if (fIsV2Assembly && + FindInArray(szLocale, g_szValidLocale_V2, LENGTH_OF(g_szValidLocale_V2))) + return TRUE; + + // Finally search OS specific cultures + if (fIsV2Assembly) + return FindInArray(szLocale, g_szValidLocale_Vista, LENGTH_OF(g_szValidLocale_Vista)); + else + return FindInArray(szLocale, g_szValidLocale_PreVista, LENGTH_OF(g_szValidLocale_PreVista)); + } + + return TRUE; +} + +#endif //FEATURE_METADATA_VALIDATOR |