4 #include <grub/module_verifier.h>
5 #include <grub/util/misc.h>
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)
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))
56 static inline grub_uint32_t
57 grub_target_to_host32_real (const struct grub_module_verifier_arch
*arch
,
61 return grub_be_to_cpu32 (in
);
63 return grub_le_to_cpu32 (in
);
66 static inline grub_uint64_t
67 grub_target_to_host64_real (const struct grub_module_verifier_arch
*arch
,
71 return grub_be_to_cpu64 (in
);
73 return grub_le_to_cpu64 (in
);
76 static inline grub_uint64_t
77 grub_host_to_target64_real (const struct grub_module_verifier_arch
*arch
,
81 return grub_cpu_to_be64 (in
);
83 return grub_cpu_to_le64 (in
);
86 static inline grub_uint32_t
87 grub_host_to_target32_real (const struct grub_module_verifier_arch
*arch
,
91 return grub_cpu_to_be32 (in
);
93 return grub_cpu_to_le32 (in
);
96 static inline grub_uint16_t
97 grub_target_to_host16_real (const struct grub_module_verifier_arch
*arch
,
101 return grub_be_to_cpu16 (in
);
103 return grub_le_to_cpu16 (in
);
106 static inline grub_uint16_t
107 grub_host_to_target16_real (const struct grub_module_verifier_arch
*arch
,
111 return grub_cpu_to_be16 (in
);
113 return grub_cpu_to_le16 (in
);
116 static inline grub_uint64_t
117 grub_host_to_target_addr_real (const struct grub_module_verifier_arch
*arch
, grub_uint64_t in
)
119 if (arch
->voidp_sizeof
== 8)
120 return grub_host_to_target64_real (arch
, in
);
122 return grub_host_to_target32_real (arch
, in
);
125 static inline grub_uint64_t
126 grub_target_to_host_real (const struct grub_module_verifier_arch
*arch
, grub_uint64_t in
)
128 if (arch
->voidp_sizeof
== 8)
129 return grub_target_to_host64_real (arch
, in
);
131 return grub_target_to_host32_real (arch
, in
);
136 find_section (const struct grub_module_verifier_arch
*arch
, Elf_Ehdr
*e
, const char *name
)
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
);
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)
154 check_license (const char * const filename
,
155 const struct grub_module_verifier_arch
*arch
, Elf_Ehdr
*e
)
157 Elf_Shdr
*s
= find_section (arch
, e
, ".module_license");
158 if (s
&& (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=GPLv3+") == 0
160 || strcmp ((char *) e
+ grub_target_to_host(s
->sh_offset
), "LICENSE=GPLv2+") == 0))
162 grub_util_error ("%s: incompatible license", filename
);
166 get_symtab (const struct grub_module_verifier_arch
*arch
, Elf_Ehdr
*e
, Elf_Word
*size
, Elf_Word
*entsize
)
169 Elf_Shdr
*s
, *sections
;
172 sections
= (Elf_Shdr
*) ((char *) e
+ grub_target_to_host (e
->e_shoff
));
173 for (i
= 0, s
= sections
;
174 i
< grub_target_to_host16 (e
->e_shnum
);
175 i
++, s
= (Elf_Shdr
*) ((char *) s
+ grub_target_to_host16 (e
->e_shentsize
)))
176 if (grub_target_to_host32 (s
->sh_type
) == SHT_SYMTAB
)
179 if (i
== grub_target_to_host16 (e
->e_shnum
))
182 sym
= (Elf_Sym
*) ((char *) e
+ grub_target_to_host (s
->sh_offset
));
183 *size
= grub_target_to_host (s
->sh_size
);
184 *entsize
= grub_target_to_host (s
->sh_entsize
);
189 is_whitelisted (const char *modname
, const char **whitelist
)
196 for (ptr
= whitelist
; *ptr
; ptr
++)
197 if (strcmp (modname
, *ptr
) == 0)
203 check_symbols (const struct grub_module_verifier_arch
*arch
,
204 Elf_Ehdr
*e
, const char *modname
,
205 const char **whitelist_empty
)
208 Elf_Word size
, entsize
;
211 /* Module without symbol table and without .moddeps section is useless
212 at boot time, so catch it early to prevent build errors */
213 sym
= get_symtab (arch
, e
, &size
, &entsize
);
218 /* However some modules are dependencies-only,
219 e.g. insmod all_video pulls in all video drivers.
220 Some platforms e.g. xen have no video drivers, so
221 the module does nothing. */
222 if (is_whitelisted (modname
, whitelist_empty
))
225 s
= find_section (arch
, e
, ".moddeps");
228 grub_util_error ("%s: no symbol table and no .moddeps section", modname
);
231 grub_util_error ("%s: no symbol table and empty .moddeps section", modname
);
238 i
++, sym
= (Elf_Sym
*) ((char *) sym
+ entsize
))
240 unsigned char type
= ELF_ST_TYPE (sym
->st_info
);
252 return grub_util_error ("%s: unknown symbol type `%d'", modname
, (int) type
);
258 is_symbol_local(Elf_Sym
*sym
)
260 switch (ELF_ST_TYPE (sym
->st_info
))
264 if (sym
->st_name
!= 0 && sym
->st_shndx
== 0)
278 section_check_relocations (const char * const modname
,
279 const struct grub_module_verifier_arch
*arch
, void *ehdr
,
280 Elf_Shdr
*s
, size_t target_seg_size
)
284 Elf_Word symtabsize
, symtabentsize
;
286 symtab
= get_symtab (arch
, ehdr
, &symtabsize
, &symtabentsize
);
288 grub_util_error ("%s: relocation without symbol table", modname
);
290 for (rel
= (Elf_Rel
*) ((char *) ehdr
+ grub_target_to_host (s
->sh_offset
)),
291 max
= (Elf_Rel
*) ((char *) rel
+ grub_target_to_host (s
->sh_size
));
293 rel
= (Elf_Rel
*) ((char *) rel
+ grub_target_to_host (s
->sh_entsize
)))
298 if (target_seg_size
< grub_target_to_host (rel
->r_offset
))
299 grub_util_error ("%s: reloc offset is out of the segment", modname
);
302 if (arch
->machine
== EM_MIPS
&& arch
->voidp_sizeof
== 8)
303 r_info
= ((grub_uint64_t
) rel
->r_info
<< 32) |
304 (grub_uint32_t
) grub_be_to_cpu64 (rel
->r_info
);
306 r_info
= grub_target_to_host (rel
->r_info
);
308 grub_uint32_t type
= ELF_R_TYPE (r_info
);
310 if (arch
->machine
== EM_SPARCV9
)
313 for (i
= 0; arch
->supported_relocations
[i
] != -1; i
++)
314 if (type
== arch
->supported_relocations
[i
])
316 if (arch
->supported_relocations
[i
] != -1)
318 if (!arch
->short_relocations
)
319 grub_util_error ("%s: unsupported relocation 0x%x", modname
, type
);
320 for (i
= 0; arch
->short_relocations
[i
] != -1; i
++)
321 if (type
== arch
->short_relocations
[i
])
323 if (arch
->short_relocations
[i
] == -1)
324 grub_util_error ("%s: unsupported relocation 0x%x", modname
, type
);
325 sym
= (Elf_Sym
*) ((char *) symtab
+ symtabentsize
* ELF_R_SYM (r_info
));
327 if (is_symbol_local (sym
))
329 grub_util_error ("%s: relocation 0x%x is not module-local", modname
, type
);
331 #if defined(MODULEVERIFIER_ELF64)
332 if (arch
->machine
== EM_AARCH64
)
334 unsigned unmatched_adr_got_page
= 0;
336 for (rel
= (Elf_Rel
*) ((char *) ehdr
+ grub_target_to_host (s
->sh_offset
)),
337 max
= (Elf_Rel
*) ((char *) rel
+ grub_target_to_host (s
->sh_size
));
339 rel
= (Elf_Rel
*) ((char *) rel
+ grub_target_to_host (s
->sh_entsize
)))
341 switch (ELF_R_TYPE (grub_target_to_host (rel
->r_info
)))
343 case R_AARCH64_ADR_GOT_PAGE
:
344 unmatched_adr_got_page
++;
345 for (rel2
= (Elf_Rela
*) ((char *) rel
+ grub_target_to_host (s
->sh_entsize
));
346 rel2
< (Elf_Rela
*) max
;
347 rel2
= (Elf_Rela
*) ((char *) rel2
+ grub_target_to_host (s
->sh_entsize
)))
348 if (ELF_R_SYM (rel2
->r_info
)
349 == ELF_R_SYM (rel
->r_info
)
350 && ((Elf_Rela
*) rel
)->r_addend
== rel2
->r_addend
351 && ELF_R_TYPE (rel2
->r_info
) == R_AARCH64_LD64_GOT_LO12_NC
)
353 if (rel2
>= (Elf_Rela
*) max
)
354 grub_util_error ("%s: ADR_GOT_PAGE without matching LD64_GOT_LO12_NC", modname
);
356 case R_AARCH64_LD64_GOT_LO12_NC
:
357 if (unmatched_adr_got_page
== 0)
358 grub_util_error ("%s: LD64_GOT_LO12_NC without matching ADR_GOT_PAGE", modname
);
359 unmatched_adr_got_page
--;
368 check_relocations (const char * const modname
,
369 const struct grub_module_verifier_arch
*arch
, Elf_Ehdr
*e
)
374 for (i
= 0, s
= (Elf_Shdr
*) ((char *) e
+ grub_target_to_host (e
->e_shoff
));
375 i
< grub_target_to_host16 (e
->e_shnum
);
376 i
++, s
= (Elf_Shdr
*) ((char *) s
+ grub_target_to_host16 (e
->e_shentsize
)))
377 if (grub_target_to_host32 (s
->sh_type
) == SHT_REL
|| grub_target_to_host32 (s
->sh_type
) == SHT_RELA
)
381 if (grub_target_to_host32 (s
->sh_type
) == SHT_REL
&& !(arch
->flags
& GRUB_MODULE_VERIFY_SUPPORTS_REL
))
382 grub_util_error ("%s: unsupported SHT_REL", modname
);
383 if (grub_target_to_host32 (s
->sh_type
) == SHT_RELA
&& !(arch
->flags
& GRUB_MODULE_VERIFY_SUPPORTS_RELA
))
384 grub_util_error ("%s: unsupported SHT_RELA", modname
);
386 /* Find the target segment. */
387 if (grub_target_to_host32 (s
->sh_info
) >= grub_target_to_host16 (e
->e_shnum
))
388 grub_util_error ("%s: orphaned reloc section", modname
);
389 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
));
391 section_check_relocations (modname
, arch
, e
, s
, grub_target_to_host (ts
->sh_size
));
396 SUFFIX(grub_module_verify
) (const char * const filename
,
397 void *module_img
, size_t size
,
398 const struct grub_module_verifier_arch
*arch
,
399 const char **whitelist_empty
)
401 Elf_Ehdr
*e
= module_img
;
403 /* Check the header size. */
404 if (size
< sizeof (Elf_Ehdr
))
405 grub_util_error ("%s: ELF header smaller than expected", filename
);
407 /* Check the magic numbers. */
408 if (e
->e_ident
[EI_MAG0
] != ELFMAG0
409 || e
->e_ident
[EI_MAG1
] != ELFMAG1
410 || e
->e_ident
[EI_MAG2
] != ELFMAG2
411 || e
->e_ident
[EI_MAG3
] != ELFMAG3
412 || e
->e_ident
[EI_VERSION
] != EV_CURRENT
413 || grub_target_to_host32 (e
->e_version
) != EV_CURRENT
)
414 grub_util_error ("%s: invalid arch-independent ELF magic", filename
);
416 if (e
->e_ident
[EI_CLASS
] != ELFCLASSXX
417 || e
->e_ident
[EI_DATA
] != (arch
->bigendian
? ELFDATA2MSB
: ELFDATA2LSB
)
418 || grub_target_to_host16 (e
->e_machine
) != arch
->machine
)
419 grub_util_error ("%s: invalid arch-dependent ELF magic", filename
);
421 if (grub_target_to_host16 (e
->e_type
) != ET_REL
)
423 grub_util_error ("%s: this ELF file is not of the right type", filename
);
426 /* Make sure that every section is within the core. */
427 if (size
< grub_target_to_host (e
->e_shoff
)
428 + (grub_uint32_t
) grub_target_to_host16 (e
->e_shentsize
) * grub_target_to_host16(e
->e_shnum
))
430 grub_util_error ("%s: ELF sections outside core", filename
);
433 check_license (filename
, arch
, e
);
438 s
= find_section (arch
, e
, ".modname");
440 grub_util_error ("%s: no module name found", filename
);
442 modname
= (const char *) e
+ grub_target_to_host (s
->sh_offset
);
444 check_symbols(arch
, e
, modname
, whitelist_empty
);
445 check_relocations(modname
, arch
, e
);