2009-02-11 Bill Holmes <billholmes54@gmail.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 must take PE32+ into account
31 */
32
33 typedef struct {
34         guint32 baseRVA;
35         guint32 baseOffset;
36         guint32 size;
37 } SectionHeader;
38
39 typedef struct {
40         const char *data;
41         guint32 size;
42         GSList *errors;
43         int valid;
44         guint32 section_count;
45         SectionHeader *sections;
46
47         guint32 data_dir_count;
48 } VerifyContext;
49
50 #define ADD_VERIFY_INFO(__ctx, __msg, __status, __exception)    \
51         do {    \
52                 MonoVerifyInfoExtended *vinfo = g_new (MonoVerifyInfoExtended, 1);      \
53                 vinfo->info.status = __status;  \
54                 vinfo->info.message = ( __msg); \
55                 vinfo->exception_type = (__exception);  \
56                 (__ctx)->errors = g_slist_prepend ((__ctx)->errors, vinfo);     \
57         } while (0)
58
59
60 #define ADD_ERROR(__ctx, __msg) \
61         do {    \
62                 ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \
63                 (__ctx)->valid = 0; \
64                 return; \
65         } while (0)
66
67 #define CHECK_STATE() do { if (!ctx.valid) goto cleanup; } while (0)
68
69 static guint32
70 pe_signature_offset (VerifyContext *ctx)
71 {
72         return read32 (ctx->data + 0x3c);
73 }
74
75 static guint32
76 pe_header_offset (VerifyContext *ctx)
77 {
78         return read32 (ctx->data + 0x3c) + 4;
79 }
80
81
82 static void
83 verify_msdos_header (VerifyContext *ctx)
84 {
85         guint32 lfanew;
86         if (ctx->size < 128)
87                 ADD_ERROR (ctx, g_strdup ("Not enough space for the MS-DOS header"));
88         if (ctx->data [0] != 0x4d || ctx->data [1] != 0x5a)
89                 ADD_ERROR (ctx,  g_strdup ("Invalid MS-DOS watermark"));
90         lfanew = pe_signature_offset (ctx);
91         if (lfanew > ctx->size - 4)
92                 ADD_ERROR (ctx, g_strdup ("MS-DOS lfanew offset points to outside of the file"));
93 }
94
95 static void
96 verify_pe_header (VerifyContext *ctx)
97 {
98         guint32 offset = pe_signature_offset (ctx);
99         const char *pe_header = ctx->data + offset;
100         if (pe_header [0] != 'P' || pe_header [1] != 'E' ||pe_header [2] != 0 ||pe_header [3] != 0)
101                 ADD_ERROR (ctx,  g_strdup ("Invalid PE header watermark"));
102         pe_header += 4;
103         offset += 4;
104
105         if (offset > ctx->size - 20)
106                 ADD_ERROR (ctx, g_strdup ("File with truncated pe header"));
107         if (read16 (pe_header) != 0x14c)
108                 ADD_ERROR (ctx, g_strdup ("Invalid PE header Machine value"));
109 }
110
111 static void
112 verify_pe_optional_header (VerifyContext *ctx)
113 {
114         guint32 offset = pe_header_offset (ctx);
115         guint32 header_size;
116         const char *pe_header = ctx->data + offset;
117         const char *pe_optional_header = pe_header + 20;
118
119         header_size = read16 (pe_header + 16);
120         offset += 20;
121
122         if (header_size < 2) /*must be at least 2 or we won't be able to read magic*/
123                 ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
124
125         if (offset > ctx->size - header_size || header_size > ctx->size)
126                 ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
127
128         if (read16 (pe_optional_header) == 0x10b) {
129                 if (header_size != 224)
130                         ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header size %d", header_size));
131
132                 if (read32 (pe_optional_header + 28) != 0x400000)
133                         ADD_ERROR (ctx, g_strdup_printf ("Invalid Image base %x", read32 (pe_optional_header + 28)));
134                 if (read32 (pe_optional_header + 32) != 0x2000)
135                         ADD_ERROR (ctx, g_strdup_printf ("Invalid Section Aligmment %x", read32 (pe_optional_header + 32)));
136                 if (read32 (pe_optional_header + 36) != 0x200)
137                         ADD_ERROR (ctx, g_strdup_printf ("Invalid file Aligmment %x", read32 (pe_optional_header + 36)));
138                 /* All the junk in the middle is irrelevant, specially for mono. */
139                 ctx->data_dir_count = read32 (pe_optional_header + 92);
140                 if (ctx->data_dir_count > 0x10)
141                         ADD_ERROR (ctx, g_strdup_printf ("Too many data directories %x", ctx->data_dir_count));
142         } else {
143                 if (read16 (pe_optional_header) == 0x20B)
144                         ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle PE32+"));
145                 else
146                         ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header magic %d", read16 (pe_optional_header)));
147         }
148 }
149
150 static void
151 load_section_table (VerifyContext *ctx)
152 {
153         int i;
154         SectionHeader *sections;
155         guint32 offset =  pe_header_offset (ctx);
156         const char *ptr = ctx->data + offset;
157         guint16 num_sections = ctx->section_count = read16 (ptr + 2);
158
159         offset += 244;
160         ptr += 244;
161
162         if (num_sections * 40 > ctx->size - offset)
163                 ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
164
165         sections = ctx->sections = g_new0 (SectionHeader, num_sections);
166         for (i = 0; i < num_sections; ++i) {
167                 sections [i].size = read32 (ptr + 8);
168                 sections [i].baseRVA = read32 (ptr + 12);
169                 sections [i].baseOffset = read32 (ptr + 20);
170                 ptr += 40;
171         }
172
173         ptr = ctx->data + offset; /*reset it to the beggining*/
174         for (i = 0; i < num_sections; ++i) {
175                 guint32 raw_size;
176                 if (sections [i].baseOffset == 0)
177                         ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle sections with intialized data only"));
178                 if (sections [i].baseOffset >= ctx->size)
179                         ADD_ERROR (ctx, g_strdup_printf ("Invalid PointerToRawData %x points beyond EOF", sections [i].baseOffset));
180                 if (sections [i].size > ctx->size - sections [i].baseOffset)
181                         ADD_ERROR (ctx, g_strdup ("Invalid VirtualSize points beyond EOF"));
182
183                 raw_size = read32 (ptr + 16);
184                 if (raw_size < sections [i].size)
185                         ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle sections with SizeOfRawData < VirtualSize"));
186
187                 if (raw_size > ctx->size - sections [i].baseOffset)
188                         ADD_ERROR (ctx, g_strdup_printf ("Invalid SizeOfRawData %x points beyond EOF", raw_size));
189                 
190                 /*We ignore the line number junk*/
191
192                 //FIXME relocations
193                 ptr += 40;
194         }
195         
196         
197 }
198
199 GSList*
200 mono_image_verify (const char *data, guint32 size)
201 {
202         VerifyContext ctx;
203         memset (&ctx, 0, sizeof (VerifyContext));
204         ctx.data = data;
205         ctx.size = size;
206         ctx.valid = 1;
207
208         verify_msdos_header (&ctx);
209         CHECK_STATE();
210         verify_pe_header (&ctx);
211         CHECK_STATE();
212         verify_pe_optional_header (&ctx);
213         CHECK_STATE();
214         load_section_table (&ctx);
215         CHECK_STATE();
216
217 cleanup:
218         g_free (ctx.sections);
219         return ctx.errors;
220 }