2009-03-27 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / metadata / metadata-verify.c
1 /*
2  * metadata-verify.c: Metadata verfication support
3  *
4  * Author:
5  *      Mono Project (http://www.mono-project.com)
6  *
7  * Copyright (C) 2005-2008 Novell, Inc. (http://www.novell.com)
8  */
9
10 #include <mono/metadata/object-internals.h>
11 #include <mono/metadata/verify.h>
12 #include <mono/metadata/verify-internals.h>
13 #include <mono/metadata/opcodes.h>
14 #include <mono/metadata/tabledefs.h>
15 #include <mono/metadata/reflection.h>
16 #include <mono/metadata/debug-helpers.h>
17 #include <mono/metadata/mono-endian.h>
18 #include <mono/metadata/metadata.h>
19 #include <mono/metadata/metadata-internals.h>
20 #include <mono/metadata/class-internals.h>
21 #include <mono/metadata/tokentype.h>
22 #include <string.h>
23 #include <signal.h>
24 #include <ctype.h>
25
26 /*
27  TODO add fail fast mode
28  TODO add PE32+ support
29  TODO verify the entry point RVA and content.
30  TODO load_section_table and load_data_directories must take PE32+ into account
31  TODO add section relocation support
32  TODO verify the relocation table, since we really don't use, no need so far.
33  TODO do full PECOFF resources verification 
34  TODO verify in the CLI header entry point and resources 
35 */
36
37 #define INVALID_OFFSET ((guint32)-1)
38
39 enum {
40         IMPORT_TABLE_IDX = 1, 
41         RESOURCE_TABLE_IDX = 2,
42         RELOCATION_TABLE_IDX = 5,
43         IAT_IDX = 12,
44         CLI_HEADER_IDX = 14,
45 };
46
47 enum {
48         STRINGS_STREAM,
49         USER_STRINGS_STREAM,
50         BLOB_STREAM,
51         GUID_STREAM,
52         TILDE_STREAM
53 };
54
55 typedef struct {
56         guint32 rva;
57         guint32 size;
58         guint32 translated_offset;
59 } DataDirectory;
60
61 typedef struct {
62         guint32 offset;
63         guint32 size;
64 } OffsetAndSize;
65
66 typedef struct {
67         guint32 baseRVA;
68         guint32 baseOffset;
69         guint32 size;
70         guint32 rellocationsRVA;
71         guint16 numberOfRelocations;
72 } SectionHeader;
73
74 typedef struct {
75         guint32 row_count;
76 } TableInfo;
77
78 typedef struct {
79         const char *data;
80         guint32 size;
81         GSList *errors;
82         int valid;
83         guint32 section_count;
84         SectionHeader *sections;
85         gboolean wide_strings, wide_guid, wide_blob;
86
87         DataDirectory data_directories [16];
88         OffsetAndSize metadata_streams [5]; //offset from begin of the image
89         TableInfo tables [MONO_TABLE_NUM];
90 } VerifyContext;
91
92 #define ADD_VERIFY_INFO(__ctx, __msg, __status, __exception)    \
93         do {    \
94                 MonoVerifyInfoExtended *vinfo = g_new (MonoVerifyInfoExtended, 1);      \
95                 vinfo->info.status = __status;  \
96                 vinfo->info.message = ( __msg); \
97                 vinfo->exception_type = (__exception);  \
98                 (__ctx)->errors = g_slist_prepend ((__ctx)->errors, vinfo);     \
99         } while (0)
100
101
102 #define ADD_ERROR(__ctx, __msg) \
103         do {    \
104                 ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \
105                 (__ctx)->valid = 0; \
106                 return; \
107         } while (0)
108
109 #define CHECK_STATE() do { if (!ctx.valid) goto cleanup; } while (0)
110
111 #define CHECK_ERROR() do { if (!ctx->valid) return; } while (0)
112
113 static guint32
114 pe_signature_offset (VerifyContext *ctx)
115 {
116         return read32 (ctx->data + 0x3c);
117 }
118
119 static guint32
120 pe_header_offset (VerifyContext *ctx)
121 {
122         return read32 (ctx->data + 0x3c) + 4;
123 }
124
125 static gboolean
126 bounds_check_virtual_address (VerifyContext *ctx, guint32 rva, guint32 size)
127 {
128         int i;
129
130         if (!ctx->sections)
131                 return FALSE;
132
133         for (i = 0; i < ctx->section_count; ++i) {
134                 guint32 base = ctx->sections [i].baseRVA;
135                 guint32 end = ctx->sections [i].baseRVA + ctx->sections [i].size;
136                 if (rva >= base && rva + size <= end)
137                         return TRUE;
138         }
139         return FALSE;
140 }
141
142 static gboolean
143 bounds_check_offset (DataDirectory *dir, guint32 offset, guint32 size)
144 {
145         if (dir->translated_offset > offset)
146                 return FALSE;
147         if (dir->size < size)
148                 return FALSE;
149         return offset + size <= dir->translated_offset + dir->size;
150 }
151
152
153 static guint32
154 translate_rva (VerifyContext *ctx, guint32 rva)
155 {
156         int i;
157
158         if (!ctx->sections)
159                 return FALSE;
160
161         for (i = 0; i < ctx->section_count; ++i) {
162                 guint32 base = ctx->sections [i].baseRVA;
163                 guint32 end = ctx->sections [i].baseRVA + ctx->sections [i].size;
164                 if (rva >= base && rva <= end) {
165                         guint32 res = (rva - base) + ctx->sections [i].baseOffset;
166                         /* double check */
167                         return res >= ctx->size ? INVALID_OFFSET : res;
168                 }
169         }
170
171         return INVALID_OFFSET;
172 }
173
174 static void
175 verify_msdos_header (VerifyContext *ctx)
176 {
177         guint32 lfanew;
178         if (ctx->size < 128)
179                 ADD_ERROR (ctx, g_strdup ("Not enough space for the MS-DOS header"));
180         if (ctx->data [0] != 0x4d || ctx->data [1] != 0x5a)
181                 ADD_ERROR (ctx,  g_strdup ("Invalid MS-DOS watermark"));
182         lfanew = pe_signature_offset (ctx);
183         if (lfanew > ctx->size - 4)
184                 ADD_ERROR (ctx, g_strdup ("MS-DOS lfanew offset points to outside of the file"));
185 }
186
187 static void
188 verify_pe_header (VerifyContext *ctx)
189 {
190         guint32 offset = pe_signature_offset (ctx);
191         const char *pe_header = ctx->data + offset;
192         if (pe_header [0] != 'P' || pe_header [1] != 'E' ||pe_header [2] != 0 ||pe_header [3] != 0)
193                 ADD_ERROR (ctx,  g_strdup ("Invalid PE header watermark"));
194         pe_header += 4;
195         offset += 4;
196
197         if (offset > ctx->size - 20)
198                 ADD_ERROR (ctx, g_strdup ("File with truncated pe header"));
199         if (read16 (pe_header) != 0x14c)
200                 ADD_ERROR (ctx, g_strdup ("Invalid PE header Machine value"));
201 }
202
203 static void
204 verify_pe_optional_header (VerifyContext *ctx)
205 {
206         guint32 offset = pe_header_offset (ctx);
207         guint32 header_size, file_alignment;
208         const char *pe_header = ctx->data + offset;
209         const char *pe_optional_header = pe_header + 20;
210
211         header_size = read16 (pe_header + 16);
212         offset += 20;
213
214         if (header_size < 2) /*must be at least 2 or we won't be able to read magic*/
215                 ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
216
217         if (offset > ctx->size - header_size || header_size > ctx->size)
218                 ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
219
220         if (read16 (pe_optional_header) == 0x10b) {
221                 if (header_size != 224)
222                         ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header size %d", header_size));
223
224                 /*if (read32 (pe_optional_header + 28) != 0x400000)
225                         ADD_ERROR (ctx, g_strdup_printf ("Invalid Image base %x", read32 (pe_optional_header + 28)));*/
226                 if (read32 (pe_optional_header + 32) != 0x2000)
227                         ADD_ERROR (ctx, g_strdup_printf ("Invalid Section Aligmnent %x", read32 (pe_optional_header + 32)));
228                 file_alignment = read32 (pe_optional_header + 36);
229                 if (file_alignment != 0x200 && file_alignment != 0x1000)
230                         ADD_ERROR (ctx, g_strdup_printf ("Invalid file Aligmnent %x", file_alignment));
231                 /* All the junk in the middle is irrelevant, specially for mono. */
232                 if (read32 (pe_optional_header + 92) > 0x10)
233                         ADD_ERROR (ctx, g_strdup_printf ("Too many data directories %x", read32 (pe_optional_header + 92)));
234         } else {
235                 if (read16 (pe_optional_header) == 0x20B)
236                         ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle PE32+"));
237                 else
238                         ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header magic %d", read16 (pe_optional_header)));
239         }
240 }
241
242 static void
243 load_section_table (VerifyContext *ctx)
244 {
245         int i;
246         SectionHeader *sections;
247         guint32 offset =  pe_header_offset (ctx);
248         const char *ptr = ctx->data + offset;
249         guint16 num_sections = ctx->section_count = read16 (ptr + 2);
250
251         offset += 244;/*FIXME, this constant is different under PE32+*/
252         ptr += 244;
253
254         if (num_sections * 40 > ctx->size - offset)
255                 ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
256
257         sections = ctx->sections = g_new0 (SectionHeader, num_sections);
258         for (i = 0; i < num_sections; ++i) {
259                 sections [i].size = read32 (ptr + 8);
260                 sections [i].baseRVA = read32 (ptr + 12);
261                 sections [i].baseOffset = read32 (ptr + 20);
262                 sections [i].rellocationsRVA = read32 (ptr + 24);
263                 sections [i].numberOfRelocations = read16 (ptr + 32);
264                 ptr += 40;
265         }
266
267         ptr = ctx->data + offset; /*reset it to the beggining*/
268         for (i = 0; i < num_sections; ++i) {
269                 guint32 raw_size, flags;
270                 if (sections [i].baseOffset == 0)
271                         ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle sections with intialized data only"));
272                 if (sections [i].baseOffset >= ctx->size)
273                         ADD_ERROR (ctx, g_strdup_printf ("Invalid PointerToRawData %x points beyond EOF", sections [i].baseOffset));
274                 if (sections [i].size > ctx->size - sections [i].baseOffset)
275                         ADD_ERROR (ctx, g_strdup ("Invalid VirtualSize points beyond EOF"));
276
277                 raw_size = read32 (ptr + 16);
278                 if (raw_size < sections [i].size)
279                         ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle sections with SizeOfRawData < VirtualSize"));
280
281                 if (raw_size > ctx->size - sections [i].baseOffset)
282                         ADD_ERROR (ctx, g_strdup_printf ("Invalid SizeOfRawData %x points beyond EOF", raw_size));
283
284                 if (sections [i].rellocationsRVA || sections [i].numberOfRelocations)
285                         ADD_ERROR (ctx, g_strdup_printf ("Metadata verifier doesn't handle section relocation"));
286
287                 flags = read32 (ptr + 36);
288                 /*TODO 0xFE0000E0 is all flags from cil-coff.h OR'd. Make it a less magical number*/
289                 if (flags == 0 || (flags & ~0xFE0000E0) != 0)
290                         ADD_ERROR (ctx, g_strdup_printf ("Invalid section flags %x", flags));
291
292                 ptr += 40;
293         }
294 }
295
296 static gboolean
297 is_valid_data_directory (int i)
298 {
299         /*LAMESPEC 4 == certificate 6 == debug, MS uses both*/
300         return i == 1 || i == 2 || i == 5 || i == 12 || i == 14 || i == 4 || i == 6; 
301 }
302
303 static void
304 load_data_directories (VerifyContext *ctx)
305 {
306         guint32 offset =  pe_header_offset (ctx) + 116; /*FIXME, this constant is different under PE32+*/
307         const char *ptr = ctx->data + offset;
308         int i;
309
310         for (i = 0; i < 16; ++i) {
311                 guint32 rva = read32 (ptr);
312                 guint32 size = read32 (ptr + 4);
313
314                 if ((rva != 0 || size != 0) && !is_valid_data_directory (i))
315                         ADD_ERROR (ctx, g_strdup_printf ("Invalid data directory %d", i));
316
317                 if (rva != 0 && !bounds_check_virtual_address (ctx, rva, size))
318                         ADD_ERROR (ctx, g_strdup_printf ("Invalid data directory %d rva/size pair %x/%x", i, rva, size));
319
320                 ctx->data_directories [i].rva = rva;
321                 ctx->data_directories [i].size = size;
322                 ctx->data_directories [i].translated_offset = translate_rva (ctx, rva);
323
324                 ptr += 8;
325         }
326 }
327
328 #define SIZE_OF_MSCOREE (sizeof ("mscoree.dll"))
329
330 #define SIZE_OF_CORMAIN (sizeof ("_CorExeMain"))
331
332 static void
333 verify_hint_name_table (VerifyContext *ctx, guint32 import_rva, const char *table_name)
334 {
335         const char *ptr;
336         guint32 hint_table_rva;
337
338         import_rva = translate_rva (ctx, import_rva);
339         g_assert (import_rva != INVALID_OFFSET);
340
341         hint_table_rva = read32 (ctx->data + import_rva);
342         if (!bounds_check_virtual_address (ctx, hint_table_rva, SIZE_OF_CORMAIN + 2))
343                 ADD_ERROR (ctx, g_strdup_printf ("Invalid Hint/Name rva %d for %s", hint_table_rva, table_name));
344
345         hint_table_rva = translate_rva (ctx, hint_table_rva);
346         g_assert (hint_table_rva != INVALID_OFFSET);
347         ptr = ctx->data + hint_table_rva + 2;
348
349         if (memcmp ("_CorExeMain", ptr, SIZE_OF_CORMAIN) && memcmp ("_CorDllMain", ptr, SIZE_OF_CORMAIN)) {
350                 char name[SIZE_OF_CORMAIN];
351                 memcpy (name, ptr, SIZE_OF_CORMAIN);
352                 name [SIZE_OF_CORMAIN - 1] = 0;
353                 ADD_ERROR (ctx, g_strdup_printf ("Invalid Hint / Name: '%s'", name));
354         }
355 }
356
357 static void
358 verify_import_table (VerifyContext *ctx)
359 {
360         DataDirectory it = ctx->data_directories [IMPORT_TABLE_IDX];
361         guint32 offset = it.translated_offset;
362         const char *ptr = ctx->data + offset;
363         guint32 name_rva, ilt_rva, iat_rva;
364
365         g_assert (offset != INVALID_OFFSET);
366
367         if (it.size < 40)
368                 ADD_ERROR (ctx, g_strdup_printf ("Import table size %d is smaller than 40", it.size));
369
370         ilt_rva = read32 (ptr);
371         if (!bounds_check_virtual_address (ctx, ilt_rva, 8))
372                 ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Lookup Table rva %x", ilt_rva));
373
374         name_rva = read32 (ptr + 12);
375         if (!bounds_check_virtual_address (ctx, name_rva, SIZE_OF_MSCOREE))
376                 ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Table Name rva %x", name_rva));
377
378         iat_rva = read32 (ptr + 16);
379         if (!bounds_check_virtual_address (ctx, iat_rva, 8))
380                 ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Address Table rva %x", iat_rva));
381
382         if (iat_rva != ctx->data_directories [IAT_IDX].rva)
383                 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));
384
385         name_rva = translate_rva (ctx, name_rva);
386         g_assert (name_rva != INVALID_OFFSET);
387         ptr = ctx->data + name_rva;
388
389         if (memcmp ("mscoree.dll", ptr, SIZE_OF_MSCOREE)) {
390                 char name[SIZE_OF_MSCOREE];
391                 memcpy (name, ptr, SIZE_OF_MSCOREE);
392                 name [SIZE_OF_MSCOREE - 1] = 0;
393                 ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Table Name: '%s'", name));
394         }
395         
396         verify_hint_name_table (ctx, ilt_rva, "Import Lookup Table");
397         CHECK_ERROR ();
398         verify_hint_name_table (ctx, iat_rva, "Import Address Table");
399 }
400
401 static void
402 verify_resources_table (VerifyContext *ctx)
403 {
404         DataDirectory it = ctx->data_directories [RESOURCE_TABLE_IDX];
405         guint32 offset;
406         guint16 named_entries, id_entries;
407         const char *ptr, *root, *end;
408
409         if (it.rva == 0)
410                 return;
411
412         if (it.size < 16)
413                 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));
414
415         offset = it.translated_offset;
416         root = ptr = ctx->data + offset;
417         end = root + it.size;
418
419         g_assert (offset != INVALID_OFFSET);
420
421         named_entries = read16 (ptr + 12);
422         id_entries = read16 (ptr + 14);
423
424         printf ("named %d id_entries %d\n", named_entries, id_entries);
425         if ((named_entries + id_entries) * 8 + 16 > it.size)
426                 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));
427
428         if (named_entries || id_entries)
429                 ADD_ERROR (ctx, g_strdup_printf ("The metadata verifier doesn't support full verification of PECOFF resources"));
430 }
431
432 static void
433 verify_cli_header (VerifyContext *ctx)
434 {
435         DataDirectory it = ctx->data_directories [CLI_HEADER_IDX];
436         guint32 offset;
437         const char *ptr;
438         int i;
439
440         if (it.rva == 0)
441                 ADD_ERROR (ctx, g_strdup_printf ("CLI header missing"));
442
443         if (it.size != 72)
444                 ADD_ERROR (ctx, g_strdup_printf ("Invalid cli header size in data directory %d must be 72", it.size));
445
446         offset = it.translated_offset;
447         ptr = ctx->data + offset;
448
449         g_assert (offset != INVALID_OFFSET);
450
451         if (read16 (ptr) != 72)
452                 ADD_ERROR (ctx, g_strdup_printf ("Invalid cli header size %d must be 72", read16 (ptr)));
453
454         if (!bounds_check_virtual_address (ctx, read32 (ptr + 8), read32 (ptr + 12)))
455                 ADD_ERROR (ctx, g_strdup_printf ("Invalid medatata section rva/size pair %x/%x", read32 (ptr + 8), read32 (ptr + 12)));
456
457         if (!read32 (ptr + 8) || !read32 (ptr + 12))
458                 ADD_ERROR (ctx, g_strdup_printf ("Missing medatata section in the CLI header"));
459
460         if ((read32 (ptr + 16) & ~0x0001000B) != 0)
461                 ADD_ERROR (ctx, g_strdup_printf ("Invalid CLI header flags"));
462
463         ptr += 24;
464         for (i = 0; i < 6; ++i) {
465                 guint32 rva = read32 (ptr);
466                 guint32 size = read32 (ptr + 4);
467
468                 if (rva != 0 && !bounds_check_virtual_address (ctx, rva, size))
469                         ADD_ERROR (ctx, g_strdup_printf ("Invalid cli section %i rva/size pair %x/%x", i, rva, size));
470
471                 ptr += 8;
472
473                 if (rva && i > 1)
474                         ADD_ERROR (ctx, g_strdup_printf ("Metadata verifier doesn't support cli header section %d", i));
475         }
476 }
477
478 static guint32
479 pad4 (guint32 offset)
480 {
481         if (offset & 0x3) //pad to the next 4 byte boundary
482                 offset = (offset & ~0x3) + 4;
483         return offset;
484 }
485
486 static void
487 verify_metadata_header (VerifyContext *ctx)
488 {
489         int i;
490         DataDirectory it = ctx->data_directories [CLI_HEADER_IDX];
491         guint32 offset;
492         const char *ptr;
493
494         offset = it.translated_offset;
495         ptr = ctx->data + offset;
496         g_assert (offset != INVALID_OFFSET);
497
498         //build a directory entry for the metadata root
499         ptr += 8;
500         it.rva = read32 (ptr);
501         ptr += 4;
502         it.size = read32 (ptr);
503         it.translated_offset = offset = translate_rva (ctx, it.rva);
504
505         ptr = ctx->data + offset;
506         g_assert (offset != INVALID_OFFSET);
507
508         if (it.size < 20)
509                 ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small %d (at least 20 bytes required for initial decoding)", it.size));
510
511         if (read32 (ptr) != 0x424A5342)
512                 ADD_ERROR (ctx, g_strdup_printf ("Invalid metadata signature, expected 0x424A5342 but got %08x", read32 (ptr)));
513
514         offset = pad4 (offset + 16 + read32 (ptr + 12));
515
516         if (!bounds_check_offset (&it, offset, 4))
517                 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));
518
519         ptr = ctx->data + offset; //move to streams header 
520
521         if (read16 (ptr + 2) != 5)
522                 ADD_ERROR (ctx, g_strdup_printf ("Metadata root section have %d streams (it must have exactly 5)", read16 (ptr + 2)));
523
524         ptr += 4;
525         offset += 4;
526
527         for (i = 0; i < 5; ++i) {
528                 guint32 stream_off, stream_size;
529                 int string_size, stream_idx;
530
531                 if (!bounds_check_offset (&it, offset, 8))
532                         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));
533
534                 stream_off = it.translated_offset + read32 (ptr);
535                 stream_size = read32 (ptr + 4);
536
537                 if (!bounds_check_offset (&it,  stream_off, stream_size))
538                         ADD_ERROR (ctx, g_strdup_printf ("Invalid stream header %d offset/size pair %x/%x", 0, stream_off, stream_size));
539
540                 ptr += 8;
541                 offset += 8;
542
543                 for (string_size = 0; string_size < 32; ++string_size) {
544                         if (!bounds_check_offset (&it, offset++, 1))
545                                 ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small to decode stream header %d name", i));
546                         if (!ptr [string_size])
547                                 break;
548                 }
549
550                 if (ptr [string_size])
551                         ADD_ERROR (ctx, g_strdup_printf ("Metadata stream header %d name larger than 32 bytes", i));
552
553                 if (!strncmp ("#Strings", ptr, 9))
554                         stream_idx = STRINGS_STREAM;
555                 else if (!strncmp ("#US", ptr, 4))
556                         stream_idx = USER_STRINGS_STREAM;
557                 else if (!strncmp ("#Blob", ptr, 6))
558                         stream_idx = BLOB_STREAM;
559                 else if (!strncmp ("#GUID", ptr, 6))
560                         stream_idx = GUID_STREAM;
561                 else if (!strncmp ("#~", ptr, 3))
562                         stream_idx = TILDE_STREAM;
563                 else
564                         ADD_ERROR (ctx, g_strdup_printf ("Metadata stream header %d invalid name %s", i, ptr));
565
566                 if (ctx->metadata_streams [stream_idx].offset != 0)
567                         ADD_ERROR (ctx, g_strdup_printf ("Duplicated metadata stream header %s", ptr));
568
569                 ctx->metadata_streams [stream_idx].offset = stream_off;
570                 ctx->metadata_streams [stream_idx].size = stream_size;
571
572                 offset = pad4 (offset);
573                 ptr = ctx->data + offset;
574         }
575 }
576
577 static void
578 verify_tables_schema (VerifyContext *ctx)
579 {
580         OffsetAndSize tables_area = ctx->metadata_streams [TILDE_STREAM];
581         unsigned offset = tables_area.offset;
582         const char *ptr = ctx->data + offset;
583         guint64 valid_tables;
584         guint32 count;
585         int i;
586
587         if (tables_area.size < 24)
588                 ADD_ERROR (ctx, g_strdup_printf ("Table schemata size (%d) too small to for initial decoding (requires 24 bytes)", tables_area.size));
589
590         if (ptr [4] != 2)
591                 ADD_ERROR (ctx, g_strdup_printf ("Invalid table schemata major version %d, expected 2", ptr [4]));
592         if (ptr [5] != 0)
593                 ADD_ERROR (ctx, g_strdup_printf ("Invalid table schemata minor version %d, expected 0", ptr [5]));
594
595         if ((ptr [6] & ~0x7) != 0)
596                 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]));
597
598         ctx->wide_strings = ptr [6] & 0x1;
599         ctx->wide_guid = ptr [6] & 0x2;
600         ctx->wide_blob = ptr [6] & 04;
601
602         valid_tables = read64 (ptr + 8);
603         count = 0;
604         for (i = 0; i < 64; ++i) {
605                 if (!(valid_tables & ((guint64)1 << i)))
606                         continue;
607
608                 /*MS Extensions: 0x3 0x5 0x7 0x13 0x16
609                   Unused: 0x1E 0x1F 0x2D-0x3F
610                   We don't care about the MS extensions.*/
611                 if (i == 0x3 || i == 0x5 || i == 0x7 || i == 0x13 || i == 0x16)
612                         ADD_ERROR (ctx, g_strdup_printf ("The metadata verifies doesn't support MS specific table %x", i));
613                 if (i == 0x1E || i == 0x1F || i >= 0x2D)
614                         ADD_ERROR (ctx, g_strdup_printf ("Invalid table %x", i));
615                 ++count;
616         }
617
618         if (tables_area.size < 24 + count * 4)
619                 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));
620
621 }
622
623
624 GSList*
625 mono_image_verify (const char *data, guint32 size)
626 {
627         VerifyContext ctx;
628         memset (&ctx, 0, sizeof (VerifyContext));
629         ctx.data = data;
630         ctx.size = size;
631         ctx.valid = 1;
632
633         verify_msdos_header (&ctx);
634         CHECK_STATE();
635         verify_pe_header (&ctx);
636         CHECK_STATE();
637         verify_pe_optional_header (&ctx);
638         CHECK_STATE();
639         load_section_table (&ctx);
640         CHECK_STATE();
641         load_data_directories (&ctx);
642         CHECK_STATE();
643         verify_import_table (&ctx);
644         CHECK_STATE();
645         /*No need to check the IAT directory entry, it's content is indirectly verified by verify_import_table*/
646         verify_resources_table (&ctx);
647         CHECK_STATE();
648         verify_cli_header (&ctx);
649         CHECK_STATE();
650         verify_metadata_header (&ctx);
651         CHECK_STATE();
652         verify_tables_schema (&ctx);
653         CHECK_STATE();
654 cleanup:
655         g_free (ctx.sections);
656         return ctx.errors;
657 }