2 * metadata-verify.c: Metadata verfication support
5 * Mono Project (http://www.mono-project.com)
7 * Copyright (C) 2005-2008 Novell, Inc. (http://www.novell.com)
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>
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
44 guint32 section_count
;
45 SectionHeader
*sections
;
47 guint32 data_dir_count
;
50 #define ADD_VERIFY_INFO(__ctx, __msg, __status, __exception) \
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); \
60 #define ADD_ERROR(__ctx, __msg) \
62 ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \
67 #define CHECK_STATE() do { if (!ctx.valid) goto cleanup; } while (0)
70 pe_signature_offset (VerifyContext
*ctx
)
72 return read32 (ctx
->data
+ 0x3c);
76 pe_header_offset (VerifyContext
*ctx
)
78 return read32 (ctx
->data
+ 0x3c) + 4;
83 verify_msdos_header (VerifyContext
*ctx
)
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"));
96 verify_pe_header (VerifyContext
*ctx
)
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"));
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"));
112 verify_pe_optional_header (VerifyContext
*ctx
)
114 guint32 offset
= pe_header_offset (ctx
);
116 const char *pe_header
= ctx
->data
+ offset
;
117 const char *pe_optional_header
= pe_header
+ 20;
119 header_size
= read16 (pe_header
+ 16);
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"));
125 if (offset
> ctx
->size
- header_size
|| header_size
> ctx
->size
)
126 ADD_ERROR (ctx
, g_strdup ("Invalid PE optional header size"));
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
));
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
));
143 if (read16 (pe_optional_header
) == 0x20B)
144 ADD_ERROR (ctx
, g_strdup ("Metadata verifier doesn't handle PE32+"));
146 ADD_ERROR (ctx
, g_strdup_printf ("Invalid optional header magic %d", read16 (pe_optional_header
)));
151 load_section_table (VerifyContext
*ctx
)
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);
162 if (num_sections
* 40 > ctx
->size
- offset
)
163 ADD_ERROR (ctx
, g_strdup ("Invalid PE optional header size"));
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);
173 ptr
= ctx
->data
+ offset
; /*reset it to the beggining*/
174 for (i
= 0; i
< num_sections
; ++i
) {
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"));
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"));
187 if (raw_size
> ctx
->size
- sections
[i
].baseOffset
)
188 ADD_ERROR (ctx
, g_strdup_printf ("Invalid SizeOfRawData %x points beyond EOF", raw_size
));
190 /*We ignore the line number junk*/
200 mono_image_verify (const char *data
, guint32 size
)
203 memset (&ctx
, 0, sizeof (VerifyContext
));
208 verify_msdos_header (&ctx
);
210 verify_pe_header (&ctx
);
212 verify_pe_optional_header (&ctx
);
214 load_section_table (&ctx
);
218 g_free (ctx
.sections
);