Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Branch: master
Fetching contributors…

Cannot retrieve contributors at this time

4443 lines (3557 sloc) 149.709 kB
/*
* metadata-verify.c: Metadata verfication support
*
* Author:
* Mono Project (http://www.mono-project.com)
*
* Copyright (C) 2005-2008 Novell, Inc. (http://www.novell.com)
*/
#include <mono/metadata/object-internals.h>
#include <mono/metadata/verify.h>
#include <mono/metadata/verify-internals.h>
#include <mono/metadata/opcodes.h>
#include <mono/metadata/tabledefs.h>
#include <mono/metadata/reflection.h>
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/mono-endian.h>
#include <mono/metadata/metadata.h>
#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/metadata/attrdefs.h>
#include <mono/utils/strenc.h>
#include <mono/utils/mono-error-internals.h>
#include <mono/utils/bsearch.h>
#include <string.h>
//#include <signal.h>
#include <ctype.h>
#ifndef DISABLE_VERIFIER
/*
TODO add fail fast mode
TODO add PE32+ support
TODO verify the entry point RVA and content.
TODO load_section_table and load_data_directories must take PE32+ into account
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
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,
};
enum {
STRINGS_STREAM,
USER_STRINGS_STREAM,
BLOB_STREAM,
GUID_STREAM,
TILDE_STREAM
};
#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*/
20, /*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,
MONO_TABLE_GENERICPARAM,
#define HAS_FIELD_MARSHAL_DESC (HAS_CATTR_DESC + 22)
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_MODULEREF,
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;
guint32 translated_offset;
} DataDirectory;
typedef struct {
guint32 offset;
guint32 size;
} OffsetAndSize;
typedef struct {
guint32 baseRVA;
guint32 baseOffset;
guint32 size;
guint32 rellocationsRVA;
guint16 numberOfRelocations;
} SectionHeader;
typedef struct {
guint32 row_count;
guint32 row_size;
guint32 offset;
} TableInfo;
typedef struct {
const char *data;
guint32 size, token;
GSList *errors;
int valid;
MonoImage *image;
gboolean report_error;
gboolean report_warning;
int stage;
DataDirectory data_directories [16];
guint32 section_count;
SectionHeader *sections;
OffsetAndSize metadata_streams [5]; //offset from begin of the image
} VerifyContext;
#define ADD_VERIFY_INFO(__ctx, __msg, __status, __exception) \
do { \
MonoVerifyInfoExtended *vinfo = g_new (MonoVerifyInfoExtended, 1); \
vinfo->info.status = __status; \
vinfo->info.message = ( __msg); \
vinfo->exception_type = (__exception); \
(__ctx)->errors = g_slist_prepend ((__ctx)->errors, vinfo); \
} while (0)
#define ADD_WARNING(__ctx, __msg) \
do { \
if ((__ctx)->report_warning) { \
ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_WARNING, MONO_EXCEPTION_INVALID_PROGRAM); \
(__ctx)->valid = 0; \
return; \
} \
} while (0)
#define ADD_ERROR_NO_RETURN(__ctx, __msg) \
do { \
if ((__ctx)->report_error) \
ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \
(__ctx)->valid = 0; \
} while (0)
#define ADD_ERROR(__ctx, __msg) \
do { \
if ((__ctx)->report_error) \
ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \
(__ctx)->valid = 0; \
return; \
} while (0)
#define FAIL(__ctx, __msg) \
do { \
if ((__ctx)->report_error) \
ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \
(__ctx)->valid = 0; \
return FALSE; \
} while (0)
#define CHECK_STATE() do { if (!ctx.valid) goto cleanup; } while (0)
#define CHECK_ERROR() do { if (!ctx->valid) return; } while (0)
#define CHECK_ADD4_OVERFLOW_UN(a, b) ((guint32)(0xFFFFFFFFU) - (guint32)(b) < (guint32)(a))
#define CHECK_ADD8_OVERFLOW_UN(a, b) ((guint64)(0xFFFFFFFFFFFFFFFFUL) - (guint64)(b) < (guint64)(a))
#if SIZEOF_VOID_P == 4
#define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD4_OVERFLOW_UN(a, b)
#else
#define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD8_OVERFLOW_UN(a, b)
#endif
#define ADDP_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADDP_OVERFLOW_UN (a, b))
#define ADD_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADD4_OVERFLOW_UN (a, b))
static const char *
dword_align (const char *ptr)
{
#if SIZEOF_VOID_P == 8
return (const char *) (((guint64) (ptr + 3)) & ~3);
#else
return (const char *) (((guint32) (ptr + 3)) & ~3);
#endif
}
static void
add_from_mono_error (VerifyContext *ctx, MonoError *error)
{
if (mono_error_ok (error))
return;
ADD_ERROR (ctx, g_strdup (mono_error_get_message (error)));
mono_error_cleanup (error);
}
static guint32
pe_signature_offset (VerifyContext *ctx)
{
return read32 (ctx->data + 0x3c);
}
static guint32
pe_header_offset (VerifyContext *ctx)
{
return read32 (ctx->data + 0x3c) + 4;
}
static gboolean
bounds_check_virtual_address (VerifyContext *ctx, guint32 rva, guint32 size)
{
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;
for (i = 0; i < ctx->section_count; ++i) {
guint32 base = ctx->sections [i].baseRVA;
guint32 end = ctx->sections [i].baseRVA + ctx->sections [i].size;
if (rva >= base && rva + size <= end)
return TRUE;
}
return FALSE;
}
static gboolean
bounds_check_datadir (DataDirectory *dir, guint32 offset, guint32 size)
{
if (dir->translated_offset > offset)
return FALSE;
if (dir->size < size)
return FALSE;
return offset + size <= dir->translated_offset + dir->size;
}
static gboolean
bounds_check_offset (OffsetAndSize *off, guint32 offset, guint32 size)
{
if (off->offset > offset)
return FALSE;
if (off->size < size)
return FALSE;
return offset + size <= off->offset + off->size;
}
static guint32
translate_rva (VerifyContext *ctx, guint32 rva)
{
int i;
if (ctx->stage > STAGE_PE)
return mono_cli_rva_image_map (ctx->image, rva);
if (!ctx->sections)
return FALSE;
for (i = 0; i < ctx->section_count; ++i) {
guint32 base = ctx->sections [i].baseRVA;
guint32 end = ctx->sections [i].baseRVA + ctx->sections [i].size;
if (rva >= base && rva <= end) {
guint32 res = (rva - base) + ctx->sections [i].baseOffset;
/* double check */
return res >= ctx->size ? INVALID_OFFSET : res;
}
}
return INVALID_OFFSET;
}
static void
verify_msdos_header (VerifyContext *ctx)
{
guint32 lfanew;
if (ctx->size < 128)
ADD_ERROR (ctx, g_strdup ("Not enough space for the MS-DOS header"));
if (ctx->data [0] != 0x4d || ctx->data [1] != 0x5a)
ADD_ERROR (ctx, g_strdup ("Invalid MS-DOS watermark"));
lfanew = pe_signature_offset (ctx);
if (lfanew > ctx->size - 4)
ADD_ERROR (ctx, g_strdup ("MS-DOS lfanew offset points to outside of the file"));
}
static void
verify_pe_header (VerifyContext *ctx)
{
guint32 offset = pe_signature_offset (ctx);
const char *pe_header = ctx->data + offset;
if (pe_header [0] != 'P' || pe_header [1] != 'E' ||pe_header [2] != 0 ||pe_header [3] != 0)
ADD_ERROR (ctx, g_strdup ("Invalid PE header watermark"));
pe_header += 4;
offset += 4;
if (offset > ctx->size - 20)
ADD_ERROR (ctx, g_strdup ("File with truncated pe header"));
if (read16 (pe_header) != 0x14c)
ADD_ERROR (ctx, g_strdup ("Invalid PE header Machine value"));
}
static void
verify_pe_optional_header (VerifyContext *ctx)
{
guint32 offset = pe_header_offset (ctx);
guint32 header_size, file_alignment;
const char *pe_header = ctx->data + offset;
const char *pe_optional_header = pe_header + 20;
header_size = read16 (pe_header + 16);
offset += 20;
if (header_size < 2) /*must be at least 2 or we won't be able to read magic*/
ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
if (offset > ctx->size - header_size || header_size > ctx->size)
ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
if (read16 (pe_optional_header) == 0x10b) {
if (header_size != 224)
ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header size %d", header_size));
/* 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)));
file_alignment = read32 (pe_optional_header + 36);
if (file_alignment != 0x200 && file_alignment != 0x1000)
ADD_ERROR (ctx, g_strdup_printf ("Invalid file Aligmnent %x", file_alignment));
/* All the junk in the middle is irrelevant, specially for mono. */
if (read32 (pe_optional_header + 92) > 0x10)
ADD_ERROR (ctx, g_strdup_printf ("Too many data directories %x", read32 (pe_optional_header + 92)));
} else {
if (read16 (pe_optional_header) == 0x20B)
ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle PE32+"));
else
ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header magic %d", read16 (pe_optional_header)));
}
}
static void
load_section_table (VerifyContext *ctx)
{
int i;
SectionHeader *sections;
guint32 offset = pe_header_offset (ctx);
const char *ptr = ctx->data + offset;
guint16 num_sections = ctx->section_count = read16 (ptr + 2);
offset += 244;/*FIXME, this constant is different under PE32+*/
ptr += 244;
if (num_sections * 40 > ctx->size - offset)
ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
sections = ctx->sections = g_new0 (SectionHeader, num_sections);
for (i = 0; i < num_sections; ++i) {
sections [i].size = read32 (ptr + 8);
sections [i].baseRVA = read32 (ptr + 12);
sections [i].baseOffset = read32 (ptr + 20);
sections [i].rellocationsRVA = read32 (ptr + 24);
sections [i].numberOfRelocations = read16 (ptr + 32);
ptr += 40;
}
ptr = ctx->data + offset; /*reset it to the beggining*/
for (i = 0; i < num_sections; ++i) {
guint32 raw_size, flags;
if (sections [i].baseOffset == 0)
ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle sections with intialized data only"));
if (sections [i].baseOffset >= ctx->size)
ADD_ERROR (ctx, g_strdup_printf ("Invalid PointerToRawData %x points beyond EOF", sections [i].baseOffset));
if (sections [i].size > ctx->size - sections [i].baseOffset)
ADD_ERROR (ctx, g_strdup ("Invalid VirtualSize points beyond EOF"));
raw_size = read32 (ptr + 16);
if (raw_size < sections [i].size)
ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle sections with SizeOfRawData < VirtualSize"));
if (raw_size > ctx->size - sections [i].baseOffset)
ADD_ERROR (ctx, g_strdup_printf ("Invalid SizeOfRawData %x points beyond EOF", raw_size));
if (sections [i].rellocationsRVA || sections [i].numberOfRelocations)
ADD_ERROR (ctx, g_strdup_printf ("Metadata verifier doesn't handle section relocation"));
flags = read32 (ptr + 36);
/*TODO 0xFE0000E0 is all flags from cil-coff.h OR'd. Make it a less magical number*/
if (flags == 0 || (flags & ~0xFE0000E0) != 0)
ADD_ERROR (ctx, g_strdup_printf ("Invalid section flags %x", flags));
ptr += 40;
}
}
static gboolean
is_valid_data_directory (int i)
{
/*LAMESPEC 4 == certificate 6 == debug, MS uses both*/
return i == 1 || i == 2 || i == 5 || i == 12 || i == 14 || i == 4 || i == 6;
}
static void
load_data_directories (VerifyContext *ctx)
{
guint32 offset = pe_header_offset (ctx) + 116; /*FIXME, this constant is different under PE32+*/
const char *ptr = ctx->data + offset;
int i;
for (i = 0; i < 16; ++i) {
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));
if (rva != 0 && !bounds_check_virtual_address (ctx, rva, size))
ADD_ERROR (ctx, g_strdup_printf ("Invalid data directory %d rva/size pair %x/%x", i, rva, size));
ctx->data_directories [i].rva = rva;
ctx->data_directories [i].size = size;
ctx->data_directories [i].translated_offset = translate_rva (ctx, rva);
ptr += 8;
}
}
#define SIZE_OF_MSCOREE (sizeof ("mscoree.dll"))
#define SIZE_OF_CORMAIN (sizeof ("_CorExeMain"))
static void
verify_hint_name_table (VerifyContext *ctx, guint32 import_rva, const char *table_name)
{
const char *ptr;
guint32 hint_table_rva;
import_rva = translate_rva (ctx, import_rva);
g_assert (import_rva != INVALID_OFFSET);
hint_table_rva = read32 (ctx->data + import_rva);
if (!bounds_check_virtual_address (ctx, hint_table_rva, SIZE_OF_CORMAIN + 2))
ADD_ERROR (ctx, g_strdup_printf ("Invalid Hint/Name rva %d for %s", hint_table_rva, table_name));
hint_table_rva = translate_rva (ctx, hint_table_rva);
g_assert (hint_table_rva != INVALID_OFFSET);
ptr = ctx->data + hint_table_rva + 2;
if (memcmp ("_CorExeMain", ptr, SIZE_OF_CORMAIN) && memcmp ("_CorDllMain", ptr, SIZE_OF_CORMAIN)) {
char name[SIZE_OF_CORMAIN];
memcpy (name, ptr, SIZE_OF_CORMAIN);
name [SIZE_OF_CORMAIN - 1] = 0;
ADD_ERROR (ctx, g_strdup_printf ("Invalid Hint / Name: '%s'", name));
}
}
static void
verify_import_table (VerifyContext *ctx)
{
DataDirectory it = ctx->data_directories [IMPORT_TABLE_IDX];
guint32 offset = it.translated_offset;
const char *ptr = ctx->data + offset;
guint32 name_rva, ilt_rva, iat_rva;
g_assert (offset != INVALID_OFFSET);
if (it.size < 40)
ADD_ERROR (ctx, g_strdup_printf ("Import table size %d is smaller than 40", it.size));
ilt_rva = read32 (ptr);
if (ilt_rva && !bounds_check_virtual_address (ctx, ilt_rva, 8))
ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Lookup Table rva %x", ilt_rva));
name_rva = read32 (ptr + 12);
if (name_rva && !bounds_check_virtual_address (ctx, name_rva, SIZE_OF_MSCOREE))
ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Table Name rva %x", name_rva));
iat_rva = read32 (ptr + 16);
if (iat_rva) {
if (!bounds_check_virtual_address (ctx, iat_rva, 8))
ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Address Table rva %x", iat_rva));
if (iat_rva != ctx->data_directories [IAT_IDX].rva)
ADD_ERROR (ctx, g_strdup_printf ("Import Address Table rva %x different from data directory entry %x", read32 (ptr + 16), ctx->data_directories [IAT_IDX].rva));
}
if (name_rva) {
name_rva = translate_rva (ctx, name_rva);
g_assert (name_rva != INVALID_OFFSET);
ptr = ctx->data + name_rva;
if (memcmp ("mscoree.dll", ptr, SIZE_OF_MSCOREE)) {
char name[SIZE_OF_MSCOREE];
memcpy (name, ptr, SIZE_OF_MSCOREE);
name [SIZE_OF_MSCOREE - 1] = 0;
ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Table Name: '%s'", name));
}
}
if (ilt_rva) {
verify_hint_name_table (ctx, ilt_rva, "Import Lookup Table");
CHECK_ERROR ();
}
if (iat_rva)
verify_hint_name_table (ctx, iat_rva, "Import Address Table");
}
static void
verify_resources_table (VerifyContext *ctx)
{
DataDirectory it = ctx->data_directories [RESOURCE_TABLE_IDX];
guint32 offset;
guint16 named_entries, id_entries;
const char *ptr;
if (it.rva == 0)
return;
if (it.size < 16)
ADD_ERROR (ctx, g_strdup_printf ("Resource section is too small, must be at least 16 bytes long but it's %d long", it.size));
offset = it.translated_offset;
ptr = ctx->data + offset;
g_assert (offset != INVALID_OFFSET);
named_entries = read16 (ptr + 12);
id_entries = read16 (ptr + 14);
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 = get_data_dir (ctx, CLI_HEADER_IDX);
guint32 offset;
const char *ptr;
int i;
if (it.rva == 0)
ADD_ERROR (ctx, g_strdup_printf ("CLI header missing"));
if (it.size != 72)
ADD_ERROR (ctx, g_strdup_printf ("Invalid cli header size in data directory %d must be 72", it.size));
offset = it.translated_offset;
ptr = ctx->data + offset;
g_assert (offset != INVALID_OFFSET);
if (read16 (ptr) != 72)
ADD_ERROR (ctx, g_strdup_printf ("Invalid cli header size %d must be 72", read16 (ptr)));
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"));
if ((read32 (ptr + 16) & ~0x0001000B) != 0)
ADD_ERROR (ctx, g_strdup_printf ("Invalid CLI header flags"));
ptr += 24;
for (i = 0; i < 6; ++i) {
guint32 rva = read32 (ptr);
guint32 size = read32 (ptr + 4);
if (rva != 0 && !bounds_check_virtual_address (ctx, rva, size))
ADD_ERROR (ctx, g_strdup_printf ("Invalid cli section %i rva/size pair %x/%x", i, rva, size));
ptr += 8;
if (rva && i > 1)
ADD_ERROR (ctx, g_strdup_printf ("Metadata verifier doesn't support cli header section %d", i));
}
}
static guint32
pad4 (guint32 offset)
{
if (offset & 0x3) //pad to the next 4 byte boundary
offset = (offset & ~0x3) + 4;
return offset;
}
static void
verify_metadata_header (VerifyContext *ctx)
{
int i;
DataDirectory it = get_data_dir (ctx, CLI_HEADER_IDX);
guint32 offset, section_count;
const char *ptr;
offset = it.translated_offset;
ptr = ctx->data + offset;
g_assert (offset != INVALID_OFFSET);
//build a directory entry for the metadata root
ptr += 8;
it.rva = read32 (ptr);
ptr += 4;
it.size = read32 (ptr);
it.translated_offset = offset = translate_rva (ctx, it.rva);
ptr = ctx->data + offset;
g_assert (offset != INVALID_OFFSET);
if (it.size < 20)
ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small %d (at least 20 bytes required for initial decoding)", it.size));
if (read32 (ptr) != 0x424A5342)
ADD_ERROR (ctx, g_strdup_printf ("Invalid metadata signature, expected 0x424A5342 but got %08x", read32 (ptr)));
offset = pad4 (offset + 16 + read32 (ptr + 12));
if (!bounds_check_datadir (&it, offset, 4))
ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small %d (at least %d bytes required for flags decoding)", it.size, offset + 4 - it.translated_offset));
ptr = ctx->data + offset; //move to streams header
section_count = read16 (ptr + 2);
if (section_count < 2)
ADD_ERROR (ctx, g_strdup_printf ("Metadata root section must have at least 2 streams (#~ and #GUID)"));
ptr += 4;
offset += 4;
for (i = 0; i < section_count; ++i) {
guint32 stream_off, stream_size;
int string_size, stream_idx;
if (!bounds_check_datadir (&it, offset, 8))
ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small for initial decode of stream header %d, missing %d bytes", i, offset + 9 - it.translated_offset));
stream_off = it.translated_offset + read32 (ptr);
stream_size = read32 (ptr + 4);
if (!bounds_check_datadir (&it, stream_off, stream_size))
ADD_ERROR (ctx, g_strdup_printf ("Invalid stream header %d offset/size pair %x/%x", 0, stream_off, stream_size));
ptr += 8;
offset += 8;
for (string_size = 0; string_size < 32; ++string_size) {
if (!bounds_check_datadir (&it, offset++, 1))
ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small to decode stream header %d name", i));
if (!ptr [string_size])
break;
}
if (ptr [string_size])
ADD_ERROR (ctx, g_strdup_printf ("Metadata stream header %d name larger than 32 bytes", i));
if (!strncmp ("#Strings", ptr, 9))
stream_idx = STRINGS_STREAM;
else if (!strncmp ("#US", ptr, 4))
stream_idx = USER_STRINGS_STREAM;
else if (!strncmp ("#Blob", ptr, 6))
stream_idx = BLOB_STREAM;
else if (!strncmp ("#GUID", ptr, 6))
stream_idx = GUID_STREAM;
else if (!strncmp ("#~", ptr, 3))
stream_idx = TILDE_STREAM;
else {
ADD_WARNING (ctx, g_strdup_printf ("Metadata stream header %d invalid name %s", i, ptr));
offset = pad4 (offset);
ptr = ctx->data + offset;
continue;
}
if (ctx->metadata_streams [stream_idx].offset != 0)
ADD_ERROR (ctx, g_strdup_printf ("Duplicated metadata stream header %s", ptr));
ctx->metadata_streams [stream_idx].offset = stream_off;
ctx->metadata_streams [stream_idx].size = stream_size;
offset = pad4 (offset);
ptr = ctx->data + offset;
}
if (!ctx->metadata_streams [TILDE_STREAM].size)
ADD_ERROR (ctx, g_strdup_printf ("Metadata #~ stream missing"));
if (!ctx->metadata_streams [GUID_STREAM].size)
ADD_ERROR (ctx, g_strdup_printf ("Metadata guid stream missing"));
}
static void
verify_tables_schema (VerifyContext *ctx)
{
OffsetAndSize tables_area = ctx->metadata_streams [TILDE_STREAM];
unsigned offset = tables_area.offset;
const char *ptr = ctx->data + offset;
guint64 valid_tables;
guint32 count;
int i;
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 && 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]));
valid_tables = read64 (ptr + 8);
count = 0;
for (i = 0; i < 64; ++i) {
if (!(valid_tables & ((guint64)1 << i)))
continue;
/*MS Extensions: 0x3 0x5 0x7 0x13 0x16
Unused: 0x1E 0x1F 0x2D-0x3F
We don't care about the MS extensions.*/
if (i == 0x3 || i == 0x5 || i == 0x7 || i == 0x13 || i == 0x16)
ADD_ERROR (ctx, g_strdup_printf ("The metadata verifier doesn't support MS specific table %x", i));
if (i == 0x1E || i == 0x1F || i >= 0x2D)
ADD_ERROR (ctx, g_strdup_printf ("Invalid table %x", i));
++count;
}
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)) {
guint32 row_count = read32 (ptr);
if (row_count > (1 << 24) - 1)
ADD_ERROR (ctx, g_strdup_printf ("Invalid Table %d row count: %d. Mono only supports 16777215 rows", i, row_count));
ptr += 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
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_with_image (MonoImage *image, guint32 offset, gboolean allow_empty)
{
guint32 heap_offset = (char*)image->heap_strings.data - image->raw_data;
guint32 heap_size = image->heap_strings.size;
glong length;
const char *data = image->raw_data + heap_offset;
if (offset >= heap_size)
return FALSE;
if (CHECK_ADDP_OVERFLOW_UN (data, offset))
return FALSE;
if (!mono_utf8_validate_and_len_with_bounds (data + offset, heap_size - offset, &length, NULL))
return FALSE;
return allow_empty || length > 0;
}
static gboolean
is_valid_string_full (VerifyContext *ctx, guint32 offset, gboolean allow_empty)
{
return is_valid_string_full_with_image (ctx->image, offset, allow_empty);
}
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
get_coded_index_table (int kind, guint32 coded_token)
{
guint32 idx, bits = coded_index_desc [kind];
kind += 2;
idx = coded_token & ((1 << bits) - 1);
return coded_index_desc [kind + idx];
}
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_with_image (MonoImage *image, 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 <= image->tables [table].rows;
}
static gboolean
is_valid_coded_index (VerifyContext *ctx, int token_kind, guint32 coded_token)
{
return is_valid_coded_index_with_image (ctx->image, token_kind, coded_token);
}
typedef struct {
guint32 token;
guint32 col_size;
guint32 col_offset;
MonoTableInfo *table;
} 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 at idx %d\n", token, ((const char*)row - loc->table->base) / loc->table->row_size) );
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);
locator.table = tinfo;
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 = mono_binary_search (&locator, base, tinfo->rows, tinfo->row_size, token_locator);
if (!res)
return -1;
return (res - base) / tinfo->row_size;
}
/*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
mono_verifier_is_corlib (MonoImage *image)
{
gboolean trusted_location = !mono_security_core_clr_enabled () ?
TRUE : mono_security_core_clr_is_platform_image (image);
return trusted_location && image->module_name && !strcmp ("mscorlib.dll", image->module_name);
}
static gboolean
typedef_is_system_object (VerifyContext *ctx, guint32 *data)
{
return mono_verifier_is_corlib (ctx->image) && !string_cmp (ctx, "System", data [MONO_TYPEDEF_NAMESPACE]) && !string_cmp (ctx, "Object", data [MONO_TYPEDEF_NAME]);
}
static gboolean
decode_value (const char *_ptr, unsigned available, unsigned *value, unsigned *size)
{
unsigned char b;
const unsigned char *ptr = (const unsigned char *)_ptr;
if (!available)
return FALSE;
b = *ptr;
*value = *size = 0;
if ((b & 0x80) == 0) {
*size = 1;
*value = b;
} else if ((b & 0x40) == 0) {
if (available < 2)
return FALSE;
*size = 2;
*value = ((b & 0x3f) << 8 | ptr [1]);
} else {
if (available < 4)
return FALSE;
*size = 4;
*value = ((b & 0x1f) << 24) |
(ptr [1] << 16) |
(ptr [2] << 8) |
ptr [3];
}
return TRUE;
}
static gboolean
decode_signature_header (VerifyContext *ctx, guint32 offset, guint32 *size, const char **first_byte)
{
MonoStreamHeader blob = ctx->image->heap_blob;
guint32 value, enc_size;
if (offset >= blob.size)
return FALSE;
if (!decode_value (blob.data + offset, blob.size - offset, &value, &enc_size))
return FALSE;
if (CHECK_ADD4_OVERFLOW_UN (offset, enc_size))
return FALSE;
offset += enc_size;
if (ADD_IS_GREATER_OR_OVF (offset, value, blob.size))
return FALSE;
*size = value;
*first_byte = blob.data + offset;
return TRUE;
}
static gboolean
safe_read (const char **_ptr, const char *limit, unsigned *dest, int size)
{
const char *ptr = *_ptr;
if (ptr + size > limit)
return FALSE;
switch (size) {
case 1:
*dest = *((guint8*)ptr);
++ptr;
break;
case 2:
*dest = read16 (ptr);
ptr += 2;
break;
case 4:
*dest = read32 (ptr);
ptr += 4;
break;
}
*_ptr = ptr;
return TRUE;
}
static gboolean
safe_read_compressed_int (const char **_ptr, const char *limit, unsigned *dest)
{
unsigned size = 0;
const char *ptr = *_ptr;
gboolean res = decode_value (ptr, limit - ptr, dest, &size);
*_ptr = ptr + size;
return res;
}
#define safe_read8(VAR, PTR, LIMIT) safe_read (&PTR, LIMIT, &VAR, 1)
#define safe_read_cint(VAR, PTR, LIMIT) safe_read_compressed_int (&PTR, LIMIT, &VAR)
#define safe_read16(VAR, PTR, LIMIT) safe_read (&PTR, LIMIT, &VAR, 2)
#define safe_read32(VAR, PTR, LIMIT) safe_read (&PTR, LIMIT, &VAR, 4)
static gboolean
parse_type (VerifyContext *ctx, const char **_ptr, const char *end);
static gboolean
parse_method_signature (VerifyContext *ctx, const char **_ptr, const char *end, gboolean allow_sentinel, gboolean allow_unmanaged);
static gboolean
parse_custom_mods (VerifyContext *ctx, const char **_ptr, const char *end)
{
const char *ptr = *_ptr;
unsigned type = 0;
unsigned token = 0;
while (TRUE) {
if (!safe_read8 (type, ptr, end))
FAIL (ctx, g_strdup ("CustomMod: Not enough room for the type"));
if (type != MONO_TYPE_CMOD_REQD && type != MONO_TYPE_CMOD_OPT) {
--ptr;
break;
}
if (!safe_read_cint (token, ptr, end))
FAIL (ctx, g_strdup ("CustomMod: Not enough room for the token"));
if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, token) || !get_coded_index_token (TYPEDEF_OR_REF_DESC, token))
FAIL (ctx, g_strdup_printf ("CustomMod: invalid TypeDefOrRef token %x", token));
}
*_ptr = ptr;
return TRUE;
}
static gboolean
parse_array_shape (VerifyContext *ctx, const char **_ptr, const char *end)
{
const char *ptr = *_ptr;
unsigned val = 0;
unsigned size, num, i;
if (!safe_read8 (val, ptr, end))
FAIL (ctx, g_strdup ("ArrayShape: Not enough room for Rank"));
if (val == 0)
FAIL (ctx, g_strdup ("ArrayShape: Invalid shape with zero Rank"));
if (!safe_read_cint (size, ptr, end))
FAIL (ctx, g_strdup ("ArrayShape: Not enough room for NumSizes"));
for (i = 0; i < size; ++i) {
if (!safe_read_cint (num, ptr, end))
FAIL (ctx, g_strdup_printf ("ArrayShape: Not enough room for Size of rank %d", i + 1));
}
if (!safe_read_cint (size, ptr, end))
FAIL (ctx, g_strdup ("ArrayShape: Not enough room for NumLoBounds"));
for (i = 0; i < size; ++i) {
if (!safe_read_cint (num, ptr, end))
FAIL (ctx, g_strdup_printf ("ArrayShape: Not enough room for LoBound of rank %d", i + 1));
}
*_ptr = ptr;
return TRUE;
}
static gboolean
parse_generic_inst (VerifyContext *ctx, const char **_ptr, const char *end)
{
const char *ptr = *_ptr;
unsigned type;
unsigned count, token, i;
if (!safe_read8 (type, ptr, end))
FAIL (ctx, g_strdup ("GenericInst: Not enough room for kind"));
if (type != MONO_TYPE_CLASS && type != MONO_TYPE_VALUETYPE)
FAIL (ctx, g_strdup_printf ("GenericInst: Invalid GenericInst kind %x\n", type));
if (!safe_read_cint (token, ptr, end))
FAIL (ctx, g_strdup ("GenericInst: Not enough room for type token"));
if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, token) || !get_coded_index_token (TYPEDEF_OR_REF_DESC, token))
FAIL (ctx, g_strdup_printf ("GenericInst: invalid TypeDefOrRef token %x", token));
if (ctx->token) {
if (mono_metadata_token_index (ctx->token) == get_coded_index_token (TYPEDEF_OR_REF_DESC, token) &&
mono_metadata_token_table (ctx->token) == get_coded_index_table (TYPEDEF_OR_REF_DESC, token))
FAIL (ctx, g_strdup_printf ("Type: Recurside generic instance specification (%x). A type signature can't reference itself", ctx->token));
}
if (!safe_read_cint (count, ptr, end))
FAIL (ctx, g_strdup ("GenericInst: Not enough room for argument count"));
if (count == 0)
FAIL (ctx, g_strdup ("GenericInst: Zero arguments generic instance"));
for (i = 0; i < count; ++i) {
if (!parse_custom_mods (ctx, &ptr, end))
FAIL (ctx, g_strdup ("Type: Failed to parse pointer custom attr"));
if (!parse_type (ctx, &ptr, end))
FAIL (ctx, g_strdup_printf ("GenericInst: invalid generic argument %d", i + 1));
}
*_ptr = ptr;
return TRUE;
}
static gboolean
parse_type (VerifyContext *ctx, const char **_ptr, const char *end)
{
const char *ptr = *_ptr;
unsigned type;
unsigned token = 0;
if (!safe_read8 (type, ptr, end))
FAIL (ctx, g_strdup ("Type: Not enough room for the type"));
if (!((type >= MONO_TYPE_BOOLEAN && type <= MONO_TYPE_PTR) ||
(type >= MONO_TYPE_VALUETYPE && type <= MONO_TYPE_GENERICINST) ||
(type >= MONO_TYPE_I && type <= MONO_TYPE_U) ||
(type >= MONO_TYPE_FNPTR && type <= MONO_TYPE_MVAR)))
FAIL (ctx, g_strdup_printf ("Type: Invalid type kind %x\n", type));
switch (type) {
case MONO_TYPE_PTR:
if (!parse_custom_mods (ctx, &ptr, end))
FAIL (ctx, g_strdup ("Type: Failed to parse pointer custom attr"));
if (!safe_read8 (type, ptr, end))
FAIL (ctx, g_strdup ("Type: Not enough room to parse the pointer type"));
if (type != MONO_TYPE_VOID) {
--ptr;
if (!parse_type (ctx, &ptr, end))
FAIL (ctx, g_strdup ("Type: Could not parse pointer type"));
}
break;
case MONO_TYPE_VALUETYPE:
case MONO_TYPE_CLASS:
if (!safe_read_cint (token, ptr, end))
FAIL (ctx, g_strdup ("Type: Not enough room for the type token"));
if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, token) || !get_coded_index_token (TYPEDEF_OR_REF_DESC, token))
FAIL (ctx, g_strdup_printf ("Type: invalid TypeDefOrRef token %x", token));
if (!get_coded_index_token (TYPEDEF_OR_REF_DESC, token))
FAIL (ctx, g_strdup_printf ("Type: zero TypeDefOrRef token %x", token));
if (ctx->token) {
if (mono_metadata_token_index (ctx->token) == get_coded_index_token (TYPEDEF_OR_REF_DESC, token) &&
mono_metadata_token_table (ctx->token) == get_coded_index_table (TYPEDEF_OR_REF_DESC, token))
FAIL (ctx, g_strdup_printf ("Type: Recursive type specification (%x). A type signature can't reference itself", ctx->token));
}
break;
case MONO_TYPE_VAR:
case MONO_TYPE_MVAR:
if (!safe_read_cint (token, ptr, end))
FAIL (ctx, g_strdup ("Type: Not enough room for to decode generic argument number"));
break;
case MONO_TYPE_ARRAY:
if (!parse_type (ctx, &ptr, end))
FAIL (ctx, g_strdup ("Type: Could not parse array type"));
if (!parse_array_shape (ctx, &ptr, end))
FAIL (ctx, g_strdup ("Type: Could not parse array shape"));
break;
case MONO_TYPE_GENERICINST:
if (!parse_generic_inst (ctx, &ptr, end))
FAIL (ctx, g_strdup ("Type: Could not parse generic inst"));
break;
case MONO_TYPE_FNPTR:
if (!parse_method_signature (ctx, &ptr, end, TRUE, TRUE))
FAIL (ctx, g_strdup ("Type: Could not parse method pointer signature"));
break;
case MONO_TYPE_SZARRAY:
if (!parse_custom_mods (ctx, &ptr, end))
FAIL (ctx, g_strdup ("Type: Failed to parse array element custom attr"));
if (!parse_type (ctx, &ptr, end))
FAIL (ctx, g_strdup ("Type: Could not parse array type"));
break;
}
*_ptr = ptr;
return TRUE;
}
static gboolean
parse_return_type (VerifyContext *ctx, const char **_ptr, const char *end)
{
const char *ptr;
unsigned type = 0;
if (!parse_custom_mods (ctx, _ptr, end))
return FALSE;
ptr = *_ptr;
if (!safe_read8 (type, ptr, end))
FAIL (ctx, g_strdup ("ReturnType: Not enough room for the type"));
if (type == MONO_TYPE_VOID || type == MONO_TYPE_TYPEDBYREF) {
*_ptr = ptr;
return TRUE;
}
//it's a byref, update the cursor ptr
if (type == MONO_TYPE_BYREF)
*_ptr = ptr;
return parse_type (ctx, _ptr, end);
}
static gboolean
parse_param (VerifyContext *ctx, const char **_ptr, const char *end)
{
const char *ptr;
unsigned type = 0;
if (!parse_custom_mods (ctx, _ptr, end))
return FALSE;
ptr = *_ptr;
if (!safe_read8 (type, ptr, end))
FAIL (ctx, g_strdup ("Param: Not enough room for the type"));
if (type == MONO_TYPE_TYPEDBYREF) {
*_ptr = ptr;
return TRUE;
}
//it's a byref, update the cursor ptr
if (type == MONO_TYPE_BYREF) {
*_ptr = ptr;
if (!parse_custom_mods (ctx, _ptr, end))
return FALSE;
}
return parse_type (ctx, _ptr, end);
}
static gboolean
parse_method_signature (VerifyContext *ctx, const char **_ptr, const char *end, gboolean allow_sentinel, gboolean allow_unmanaged)
{
unsigned cconv = 0;
unsigned param_count = 0, gparam_count = 0, type = 0, i;
const char *ptr = *_ptr;
gboolean saw_sentinel = FALSE;
if (!safe_read8 (cconv, ptr, end))
FAIL (ctx, g_strdup ("MethodSig: Not enough room for the call conv"));
if (cconv & 0x80)
FAIL (ctx, g_strdup ("MethodSig: CallConv has 0x80 set"));
if (allow_unmanaged) {
if ((cconv & 0x0F) > MONO_CALL_VARARG)
FAIL (ctx, g_strdup_printf ("MethodSig: CallConv is not valid, it's %x", cconv & 0x0F));
} else if ((cconv & 0x0F) != MONO_CALL_DEFAULT && (cconv & 0x0F) != MONO_CALL_VARARG)
FAIL (ctx, g_strdup_printf ("MethodSig: CallConv is not Default or Vararg, it's %x", cconv & 0x0F));
if ((cconv & 0x10) && !safe_read_cint (gparam_count, ptr, end))
FAIL (ctx, g_strdup ("MethodSig: Not enough room for the generic param count"));
if ((cconv & 0x10) && gparam_count == 0)
FAIL (ctx, g_strdup ("MethodSig: Signature with generics but zero arity"));
if (allow_unmanaged && (cconv & 0x10))
FAIL (ctx, g_strdup ("MethodSig: Standalone signature with generic params"));
if (!safe_read_cint (param_count, ptr, end))
FAIL (ctx, g_strdup ("MethodSig: Not enough room for the param count"));
if (!parse_return_type (ctx, &ptr, end))
FAIL (ctx, g_strdup ("MethodSig: Error parsing return type"));
for (i = 0; i < param_count; ++i) {
if (allow_sentinel) {
if (!safe_read8 (type, ptr, end))
FAIL (ctx, g_strdup_printf ("MethodSig: Not enough room for param %d type", i));
if (type == MONO_TYPE_SENTINEL) {
if ((cconv & 0x0F) != MONO_CALL_VARARG)
FAIL (ctx, g_strdup ("MethodSig: Found sentinel but signature is not vararg"));
if (saw_sentinel)
FAIL (ctx, g_strdup ("MethodSig: More than one sentinel type"));
saw_sentinel = TRUE;
} else {
--ptr;
}
}
if (!parse_param (ctx, &ptr, end))
FAIL (ctx, g_strdup_printf ("MethodSig: Error parsing arg %d", i));
}
*_ptr = ptr;
return TRUE;
}
static gboolean
parse_property_signature (VerifyContext *ctx, const char **_ptr, const char *end)
{
unsigned sig = 0;
unsigned param_count = 0, i;
const char *ptr = *_ptr;
if (!safe_read8 (sig, ptr, end))
FAIL (ctx, g_strdup ("PropertySig: Not enough room for signature"));
if (sig != 0x08 && sig != 0x28)
FAIL (ctx, g_strdup_printf ("PropertySig: Signature is not 0x28 or 0x08: %x", sig));
if (!safe_read_cint (param_count, ptr, end))
FAIL (ctx, g_strdup ("PropertySig: Not enough room for the param count"));
if (!parse_custom_mods (ctx, &ptr, end))
return FALSE;
if (!parse_type (ctx, &ptr, end))
FAIL (ctx, g_strdup ("PropertySig: Could not parse property type"));
for (i = 0; i < param_count; ++i) {
if (!parse_custom_mods (ctx, &ptr, end))
FAIL (ctx, g_strdup ("Type: Failed to parse pointer custom attr"));
if (!parse_type (ctx, &ptr, end))
FAIL (ctx, g_strdup_printf ("PropertySig: Error parsing arg %d", i));
}
*_ptr = ptr;
return TRUE;
}
static gboolean
parse_field (VerifyContext *ctx, const char **_ptr, const char *end)
{
const char *ptr = *_ptr;
unsigned signature = 0;
if (!safe_read8 (signature, ptr, end))
FAIL (ctx, g_strdup ("Field: Not enough room for field signature"));
if (signature != 0x06)
FAIL (ctx, g_strdup_printf ("Field: Invalid signature 0x%x, must be 6", signature));
if (!parse_custom_mods (ctx, &ptr, end))
return FALSE;
if (safe_read8 (signature, ptr, end)) {
if (signature != MONO_TYPE_BYREF)
--ptr;
}
*_ptr = ptr;
return parse_type (ctx, _ptr, end);
}
static gboolean
parse_locals_signature (VerifyContext *ctx, const char **_ptr, const char *end)
{
unsigned sig = 0;
unsigned locals_count = 0, i;
const char *ptr = *_ptr;
if (!safe_read8 (sig, ptr, end))
FAIL (ctx, g_strdup ("LocalsSig: Not enough room for signature"));
if (sig != 0x07)
FAIL (ctx, g_strdup_printf ("LocalsSig: Signature is not 0x28 or 0x08: %x", sig));
if (!safe_read_cint (locals_count, ptr, end))
FAIL (ctx, g_strdup ("LocalsSig: Not enough room for the param count"));
/* LAMEIMPL: MS sometimes generates empty local signatures and its verifier is ok with.
if (locals_count == 0)
FAIL (ctx, g_strdup ("LocalsSig: Signature with zero locals"));
*/
for (i = 0; i < locals_count; ++i) {
if (!safe_read8 (sig, ptr, end))
FAIL (ctx, g_strdup ("LocalsSig: Not enough room for type"));
while (sig == MONO_TYPE_CMOD_REQD || sig == MONO_TYPE_CMOD_OPT || sig == MONO_TYPE_PINNED) {
if (sig != MONO_TYPE_PINNED && !parse_custom_mods (ctx, &ptr, end))
FAIL (ctx, g_strdup_printf ("LocalsSig: Error parsing local %d", i));
if (!safe_read8 (sig, ptr, end))
FAIL (ctx, g_strdup ("LocalsSig: Not enough room for type"));
}
if (sig == MONO_TYPE_BYREF) {
if (!safe_read8 (sig, ptr, end))
FAIL (ctx, g_strdup_printf ("Type: Not enough room for byref type for local %d", i));
if (sig == MONO_TYPE_TYPEDBYREF)
FAIL (ctx, g_strdup_printf ("Type: Invalid type typedref& for local %d", i));
}
if (sig == MONO_TYPE_TYPEDBYREF)
continue;
--ptr;
if (!parse_type (ctx, &ptr, end))
FAIL (ctx, g_strdup_printf ("LocalsSig: Error parsing local %d", i));
}
*_ptr = ptr;
return TRUE;
}
static gboolean
is_valid_field_signature (VerifyContext *ctx, guint32 offset)
{
guint32 size = 0;
unsigned signature = 0;
const char *ptr = NULL, *end;
if (!decode_signature_header (ctx, offset, &size, &ptr))
FAIL (ctx, g_strdup ("FieldSig: Could not decode signature header"));
end = ptr + size;
if (!safe_read8 (signature, ptr, end))
FAIL (ctx, g_strdup ("FieldSig: Not enough room for the signature"));
if (signature != 6)
FAIL (ctx, g_strdup_printf ("FieldSig: Invalid signature %x", signature));
--ptr;
return parse_field (ctx, &ptr, end);
}
static gboolean
is_valid_method_signature (VerifyContext *ctx, guint32 offset)
{
guint32 size = 0;
const char *ptr = NULL, *end;
if (!decode_signature_header (ctx, offset, &size, &ptr))
FAIL (ctx, g_strdup ("MethodSig: Could not decode signature header"));
end = ptr + size;
return parse_method_signature (ctx, &ptr, end, FALSE, FALSE);
}
static gboolean
is_valid_memberref_method_signature (VerifyContext *ctx, guint32 offset)
{
guint32 size = 0;
const char *ptr = NULL, *end;
if (!decode_signature_header (ctx, offset, &size, &ptr))
FAIL (ctx, g_strdup ("MemberRefSig: Could not decode signature header"));
end = ptr + size;
return parse_method_signature (ctx, &ptr, end, TRUE, FALSE);
}
static gboolean
is_valid_method_or_field_signature (VerifyContext *ctx, guint32 offset)
{
guint32 size = 0;
unsigned signature = 0;
const char *ptr = NULL, *end;
if (!decode_signature_header (ctx, offset, &size, &ptr))
FAIL (ctx, g_strdup ("MemberRefSig: Could not decode signature header"));
end = ptr + size;
if (!safe_read8 (signature, ptr, end))
FAIL (ctx, g_strdup ("MemberRefSig: Not enough room for the call conv"));
--ptr;
if (signature == 0x06)
return parse_field (ctx, &ptr, end);
return parse_method_signature (ctx, &ptr, end, TRUE, FALSE);
}
static gboolean
is_valid_cattr_blob (VerifyContext *ctx, guint32 offset)
{
guint32 size = 0;
unsigned prolog = 0;
const char *ptr = NULL, *end;
if (!offset)
return TRUE;
if (!decode_signature_header (ctx, offset, &size, &ptr))
FAIL (ctx, g_strdup ("CustomAttribute: Could not decode signature header"));
end = ptr + size;
if (!safe_read16 (prolog, ptr, end))
FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for prolog"));
if (prolog != 1)
FAIL (ctx, g_strdup_printf ("CustomAttribute: Prolog is 0x%x, expected 0x1", prolog));
return TRUE;
}
static gboolean
is_valid_cattr_type (MonoType *type)
{
MonoClass *klass;
if (type->type == MONO_TYPE_OBJECT || (type->type >= MONO_TYPE_BOOLEAN && type->type <= MONO_TYPE_STRING))
return TRUE;
if (type->type == MONO_TYPE_VALUETYPE) {
klass = mono_class_from_mono_type (type);
return klass && klass->enumtype;
}
if (type->type == MONO_TYPE_CLASS)
return mono_class_from_mono_type (type) == mono_defaults.systemtype_class;
return FALSE;
}
static gboolean
is_valid_ser_string_full (VerifyContext *ctx, const char **str_start, guint32 *str_len, const char **_ptr, const char *end)
{
guint32 size = 0;
const char *ptr = *_ptr;
*str_start = NULL;
*str_len = 0;
if (ptr >= end)
FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for string size"));
/*NULL string*/
if (*ptr == (char)0xFF) {
*_ptr = ptr + 1;
return TRUE;
}
if (!safe_read_cint (size, ptr, end))
FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for string size"));
if (ADDP_IS_GREATER_OR_OVF (ptr, size, end))
FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for string"));
*str_start = ptr;
*str_len = size;
*_ptr = ptr + size;
return TRUE;
}
static gboolean
is_valid_ser_string (VerifyContext *ctx, const char **_ptr, const char *end)
{
const char *dummy_str;
guint32 dummy_int;
return is_valid_ser_string_full (ctx, &dummy_str, &dummy_int, _ptr, end);
}
static MonoClass*
get_enum_by_encoded_name (VerifyContext *ctx, const char **_ptr, const char *end)
{
MonoType *type;
MonoClass *klass;
const char *str_start = NULL;
const char *ptr = *_ptr;
char *enum_name;
guint32 str_len = 0;
if (!is_valid_ser_string_full (ctx, &str_start, &str_len, &ptr, end))
return NULL;
/*NULL or empty string*/
if (str_start == NULL || str_len == 0) {
ADD_ERROR_NO_RETURN (ctx, g_strdup ("CustomAttribute: Null or empty enum name"));
return NULL;
}
enum_name = g_memdup (str_start, str_len + 1);
enum_name [str_len] = 0;
type = mono_reflection_type_from_name (enum_name, ctx->image);
if (!type) {
ADD_ERROR_NO_RETURN (ctx, g_strdup_printf ("CustomAttribute: Invalid enum class %s", enum_name));
g_free (enum_name);
return NULL;
}
g_free (enum_name);
klass = mono_class_from_mono_type (type);
if (!klass || !klass->enumtype) {
ADD_ERROR_NO_RETURN (ctx, g_strdup_printf ("CustomAttribute:Class %s::%s is not an enum", klass->name_space, klass->name));
return NULL;
}
*_ptr = ptr;
return klass;
}
static gboolean
is_valid_fixed_param (VerifyContext *ctx, MonoType *mono_type, const char **_ptr, const char *end)
{
MonoClass *klass;
const char *ptr = *_ptr;
int elem_size = 0;
guint32 element_count, i;
int type;
klass = mono_type->data.klass;
type = mono_type->type;
handle_enum:
switch (type) {
case MONO_TYPE_BOOLEAN:
case MONO_TYPE_I1:
case MONO_TYPE_U1:
elem_size = 1;
break;
case MONO_TYPE_I2:
case MONO_TYPE_U2:
case MONO_TYPE_CHAR:
elem_size = 2;
break;
case MONO_TYPE_I4:
case MONO_TYPE_U4:
case MONO_TYPE_R4:
elem_size = 4;
break;
case MONO_TYPE_I8:
case MONO_TYPE_U8:
case MONO_TYPE_R8:
elem_size = 8;
break;
case MONO_TYPE_STRING:
*_ptr = ptr;
return is_valid_ser_string (ctx, _ptr, end);
case MONO_TYPE_OBJECT: {
unsigned sub_type = 0;
if (!safe_read8 (sub_type, ptr, end))
FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for array type"));
if (sub_type >= MONO_TYPE_BOOLEAN && sub_type <= MONO_TYPE_STRING) {
type = sub_type;
goto handle_enum;
}
if (sub_type == MONO_TYPE_ENUM) {
klass = get_enum_by_encoded_name (ctx, &ptr, end);
if (!klass)
return FALSE;
klass = klass->element_class;
type = klass->byval_arg.type;
goto handle_enum;
}
if (sub_type == 0x50) { /*Type*/
*_ptr = ptr;
return is_valid_ser_string (ctx, _ptr, end);
}
if (sub_type == MONO_TYPE_SZARRAY) {
MonoType simple_type = {{0}};
unsigned etype = 0;
if (!safe_read8 (etype, ptr, end))
FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for array element type"));
if (etype == MONO_TYPE_ENUM) {
klass = get_enum_by_encoded_name (ctx, &ptr, end);
if (!klass)
return FALSE;
} else if (etype == 0x50 || etype == MONO_TYPE_CLASS) {
klass = mono_defaults.systemtype_class;
} else if ((etype >= MONO_TYPE_BOOLEAN && etype <= MONO_TYPE_STRING) || etype == 0x51) {
simple_type.type = etype == 0x51 ? MONO_TYPE_OBJECT : etype;
klass = mono_class_from_mono_type (&simple_type);
} else
FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid array element type %x", etype));
type = MONO_TYPE_SZARRAY;
goto handle_enum;
}
FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid boxed object type %x", sub_type));
}
case MONO_TYPE_CLASS:
if (klass != mono_defaults.systemtype_class)
FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid class parameter type %s:%s ",klass->name_space, klass->name));
*_ptr = ptr;
return is_valid_ser_string (ctx, _ptr, end);
case MONO_TYPE_VALUETYPE:
if (!klass || !klass->enumtype)
FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid valuetype parameter expected enum %s:%s ",klass->name_space, klass->name));
klass = klass->element_class;
type = klass->byval_arg.type;
goto handle_enum;
case MONO_TYPE_SZARRAY:
mono_type = &klass->byval_arg;
if (!is_valid_cattr_type (mono_type))
FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid array element type %s:%s ",klass->name_space, klass->name));
if (!safe_read32 (element_count, ptr, end))
FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid class parameter type %s:%s ",klass->name_space, klass->name));
if (element_count == 0xFFFFFFFFu) {
*_ptr = ptr;
return TRUE;
}
for (i = 0; i < element_count; ++i) {
if (!is_valid_fixed_param (ctx, mono_type, &ptr, end))
return FALSE;
}
*_ptr = ptr;
return TRUE;
default:
FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid parameter type %x ", type));
}
if (ADDP_IS_GREATER_OR_OVF (ptr, elem_size, end))
FAIL (ctx, g_strdup ("CustomAttribute: Not enough space for element"));
*_ptr = ptr + elem_size;
return TRUE;
}
static gboolean
is_valid_cattr_content (VerifyContext *ctx, MonoMethod *ctor, const char *ptr, guint32 size)
{
MonoError error;
unsigned prolog = 0;
const char *end;
MonoMethodSignature *sig;
int args, i;
unsigned num_named;
if (!ctor)
FAIL (ctx, g_strdup ("CustomAttribute: Invalid constructor"));
sig = mono_method_signature_checked (ctor, &error);
if (!mono_error_ok (&error)) {
ADD_ERROR_NO_RETURN (ctx, g_strdup_printf ("CustomAttribute: Invalid constructor signature %s", mono_error_get_message (&error)));
mono_error_cleanup (&error);
return FALSE;
}
if (sig->sentinelpos != -1 || sig->call_convention == MONO_CALL_VARARG)
FAIL (ctx, g_strdup ("CustomAttribute: Constructor cannot have VARAG signature"));
end = ptr + size;
if (!safe_read16 (prolog, ptr, end))
FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for prolog"));
if (prolog != 1)
FAIL (ctx, g_strdup_printf ("CustomAttribute: Prolog is 0x%x, expected 0x1", prolog));
args = sig->param_count;
for (i = 0; i < args; ++i) {
MonoType *arg_type = sig->params [i];
if (!is_valid_fixed_param (ctx, arg_type, &ptr, end))
return FALSE;
}
if (!safe_read16 (num_named, ptr, end))
FAIL (ctx, g_strdup ("CustomAttribute: Not enough space for num_named field"));
for (i = 0; i < num_named; ++i) {
MonoType *type, simple_type = {{0}};
unsigned kind;
if (!safe_read8 (kind, ptr, end))
FAIL (ctx, g_strdup_printf ("CustomAttribute: Not enough space for named parameter %d kind", i));
if (kind != 0x53 && kind != 0x54)
FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid named parameter %d kind %x", i, kind));
if (!safe_read8 (kind, ptr, end))
FAIL (ctx, g_strdup_printf ("CustomAttribute: Not enough space for named parameter %d type", i));
if (kind >= MONO_TYPE_BOOLEAN && kind <= MONO_TYPE_STRING) {
simple_type.type = kind;
type = &simple_type;
} else if (kind == MONO_TYPE_ENUM) {
MonoClass *klass = get_enum_by_encoded_name (ctx, &ptr, end);
if (!klass)
return FALSE;
type = &klass->byval_arg;
} else if (kind == 0x50) {
type = &mono_defaults.systemtype_class->byval_arg;
} else if (kind == 0x51) {
type = &mono_defaults.object_class->byval_arg;
} else if (kind == MONO_TYPE_SZARRAY) {
MonoClass *klass;
unsigned etype = 0;
if (!safe_read8 (etype, ptr, end))
FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for array element type"));
if (etype == MONO_TYPE_ENUM) {
klass = get_enum_by_encoded_name (ctx, &ptr, end);
if (!klass)
return FALSE;
} else if (etype == 0x50 || etype == MONO_TYPE_CLASS) {
klass = mono_defaults.systemtype_class;
} else if ((etype >= MONO_TYPE_BOOLEAN && etype <= MONO_TYPE_STRING) || etype == 0x51) {
simple_type.type = etype == 0x51 ? MONO_TYPE_OBJECT : etype;
klass = mono_class_from_mono_type (&simple_type);
} else
FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid array element type %x", etype));
type = &mono_array_class_get (klass, 1)->byval_arg;
} else {
FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid named parameter type %x", kind));
}
if (!is_valid_ser_string (ctx, &ptr, end))
return FALSE;
if (!is_valid_fixed_param (ctx, type, &ptr, end))
return FALSE;
}
return TRUE;
}
static gboolean
is_valid_marshal_spec (VerifyContext *ctx, guint32 offset)
{
OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob);
//TODO do proper verification
return blob.size >= 1 && blob.size - 1 >= offset;
}
static gboolean
is_valid_permission_set (VerifyContext *ctx, guint32 offset)
{
OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob);
//TODO do proper verification
return blob.size >= 1 && blob.size - 1 >= offset;
}
static gboolean
is_valid_standalonesig_blob (VerifyContext *ctx, guint32 offset)
{
guint32 size = 0;
unsigned signature = 0;
const char *ptr = NULL, *end;
if (!decode_signature_header (ctx, offset, &size, &ptr))
FAIL (ctx, g_strdup ("StandAloneSig: Could not decode signature header"));
end = ptr + size;
if (!safe_read8 (signature, ptr, end))
FAIL (ctx, g_strdup ("StandAloneSig: Not enough room for the call conv"));
--ptr;
if (signature == 0x07)
return parse_locals_signature (ctx, &ptr, end);
/*F# and managed C++ produce standalonesig for fields even thou the spec doesn't mention it.*/
if (signature == 0x06)
return parse_field (ctx, &ptr, end);
return parse_method_signature (ctx, &ptr, end, TRUE, TRUE);
}
static gboolean
is_valid_property_sig_blob (VerifyContext *ctx, guint32 offset)
{
guint32 size = 0;
const char *ptr = NULL, *end;
if (!decode_signature_header (ctx, offset, &size, &ptr))
FAIL (ctx, g_strdup ("PropertySig: Could not decode signature header"));
end = ptr + size;
return parse_property_signature (ctx, &ptr, end);
}
static gboolean
is_valid_typespec_blob (VerifyContext *ctx, guint32 offset)
{
guint32 size = 0;
const char *ptr = NULL, *end;
unsigned type = 0;
if (!decode_signature_header (ctx, offset, &size, &ptr))
FAIL (ctx, g_strdup ("TypeSpec: Could not decode signature header"));
end = ptr + size;
if (!parse_custom_mods (ctx, &ptr, end))
return FALSE;
if (!safe_read8 (type, ptr, end))
FAIL (ctx, g_strdup ("TypeSpec: Not enough room for type"));
if (type == MONO_TYPE_BYREF) {
if (!safe_read8 (type, ptr, end))
FAIL (ctx, g_strdup ("TypeSpec: Not enough room for byref type"));
if (type == MONO_TYPE_TYPEDBYREF)
FAIL (ctx, g_strdup ("TypeSpec: Invalid type typedref&"));
}
if (type == MONO_TYPE_TYPEDBYREF)
return TRUE;
--ptr;
return parse_type (ctx, &ptr, end);
}
static gboolean
is_valid_methodspec_blob (VerifyContext *ctx, guint32 offset)
{
guint32 size = 0;
const char *ptr = NULL, *end;
unsigned type = 0;
unsigned count = 0, i;
if (!decode_signature_header (ctx, offset, &size, &ptr))
FAIL (ctx, g_strdup ("MethodSpec: Could not decode signature header"));
end = ptr + size;
if (!safe_read8 (type, ptr, end))
FAIL (ctx, g_strdup ("MethodSpec: Not enough room for call convention"));
if (type != 0x0A)
FAIL (ctx, g_strdup_printf ("MethodSpec: Invalid call convention 0x%x, expected 0x0A", type));
if (!safe_read_cint (count, ptr, end))
FAIL (ctx, g_strdup ("MethodSpec: Not enough room for parameter count"));
if (!count)
FAIL (ctx, g_strdup ("MethodSpec: Zero generic argument count"));
for (i = 0; i < count; ++i) {
if (!parse_custom_mods (ctx, &ptr, end))
return FALSE;
if (!parse_type (ctx, &ptr, end))
FAIL (ctx, g_strdup_printf ("MethodSpec: Could not parse parameter %d", i + 1));
}
return TRUE;
}
static gboolean
is_valid_blob_object (VerifyContext *ctx, guint32 offset, guint32 minsize)
{
OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob);
guint32 entry_size, bytes;
if (blob.size < offset)
return FALSE;
if (!decode_value (ctx->data + offset + blob.offset, blob.size - blob.offset, &entry_size, &bytes))
return FALSE;
if (entry_size < minsize)
return FALSE;
if (CHECK_ADD4_OVERFLOW_UN (entry_size, bytes))
return FALSE;
entry_size += bytes;
return !ADD_IS_GREATER_OR_OVF (offset, entry_size, blob.size);
}
static gboolean
is_valid_constant (VerifyContext *ctx, guint32 type, guint32 offset)
{
OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob);
guint32 size, entry_size, bytes;
if (blob.size < offset)
FAIL (ctx, g_strdup ("ContantValue: invalid offset"));
if (!decode_value (ctx->data + offset + blob.offset, blob.size - blob.offset, &entry_size, &bytes))
FAIL (ctx, g_strdup ("ContantValue: not enough space to decode size"));
if (type == MONO_TYPE_STRING) {
//String is encoded as: compressed_int:len len *bytes
offset += bytes;
if (ADD_IS_GREATER_OR_OVF (offset, entry_size, blob.size))
FAIL (ctx, g_strdup_printf ("ContantValue: not enough space for string, required %d but got %d", entry_size * 2, blob.size - offset));
return TRUE;
}
switch (type) {
case MONO_TYPE_BOOLEAN:
case MONO_TYPE_I1:
case MONO_TYPE_U1:
size = 1;
break;
case MONO_TYPE_CHAR:
case MONO_TYPE_I2:
case MONO_TYPE_U2:
size = 2;
break;
case MONO_TYPE_I4:
case MONO_TYPE_U4:
case MONO_TYPE_R4:
case MONO_TYPE_CLASS:
size = 4;
break;
case MONO_TYPE_I8:
case MONO_TYPE_U8:
case MONO_TYPE_R8:
size = 8;
break;
default:
g_assert_not_reached ();
}
if (size != entry_size)
FAIL (ctx, g_strdup_printf ("ContantValue: Expected size %d but got %d", size, entry_size));
offset += bytes;
if (ADD_IS_GREATER_OR_OVF (offset, size, blob.size))
FAIL (ctx, g_strdup_printf ("ContantValue: Not enough room for constant, required %d but have %d", size, blob.size - offset));
if (type == MONO_TYPE_CLASS && read32 (ctx->data + blob.offset + offset))
FAIL (ctx, g_strdup_printf ("ContantValue: Type is class but value is not null"));
return TRUE;
}
#define FAT_HEADER_INVALID_FLAGS ~(0x3 | 0x8 | 0x10 | 0xF000)
//only 0x01, 0x40 and 0x80 are allowed
#define SECTION_HEADER_INVALID_FLAGS 0x3E
static gboolean
is_valid_method_header (VerifyContext *ctx, guint32 rva, guint32 *locals_token)
{
unsigned local_vars_tok, code_size, offset = mono_cli_rva_image_map (ctx->image, rva);
unsigned header = 0;
unsigned fat_header = 0, size = 0, max_stack;
const char *ptr = NULL, *end;
*locals_token = 0;
if (offset == INVALID_ADDRESS)
FAIL (ctx, g_strdup ("MethodHeader: Invalid RVA"));
ptr = ctx->data + offset;
end = ctx->data + ctx->size; /*no worries if it spawns multiple sections*/
if (!safe_read8 (header, ptr, end))
FAIL (ctx, g_strdup ("MethodHeader: Not enough room for header"));
switch (header & 0x3) {
case 0:
case 1:
FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid header type 0x%x", header & 0x3));
case 2:
header >>= 2;
if (ADDP_IS_GREATER_OR_OVF (ptr, header, end))
FAIL (ctx, g_strdup_printf ("MethodHeader: Not enough room for method body. Required %d, but only %d is available", header, (int)(end - ptr)));
return TRUE;
}
//FAT HEADER
--ptr;
if (!safe_read16 (fat_header, ptr, end))
FAIL (ctx, g_strdup ("MethodHeader: Not enough room for fat header"));
size = (fat_header >> 12) & 0xF;
if (size != 3)
FAIL (ctx, g_strdup ("MethodHeader: header size must be 3"));
if (!safe_read16 (max_stack, ptr, end))
FAIL (ctx, g_strdup ("MethodHeader: Not enough room for max stack"));
if (!safe_read32 (code_size, ptr, end))
FAIL (ctx, g_strdup ("MethodHeader: Not enough room for code size"));
if (!safe_read32 (local_vars_tok, ptr, end))
FAIL (ctx, g_strdup ("MethodHeader: Not enough room for local vars tok"));
if (local_vars_tok) {
if (((local_vars_tok >> 24) & 0xFF) != 0x11)
FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid local vars signature table 0x%x", ((local_vars_tok >> 24) & 0xFF)));
if ((local_vars_tok & 0xFFFFFF) > ctx->image->tables [MONO_TABLE_STANDALONESIG].rows)
FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid local vars signature points to invalid row 0x%x", local_vars_tok & 0xFFFFFF));
if (!(local_vars_tok & 0xFFFFFF))
FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid local vars signature with zero index"));
*locals_token = local_vars_tok & 0xFFFFFF;
}
if (fat_header & FAT_HEADER_INVALID_FLAGS)
FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid fat signature flags %x", fat_header & FAT_HEADER_INVALID_FLAGS));
if (ADDP_IS_GREATER_OR_OVF (ptr, code_size, end))
FAIL (ctx, g_strdup_printf ("MethodHeader: Not enough room for code %d", code_size));
if (!(fat_header & 0x08))
return TRUE;
ptr += code_size;
do {
unsigned section_header = 0, section_size = 0;
gboolean is_fat;
ptr = dword_align (ptr);
if (!safe_read32 (section_header, ptr, end))
FAIL (ctx, g_strdup ("MethodHeader: Not enough room for data section header"));
if (section_header & SECTION_HEADER_INVALID_FLAGS)
FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid section header flags 0x%x", section_header & SECTION_HEADER_INVALID_FLAGS));
is_fat = (section_header & METHOD_HEADER_SECTION_FAT_FORMAT) != 0;
section_size = (section_header >> 8) & (is_fat ? 0xFFFFFF : 0xFF);
if (section_size < 4)
FAIL (ctx, g_strdup_printf ("MethodHeader: Section size too small"));
if (ADDP_IS_GREATER_OR_OVF (ptr, section_size - 4, end)) /*must be section_size -4 as ptr was incremented by safe_read32*/
FAIL (ctx, g_strdup_printf ("MethodHeader: Not enough room for section content %d", section_size));
if (section_header & METHOD_HEADER_SECTION_EHTABLE) {
guint32 i, clauses = section_size / (is_fat ? 24 : 12);
/*
LAMEIMPL: MS emits section_size without accounting for header size.
Mono does as the spec says. section_size is header + section
MS's peverify happily accepts both.
*/
if ((clauses * (is_fat ? 24 : 12) != section_size) && (clauses * (is_fat ? 24 : 12) + 4 != section_size))
FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid EH section size %d, it's not of the expected size %d", section_size, clauses * (is_fat ? 24 : 12)));
/* only verify the class token is verified as the rest is done by the IL verifier*/
for (i = 0; i < clauses; ++i) {
unsigned flags = *(unsigned char*)ptr;
unsigned class_token = 0;
ptr += (is_fat ? 20 : 8);
if (!safe_read32 (class_token, ptr, end))
FAIL (ctx, g_strdup_printf ("MethodHeader: Not enough room for section %d", i));
if (flags == MONO_EXCEPTION_CLAUSE_NONE && class_token) {
guint table = mono_metadata_token_table (class_token);
if (table != MONO_TABLE_TYPEREF && table != MONO_TABLE_TYPEDEF && table != MONO_TABLE_TYPESPEC)
FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid section %d class token table %x", i, table));
if (mono_metadata_token_index (class_token) > ctx->image->tables [table].rows)
FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid section %d class token index %x", i, mono_metadata_token_index (class_token)));
}
}
}
if (!(section_header & METHOD_HEADER_SECTION_MORE_SECTS))
break;
} while (1);
return TRUE;
}
static void
verify_module_table (VerifyContext *ctx)
{
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];
MonoError error;
guint32 i;
for (i = 0; i < table->rows; ++i) {
mono_verifier_verify_typeref_row (ctx->image, i, &error);
add_from_mono_error (ctx, &error);
}
}
/*bits 9,11,14,15,19,21,24-31 */
#define INVALID_TYPEDEF_FLAG_BITS ((1 << 6) | (1 << 9) | (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, visibility;
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_FLAGS] & TYPE_ATTRIBUTE_INTERFACE) && (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));
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 (data [MONO_TYPEDEF_EXTENDS] && !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 (data [MONO_TYPEDEF_EXTENDS] && !get_coded_index_token (TYPEDEF_OR_REF_DESC, data [MONO_TYPEDEF_EXTENDS]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d zero coded extend field coded index 0x%08x", i, data [MONO_TYPEDEF_EXTENDS]));
visibility = data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_VISIBILITY_MASK;
if ((visibility >= TYPE_ATTRIBUTE_NESTED_PUBLIC && visibility <= TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM) &&
search_sorted_table (ctx, MONO_TABLE_NESTEDCLASS, MONO_NESTED_CLASS_NESTED, i + 1) == -1)
ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d has nested visibility but no rows in the NestedClass table", 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];
}
}
static void
verify_typedef_table_full (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEDEF];
guint32 data [MONO_TYPEDEF_SIZE];
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 (i == 0) {
/*XXX it's ok if <module> extends object, or anything at all, actually. */
/*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"));
*/
continue;
}
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));
} else {
gboolean is_sys_obj = typedef_is_system_object (ctx, data);
gboolean has_parent = get_coded_index_token (TYPEDEF_OR_REF_DESC, data [MONO_TYPEDEF_EXTENDS]) != 0;
if (is_sys_obj) {
if (has_parent)
ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for System.Object must have a null extend field", i));
} else {
if (!has_parent) {
ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for non-interface type must have a non-null extend field", i));
}
}
}
}
}
/*bits 3,11,14 */
#define INVALID_FIELD_FLAG_BITS ((1 << 3) | (1 << 11) | (1 << 14))
static void
verify_field_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELD];
guint32 data [MONO_FIELD_SIZE], flags, module_field_list;
int i;
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);
}
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]));
if (data [MONO_FIELD_SIGNATURE] && !is_valid_blob_object (ctx, data [MONO_FIELD_SIGNATURE], 1))
ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid signature blob token 0x%x", i, data [MONO_FIELD_SIGNATURE]));
//TODO verify contant flag
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));
}
}
}
static void
verify_field_table_full (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELD];
guint32 data [MONO_FIELD_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_FIELD_SIZE);
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]));
}
}
/*bits 8,9,10,11,13,14,15*/
#define INVALID_METHOD_IMPLFLAG_BITS ((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_FINAL)
ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is Abstract and Final", 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_WARNING (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 (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_FAMILY || access == METHOD_ATTRIBUTE_FAM_AND_ASSEM || access == METHOD_ATTRIBUTE_FAM_OR_ASSEM)
ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is a global method but not CompilerControled, Public, Private or Assembly", 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) {
if (flags & METHOD_ATTRIBUTE_VIRTUAL)
ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is PinvokeImpl and Virtual", i));
if (!(flags & METHOD_ATTRIBUTE_STATIC))
ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is PinvokeImpl but not Static", 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 | METHOD_ATTRIBUTE_PINVOKE_IMPL)) || (implflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL))
ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d has RVA != 0 but is either Abstract, InternalCall or PinvokeImpl", 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));
} 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_SIGNATURE] && !is_valid_blob_object (ctx, data [MONO_METHOD_SIGNATURE], 1))
ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid signature blob token 0x%x", i, data [MONO_METHOD_SIGNATURE]));
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 void
verify_method_table_full (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHOD];
guint32 data [MONO_METHOD_SIZE], rva, locals_token;
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_METHOD_SIZE);
rva = data [MONO_METHOD_RVA];
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 0x%08x", i, data [MONO_METHOD_SIGNATURE]));
if (rva && !is_valid_method_header (ctx, rva, &locals_token))
ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d RVA points to an invalid method header", i));
}
}
static guint32
get_next_param_count (VerifyContext *ctx, guint32 *current_method)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHOD];
guint32 row = *current_method;
guint32 paramlist, tmp;
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;
}
/*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;
if (ctx->image->tables [MONO_TABLE_METHOD].rows == 0) {
if (table->rows > 0)
ADD_ERROR (ctx, g_strdup ("Param table has rows while the method table has zero"));
return;
}
remaining_params = get_next_param_count (ctx, &current_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]));
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, &current_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_INTERFACEIMPL_CLASS]));
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_memberref_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_MEMBERREF];
guint32 data [MONO_MEMBERREF_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_MEMBERREF_SIZE);
if (!is_valid_coded_index (ctx, MEMBERREF_PARENT_DESC, data [MONO_MEMBERREF_CLASS]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid MemberRef row %d Class field coded index 0x%08x", i, data [MONO_MEMBERREF_CLASS]));
if (!get_coded_index_token (MEMBERREF_PARENT_DESC, data [MONO_MEMBERREF_CLASS]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid MemberRef row %d Class field coded is null", i));
if (!is_valid_non_empty_string (ctx, data [MONO_MEMBERREF_NAME]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid MemberRef row %d Name field coded is invalid or empty 0x%08x", i, data [MONO_MEMBERREF_NAME]));
if (data [MONO_MEMBERREF_SIGNATURE] && !is_valid_blob_object (ctx, data [MONO_MEMBERREF_SIGNATURE], 1))
ADD_ERROR (ctx, g_strdup_printf ("Invalid MemberRef row %d invalid signature blob token 0x%x", i, data [MONO_MEMBERREF_SIGNATURE]));
}
}
static void
verify_memberref_table_full (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_MEMBERREF];
guint32 data [MONO_MEMBERREF_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_MEMBERREF_SIZE);
if (!is_valid_method_or_field_signature (ctx, data [MONO_MEMBERREF_SIGNATURE]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid MemberRef row %d Signature field 0x%08x", i, data [MONO_MEMBERREF_SIGNATURE]));
}
}
static void
verify_constant_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_CONSTANT];
guint32 data [MONO_CONSTANT_SIZE], type;
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_CONSTANT_SIZE);
type = data [MONO_CONSTANT_TYPE];
if (!((type >= MONO_TYPE_BOOLEAN && type <= MONO_TYPE_STRING) || type == MONO_TYPE_CLASS))
ADD_ERROR (ctx, g_strdup_printf ("Invalid Constant row %d Type field 0x%08x", i, type));
if (!is_valid_coded_index (ctx, HAS_CONSTANT_DESC, data [MONO_CONSTANT_PARENT]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid Constant row %d Parent field coded index 0x%08x", i, data [MONO_CONSTANT_PARENT]));
if (!get_coded_index_token (HAS_CONSTANT_DESC, data [MONO_CONSTANT_PARENT]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid Constant row %d Parent field coded is null", i));
if (!is_valid_constant (ctx, type, data [MONO_CONSTANT_VALUE]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid Constant row %d Value field 0x%08x", i, data [MONO_CONSTANT_VALUE]));
}
}
static void
verify_cattr_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_CUSTOMATTRIBUTE];
guint32 data [MONO_CUSTOM_ATTR_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_CUSTOM_ATTR_SIZE);
if (!is_valid_coded_index (ctx, HAS_CATTR_DESC, data [MONO_CUSTOM_ATTR_PARENT]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute row %d Parent field 0x%08x", i, data [MONO_CUSTOM_ATTR_PARENT]));
if (!is_valid_coded_index (ctx, CATTR_TYPE_DESC, data [MONO_CUSTOM_ATTR_TYPE]) || !get_coded_index_token (CATTR_TYPE_DESC, data [MONO_CUSTOM_ATTR_TYPE]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute row %d Type field 0x%08x", i, data [MONO_CUSTOM_ATTR_TYPE]));
if (data [MONO_CUSTOM_ATTR_VALUE] && !is_valid_blob_object (ctx, data [MONO_CUSTOM_ATTR_VALUE], 0))
ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute row %d invalid value blob 0x%x", i, data [MONO_CUSTOM_ATTR_VALUE]));
}
}
static void
verify_cattr_table_full (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_CUSTOMATTRIBUTE];
MonoMethod *ctor;
const char *ptr;
guint32 data [MONO_CUSTOM_ATTR_SIZE], mtoken, size;
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_CUSTOM_ATTR_SIZE);
if (!is_valid_cattr_blob (ctx, data [MONO_CUSTOM_ATTR_VALUE]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute row %d Value field 0x%08x", i, data [MONO_CUSTOM_ATTR_VALUE]));
mtoken = data [MONO_CUSTOM_ATTR_TYPE] >> MONO_CUSTOM_ATTR_TYPE_BITS;
switch (data [MONO_CUSTOM_ATTR_TYPE] & MONO_CUSTOM_ATTR_TYPE_MASK) {
case MONO_CUSTOM_ATTR_TYPE_METHODDEF:
mtoken |= MONO_TOKEN_METHOD_DEF;
break;
case MONO_CUSTOM_ATTR_TYPE_MEMBERREF:
mtoken |= MONO_TOKEN_MEMBER_REF;
break;
default:
ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute constructor row %d Token 0x%08x", i, data [MONO_CUSTOM_ATTR_TYPE]));
}
ctor = mono_get_method (ctx->image, mtoken, NULL);
/*This can't fail since this is checked in is_valid_cattr_blob*/
g_assert (decode_signature_header (ctx, data [MONO_CUSTOM_ATTR_VALUE], &size, &ptr));
if (!is_valid_cattr_content (ctx, ctor, ptr, size))
ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute content row %d Value field 0x%08x", i, data [MONO_CUSTOM_ATTR_VALUE]));
}
}
static void
verify_field_marshal_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELDMARSHAL];
guint32 data [MONO_FIELD_MARSHAL_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_FIELD_MARSHAL_SIZE);
if (!is_valid_coded_index (ctx, HAS_FIELD_MARSHAL_DESC, data [MONO_FIELD_MARSHAL_PARENT]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldMarshal row %d Parent field 0x%08x", i, data [MONO_FIELD_MARSHAL_PARENT]));
if (!get_coded_index_token (HAS_FIELD_MARSHAL_DESC, data [MONO_FIELD_MARSHAL_PARENT]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldMarshal row %d Parent field is null", i));
if (!data [MONO_FIELD_MARSHAL_NATIVE_TYPE])
ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldMarshal row %d NativeType field is null", i));
if (!is_valid_blob_object (ctx, data [MONO_FIELD_MARSHAL_NATIVE_TYPE], 1))
ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldMarshal row %d invalid NativeType blob 0x%x", i, data [MONO_FIELD_MARSHAL_NATIVE_TYPE]));
}
}
static void
verify_field_marshal_table_full (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELDMARSHAL];
guint32 data [MONO_FIELD_MARSHAL_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_FIELD_MARSHAL_SIZE);
if (!is_valid_marshal_spec (ctx, data [MONO_FIELD_MARSHAL_NATIVE_TYPE]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldMarshal row %d NativeType field 0x%08x", i, data [MONO_FIELD_MARSHAL_NATIVE_TYPE]));
}
}
static void
verify_decl_security_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_DECLSECURITY];
guint32 data [MONO_DECL_SECURITY_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_DECL_SECURITY_SIZE);
if (!is_valid_coded_index (ctx, HAS_DECL_SECURITY_DESC, data [MONO_DECL_SECURITY_PARENT]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid DeclSecurity row %d Parent field 0x%08x", i, data [MONO_DECL_SECURITY_PARENT]));
if (!get_coded_index_token (HAS_DECL_SECURITY_DESC, data [MONO_DECL_SECURITY_PARENT]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid DeclSecurity row %d Parent field is null", i));
if (!data [MONO_DECL_SECURITY_PERMISSIONSET])
ADD_ERROR (ctx, g_strdup_printf ("Invalid DeclSecurity row %d PermissionSet field is null", i));
}
}
static void
verify_decl_security_table_full (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_DECLSECURITY];
guint32 data [MONO_DECL_SECURITY_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_DECL_SECURITY_SIZE);
if (!is_valid_permission_set (ctx, data [MONO_DECL_SECURITY_PERMISSIONSET]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid DeclSecurity row %d PermissionSet field 0x%08x", i, data [MONO_DECL_SECURITY_PERMISSIONSET]));
}
}
static void
verify_class_layout_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_CLASSLAYOUT];
guint32 data [MONO_CLASS_LAYOUT_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_CLASS_LAYOUT_SIZE);
if (!data [MONO_CLASS_LAYOUT_PARENT] || data[MONO_CLASS_LAYOUT_PARENT] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows + 1)
ADD_ERROR (ctx, g_strdup_printf ("Invalid ClassLayout row %d Parent field 0x%08x", i, data [MONO_TABLE_TYPEDEF]));
switch (data [MONO_CLASS_LAYOUT_PACKING_SIZE]) {
case 0:
case 1:
case 2:
case 4:
case 8:
case 16:
case 32:
case 64:
case 128:
break;
default:
ADD_ERROR (ctx, g_strdup_printf ("Invalid ClassLayout row %d Packing field %d", i, data [MONO_CLASS_LAYOUT_PACKING_SIZE]));
}
}
}
static void
verify_field_layout_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELDLAYOUT];
guint32 data [MONO_FIELD_LAYOUT_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_FIELD_LAYOUT_SIZE);
if (!data [MONO_FIELD_LAYOUT_FIELD] || data[MONO_FIELD_LAYOUT_FIELD] > ctx->image->tables [MONO_TABLE_FIELD].rows + 1)
ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldLayout row %d Field field 0x%08x", i, data [MONO_FIELD_LAYOUT_FIELD]));
}
}
static void
verify_standalonesig_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_STANDALONESIG];
guint32 data [MONO_STAND_ALONE_SIGNATURE_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_STAND_ALONE_SIGNATURE_SIZE);
if (data [MONO_STAND_ALONE_SIGNATURE] && !is_valid_blob_object (ctx, data [MONO_STAND_ALONE_SIGNATURE], 1))
ADD_ERROR (ctx, g_strdup_printf ("Invalid StandAloneSig row %d invalid signature 0x%x", i, data [MONO_STAND_ALONE_SIGNATURE]));
}
}
static void
verify_standalonesig_table_full (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_STANDALONESIG];
guint32 data [MONO_STAND_ALONE_SIGNATURE_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_STAND_ALONE_SIGNATURE_SIZE);
if (!is_valid_standalonesig_blob (ctx, data [MONO_STAND_ALONE_SIGNATURE]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid StandAloneSig row %d Signature field 0x%08x", i, data [MONO_STAND_ALONE_SIGNATURE]));
}
}
static void
verify_eventmap_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_EVENTMAP];
guint32 data [MONO_EVENT_MAP_SIZE], eventlist = 0;
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_EVENT_MAP_SIZE);
if (!data [MONO_EVENT_MAP_PARENT] || data [MONO_EVENT_MAP_PARENT] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows + 1)
ADD_ERROR (ctx, g_strdup_printf ("Invalid EventMap row %d Parent field 0x%08x", i, data [MONO_EVENT_MAP_PARENT]));
if (!data [MONO_EVENT_MAP_EVENTLIST] || data [MONO_EVENT_MAP_EVENTLIST] <= eventlist)
ADD_ERROR (ctx, g_strdup_printf ("Invalid EventMap row %d EventList field %d", i, data [MONO_EVENT_MAP_EVENTLIST]));
eventlist = data [MONO_EVENT_MAP_EVENTLIST];
}
}
#define INVALID_EVENT_FLAGS_BITS ~((1 << 9) | (1 << 10))
static void
verify_event_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_EVENT];
guint32 data [MONO_EVENT_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_EVENT_SIZE);
if (data [MONO_EVENT_FLAGS] & INVALID_EVENT_FLAGS_BITS)
ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d EventFlags field %08x", i, data [MONO_EVENT_FLAGS]));
if (!is_valid_non_empty_string (ctx, data [MONO_EVENT_NAME]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d Name field %08x", i, data [MONO_EVENT_NAME]));
if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, data [MONO_EVENT_TYPE]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d EventType field %08x", i, data [MONO_EVENT_TYPE]));
}
}
static void
verify_event_table_full (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_EVENT];
MonoTableInfo *sema_table = &ctx->image->tables [MONO_TABLE_METHODSEMANTICS];
guint32 data [MONO_EVENT_SIZE], sema_data [MONO_METHOD_SEMA_SIZE], token;
gboolean found_add, found_remove;
int i, idx;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_EVENT_SIZE);
token = make_coded_token (HAS_SEMANTICS_DESC, MONO_TABLE_EVENT, i);
idx = search_sorted_table (ctx, MONO_TABLE_METHODSEMANTICS, MONO_METHOD_SEMA_ASSOCIATION, token);
if (idx == -1)
ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d has no AddOn or RemoveOn associated methods", i));
//first we move to the first row for this event
while (idx > 0) {
if (mono_metadata_decode_row_col (sema_table, idx - 1, MONO_METHOD_SEMA_ASSOCIATION) != token)
break;
--idx;
}
//now move forward looking for AddOn and RemoveOn rows
found_add = found_remove = FALSE;
while (idx < sema_table->rows) {
mono_metadata_decode_row (sema_table, idx, sema_data, MONO_METHOD_SEMA_SIZE);
if (sema_data [MONO_METHOD_SEMA_ASSOCIATION] != token)
break;
if (sema_data [MONO_METHOD_SEMA_SEMANTICS] & METHOD_SEMANTIC_ADD_ON)
found_add = TRUE;
if (sema_data [MONO_METHOD_SEMA_SEMANTICS] & METHOD_SEMANTIC_REMOVE_ON)
found_remove = TRUE;
if (found_add && found_remove)
break;
++idx;
}
if (!found_add)
ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d has no AddOn associated method", i));
if (!found_remove)
ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d has no RemoveOn associated method", i));
}
}
static void
verify_propertymap_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_PROPERTYMAP];
guint32 data [MONO_PROPERTY_MAP_SIZE], propertylist = 0;
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_PROPERTY_MAP_SIZE);
if (!data [MONO_PROPERTY_MAP_PARENT] || data [MONO_PROPERTY_MAP_PARENT] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows + 1)
ADD_ERROR (ctx, g_strdup_printf ("Invalid PropertyMap row %d Parent field 0x%08x", i, data [MONO_PROPERTY_MAP_PARENT]));
if (!data [MONO_PROPERTY_MAP_PROPERTY_LIST] || data [MONO_PROPERTY_MAP_PROPERTY_LIST] <= propertylist)
ADD_ERROR (ctx, g_strdup_printf ("Invalid PropertyMap row %d PropertyList field %d", i, data [MONO_PROPERTY_MAP_PROPERTY_LIST]));
propertylist = data [MONO_PROPERTY_MAP_PROPERTY_LIST];
}
}
#define INVALID_PROPERTY_FLAGS_BITS ~((1 << 9) | (1 << 10) | (1 << 12))
static void
verify_property_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_PROPERTY];
guint32 data [MONO_PROPERTY_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_PROPERTY_SIZE);
if (data [MONO_PROPERTY_FLAGS] & INVALID_PROPERTY_FLAGS_BITS)
ADD_ERROR (ctx, g_strdup_printf ("Invalid Property row %d PropertyFlags field %08x", i, data [MONO_PROPERTY_FLAGS]));
if (!is_valid_non_empty_string (ctx, data [MONO_PROPERTY_NAME]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid Property row %d Name field %08x", i, data [MONO_PROPERTY_NAME]));
if (!is_valid_property_sig_blob (ctx, data [MONO_PROPERTY_TYPE]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid Property row %d Type field %08x", i, data [MONO_PROPERTY_TYPE]));
if ((data [MONO_PROPERTY_FLAGS] & PROPERTY_ATTRIBUTE_HAS_DEFAULT) &&
search_sorted_table (ctx, MONO_TABLE_CONSTANT, MONO_CONSTANT_PARENT, make_coded_token (HAS_CONSTANT_DESC, MONO_TABLE_PROPERTY, i)) == -1)
ADD_ERROR (ctx, g_strdup_printf ("Invalid Property row %d has HasDefault but there is no corresponding row in the Constant table", i));
}
}
static void
verify_methodimpl_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHODIMPL];
guint32 data [MONO_METHODIMPL_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_METHODIMPL_SIZE);
if (!data [MONO_METHODIMPL_CLASS] || data [MONO_METHODIMPL_CLASS] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows + 1)
ADD_ERROR (ctx, g_strdup_printf ("Invalid MethodImpl row %d Class field %08x", i, data [MONO_TABLE_TYPEDEF]));
if (!get_coded_index_token (METHODDEF_OR_REF_DESC, data [MONO_METHODIMPL_BODY]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid MethodImpl row %d MethodBody field %08x", i, data [MONO_METHODIMPL_BODY]));
if (!is_valid_coded_index (ctx, METHODDEF_OR_REF_DESC, data [MONO_METHODIMPL_BODY]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid MethodImpl row %d MethodBody field %08x", i, data [MONO_METHODIMPL_BODY]));
if (!get_coded_index_token (METHODDEF_OR_REF_DESC, data [MONO_METHODIMPL_DECLARATION]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid MethodImpl row %d MethodDeclaration field %08x", i, data [MONO_METHODIMPL_DECLARATION]));
if (!is_valid_coded_index (ctx, METHODDEF_OR_REF_DESC, data [MONO_METHODIMPL_DECLARATION]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid MethodImpl row %d MethodDeclaration field %08x", i, data [MONO_METHODIMPL_DECLARATION]));
}
}
static void
verify_moduleref_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_MODULEREF];
guint32 data [MONO_MODULEREF_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_MODULEREF_SIZE);
if (!is_valid_non_empty_string (ctx, data[MONO_MODULEREF_NAME]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid ModuleRef row %d name field %08x", i, data [MONO_MODULEREF_NAME]));
}
}
static void
verify_typespec_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPESPEC];
guint32 data [MONO_TYPESPEC_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_TYPESPEC_SIZE);
if (data [MONO_TYPESPEC_SIGNATURE] && !is_valid_blob_object (ctx, data [MONO_TYPESPEC_SIGNATURE], 1))
ADD_ERROR (ctx, g_strdup_printf ("Invalid TypeSpec row %d Signature field %08x", i, data [MONO_TYPESPEC_SIGNATURE]));
}
}
static void
verify_typespec_table_full (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPESPEC];
guint32 data [MONO_TYPESPEC_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_TYPESPEC_SIZE);
ctx->token = (i + 1) | MONO_TOKEN_TYPE_SPEC;
if (!is_valid_typespec_blob (ctx, data [MONO_TYPESPEC_SIGNATURE]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid TypeSpec row %d Signature field %08x", i, data [MONO_TYPESPEC_SIGNATURE]));
}
ctx->token = 0;
}
#define INVALID_IMPLMAP_FLAGS_BITS ~((1 << 0) | (1 << 1) | (1 << 2) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 12) | (1 << 13))
static void
verify_implmap_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_IMPLMAP];
guint32 data [MONO_IMPLMAP_SIZE], cconv;
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_IMPLMAP_SIZE);
if (data [MONO_IMPLMAP_FLAGS] & INVALID_IMPLMAP_FLAGS_BITS)
ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d Flags field %08x", i, data [MONO_IMPLMAP_FLAGS]));
cconv = data [MONO_IMPLMAP_FLAGS] & PINVOKE_ATTRIBUTE_CALL_CONV_MASK;
if (cconv == 0 || cconv == 0x0600 || cconv == 0x0700)
ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d Invalid call conv field %x", i, cconv));
if (!is_valid_coded_index (ctx, MEMBER_FORWARDED_DESC, data [MONO_IMPLMAP_MEMBER]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d Invalid MemberForward token %x", i, data [MONO_IMPLMAP_MEMBER]));
if (get_coded_index_table (MEMBER_FORWARDED_DESC, data [MONO_IMPLMAP_MEMBER]) != MONO_TABLE_METHOD)
ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d only methods are supported token %x", i, data [MONO_IMPLMAP_MEMBER]));
if (!get_coded_index_token (MEMBER_FORWARDED_DESC, data [MONO_IMPLMAP_MEMBER]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d null token", i));
if (!is_valid_non_empty_string (ctx, data [MONO_IMPLMAP_NAME]))
ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d ImportName Token %x", i, data [MONO_IMPLMAP_NAME]));
if (!data [MONO_IMPLMAP_SCOPE] || data [MONO_IMPLMAP_SCOPE] > ctx->image->tables [MONO_TABLE_MODULEREF].rows)
ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d Invalid ImportScope token %x", i, data [MONO_IMPLMAP_SCOPE]));
}
}
static void
verify_fieldrva_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELDRVA];
guint32 data [MONO_FIELD_RVA_SIZE];
int i;
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_FIELD_RVA_SIZE);
if (!data [MONO_FIELD_RVA_RVA] || mono_cli_rva_image_map (ctx->image, data [MONO_FIELD_RVA_RVA]) == INVALID_ADDRESS)
ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldRVA row %d RVA %08x", i, data [MONO_FIELD_RVA_RVA]));
if (!data [MONO_FIELD_RVA_FIELD] || data [MONO_FIELD_RVA_FIELD] > ctx->image->tables [MONO_TABLE_FIELD].rows + 1)
ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldRVA row %d Field %08x", i, data [MONO_FIELD_RVA_FIELD]));
}
}
#define INVALID_ASSEMBLY_FLAGS_BITS ~((1 << 0) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 14) | (1 << 15))
static void
verify_assembly_table (VerifyContext *ctx)
{
MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_ASSEMBLY];
guint32 data [MONO_ASSEMBLY_SIZE], hash;
int i;
if (table->rows > 1)
ADD_ERROR (ctx, g_strdup_printf ("Assembly table can have zero or one rows, but now %d", table->rows));
for (i = 0; i < table->rows; ++i) {
mono_metadata_decode_row (table, i, data, MONO_ASSEMBLY_SIZE);
hash = data [MONO_ASSEMBLY_HASH_ALG];
if (!(hash == 0 || hash == 0x8003 || hash == 0x8004))
ADD_ERROR (ctx, g_strdup_printf ("Assembly table row %d has invalid HashAlgId %x", i, hash));
if (data [MONO_ASSEMBLY_FLAGS] & INVALID_ASSEMBLY_FLAGS_BITS)
ADD_ERROR (ctx, g_strdup_printf ("Assembly table row %d has invalid Flags %08x", i, data [MONO_ASSEMBLY_FLAGS]));
if (data [MONO_ASSEMBLY_PUBLIC_KEY] && !is_valid_blob_object (ctx, data [MONO_ASSEMBLY_PUBLIC_KEY], 1))
ADD_ERROR (ctx, g_strdup_printf ("Assembly table row %d has invalid PublicKey %08x", i, data [MONO_ASSEMBLY_FLAGS]));
if (!is_valid_non_empty_string (ctx, data [MONO_ASSEMBLY_NAME]))
ADD_ERROR (ctx, g_strdup_printf ("Assembly table row %d has invalid Name %08x", i, data [MONO_ASSEMBLY_NAME]));
if (data [MONO_ASSEMBLY_CULTURE] && !is_valid_string (ctx, data [MONO_ASSEMBLY_CULTURE]))
ADD_ERROR (ctx, g_strdup_printf ("Assembly table row %d has invalid Culture %08x", i, data [MONO_ASSEMBLY_CULTURE]));
}
}