ieee1275: split up grub_machine_get_bootlocation
[grub.git] / util / grub-module-verifierXX.c
1 #include <string.h>
2
3 #include <grub/elf.h>
4 #include <grub/module_verifier.h>
5 #include <grub/util/misc.h>
6
7 #if defined(MODULEVERIFIER_ELF32)
8 # define SUFFIX(x)      x ## 32
9 # define ELFCLASSXX     ELFCLASS32
10 # define Elf_Ehdr       Elf32_Ehdr
11 # define Elf_Phdr       Elf32_Phdr
12 # define Elf_Nhdr       Elf32_Nhdr
13 # define Elf_Addr       Elf32_Addr
14 # define Elf_Sym        Elf32_Sym
15 # define Elf_Off        Elf32_Off
16 # define Elf_Shdr       Elf32_Shdr
17 # define Elf_Rela       Elf32_Rela
18 # define Elf_Rel        Elf32_Rel
19 # define Elf_Word       Elf32_Word
20 # define Elf_Half       Elf32_Half
21 # define Elf_Section    Elf32_Section
22 # define ELF_R_SYM(val)         ELF32_R_SYM(val)
23 # define ELF_R_TYPE(val)                ELF32_R_TYPE(val)
24 # define ELF_ST_TYPE(val)               ELF32_ST_TYPE(val)
25 #elif defined(MODULEVERIFIER_ELF64)
26 # define SUFFIX(x)      x ## 64
27 # define ELFCLASSXX     ELFCLASS64
28 # define Elf_Ehdr       Elf64_Ehdr
29 # define Elf_Phdr       Elf64_Phdr
30 # define Elf_Nhdr       Elf64_Nhdr
31 # define Elf_Addr       Elf64_Addr
32 # define Elf_Sym        Elf64_Sym
33 # define Elf_Off        Elf64_Off
34 # define Elf_Shdr       Elf64_Shdr
35 # define Elf_Rela       Elf64_Rela
36 # define Elf_Rel        Elf64_Rel
37 # define Elf_Word       Elf64_Word
38 # define Elf_Half       Elf64_Half
39 # define Elf_Section    Elf64_Section
40 # define ELF_R_SYM(val)         ELF64_R_SYM(val)
41 # define ELF_R_TYPE(val)                ELF64_R_TYPE(val)
42 # define ELF_ST_TYPE(val)               ELF64_ST_TYPE(val)
43 #else
44 #error "I'm confused"
45 #endif
46
47 #define grub_target_to_host32(x) (grub_target_to_host32_real (arch, (x)))
48 #define grub_host_to_target32(x) (grub_host_to_target32_real (arch, (x)))
49 #define grub_target_to_host64(x) (grub_target_to_host64_real (arch, (x)))
50 #define grub_host_to_target64(x) (grub_host_to_target64_real (arch, (x)))
51 #define grub_host_to_target_addr(x) (grub_host_to_target_addr_real (arch, (x)))
52 #define grub_target_to_host16(x) (grub_target_to_host16_real (arch, (x)))
53 #define grub_host_to_target16(x) (grub_host_to_target16_real (arch, (x)))
54 #define grub_target_to_host(val) grub_target_to_host_real(arch, (val))
55
56 static inline grub_uint32_t
57 grub_target_to_host32_real (const struct grub_module_verifier_arch *arch,
58                             grub_uint32_t in)
59 {
60   if (arch->bigendian)
61     return grub_be_to_cpu32 (in);
62   else
63     return grub_le_to_cpu32 (in);
64 }
65
66 static inline grub_uint64_t
67 grub_target_to_host64_real (const struct grub_module_verifier_arch *arch,
68                             grub_uint64_t in)
69 {
70   if (arch->bigendian)
71     return grub_be_to_cpu64 (in);
72   else
73     return grub_le_to_cpu64 (in);
74 }
75
76 static inline grub_uint64_t
77 grub_host_to_target64_real (const struct grub_module_verifier_arch *arch,
78                             grub_uint64_t in)
79 {
80   if (arch->bigendian)
81     return grub_cpu_to_be64 (in);
82   else
83     return grub_cpu_to_le64 (in);
84 }
85
86 static inline grub_uint32_t
87 grub_host_to_target32_real (const struct grub_module_verifier_arch *arch,
88                             grub_uint32_t in)
89 {
90   if (arch->bigendian)
91     return grub_cpu_to_be32 (in);
92   else
93     return grub_cpu_to_le32 (in);
94 }
95
96 static inline grub_uint16_t
97 grub_target_to_host16_real (const struct grub_module_verifier_arch *arch,
98                             grub_uint16_t in)
99 {
100   if (arch->bigendian)
101     return grub_be_to_cpu16 (in);
102   else
103     return grub_le_to_cpu16 (in);
104 }
105
106 static inline grub_uint16_t
107 grub_host_to_target16_real (const struct grub_module_verifier_arch *arch,
108                             grub_uint16_t in)
109 {
110   if (arch->bigendian)
111     return grub_cpu_to_be16 (in);
112   else
113     return grub_cpu_to_le16 (in);
114 }
115
116 static inline grub_uint64_t
117 grub_host_to_target_addr_real (const struct grub_module_verifier_arch *arch, grub_uint64_t in)
118 {
119   if (arch->voidp_sizeof == 8)
120     return grub_host_to_target64_real (arch, in);
121   else
122     return grub_host_to_target32_real (arch, in);
123 }
124
125 static inline grub_uint64_t
126 grub_target_to_host_real (const struct grub_module_verifier_arch *arch, grub_uint64_t in)
127 {
128   if (arch->voidp_sizeof == 8)
129     return grub_target_to_host64_real (arch, in);
130   else
131     return grub_target_to_host32_real (arch, in);
132 }
133
134
135 static Elf_Shdr *
136 find_section (const struct grub_module_verifier_arch *arch, Elf_Ehdr *e, const char *name)
137 {
138   Elf_Shdr *s;
139   const char *str;
140   unsigned i;
141
142   s = (Elf_Shdr *) ((char *) e + grub_target_to_host (e->e_shoff) + grub_target_to_host16 (e->e_shstrndx) * grub_target_to_host16 (e->e_shentsize));
143   str = (char *) e + grub_target_to_host (s->sh_offset);
144
145   for (i = 0, s = (Elf_Shdr *) ((char *) e + grub_target_to_host (e->e_shoff));
146        i < grub_target_to_host16 (e->e_shnum);
147        i++, s = (Elf_Shdr *) ((char *) s + grub_target_to_host16 (e->e_shentsize)))
148     if (strcmp (str + grub_target_to_host32 (s->sh_name), name) == 0)
149       return s;
150   return NULL;
151 }
152
153 static void
154 check_license (const struct grub_module_verifier_arch *arch, Elf_Ehdr *e)
155 {
156   Elf_Shdr *s = find_section (arch, e, ".module_license");
157   if (s && (strcmp ((char *) e + grub_target_to_host(s->sh_offset), "LICENSE=GPLv3") == 0
158             || strcmp ((char *) e + grub_target_to_host(s->sh_offset), "LICENSE=GPLv3+") == 0
159             || strcmp ((char *) e + grub_target_to_host(s->sh_offset), "LICENSE=GPLv2+") == 0))
160     return;
161   grub_util_error ("incompatible license");
162 }
163
164 static Elf_Sym *
165 get_symtab (const struct grub_module_verifier_arch *arch, Elf_Ehdr *e, Elf_Word *size, Elf_Word *entsize)
166 {
167   unsigned i;
168   Elf_Shdr *s, *sections;
169   Elf_Sym *sym;
170
171   sections = (Elf_Shdr *) ((char *) e + grub_target_to_host (e->e_shoff));
172   for (i = 0, s = sections;
173        i < grub_target_to_host16 (e->e_shnum);
174        i++, s = (Elf_Shdr *) ((char *) s + grub_target_to_host16 (e->e_shentsize)))
175     if (grub_target_to_host32 (s->sh_type) == SHT_SYMTAB)
176       break;
177
178   if (i == grub_target_to_host16 (e->e_shnum))
179     return NULL;
180
181   sym = (Elf_Sym *) ((char *) e + grub_target_to_host (s->sh_offset));
182   *size = grub_target_to_host (s->sh_size);
183   *entsize = grub_target_to_host (s->sh_entsize);
184   return sym;
185 }
186
187 static int
188 is_whitelisted (const char *modname, const char **whitelist)
189 {
190   const char **ptr;
191   if (!whitelist)
192     return 0;
193   if (!modname)
194     return 0;
195   for (ptr = whitelist; *ptr; ptr++)
196     if (strcmp (modname, *ptr) == 0)
197       return 1;
198   return 0;
199 }
200
201 static void
202 check_symbols (const struct grub_module_verifier_arch *arch,
203                Elf_Ehdr *e, const char *modname,
204                const char **whitelist_empty)
205 {
206   Elf_Sym *sym;
207   Elf_Word size, entsize;
208   unsigned i;
209
210   /* Module without symbol table and without .moddeps section is useless
211      at boot time, so catch it early to prevent build errors */
212   sym = get_symtab (arch, e, &size, &entsize);
213   if (!sym)
214     {
215       Elf_Shdr *s;
216
217       /* However some modules are dependencies-only,
218          e.g. insmod all_video pulls in all video drivers.
219          Some platforms e.g. xen have no video drivers, so
220          the module does nothing.  */
221       if (is_whitelisted (modname, whitelist_empty))
222         return;
223
224       s = find_section (arch, e, ".moddeps");
225
226       if (!s)
227         grub_util_error ("no symbol table and no .moddeps section");
228
229       if (!s->sh_size)
230         grub_util_error ("no symbol table and empty .moddeps section");
231
232       return;
233     }
234
235   for (i = 0;
236        i < size / entsize;
237        i++, sym = (Elf_Sym *) ((char *) sym + entsize))
238     {
239       unsigned char type = ELF_ST_TYPE (sym->st_info);
240
241       switch (type)
242         {
243         case STT_NOTYPE:
244         case STT_OBJECT:
245         case STT_FUNC:
246         case STT_SECTION:
247         case STT_FILE:
248           break;
249
250         default:
251           return grub_util_error ("unknown symbol type `%d'", (int) type);
252         }
253     }
254 }
255
256 static int
257 is_symbol_local(Elf_Sym *sym)
258 {
259   switch (ELF_ST_TYPE (sym->st_info))
260     {
261     case STT_NOTYPE:
262     case STT_OBJECT:
263       if (sym->st_name != 0 && sym->st_shndx == 0)
264         return 0;
265       return 1;
266
267     case STT_FUNC:
268     case STT_SECTION:
269       return 1;
270
271     default:
272       return 0;
273     }
274 }
275
276 static void
277 section_check_relocations (const struct grub_module_verifier_arch *arch, void *ehdr,
278                            Elf_Shdr *s, size_t target_seg_size)
279 {
280   Elf_Rel *rel, *max;
281   Elf_Sym *symtab;
282   Elf_Word symtabsize, symtabentsize;
283
284   symtab = get_symtab (arch, ehdr, &symtabsize, &symtabentsize);
285   if (!symtab)
286     grub_util_error ("relocation without symbol table");
287
288   for (rel = (Elf_Rel *) ((char *) ehdr + grub_target_to_host (s->sh_offset)),
289          max = (Elf_Rel *) ((char *) rel + grub_target_to_host (s->sh_size));
290        rel < max;
291        rel = (Elf_Rel *) ((char *) rel + grub_target_to_host (s->sh_entsize)))
292     {
293       Elf_Sym *sym;
294       unsigned i;
295
296       if (target_seg_size < grub_target_to_host (rel->r_offset))
297         grub_util_error ("reloc offset is out of the segment");
298
299       grub_uint32_t type = ELF_R_TYPE (grub_target_to_host (rel->r_info));
300
301       if (arch->machine == EM_SPARCV9)
302         type &= 0xff;
303
304       for (i = 0; arch->supported_relocations[i] != -1; i++)
305         if (type == arch->supported_relocations[i])
306           break;
307       if (arch->supported_relocations[i] != -1)
308         continue;
309       if (!arch->short_relocations)
310         grub_util_error ("unsupported relocation 0x%x", type);
311       for (i = 0; arch->short_relocations[i] != -1; i++)
312         if (type == arch->short_relocations[i])
313           break;
314       if (arch->short_relocations[i] == -1)
315         grub_util_error ("unsupported relocation 0x%x", type);
316       sym = (Elf_Sym *) ((char *) symtab + symtabentsize * ELF_R_SYM (grub_target_to_host (rel->r_info)));
317
318       if (is_symbol_local (sym))
319         continue;
320       grub_util_error ("relocation 0x%x is not module-local", type);
321     }
322 #if defined(MODULEVERIFIER_ELF64)
323   if (arch->machine == EM_AARCH64)
324     {
325       unsigned unmatched_adr_got_page = 0;
326       Elf_Rela *rel2;
327       for (rel = (Elf_Rel *) ((char *) ehdr + grub_target_to_host (s->sh_offset)),
328              max = (Elf_Rel *) ((char *) rel + grub_target_to_host (s->sh_size));
329            rel < max;
330            rel = (Elf_Rel *) ((char *) rel + grub_target_to_host (s->sh_entsize)))
331         {
332           switch (ELF_R_TYPE (grub_target_to_host (rel->r_info)))
333             {
334             case R_AARCH64_ADR_GOT_PAGE:
335               unmatched_adr_got_page++;
336               for (rel2 = (Elf_Rela *) ((char *) rel + grub_target_to_host (s->sh_entsize));
337                    rel2 < (Elf_Rela *) max;
338                    rel2 = (Elf_Rela *) ((char *) rel2 + grub_target_to_host (s->sh_entsize)))
339                 if (ELF_R_SYM (rel2->r_info)
340                     == ELF_R_SYM (rel->r_info)
341                     && ((Elf_Rela *) rel)->r_addend == rel2->r_addend
342                     && ELF_R_TYPE (rel2->r_info) == R_AARCH64_LD64_GOT_LO12_NC)
343                   break;
344               if (rel2 >= (Elf_Rela *) max)
345                 grub_util_error ("ADR_GOT_PAGE without matching LD64_GOT_LO12_NC");
346               break;
347             case R_AARCH64_LD64_GOT_LO12_NC:
348               if (unmatched_adr_got_page == 0)
349                 grub_util_error ("LD64_GOT_LO12_NC without matching ADR_GOT_PAGE");
350               unmatched_adr_got_page--;
351               break;
352             }
353         }
354     }
355 #endif
356 }
357
358 static void
359 check_relocations (const struct grub_module_verifier_arch *arch, Elf_Ehdr *e)
360 {
361   Elf_Shdr *s;
362   unsigned i;
363
364   for (i = 0, s = (Elf_Shdr *) ((char *) e + grub_target_to_host (e->e_shoff));
365        i < grub_target_to_host16 (e->e_shnum);
366        i++, s = (Elf_Shdr *) ((char *) s + grub_target_to_host16 (e->e_shentsize)))
367     if (grub_target_to_host32 (s->sh_type) == SHT_REL || grub_target_to_host32 (s->sh_type) == SHT_RELA)
368       {
369         Elf_Shdr *ts;
370
371         if (grub_target_to_host32 (s->sh_type) == SHT_REL && !(arch->flags & GRUB_MODULE_VERIFY_SUPPORTS_REL))
372           grub_util_error ("unsupported SHT_REL");
373         if (grub_target_to_host32 (s->sh_type) == SHT_RELA && !(arch->flags & GRUB_MODULE_VERIFY_SUPPORTS_RELA))
374           grub_util_error ("unsupported SHT_RELA");
375
376         /* Find the target segment.  */
377         if (grub_target_to_host32 (s->sh_info) >= grub_target_to_host16 (e->e_shnum))
378           grub_util_error ("orphaned reloc section");
379         ts = (Elf_Shdr *) ((char *) e + grub_target_to_host (e->e_shoff) + grub_target_to_host32 (s->sh_info) * grub_target_to_host16 (e->e_shentsize));
380
381         section_check_relocations (arch, e, s, grub_target_to_host (ts->sh_size));
382       }
383 }
384
385 void
386 SUFFIX(grub_module_verify) (void *module_img, size_t size,
387                             const struct grub_module_verifier_arch *arch,
388                             const char **whitelist_empty)
389 {
390   Elf_Ehdr *e = module_img;
391
392   /* Check the header size.  */
393   if (size < sizeof (Elf_Ehdr))
394     grub_util_error ("ELF header smaller than expected");
395
396   /* Check the magic numbers.  */
397   if (e->e_ident[EI_MAG0] != ELFMAG0
398       || e->e_ident[EI_MAG1] != ELFMAG1
399       || e->e_ident[EI_MAG2] != ELFMAG2
400       || e->e_ident[EI_MAG3] != ELFMAG3
401       || e->e_ident[EI_VERSION] != EV_CURRENT
402       || grub_target_to_host32 (e->e_version) != EV_CURRENT)
403     grub_util_error ("invalid arch-independent ELF magic");
404
405   if (e->e_ident[EI_CLASS] != ELFCLASSXX
406       || e->e_ident[EI_DATA] != (arch->bigendian ? ELFDATA2MSB : ELFDATA2LSB)
407       || grub_target_to_host16 (e->e_machine) != arch->machine)
408     grub_util_error ("invalid arch-dependent ELF magic");
409
410   if (grub_target_to_host16 (e->e_type) != ET_REL)
411     {
412       grub_util_error ("this ELF file is not of the right type");
413     }
414
415   /* Make sure that every section is within the core.  */
416   if (size < grub_target_to_host (e->e_shoff)
417       + (grub_uint32_t) grub_target_to_host16 (e->e_shentsize) * grub_target_to_host16(e->e_shnum))
418     {
419       grub_util_error ("ELF sections outside core");
420     }
421
422   check_license (arch, e);
423
424   Elf_Shdr *s;
425   const char *modname;
426
427   s = find_section (arch, e, ".modname");
428   if (!s)
429     grub_util_error ("no module name found");
430
431   modname = (const char *) e + grub_target_to_host (s->sh_offset);
432
433   check_symbols(arch, e, modname, whitelist_empty);
434   check_relocations(arch, e);
435 }