update copyright notices
[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 */
31
32 typedef struct {
33         const char *data;
34         guint32 size;
35         GSList *errors;
36         int valid;
37
38         guint32 data_dir_count;
39 } VerifyContext;
40
41 #define ADD_VERIFY_INFO(__ctx, __msg, __status, __exception)    \
42         do {    \
43                 MonoVerifyInfoExtended *vinfo = g_new (MonoVerifyInfoExtended, 1);      \
44                 vinfo->info.status = __status;  \
45                 vinfo->info.message = ( __msg); \
46                 vinfo->exception_type = (__exception);  \
47                 (__ctx)->errors = g_slist_prepend ((__ctx)->errors, vinfo);     \
48         } while (0)
49
50
51 #define ADD_ERROR(__ctx, __msg) \
52         do {    \
53                 ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \
54                 (__ctx)->valid = 0; \
55         } while (0)
56
57 #define CHECK_STATE() do { if (!ctx.valid) goto cleanup; } while (0)
58
59 static guint32
60 pe_signature_offset (VerifyContext *ctx)
61 {
62         return read32 (ctx->data + 0x3c);
63 }
64
65 static void
66 verify_msdos_header (VerifyContext *ctx)
67 {
68         guint32 lfanew;
69         if (ctx->size < 128) {
70                 ADD_ERROR (ctx, g_strdup ("Not enough space for the MS-DOS header"));
71                 return;
72         }
73         if (ctx->data [0] != 0x4d || ctx->data [1] != 0x5a)
74                 ADD_ERROR (ctx,  g_strdup ("Invalid MS-DOS watermark"));
75         lfanew = pe_signature_offset (ctx);
76         if (lfanew > ctx->size - 4)
77                 ADD_ERROR (ctx, g_strdup ("MS-DOS lfanew offset points to outside of the file"));
78 }
79
80 static void
81 verify_pe_header (VerifyContext *ctx)
82 {
83         guint32 offset = pe_signature_offset (ctx);
84         const char *pe_header = ctx->data + offset;
85         if (pe_header [0] != 'P' || pe_header [1] != 'E' ||pe_header [2] != 0 ||pe_header [3] != 0)
86                 ADD_ERROR (ctx,  g_strdup ("Invalid PE header watermark"));
87         pe_header += 4;
88         offset += 4;
89
90         if (offset > ctx->size - 20)
91                 ADD_ERROR (ctx, g_strdup ("File with truncated pe header"));
92         if (read16 (pe_header) != 0x14c)
93                 ADD_ERROR (ctx, g_strdup ("Invalid PE header Machine value"));
94
95 }
96
97 static void
98 verify_pe_optional_header (VerifyContext *ctx)
99 {
100         guint32 offset = pe_signature_offset (ctx) + 4;
101         guint32 header_size;
102         const char *pe_header = ctx->data + offset;
103         const char *pe_optional_header = pe_header + 20;
104
105         header_size = read16 (pe_header + 16);
106         offset += 20;
107
108         if (header_size < 2) /*must be at least 2 or we won't be able to read magic*/
109                 ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
110
111         if (offset > ctx->size - header_size || header_size > ctx->size) {
112                 ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
113                 return;
114         }
115
116         if (read16 (pe_optional_header) == 0x10b) {
117                 if (header_size != 224)
118                         ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header size %d", header_size));
119
120                 if (read32 (pe_optional_header + 28) != 0x400000)
121                         ADD_ERROR (ctx, g_strdup_printf ("Invalid Image base %x", read32 (pe_optional_header + 28)));
122                 if (read32 (pe_optional_header + 32) != 0x2000)
123                         ADD_ERROR (ctx, g_strdup_printf ("Invalid Section Aligmment %x", read32 (pe_optional_header + 32)));
124                 if (read32 (pe_optional_header + 36) != 0x200)
125                         ADD_ERROR (ctx, g_strdup_printf ("Invalid file Aligmment %x", read32 (pe_optional_header + 36)));
126                 /* All the junk in the middle is irrelevant, specially for mono. */
127                 ctx->data_dir_count = read32 (pe_optional_header + 92);
128                 if (ctx->data_dir_count > 0x10)
129                         ADD_ERROR (ctx, g_strdup_printf ("Too many data directories %x", ctx->data_dir_count));
130         } else {
131                 if (read16 (pe_optional_header) == 0x20B)
132                         ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle PE32+"));
133                 else
134                         ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header magic %d", read16 (pe_optional_header)));
135         }
136
137
138 }
139
140 GSList*
141 mono_image_verify (const char *data, guint32 size)
142 {
143         VerifyContext ctx;
144         memset (&ctx, 0, sizeof (VerifyContext));
145         ctx.data = data;
146         ctx.size = size;
147         ctx.valid = 1;
148
149         verify_msdos_header (&ctx);
150         CHECK_STATE();
151         verify_pe_header (&ctx);
152         CHECK_STATE();
153         verify_pe_optional_header (&ctx);
154         CHECK_STATE();
155
156 cleanup:
157         return ctx.errors;
158 }