#include <mono/metadata/metadata-internals.h>
#include <mono/metadata/class-internals.h>
#include <mono/metadata/tokentype.h>
+#include <mono/metadata/security-manager.h>
+#include <mono/metadata/security-core-clr.h>
+#include <mono/metadata/cil-coff.h>
+#include <mono/utils/strenc.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
TODO add section relocation support
TODO verify the relocation table, since we really don't use, no need so far.
TODO do full PECOFF resources verification
- TODO verify in the CLI header entry point and resources
- FIXME has_cattr coded index / 8 -> Permission table? -- it's decl security
+ TODO verify in the CLI header entry point and resources
+ TODO implement null token typeref validation
+ TODO verify table wide invariants for typedef (sorting and uniqueness)
+ TODO implement proper authenticode data directory validation
+ TODO verify properties that require multiple tables to be valid
FIXME use subtraction based bounds checking to avoid overflows
+ FIXME get rid of metadata_streams and other fields from VerifyContext
*/
+#ifdef MONO_VERIFIER_DEBUG
+#define VERIFIER_DEBUG(code) do { code; } while (0)
+#else
+#define VERIFIER_DEBUG(code)
+#endif
+
#define INVALID_OFFSET ((guint32)-1)
+#define INVALID_ADDRESS 0xffffffff
+
+enum {
+ STAGE_PE,
+ STAGE_CLI,
+ STAGE_TABLES
+};
enum {
IMPORT_TABLE_IDX = 1,
RESOURCE_TABLE_IDX = 2,
+ CERTIFICATE_TABLE_IDX = 4,
RELOCATION_TABLE_IDX = 5,
IAT_IDX = 12,
CLI_HEADER_IDX = 14,
TILDE_STREAM
};
-enum {
- COL_UINT8,
- COL_UINT16,
- COL_UINT32,
-
- COL_STRING,
- COL_GUID,
- COL_BLOB,
-
- COL_TYPE_DEF_OR_REF, /*includes typespec*/
- COL_HAS_CONSTANT,
- COL_HAS_CATTR,
- COL_HAS_FIELD_MARSHAL,
- COL_HAS_DECL_SECURITY,
- COL_MEMBER_REF_PARENT,
- COL_HAS_SEMANTICS,
- COL_METHOD_DEF_OR_REF,
- COL_MEMBER_FORWARDED,
- COL_IMPLEMENTATION,
- COL_CATTR_TYPE,
- COL_RES_SCOPE,
- COL_TYPE_OR_METHOD_DEF,
-
- COL_TYPE_DEF,
- COL_METHOD_DEF,
- COL_FIELD,
- COL_PARAM,
- COL_PROPERTY,
- COL_EVENT,
- COL_GENERIC_PARAM,
- COL_ASSEMBLY_REF,
- COL_MODULE_REF,
-
- COL_LAST
-};
-const static unsigned char table_desc [] = {
- /* 0x00 Module */
- COL_UINT16, /*Generation*/
- COL_STRING, /*Name*/
- COL_GUID, /*Mvid*/
- COL_GUID, /*EncId*/
- COL_GUID, /*EncBaseId*/
- COL_LAST,
-
- /* 0x01 TypeRef */
- COL_RES_SCOPE, /*ResolutionScope*/
- COL_STRING, /*TypeName*/
- COL_STRING, /*TypeNameSpace*/
- COL_LAST,
-
- /* 0x02 TypeDef */
- COL_UINT32, /*Flags*/
- COL_STRING, /*TypeName*/
- COL_STRING, /*TypeNameSpace*/
- COL_TYPE_DEF_OR_REF, /*Extends*/
- COL_FIELD, /*FieldList*/
- COL_METHOD_DEF, /*FieldList*/
- COL_LAST,
-
- /* 0x03 non documented extension */
- COL_LAST,
-
- /* 0x04 Field */
- COL_UINT16, /*FieldAttributes*/
- COL_STRING, /*Name*/
- COL_BLOB, /*Signature*/
- COL_LAST,
-
- /* 0x05 non documented extension */
- COL_LAST,
-
- /* 0x06 MethodDef */
- COL_UINT32, /*RVA*/
- COL_UINT16, /*ImplFlags*/
- COL_UINT16, /*Flags*/
- COL_STRING, /*Name*/
- COL_BLOB, /*Signature*/
- COL_PARAM, /*ParamList*/
- COL_LAST,
-
- /* 0x07 non documented extension */
- COL_LAST,
-
- /* 0x08 Param */
- COL_UINT16, /*Flags*/
- COL_UINT16, /*Sequence*/
- COL_STRING, /*Name*/
- COL_LAST,
-
- /* 0x09 InterfaceImpl */
- COL_TYPE_DEF, /*Class*/
- COL_TYPE_DEF_OR_REF, /*Interface*/
- COL_LAST,
-
- /* 0x0A MemberRef */
- COL_MEMBER_REF_PARENT, /*Class*/
- COL_STRING, /*Name*/
- COL_BLOB, /*Signature*/
- COL_LAST,
-
- /* 0x0B Constant */
- COL_HAS_CONSTANT, /*Parent*/
- COL_BLOB, /*Value*/
- COL_LAST,
-
- /* 0x0C CustomAttribute */
- COL_HAS_CATTR, /*Parent*/
- COL_CATTR_TYPE, /*Type*/
- COL_BLOB, /*Value*/
- COL_LAST,
-
- /* 0x0D FieldMarshal */
- COL_HAS_FIELD_MARSHAL, /*Parent*/
- COL_BLOB, /*NativeType*/
- COL_LAST,
-
- /* 0x0E DeclSecurity */
- COL_UINT16, /*Action*/
- COL_HAS_DECL_SECURITY, /*Parent*/
- COL_BLOB, /*PermissionSet*/
- COL_LAST,
-
- /* 0x0F ClassLayout */
- COL_UINT16, /*Packingsize*/
- COL_UINT32, /*ClassSize*/
- COL_TYPE_DEF, /*Parent*/
- COL_LAST,
-
- /* 0x10 FieldLayout */
- COL_UINT32, /*Offset*/
- COL_FIELD, /*Field*/
- COL_LAST,
-
- /* 0x11 StandAloneSig */
- COL_BLOB, /*Signature*/
- COL_LAST,
-
- /* 0x12 EventMap */
- COL_TYPE_DEF, /*Parent*/
- COL_EVENT, /*EventList*/
- COL_LAST,
-
- /* 0x13 non documented extension */
- COL_LAST,
-
- /* 0x14 Event */
- COL_UINT16, /*EventFlags*/
- COL_STRING, /*Name*/
- COL_TYPE_DEF_OR_REF, /*EventType*/
- COL_LAST,
-
- /* 0x15 PropertyMap */
- COL_TYPE_DEF, /*Parent*/
- COL_PROPERTY, /*PropertyList*/
- COL_LAST,
-
- /* 0x16 non documented extension */
- COL_LAST,
-
- /* 0x17 Property */
- COL_UINT16, /*Flags*/
- COL_STRING, /*Name*/
- COL_BLOB, /*Signature*/
- COL_LAST,
-
- /* 0x18 MethodSemantics */
- COL_UINT16, /*Semantics*/
- COL_METHOD_DEF, /*Method*/
- COL_HAS_SEMANTICS, /*Association*/
- COL_LAST,
-
- /* 0x19 MethodImpl */
- COL_TYPE_DEF, /*Class*/
- COL_METHOD_DEF_OR_REF, /*MethodBody*/
- COL_METHOD_DEF_OR_REF, /*MethodDeclaration*/
- COL_LAST,
-
- /* 0x1A ModuleRef */
- COL_STRING, /*Name*/
- COL_LAST,
-
- /* 0x1B TypeSpec */
- COL_BLOB, /*Signature*/
- COL_LAST,
-
- /* 0x1C ImplMap */
- COL_UINT16, /*MappingFlags*/
- COL_MEMBER_FORWARDED, /*MappingFlags*/
- COL_STRING, /*ImportName*/
- COL_MODULE_REF, /*ImportScope*/
- COL_LAST,
-
- /* 0x1D FieldRVA */
- COL_UINT32, /*RVA*/
- COL_FIELD, /*Field*/
- COL_LAST,
-
- /* 0x1E Unused */
- COL_LAST,
-
- /* 0x1F Unused */
- COL_LAST,
-
- /* 0x20 Assembly */
- COL_UINT32, /*HashAlgId*/
- COL_UINT16, /*Major*/
- COL_UINT16, /*Minor*/
- COL_UINT16, /*Build*/
- COL_UINT16, /*Revision*/
- COL_UINT32, /*Flags*/
- COL_BLOB, /*PublicKey*/
- COL_STRING, /*Name*/
- COL_STRING, /*Culture*/
- COL_LAST,
-
- /* 0x21 AssemblyProcessor */
- COL_UINT32, /*Processor*/
- COL_LAST,
-
- /* 0x22 AssemblyOS */
- COL_UINT32, /*OSPlatformID*/
- COL_UINT32, /*OSMajorVersion*/
- COL_UINT32, /*OSMinorVersion*/
- COL_LAST,
-
- /* 0x23 AssemblyRef */
- COL_UINT16, /*Major*/
- COL_UINT16, /*Minor*/
- COL_UINT16, /*Build*/
- COL_UINT16, /*Revision*/
- COL_UINT32, /*Flags*/
- COL_BLOB, /*PublicKeyOrToken*/
- COL_STRING, /*Name*/
- COL_STRING, /*Culture*/
- COL_BLOB, /*HashValue*/
- COL_LAST,
-
- /* 0x24 AssemblyRefProcessor */
- COL_UINT32, /*Processor*/
- COL_ASSEMBLY_REF, /*AssemblyRef*/
- COL_LAST,
-
- /* 0x25 AssemblyRefOS */
- COL_UINT32, /*OSPlatformID*/
- COL_UINT32, /*OSMajorVersion*/
- COL_UINT32, /*OSMinorVersion*/
- COL_ASSEMBLY_REF, /*AssemblyRef*/
- COL_LAST,
-
- /* 0x26 Unused */
- COL_UINT32, /*Flags*/
- COL_STRING, /*Name*/
- COL_BLOB, /*HashValue*/
- COL_LAST,
-
- /* 0x27 ExportedType */
- COL_UINT32, /*Flags*/
- COL_UINT32, /*TypeDefId*/
- COL_STRING, /*TypeName*/
- COL_STRING, /*TypeNamespace*/
- COL_IMPLEMENTATION, /*Implementation*/
- COL_LAST,
-
- /* 0x28 ManifestResource */
- COL_UINT32, /*Offset*/
- COL_UINT32, /*Flags*/
- COL_STRING, /*Name*/
- COL_IMPLEMENTATION, /*Implementation*/
- COL_LAST,
-
- /* 0x29 NestedClass */
- COL_TYPE_DEF, /*NestedClass*/
- COL_TYPE_DEF, /*EnclosingClass*/
- COL_LAST,
-
- /* 0x2A GenericParam */
- COL_UINT16, /*Number*/
- COL_UINT16, /*Flags*/
- COL_TYPE_OR_METHOD_DEF, /*Owner*/
- COL_STRING, /*Name*/
- COL_LAST,
-
- /* 0x2B MethodSpec */
- COL_METHOD_DEF_OR_REF, /*Method*/
- COL_BLOB, /*Instantiation*/
- COL_LAST,
-
- /* 0x2C GenericParamConstraint */
- COL_GENERIC_PARAM, /*Owner*/
- COL_TYPE_DEF_OR_REF, /*Constraint*/
- COL_LAST,
+#define INVALID_TABLE (0xFF)
+/*format: number of bits, number of tables, tables{n. tables} */
+const static unsigned char coded_index_desc[] = {
+#define TYPEDEF_OR_REF_DESC (0)
+ 2, /*bits*/
+ 3, /*tables*/
+ MONO_TABLE_TYPEDEF,
+ MONO_TABLE_TYPEREF,
+ MONO_TABLE_TYPESPEC,
+
+#define HAS_CONSTANT_DESC (TYPEDEF_OR_REF_DESC + 5)
+ 2, /*bits*/
+ 3, /*tables*/
+ MONO_TABLE_FIELD,
+ MONO_TABLE_PARAM,
+ MONO_TABLE_PROPERTY,
+
+#define HAS_CATTR_DESC (HAS_CONSTANT_DESC + 5)
+ 5, /*bits*/
+ 19, /*tables*/
+ MONO_TABLE_METHOD,
+ MONO_TABLE_FIELD,
+ MONO_TABLE_TYPEREF,
+ MONO_TABLE_TYPEDEF,
+ MONO_TABLE_PARAM,
+ MONO_TABLE_INTERFACEIMPL,
+ MONO_TABLE_MEMBERREF,
+ MONO_TABLE_MODULE,
+ MONO_TABLE_DECLSECURITY,
+ MONO_TABLE_PROPERTY,
+ MONO_TABLE_EVENT,
+ MONO_TABLE_STANDALONESIG,
+ MONO_TABLE_MODULEREF,
+ MONO_TABLE_TYPESPEC,
+ MONO_TABLE_ASSEMBLY,
+ MONO_TABLE_ASSEMBLYREF,
+ MONO_TABLE_FILE,
+ MONO_TABLE_EXPORTEDTYPE,
+ MONO_TABLE_MANIFESTRESOURCE,
+
+#define HAS_FIELD_MARSHAL_DESC (HAS_CATTR_DESC + 21)
+ 1, /*bits*/
+ 2, /*tables*/
+ MONO_TABLE_FIELD,
+ MONO_TABLE_PARAM,
+
+#define HAS_DECL_SECURITY_DESC (HAS_FIELD_MARSHAL_DESC + 4)
+ 2, /*bits*/
+ 3, /*tables*/
+ MONO_TABLE_TYPEDEF,
+ MONO_TABLE_METHOD,
+ MONO_TABLE_ASSEMBLY,
+
+#define MEMBERREF_PARENT_DESC (HAS_DECL_SECURITY_DESC + 5)
+ 3, /*bits*/
+ 5, /*tables*/
+ MONO_TABLE_TYPEDEF,
+ MONO_TABLE_TYPEREF,
+ MONO_TABLE_MODULE,
+ MONO_TABLE_METHOD,
+ MONO_TABLE_TYPESPEC,
+
+#define HAS_SEMANTICS_DESC (MEMBERREF_PARENT_DESC + 7)
+ 1, /*bits*/
+ 2, /*tables*/
+ MONO_TABLE_EVENT,
+ MONO_TABLE_PROPERTY,
+
+#define METHODDEF_OR_REF_DESC (HAS_SEMANTICS_DESC + 4)
+ 1, /*bits*/
+ 2, /*tables*/
+ MONO_TABLE_METHOD,
+ MONO_TABLE_MEMBERREF,
+
+#define MEMBER_FORWARDED_DESC (METHODDEF_OR_REF_DESC + 4)
+ 1, /*bits*/
+ 2, /*tables*/
+ MONO_TABLE_FIELD,
+ MONO_TABLE_METHOD,
+
+#define IMPLEMENTATION_DESC (MEMBER_FORWARDED_DESC + 4)
+ 2, /*bits*/
+ 3, /*tables*/
+ MONO_TABLE_FILE,
+ MONO_TABLE_ASSEMBLYREF,
+ MONO_TABLE_EXPORTEDTYPE,
+
+#define CATTR_TYPE_DESC (IMPLEMENTATION_DESC + 5)
+ 3, /*bits*/
+ 5, /*tables*/
+ INVALID_TABLE,
+ INVALID_TABLE,
+ MONO_TABLE_METHOD,
+ MONO_TABLE_MEMBERREF,
+ INVALID_TABLE,
+
+#define RES_SCOPE_DESC (CATTR_TYPE_DESC + 7)
+ 2, /*bits*/
+ 4, /*tables*/
+ MONO_TABLE_MODULE,
+ MONO_TABLE_MODULEREF,
+ MONO_TABLE_ASSEMBLYREF,
+ MONO_TABLE_TYPEREF,
+
+#define TYPE_OR_METHODDEF_DESC (RES_SCOPE_DESC + 6)
+ 1, /*bits*/
+ 2, /*tables*/
+ MONO_TABLE_TYPEDEF,
+ MONO_TABLE_METHOD
};
-
typedef struct {
guint32 rva;
guint32 size;
typedef struct {
guint32 row_count;
guint32 row_size;
+ guint32 offset;
} TableInfo;
typedef struct {
guint32 size;
GSList *errors;
int valid;
- guint32 section_count, tables_offset;
- SectionHeader *sections;
- gboolean wide_strings, wide_guid, wide_blob;
+ gboolean is_corlib;
+ MonoImage *image;
+ gboolean report_error;
+ int stage;
DataDirectory data_directories [16];
+ guint32 section_count;
+ SectionHeader *sections;
+
OffsetAndSize metadata_streams [5]; //offset from begin of the image
- TableInfo tables [MONO_TABLE_NUM];
- guint32 field_sizes [COL_LAST];
} VerifyContext;
#define ADD_VERIFY_INFO(__ctx, __msg, __status, __exception) \
#define ADD_ERROR(__ctx, __msg) \
do { \
- ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \
+ if ((__ctx)->report_error) \
+ ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \
(__ctx)->valid = 0; \
return; \
} while (0)
{
int i;
+ if (rva + size < rva) //overflow
+ return FALSE;
+
+ if (ctx->stage > STAGE_PE) {
+ MonoCLIImageInfo *iinfo = ctx->image->image_info;
+ const int top = iinfo->cli_section_count;
+ MonoSectionTable *tables = iinfo->cli_section_tables;
+ int i;
+
+ for (i = 0; i < top; i++) {
+ guint32 base = tables->st_virtual_address;
+ guint32 end = base + tables->st_raw_data_size;
+
+ if (rva >= base && rva + size <= end)
+ return TRUE;
+
+ /*if ((addr >= tables->st_virtual_address) &&
+ (addr < tables->st_virtual_address + tables->st_raw_data_size)){
+
+ return addr - tables->st_virtual_address + tables->st_raw_data_ptr;
+ }*/
+ tables++;
+ }
+ return FALSE;
+ }
+
if (!ctx->sections)
return FALSE;
{
int i;
+ if (ctx->stage > STAGE_PE)
+ return mono_cli_rva_image_map (ctx->image, rva);
+
if (!ctx->sections)
return FALSE;
if (header_size != 224)
ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header size %d", header_size));
- /*if (read32 (pe_optional_header + 28) != 0x400000)
+ /* LAMESPEC MS plays around this value and ignore it during validation
+ if (read32 (pe_optional_header + 28) != 0x400000)
ADD_ERROR (ctx, g_strdup_printf ("Invalid Image base %x", read32 (pe_optional_header + 28)));*/
if (read32 (pe_optional_header + 32) != 0x2000)
ADD_ERROR (ctx, g_strdup_printf ("Invalid Section Aligmnent %x", read32 (pe_optional_header + 32)));
guint32 rva = read32 (ptr);
guint32 size = read32 (ptr + 4);
+ /*LAMESPEC the authenticode data directory format is different. We don't support CAS, so lets ignore for now.*/
+ if (i == CERTIFICATE_TABLE_IDX) {
+ ptr += 8;
+ continue;
+ }
if ((rva != 0 || size != 0) && !is_valid_data_directory (i))
ADD_ERROR (ctx, g_strdup_printf ("Invalid data directory %d", i));
named_entries = read16 (ptr + 12);
id_entries = read16 (ptr + 14);
- printf ("named %d id_entries %d\n", named_entries, id_entries);
if ((named_entries + id_entries) * 8 + 16 > it.size)
ADD_ERROR (ctx, g_strdup_printf ("Resource section is too small, the number of entries (%d) doesn't fit on it's size %d", named_entries + id_entries, it.size));
+ /* XXX at least one unmanaged resource is added due to a call to AssemblyBuilder::DefineVersionInfoResource ()
if (named_entries || id_entries)
ADD_ERROR (ctx, g_strdup_printf ("The metadata verifier doesn't support full verification of PECOFF resources"));
+ */
}
+/*----------nothing from here on can use data_directory---*/
+
+static DataDirectory
+get_data_dir (VerifyContext *ctx, int idx)
+{
+ MonoCLIImageInfo *iinfo = ctx->image->image_info;
+ MonoPEDirEntry *entry= &iinfo->cli_header.datadir.pe_export_table;
+ DataDirectory res;
+
+ entry += idx;
+ res.rva = entry->rva;
+ res.size = entry->size;
+ res.translated_offset = translate_rva (ctx, res.rva);
+ return res;
+
+}
static void
verify_cli_header (VerifyContext *ctx)
{
- DataDirectory it = ctx->data_directories [CLI_HEADER_IDX];
+ DataDirectory it = get_data_dir (ctx, CLI_HEADER_IDX);
guint32 offset;
const char *ptr;
int i;
if (!bounds_check_virtual_address (ctx, read32 (ptr + 8), read32 (ptr + 12)))
ADD_ERROR (ctx, g_strdup_printf ("Invalid medatata section rva/size pair %x/%x", read32 (ptr + 8), read32 (ptr + 12)));
+
if (!read32 (ptr + 8) || !read32 (ptr + 12))
ADD_ERROR (ctx, g_strdup_printf ("Missing medatata section in the CLI header"));
verify_metadata_header (VerifyContext *ctx)
{
int i;
- DataDirectory it = ctx->data_directories [CLI_HEADER_IDX];
+ DataDirectory it = get_data_dir (ctx, CLI_HEADER_IDX);
guint32 offset;
const char *ptr;
guint32 count;
int i;
+ //printf ("tables_area size %d offset %x %s\n", tables_area.size, tables_area.offset, ctx->image->name);
if (tables_area.size < 24)
ADD_ERROR (ctx, g_strdup_printf ("Table schemata size (%d) too small to for initial decoding (requires 24 bytes)", tables_area.size));
- if (ptr [4] != 2)
+ //printf ("ptr %x %x\n", ptr[4], ptr[5]);
+ if (ptr [4] != 2 && ptr [4] != 1)
ADD_ERROR (ctx, g_strdup_printf ("Invalid table schemata major version %d, expected 2", ptr [4]));
if (ptr [5] != 0)
ADD_ERROR (ctx, g_strdup_printf ("Invalid table schemata minor version %d, expected 0", ptr [5]));
if ((ptr [6] & ~0x7) != 0)
ADD_ERROR (ctx, g_strdup_printf ("Invalid table schemata heap sizes 0x%02x, only bits 0, 1 and 2 can be set", ((unsigned char *) ptr) [6]));
- ctx->wide_strings = ptr [6] & 0x1;
- ctx->wide_guid = ptr [6] & 0x2;
- ctx->wide_blob = ptr [6] & 04;
-
valid_tables = read64 (ptr + 8);
count = 0;
for (i = 0; i < 64; ++i) {
if (tables_area.size < 24 + count * 4)
ADD_ERROR (ctx, g_strdup_printf ("Table schemata size (%d) too small to for decoding row counts (requires %d bytes)", tables_area.size, 24 + count * 4));
-
ptr += 24;
for (i = 0; i < 64; ++i) {
if (valid_tables & ((guint64)1 << i)) {
- ctx->tables [i].row_count = read32 (ptr);
+ guint32 row_count = read32 (ptr);
+ if (row_count > (1 << 25) - 1)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid Table %d row count, mono only supports ", i));
ptr += 4;
}
}
- ctx->tables_offset = offset + 24 + count * 4;
+}
+
+/*----------nothing from here on can use data_directory or metadata_streams ---*/
+
+static guint32
+get_col_offset (VerifyContext *ctx, int table, int column)
+{
+ guint32 bitfield = ctx->image->tables [table].size_bitfield;
+ guint32 offset = 0;
+
+ while (column-- > 0)
+ offset += mono_metadata_table_size (bitfield, column);
+
+ return offset;
}
static guint32
-enc_index_size (guint32 bits, guint32 max)
+get_col_size (VerifyContext *ctx, int table, int column)
+{
+ return mono_metadata_table_size (ctx->image->tables [table].size_bitfield, column);
+}
+
+static OffsetAndSize
+get_metadata_stream (VerifyContext *ctx, MonoStreamHeader *header)
+{
+ OffsetAndSize res;
+ res.offset = header->data - ctx->data;
+ res.size = header->size;
+
+ return res;
+}
+
+static gboolean
+is_valid_string_full (VerifyContext *ctx, guint32 offset, gboolean allow_empty)
+{
+ OffsetAndSize strings = get_metadata_stream (ctx, &ctx->image->heap_strings);
+ glong length;
+ const char *data = ctx->data + strings.offset;
+
+ if (offset >= strings.size)
+ return FALSE;
+ if (data + offset < data) //FIXME, use a generalized and smart unsigned add with overflow check and fix the whole thing
+ return FALSE;
+
+ if (!mono_utf8_validate_and_len_with_bounds (data + offset, strings.size - offset, &length, NULL))
+ return FALSE;
+ return allow_empty || length > 0;
+}
+
+static gboolean
+is_valid_string (VerifyContext *ctx, guint32 offset)
+{
+ return is_valid_string_full (ctx, offset, TRUE);
+}
+
+static gboolean
+is_valid_non_empty_string (VerifyContext *ctx, guint32 offset)
+{
+ return is_valid_string_full (ctx, offset, FALSE);
+}
+
+static gboolean
+is_valid_guid (VerifyContext *ctx, guint32 offset)
+{
+ OffsetAndSize guids = get_metadata_stream (ctx, &ctx->image->heap_guid);
+ return guids.size >= 8 && guids.size - 8 >= offset;
+}
+
+static guint32
+get_coded_index_token (int token_kind, guint32 coded_token)
+{
+ guint32 bits = coded_index_desc [token_kind];
+ return coded_token >> bits;
+}
+
+static guint32
+make_coded_token (int kind, guint32 table, guint32 table_idx)
+{
+ guint32 bits = coded_index_desc [kind++];
+ guint32 tables = coded_index_desc [kind++];
+ guint32 i;
+ for (i = 0; i < tables; ++i) {
+ if (coded_index_desc [kind++] == table)
+ return ((table_idx + 1) << bits) | i;
+ }
+ g_assert_not_reached ();
+ return -1;
+}
+
+static gboolean
+is_valid_coded_index (VerifyContext *ctx, int token_kind, guint32 coded_token)
+{
+ guint32 bits = coded_index_desc [token_kind++];
+ guint32 table_count = coded_index_desc [token_kind++];
+ guint32 table = coded_token & ((1 << bits) - 1);
+ guint32 token = coded_token >> bits;
+
+ if (table >= table_count)
+ return FALSE;
+
+ /*token_kind points to the first table idx*/
+ table = coded_index_desc [token_kind + table];
+
+ if (table == INVALID_TABLE)
+ return FALSE;
+ return token <= ctx->image->tables [table].rows;
+}
+
+typedef struct {
+ guint32 token;
+ guint32 col_size;
+ guint32 col_offset;
+} RowLocator;
+
+static int
+token_locator (const void *a, const void *b)
+{
+ RowLocator *loc = (RowLocator *)a;
+ unsigned const char *row = (unsigned const char *)b;
+ guint32 token = loc->col_size == 2 ? read16 (row + loc->col_offset) : read32 (row + loc->col_offset);
+
+ VERIFIER_DEBUG ( printf ("\tfound token %x\n", token) );
+ return (int)loc->token - (int)token;
+}
+
+static int
+search_sorted_table (VerifyContext *ctx, int table, int column, guint32 coded_token)
+{
+ MonoTableInfo *tinfo = &ctx->image->tables [table];
+ RowLocator locator;
+ const char *res, *base;
+ locator.token = coded_token;
+ locator.col_offset = get_col_offset (ctx, table, column);
+ locator.col_size = get_col_size (ctx, table, column);
+ base = tinfo->base;
+
+ VERIFIER_DEBUG ( printf ("looking token %x table %d col %d rsize %d roff %d\n", coded_token, table, column, locator.col_size, locator.col_offset) );
+ res = bsearch (&locator, base, tinfo->rows, tinfo->row_size, token_locator);
+ if (!res)
+ return -1;
+
+ return (res - base) / tinfo->rows;
+}
+
+/*WARNING: This function doesn't verify if the strings @offset points to a valid string*/
+static const char*
+get_string_ptr (VerifyContext *ctx, guint offset)
+{
+ return ctx->image->heap_strings.data + offset;
+}
+
+/*WARNING: This function doesn't verify if the strings @offset points to a valid string*/
+static int
+string_cmp (VerifyContext *ctx, const char *str, guint offset)
+{
+ if (offset == 0)
+ return strcmp (str, "");
+
+ return strcmp (str, get_string_ptr (ctx, offset));
+}
+
+static gboolean
+typedef_is_system_object (VerifyContext *ctx, guint32 *data)
+{
+ return ctx->is_corlib && !string_cmp (ctx, "System", data [MONO_TYPEDEF_NAME]) && !string_cmp (ctx, "Object", data [MONO_TYPEDEF_NAMESPACE]);
+}
+
+static gboolean
+is_valid_field_signature (VerifyContext *ctx, guint32 offset)
+{
+ OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob);
+ //TODO do proper verification
+ return blob.size >= 2 && blob.size - 2 >= offset;
+}
+
+static gboolean
+is_valid_method_signature (VerifyContext *ctx, guint32 offset)
+{
+ OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob);
+ //TODO do proper verification
+ return blob.size >= 2 && blob.size - 2 >= offset;
+}
+
+static gboolean
+is_valid_method_header (VerifyContext *ctx, guint32 rva)
+{
+ //TODO do proper method header validation
+ return mono_cli_rva_image_map (ctx->image, rva) != INVALID_ADDRESS;
+}
+
+static void
+verify_module_table (VerifyContext *ctx)
{
- guint32 size = 1 << (16 - bits);
- return max >= size ? 4 : 2;
+ MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_MODULE];
+ guint32 data [MONO_MODULE_SIZE];
+
+ if (table->rows != 1)
+ ADD_ERROR (ctx, g_strdup_printf ("Module table must have exactly one row, but have %d", table->rows));
+
+ mono_metadata_decode_row (table, 0, data, MONO_MODULE_SIZE);
+
+ if (!is_valid_non_empty_string (ctx, data [MONO_MODULE_NAME]))
+ ADD_ERROR (ctx, g_strdup_printf ("Module has an invalid name, string index 0x%08x", data [MONO_MODULE_NAME]));
+
+ if (!is_valid_guid (ctx, data [MONO_MODULE_MVID]))
+ ADD_ERROR (ctx, g_strdup_printf ("Module has an invalid Mvid, guid index %x", data [MONO_MODULE_MVID]));
+
+ if (data [MONO_MODULE_ENC] != 0)
+ ADD_ERROR (ctx, g_strdup_printf ("Module has a non zero Enc field %x", data [MONO_MODULE_ENC]));
+
+ if (data [MONO_MODULE_ENCBASE] != 0)
+ ADD_ERROR (ctx, g_strdup_printf ("Module has a non zero EncBase field %x", data [MONO_MODULE_ENCBASE]));
+}
+
+static void
+verify_typeref_table (VerifyContext *ctx)
+{
+ MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEREF];
+ guint32 data [MONO_TYPEREF_SIZE];
+ int i;
+
+ for (i = 0; i < table->rows; ++i) {
+ mono_metadata_decode_row (table, i, data, MONO_TYPEREF_SIZE);
+ if (!is_valid_coded_index (ctx, RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE]))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d coded index 0x%08x", i, data [MONO_TYPEREF_SCOPE]));
+
+ if (!get_coded_index_token (RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE]))
+ ADD_ERROR (ctx, g_strdup_printf ("The metadata verifier doesn't support null ResolutionScope tokens for typeref row %d", i));
+
+ if (!data [MONO_TYPEREF_NAME] || !is_valid_non_empty_string (ctx, data [MONO_TYPEREF_NAME]))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d name token 0x%08x", i, data [MONO_TYPEREF_NAME]));
+
+ if (data [MONO_TYPEREF_NAMESPACE] && !is_valid_non_empty_string (ctx, data [MONO_TYPEREF_NAMESPACE]))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d namespace token 0x%08x", i, data [MONO_TYPEREF_NAMESPACE]));
+ }
+}
+
+/*bits 9,11,14,15,19,21,24-31 */
+#define INVALID_TYPEDEF_FLAG_BITS ((1 << 9) | (1 << 11) | (1 << 14) | (1 << 15) | (1 << 19) | (1 << 21) | 0xFF000000)
+static void
+verify_typedef_table (VerifyContext *ctx)
+{
+ MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEDEF];
+ guint32 data [MONO_TYPEDEF_SIZE];
+ guint32 fieldlist = 1, methodlist = 1;
+ int i;
+
+ if (table->rows == 0)
+ ADD_ERROR (ctx, g_strdup_printf ("Typedef table must have exactly at least one row"));
+
+ for (i = 0; i < table->rows; ++i) {
+ mono_metadata_decode_row (table, i, data, MONO_TYPEDEF_SIZE);
+ if (data [MONO_TYPEDEF_FLAGS] & INVALID_TYPEDEF_FLAG_BITS)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid flags field 0x%08x", i, data [MONO_TYPEDEF_FLAGS]));
+
+ if ((data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_LAYOUT_MASK) == 0x18)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid class layout 0x18", i));
+
+ if ((data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_STRING_FORMAT_MASK) == 0x30000)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d mono doesn't support custom string format", i));
+
+ if ((data [MONO_TYPEDEF_FLAGS] & 0xC00000) != 0)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d mono doesn't support custom string format", i));
+
+ if (!data [MONO_TYPEDEF_NAME] || !is_valid_non_empty_string (ctx, data [MONO_TYPEDEF_NAME]))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid name token %08x", i, data [MONO_TYPEDEF_NAME]));
+
+ if (data [MONO_TYPEREF_NAMESPACE] && !is_valid_non_empty_string (ctx, data [MONO_TYPEREF_NAMESPACE]))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid namespace token %08x", i, data [MONO_TYPEREF_NAMESPACE]));
+
+ if (i == 0) {
+ if (data [MONO_TYPEDEF_EXTENDS] != 0)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row 0 for the special <module> type must have a null extend field"));
+ } else {
+ if (typedef_is_system_object (ctx, data) && data [MONO_TYPEDEF_EXTENDS] != 0)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for System.Object must have a null extend field", i));
+
+ if (data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_INTERFACE) {
+ if (data [MONO_TYPEDEF_EXTENDS])
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for interface type must have a null extend field", i));
+ if ((data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_ABSTRACT) == 0)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for interface type must be abstract", i));
+ } else {
+ if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, data [MONO_TYPEDEF_EXTENDS]))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d extend field coded index 0x%08x", i, data [MONO_TYPEDEF_EXTENDS]));
+
+ if (!get_coded_index_token (TYPEDEF_OR_REF_DESC, data [MONO_TYPEDEF_EXTENDS]))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for non-interface type must have a non-null extend field", i));
+ }
+ }
+
+ if (data [MONO_TYPEDEF_FIELD_LIST] == 0)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d FieldList be be >= 1", i));
+
+ if (data [MONO_TYPEDEF_FIELD_LIST] > ctx->image->tables [MONO_TABLE_FIELD].rows + 1)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d FieldList rowid 0x%08x is out of range", i, data [MONO_TYPEDEF_FIELD_LIST]));
+
+ if (data [MONO_TYPEDEF_FIELD_LIST] < fieldlist)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d FieldList rowid 0x%08x can't be smaller than of previous row 0x%08x", i, data [MONO_TYPEDEF_FIELD_LIST], fieldlist));
+
+ if (data [MONO_TYPEDEF_METHOD_LIST] == 0)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d MethodList be be >= 1", i));
+
+ if (data [MONO_TYPEDEF_METHOD_LIST] > ctx->image->tables [MONO_TABLE_METHOD].rows + 1)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d MethodList rowid 0x%08x is out of range", i, data [MONO_TYPEDEF_METHOD_LIST]));
+
+ if (data [MONO_TYPEDEF_METHOD_LIST] < methodlist)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d MethodList rowid 0x%08x can't be smaller than of previous row 0x%08x", i, data [MONO_TYPEDEF_METHOD_LIST], methodlist));
+
+
+ fieldlist = data [MONO_TYPEDEF_FIELD_LIST];
+ methodlist = data [MONO_TYPEDEF_METHOD_LIST];
+ }
}
+/*bits 3,11,14 */
+#define INVALID_FIELD_FLAG_BITS ((1 << 3) | (1 << 11) | (1 << 14))
static void
-calc_fields_size (VerifyContext *ctx)
+verify_field_table (VerifyContext *ctx)
{
-#define TS(T) (ctx->tables [T].row_count)
-#define MAX2(TA,TB) MAX (TS (TA), TS (TB))
-#define MAX3(TA,TB,TC) MAX (TS (TA), MAX (TS (TB), TS (TC)))
-#define TB_SIZE(T) (TS (T) >= (1 << 16) ? 4 : 2)
+ MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELD];
+ guint32 data [MONO_FIELD_SIZE], flags, module_field_list;
+ int i;
- int tmp;
- memset (ctx->field_sizes, 0, sizeof (guint32) * COL_LAST);
+ module_field_list = (guint32)-1;
+ if (ctx->image->tables [MONO_TABLE_TYPEDEF].rows > 1) {
+ MonoTableInfo *type = &ctx->image->tables [MONO_TABLE_TYPEDEF];
+ module_field_list = mono_metadata_decode_row_col (type, 1, MONO_TYPEDEF_FIELD_LIST);
+ }
- ctx->field_sizes [COL_UINT8] = 1;
- ctx->field_sizes [COL_UINT16] = 2;
- ctx->field_sizes [COL_UINT32] = 4;
-
- ctx->field_sizes [COL_STRING] = ctx->wide_strings ? 4 : 2;
- ctx->field_sizes [COL_GUID] = ctx->wide_guid ? 4 : 2;
- ctx->field_sizes [COL_BLOB] = ctx->wide_blob? 4 : 2;
-
- ctx->field_sizes [COL_TYPE_DEF_OR_REF] = enc_index_size (2, MAX3 (MONO_TABLE_TYPEDEF, MONO_TABLE_TYPEREF, MONO_TABLE_TYPESPEC));
- ctx->field_sizes [COL_HAS_CONSTANT] = enc_index_size (2, MAX3 (MONO_TABLE_FIELD, MONO_TABLE_PARAM, MONO_TABLE_PROPERTY));
-
- tmp = MAX3 (MONO_TABLE_METHOD, MONO_TABLE_FIELD, MONO_TABLE_TYPEREF);
- tmp = MAX (tmp, MAX3 (MONO_TABLE_TYPEDEF, MONO_TABLE_PARAM, MONO_TABLE_INTERFACEIMPL));
- tmp = MAX (tmp, MAX3 (MONO_TABLE_MEMBERREF, MONO_TABLE_MODULE, COL_HAS_DECL_SECURITY));
- tmp = MAX (tmp, MAX3 (MONO_TABLE_PROPERTY, MONO_TABLE_EVENT, MONO_TABLE_STANDALONESIG));
- tmp = MAX (tmp, MAX3 (MONO_TABLE_MODULEREF, MONO_TABLE_TYPESPEC, MONO_TABLE_ASSEMBLY));
- tmp = MAX (tmp, MAX3 (MONO_TABLE_ASSEMBLYREF, MONO_TABLE_FILE, MONO_TABLE_EXPORTEDTYPE));
- tmp = MAX (tmp, MONO_TABLE_MANIFESTRESOURCE);
- ctx->field_sizes [COL_HAS_CATTR] = enc_index_size (5, tmp);
-
- ctx->field_sizes [COL_HAS_FIELD_MARSHAL] = enc_index_size (1, MAX2 (MONO_TABLE_FIELD, MONO_TABLE_PARAM));
- ctx->field_sizes [COL_HAS_DECL_SECURITY] = enc_index_size (2, MAX3 (MONO_TABLE_TYPEDEF, MONO_TABLE_METHOD, MONO_TABLE_ASSEMBLY));
-
- tmp = MAX3 (MONO_TABLE_TYPEDEF, MONO_TABLE_TYPEREF, MONO_TABLE_MODULEREF);
- tmp = MAX (tmp, MAX2 (MONO_TABLE_METHOD, MONO_TABLE_TYPESPEC));
- ctx->field_sizes [COL_MEMBER_REF_PARENT] = enc_index_size (3, tmp);
-
- ctx->field_sizes [COL_HAS_SEMANTICS] = enc_index_size (1, MAX2 (MONO_TABLE_EVENT, MONO_TABLE_PROPERTY));
- ctx->field_sizes [COL_METHOD_DEF_OR_REF] = enc_index_size (1, MAX2 (MONO_TABLE_METHOD, MONO_TABLE_MEMBERREF));
- ctx->field_sizes [COL_MEMBER_FORWARDED] = enc_index_size (1, MAX2 (MONO_TABLE_FIELD, MONO_TABLE_METHOD));
- ctx->field_sizes [COL_IMPLEMENTATION] = enc_index_size (2, MAX3 (MONO_TABLE_FILE, MONO_TABLE_ASSEMBLYREF, MONO_TABLE_EXPORTEDTYPE));
-
- ctx->field_sizes [COL_CATTR_TYPE] = enc_index_size (3, MAX2 (MONO_TABLE_METHOD, MONO_TABLE_MEMBERREF));
- ctx->field_sizes [COL_RES_SCOPE] = enc_index_size (2, MAX (MAX2 (MONO_TABLE_MODULE, MONO_TABLE_MODULEREF), MAX2 (MONO_TABLE_ASSEMBLYREF, MONO_TABLE_TYPEREF)));
- ctx->field_sizes [COL_TYPE_OR_METHOD_DEF] = enc_index_size (1, MAX2 (MONO_TABLE_TYPEDEF, MONO_TABLE_METHOD));
-
- ctx->field_sizes [COL_TYPE_DEF] = TB_SIZE (MONO_TABLE_TYPEDEF);
- ctx->field_sizes [COL_METHOD_DEF] = TB_SIZE (MONO_TABLE_TYPEDEF);
- ctx->field_sizes [COL_FIELD] = TB_SIZE (MONO_TABLE_TYPEDEF);
- ctx->field_sizes [COL_PARAM] = TB_SIZE (MONO_TABLE_TYPEDEF);
- ctx->field_sizes [COL_PROPERTY] = TB_SIZE (MONO_TABLE_TYPEDEF);
- ctx->field_sizes [COL_EVENT] = TB_SIZE (MONO_TABLE_TYPEDEF);
- ctx->field_sizes [COL_GENERIC_PARAM] = TB_SIZE (MONO_TABLE_TYPEDEF);
- ctx->field_sizes [COL_ASSEMBLY_REF] = TB_SIZE (MONO_TABLE_TYPEDEF);
- ctx->field_sizes [COL_MODULE_REF] = TB_SIZE (MONO_TABLE_TYPEDEF);
+ for (i = 0; i < table->rows; ++i) {
+ mono_metadata_decode_row (table, i, data, MONO_FIELD_SIZE);
+ flags = data [MONO_FIELD_FLAGS];
+
+ if (flags & INVALID_FIELD_FLAG_BITS)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid flags field 0x%08x", i, flags));
+
+ if ((flags & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK) == 0x7)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid field visibility 0x7", i));
+
+ if ((flags & (FIELD_ATTRIBUTE_LITERAL | FIELD_ATTRIBUTE_INIT_ONLY)) == (FIELD_ATTRIBUTE_LITERAL | FIELD_ATTRIBUTE_INIT_ONLY))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d cannot be InitOnly and Literal at the same time", i));
+
+ if ((flags & FIELD_ATTRIBUTE_RT_SPECIAL_NAME) && !(flags & FIELD_ATTRIBUTE_SPECIAL_NAME))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is RTSpecialName but not SpecialName", i));
+
+ if ((flags & FIELD_ATTRIBUTE_LITERAL) && !(flags & FIELD_ATTRIBUTE_STATIC))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is Literal but not Static", i));
+
+ if ((flags & FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL) &&
+ search_sorted_table (ctx, MONO_TABLE_FIELDMARSHAL, MONO_FIELD_MARSHAL_PARENT, make_coded_token (HAS_FIELD_MARSHAL_DESC, MONO_TABLE_FIELD, i)) == -1)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d has FieldMarshal but there is no corresponding row in the FieldMarshal table", i));
+
+ if ((flags & FIELD_ATTRIBUTE_HAS_DEFAULT) &&
+ search_sorted_table (ctx, MONO_TABLE_CONSTANT, MONO_CONSTANT_PARENT, make_coded_token (HAS_CONSTANT_DESC, MONO_TABLE_FIELD, i)) == -1)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d has Default but there is no corresponding row in the Constant table", i));
+
+ if ((flags & FIELD_ATTRIBUTE_LITERAL) &&
+ search_sorted_table (ctx, MONO_TABLE_CONSTANT, MONO_CONSTANT_PARENT, make_coded_token (HAS_CONSTANT_DESC, MONO_TABLE_FIELD, i)) == -1)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is Literal but there is no corresponding row in the Constant table", i));
+
+ if ((flags & FIELD_ATTRIBUTE_HAS_FIELD_RVA) &&
+ search_sorted_table (ctx, MONO_TABLE_FIELDRVA, MONO_FIELD_RVA_FIELD, i + 1) == -1)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d has Default but there is no corresponding row in the Constant table", i));
+
+ if (!data [MONO_FIELD_NAME] || !is_valid_non_empty_string (ctx, data [MONO_FIELD_NAME]))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid name token %08x", i, data [MONO_FIELD_NAME]));
+
+ //TODO verify contant flag
+ if (!data [MONO_FIELD_SIGNATURE] || !is_valid_field_signature (ctx, data [MONO_FIELD_SIGNATURE]))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid signature token %08x", i, data [MONO_FIELD_SIGNATURE]));
+
+ if (i + 1 < module_field_list) {
+ guint32 access = flags & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK;
+ if (!(flags & FIELD_ATTRIBUTE_STATIC))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is a global variable but is not static", i));
+ if (access != FIELD_ATTRIBUTE_COMPILER_CONTROLLED && access != FIELD_ATTRIBUTE_PRIVATE && access != FIELD_ATTRIBUTE_PUBLIC)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is a global variable but have wrong visibility %x", i, access));
+ }
+ }
+}
+
+/*bits 6,8,9,10,11,13,14,15*/
+#define INVALID_METHOD_IMPLFLAG_BITS ((1 << 6) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 13) | (1 << 14) | (1 << 15))
+static void
+verify_method_table (VerifyContext *ctx)
+{
+ MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHOD];
+ guint32 data [MONO_METHOD_SIZE], flags, implflags, rva, module_method_list, access, code_type;
+ guint32 paramlist = 1;
+ gboolean is_ctor, is_cctor;
+ const char *name;
+ int i;
+
+ module_method_list = (guint32)-1;
+ if (ctx->image->tables [MONO_TABLE_TYPEDEF].rows > 1) {
+ MonoTableInfo *type = &ctx->image->tables [MONO_TABLE_TYPEDEF];
+ module_method_list = mono_metadata_decode_row_col (type, 1, MONO_TYPEDEF_METHOD_LIST);
+ }
+
+ for (i = 0; i < table->rows; ++i) {
+ mono_metadata_decode_row (table, i, data, MONO_METHOD_SIZE);
+ rva = data [MONO_METHOD_RVA];
+ implflags = data [MONO_METHOD_IMPLFLAGS];
+ flags = data [MONO_METHOD_FLAGS];
+ access = flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK;
+ code_type = implflags & METHOD_IMPL_ATTRIBUTE_CODE_TYPE_MASK;
+
+
+ if (implflags & INVALID_METHOD_IMPLFLAG_BITS)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid implflags field 0x%08x", i, implflags));
+
+ if (access == 0x7)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid MemberAccessMask 0x7", i));
+
+ if (!data [MONO_METHOD_NAME] || !is_valid_non_empty_string (ctx, data [MONO_METHOD_NAME]))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid name field 0x%08x", i, data [MONO_METHOD_NAME]));
+
+ name = get_string_ptr (ctx, data [MONO_METHOD_NAME]);
+ is_ctor = !strcmp (".ctor", name);
+ is_cctor = !strcmp (".cctor", name);
+
+ if ((is_ctor || is_cctor) &&
+ search_sorted_table (ctx, MONO_TABLE_GENERICPARAM, MONO_GENERICPARAM_OWNER, make_coded_token (TYPE_OR_METHODDEF_DESC, MONO_TABLE_METHOD, i)) != -1)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d .ctor or .cctor has generic param", i));
+
+ if ((flags & METHOD_ATTRIBUTE_STATIC) && (flags & (METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_NEW_SLOT)))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is static and (final, virtual or new slot)", i));
+
+ if (flags & METHOD_ATTRIBUTE_ABSTRACT) {
+ if (flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is Abstract and PinvokeImpl", i));
+ if (!(flags & METHOD_ATTRIBUTE_VIRTUAL))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is Abstract but not Virtual", i));
+ }
+
+ if (access == METHOD_ATTRIBUTE_COMPILER_CONTROLLED && (flags & (METHOD_ATTRIBUTE_RT_SPECIAL_NAME | METHOD_ATTRIBUTE_SPECIAL_NAME)))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is CompileControlled and SpecialName or RtSpecialName", i));
+
+ if ((flags & METHOD_ATTRIBUTE_RT_SPECIAL_NAME) && !(flags & METHOD_ATTRIBUTE_SPECIAL_NAME))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is RTSpecialName but not SpecialName", i));
+
+ //XXX no checks against cas stuff 10,11,12,13)
+
+ //TODO check iface with .ctor (15,16)
+
+ if (!data [MONO_METHOD_SIGNATURE] || !is_valid_method_signature (ctx, data [MONO_METHOD_SIGNATURE]))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid signature token %08x", i, data [MONO_METHOD_SIGNATURE]));
+
+ if (i + 1 < module_method_list) {
+ if (!(flags & METHOD_ATTRIBUTE_STATIC))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is a global method but not Static", i));
+ if (flags & (METHOD_ATTRIBUTE_ABSTRACT | METHOD_ATTRIBUTE_VIRTUAL))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is a global method but is Abstract or Virtual", i));
+ if (!(access == METHOD_ATTRIBUTE_COMPILER_CONTROLLED || access == METHOD_ATTRIBUTE_PUBLIC || access == METHOD_ATTRIBUTE_PRIVATE))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is a global method but not CompilerControled, Public or Private", i));
+ }
+
+ //TODO check valuetype for synchronized
+
+ if ((flags & (METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_NEW_SLOT | METHOD_ATTRIBUTE_STRICT)) && !(flags & METHOD_ATTRIBUTE_VIRTUAL))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is (Final, NewSlot or Strict) but not Virtual", i));
+
+ if ((flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) && (flags & METHOD_ATTRIBUTE_VIRTUAL))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is PinvokeImpl and Virtual", i));
+
+ if (!(flags & METHOD_ATTRIBUTE_ABSTRACT) && !rva && !(flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) &&
+ !(implflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && code_type != METHOD_IMPL_ATTRIBUTE_RUNTIME)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is not Abstract and neither PinvokeImpl, Runtime, InternalCall or with RVA != 0", i));
+
+ if (access == METHOD_ATTRIBUTE_COMPILER_CONTROLLED && !(rva || (flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is CompilerControlled but neither RVA != 0 or PinvokeImpl", i));
+
+ //TODO check signature contents
+
+ if (rva) {
+ if (flags & METHOD_ATTRIBUTE_ABSTRACT)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d has RVA != 0 but is Abstract", i));
+ if (code_type == METHOD_IMPL_ATTRIBUTE_OPTIL)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d has RVA != 0 but is CodeTypeMask is neither Native, CIL or Runtime", i));
+ if (!is_valid_method_header (ctx, rva))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d RVA points to an invalid method header", i));
+ } else {
+ if (!(flags & (METHOD_ATTRIBUTE_ABSTRACT | METHOD_ATTRIBUTE_PINVOKE_IMPL)) && !(implflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && code_type != METHOD_IMPL_ATTRIBUTE_RUNTIME)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d has RVA = 0 but neither Abstract, InternalCall, Runtime or PinvokeImpl", i));
+ }
+
+ if ((flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
+ if (rva)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is PinvokeImpl but has RVA != 0", i));
+ if (search_sorted_table (ctx, MONO_TABLE_IMPLMAP, MONO_IMPLMAP_MEMBER, make_coded_token (MEMBER_FORWARDED_DESC, MONO_TABLE_METHOD, i)) == -1)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is PinvokeImpl but has no row in the ImplMap table", i));
+ }
+ if (flags & METHOD_ATTRIBUTE_RT_SPECIAL_NAME && !is_ctor && !is_cctor)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is RtSpecialName but not named .ctor or .cctor", i));
+
+ if ((is_ctor || is_cctor) && !(flags & METHOD_ATTRIBUTE_RT_SPECIAL_NAME))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is named .ctor or .cctor but is not RtSpecialName", i));
+
+ if (data [MONO_METHOD_PARAMLIST] == 0)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d ParamList be be >= 1", i));
+
+ if (data [MONO_METHOD_PARAMLIST] < paramlist)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d ParamList rowid 0x%08x can't be smaller than of previous row 0x%08x", i, data [MONO_METHOD_PARAMLIST], paramlist));
+
+ if (data [MONO_METHOD_PARAMLIST] > ctx->image->tables [MONO_TABLE_PARAM].rows + 1)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d ParamList rowid 0x%08x is out of range", i, data [MONO_METHOD_PARAMLIST]));
+
+ paramlist = data [MONO_METHOD_PARAMLIST];
+
+ }
}
static guint32
-calc_row_size (VerifyContext *ctx)
+get_next_param_count (VerifyContext *ctx, guint32 *current_method)
{
- int i, idx;
- guint64 total_size = 0;
-
- for (idx = 0, i = 0; i < 0x2D; ++i) {
- int size = 0, type;
+ MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHOD];
+ guint32 row = *current_method;
+ guint32 paramlist, tmp;
- while ((type = table_desc [idx++]) != COL_LAST)
- size += ctx->field_sizes [type];
- ctx->tables [i].row_size = size;
- total_size += (guint64)size * ctx->tables [i].row_count;
+ paramlist = mono_metadata_decode_row_col (table, row++, MONO_METHOD_PARAMLIST);
+ while (row < table->rows) {
+ tmp = mono_metadata_decode_row_col (table, row, MONO_METHOD_PARAMLIST);
+ if (tmp > paramlist) {
+ *current_method = row;
+ return tmp - paramlist;
+ }
+ ++row;
}
- if (total_size > G_MAXUINT32)
- return 0;
+ /*no more methods, all params apply to the last one*/
+ *current_method = table->rows;
+ return (guint32)-1;
+}
+
+
+#define INVALID_PARAM_FLAGS_BITS ((1 << 2) | (1 << 3) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 14) | (1 << 15))
+static void
+verify_param_table (VerifyContext *ctx)
+{
+ MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_PARAM];
+ guint32 data [MONO_PARAM_SIZE], flags, sequence = 0, remaining_params, current_method = 0;
+ gboolean first_param = TRUE;
+ int i;
+
+ remaining_params = get_next_param_count (ctx, ¤t_method);
+
+ for (i = 0; i < table->rows; ++i) {
+ mono_metadata_decode_row (table, i, data, MONO_PARAM_SIZE);
+ flags = data [MONO_PARAM_FLAGS];
+
+ if (flags & INVALID_PARAM_FLAGS_BITS)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d bad Flags value 0x%08x", i, flags));
+
+ if (search_sorted_table (ctx, MONO_TABLE_CONSTANT, MONO_CONSTANT_PARENT, make_coded_token (HAS_CONSTANT_DESC, MONO_TABLE_PARAM, i)) == -1) {
+ if (flags & PARAM_ATTRIBUTE_HAS_DEFAULT)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d HasDefault = 1 but no owned row in Contant table", i));
+ } else {
+ if (!(flags & PARAM_ATTRIBUTE_HAS_DEFAULT))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d HasDefault = 0 but has owned row in Contant table", i));
+ }
+
+ if ((flags & PARAM_ATTRIBUTE_HAS_FIELD_MARSHAL) && search_sorted_table (ctx, MONO_TABLE_FIELDMARSHAL, MONO_FIELD_MARSHAL_PARENT, make_coded_token (HAS_FIELD_MARSHAL_DESC, MONO_TABLE_PARAM, i)) == -1)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d HasFieldMarshal = 1 but no owned row in FieldMarshal table", i));
+
+ if (!is_valid_string (ctx, data [MONO_PARAM_NAME]))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d Name = 1 bad token 0x%08x", i, data [MONO_PARAM_NAME]));
- return (guint32)total_size;
+ if (!first_param && data [MONO_PARAM_SEQUENCE] <= sequence)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d sequece = %d previus param has %d", i, data [MONO_PARAM_SEQUENCE], sequence));
+
+ first_param = FALSE;
+ sequence = data [MONO_PARAM_SEQUENCE];
+ if (--remaining_params == 0) {
+ remaining_params = get_next_param_count (ctx, ¤t_method);
+ first_param = TRUE;
+ }
+ }
}
+static void
+verify_interfaceimpl_table (VerifyContext *ctx)
+{
+ MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_INTERFACEIMPL];
+ guint32 data [MONO_INTERFACEIMPL_SIZE];
+ int i;
+
+ for (i = 0; i < table->rows; ++i) {
+ mono_metadata_decode_row (table, i, data, MONO_INTERFACEIMPL_SIZE);
+ if (data [MONO_INTERFACEIMPL_CLASS] && data [MONO_INTERFACEIMPL_CLASS] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows)
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid InterfaceImpl row %d Class field 0x%08x", i, data [MONO_TABLE_TYPEDEF]));
+
+ if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, data [MONO_INTERFACEIMPL_INTERFACE]))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid InterfaceImpl row %d Inteface field coded index 0x%08x", i, data [MONO_INTERFACEIMPL_INTERFACE]));
+
+ if (!get_coded_index_token (TYPEDEF_OR_REF_DESC, data [MONO_INTERFACEIMPL_INTERFACE]))
+ ADD_ERROR (ctx, g_strdup_printf ("Invalid InterfaceImpl row %d Inteface field is null", i));
+ }
+}
static void
verify_tables_data (VerifyContext *ctx)
{
- OffsetAndSize tables_area = ctx->metadata_streams [TILDE_STREAM];
- guint table_area_size;
- calc_fields_size (ctx);
- table_area_size = calc_row_size (ctx);
+ OffsetAndSize tables_area = get_metadata_stream (ctx, &ctx->image->heap_tables);
+ guint32 size = 0, tables_offset;
+ int i;
- if (table_area_size == 0)
+ for (i = 0; i < 0x2D; ++i) {
+ MonoTableInfo *table = &ctx->image->tables [i];
+ guint32 tmp_size;
+ tmp_size = size + (guint32)table->row_size * (guint32)table->rows;
+ if (tmp_size < size) {
+ size = 0;
+ break;
+ }
+ size = tmp_size;
+ }
+
+ if (size == 0)
ADD_ERROR (ctx, g_strdup_printf ("table space is either empty or overflowed"));
- if (!bounds_check_offset (&tables_area, ctx->tables_offset, table_area_size))
- ADD_ERROR (ctx, g_strdup_printf ("Tables data require %d bytes but the only %d are available in the #~ stream", table_area_size, tables_area.size - (ctx->tables_offset - tables_area.offset)));
+ tables_offset = ctx->image->tables_base - ctx->data;
+ if (!bounds_check_offset (&tables_area, tables_offset, size))
+ ADD_ERROR (ctx, g_strdup_printf ("Tables data require %d bytes but the only %d are available in the #~ stream", size, tables_area.size - (tables_offset - tables_area.offset)));
+
+ verify_module_table (ctx);
+ CHECK_ERROR ();
+ verify_typeref_table (ctx);
+ CHECK_ERROR ();
+ verify_typedef_table (ctx);
+ CHECK_ERROR ();
+ verify_field_table (ctx);
+ CHECK_ERROR ();
+ verify_method_table (ctx);
+ CHECK_ERROR ();
+ verify_param_table (ctx);
+ CHECK_ERROR ();
+ verify_interfaceimpl_table (ctx);
+}
+
+static gboolean
+mono_verifier_is_corlib (MonoImage *image)
+{
+ gboolean trusted_location = (mono_security_get_mode () != MONO_SECURITY_MODE_CORE_CLR) ?
+ TRUE : mono_security_core_clr_is_platform_image (image);
+
+ return trusted_location && !strcmp ("mscorlib.dll", image->name);
+}
+
+static void
+init_verify_context (VerifyContext *ctx, MonoImage *image, GSList **error_list)
+{
+ memset (ctx, 0, sizeof (VerifyContext));
+ ctx->image = image;
+ ctx->report_error = error_list != NULL;
+ ctx->valid = 1;
+ ctx->size = image->raw_data_len;
+ ctx->data = image->raw_data;
+ ctx->is_corlib = mono_verifier_is_corlib (image);
+}
+
+static gboolean
+cleanup_context (VerifyContext *ctx, GSList **error_list)
+{
+ g_free (ctx->sections);
+ if (error_list)
+ *error_list = ctx->errors;
+ else
+ mono_free_verify_list (ctx->errors);
+ return ctx->valid;
}
-GSList*
-mono_image_verify (const char *data, guint32 size)
+gboolean
+mono_verifier_verify_pe_data (MonoImage *image, GSList **error_list)
{
VerifyContext ctx;
- memset (&ctx, 0, sizeof (VerifyContext));
- ctx.data = data;
- ctx.size = size;
- ctx.valid = 1;
+
+ if (!mono_verifier_is_enabled_for_image (image))
+ return TRUE;
+
+ init_verify_context (&ctx, image, error_list);
+ ctx.stage = STAGE_PE;
verify_msdos_header (&ctx);
CHECK_STATE();
CHECK_STATE();
/*No need to check the IAT directory entry, it's content is indirectly verified by verify_import_table*/
verify_resources_table (&ctx);
- CHECK_STATE();
+
+cleanup:
+ return cleanup_context (&ctx, error_list);
+}
+
+gboolean
+mono_verifier_verify_cli_data (MonoImage *image, GSList **error_list)
+{
+ VerifyContext ctx;
+
+ if (!mono_verifier_is_enabled_for_image (image))
+ return TRUE;
+
+ init_verify_context (&ctx, image, error_list);
+ ctx.stage = STAGE_CLI;
+
verify_cli_header (&ctx);
CHECK_STATE();
verify_metadata_header (&ctx);
CHECK_STATE();
verify_tables_schema (&ctx);
- CHECK_STATE();
- verify_tables_data (&ctx);
- CHECK_STATE();
+
cleanup:
- g_free (ctx.sections);
- return ctx.errors;
+ return cleanup_context (&ctx, error_list);
+}
+
+gboolean
+mono_verifier_verify_table_data (MonoImage *image, GSList **error_list)
+{
+ VerifyContext ctx;
+
+ if (!mono_verifier_is_enabled_for_image (image))
+ return TRUE;
+
+ init_verify_context (&ctx, image, error_list);
+ ctx.stage = STAGE_TABLES;
+
+ verify_tables_data (&ctx);
+
+ return cleanup_context (&ctx, error_list);
}