Check for (and abort on) multiple symbol tables
[centaur.git] / src / libelfu / elfops / check.c
1 #include <assert.h>
2 #include <stdlib.h>
3 #include <libelfu/libelfu.h>
4
5 int elfu_eCheck(Elf *e)
6 {
7   int elfclass;
8   GElf_Ehdr ehdr;
9   GElf_Phdr *phdrs = NULL;
10   GElf_Shdr *shdrs = NULL;
11   size_t i, j, numPhdr, numShdr;
12   int retval = 0;
13
14   assert(e);
15
16   elfclass = gelf_getclass(e);
17   if (elfclass == ELFCLASSNONE) {
18     ELFU_WARNELF("getclass");
19     goto ERROR;
20   }
21
22   if (!gelf_getehdr(e, &ehdr)) {
23     ELFU_WARNELF("gelf_getehdr");
24     goto ERROR;
25   }
26
27   if (ehdr.e_machine != EM_386 && ehdr.e_machine != EM_X86_64) {
28     ELFU_WARN("Sorry, only x86-32 and x86-64 ELF files are supported at the moment.\n");
29     goto ERROR;
30   }
31
32   if (elf_getphdrnum(e, &numPhdr)) {
33     ELFU_WARNELF("elf_getphdrnum");
34     goto ERROR;
35   }
36
37   if (elf_getshdrnum(e, &numShdr)) {
38     ELFU_WARNELF("elf_getshdrnum");
39     goto ERROR;
40   }
41
42
43   if (numPhdr > 0) {
44     phdrs = malloc(numPhdr * sizeof(GElf_Phdr));
45     if (!phdrs) {
46       ELFU_WARN("elfu_eCheck: malloc() failed for phdrs.\n");
47       goto ERROR;
48     }
49
50     /* Attempt to load all PHDRs at once to catch any errors early */
51     for (i = 0; i < numPhdr; i++) {
52       GElf_Phdr phdr;
53       if (gelf_getphdr(e, i, &phdr) != &phdr) {
54         ELFU_WARN("gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1));
55         goto ERROR;
56       }
57
58       phdrs[i] = phdr;
59     }
60
61     /* Check that LOAD PHDR memory ranges do not overlap, and that others
62      * are either fully contained in a LOAD range, or not at all. */
63     for (i = 0; i < numPhdr; i++) {
64       if (phdrs[i].p_type != PT_LOAD) {
65         continue;
66       }
67
68       for (j = 0; j < numPhdr; j++) {
69         if (j == i || phdrs[j].p_type != PT_LOAD) {
70           continue;
71         }
72
73         if (OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz,
74                         phdrs[j].p_vaddr, phdrs[j].p_memsz)) {
75           if (phdrs[j].p_type == PT_LOAD) {
76             ELFU_WARN("elfu_eCheck: Found LOAD PHDRs that overlap in memory.\n");
77             goto ERROR;
78           } else if (!FULLY_OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz,
79                                         phdrs[j].p_vaddr, phdrs[j].p_memsz)) {
80             ELFU_WARN("elfu_eCheck: PHDRs %d and %d partially overlap in memory.\n", i, j);
81             goto ERROR;
82           }
83         }
84       }
85     }
86   }
87
88
89   if (numShdr > 1) {
90     /* SHDRs should not overlap with PHDRs. */
91     if (OVERLAPPING(ehdr.e_shoff, numShdr * ehdr.e_shentsize,
92                     ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) {
93       ELFU_WARN("elfu_eCheck: SHDRs overlap with PHDRs.\n");
94       goto ERROR;
95     }
96
97     shdrs = malloc(numShdr * sizeof(GElf_Shdr));
98     if (!shdrs) {
99       ELFU_WARN("elfu_eCheck: malloc() failed for shdrs.\n");
100       goto ERROR;
101     }
102
103     /* Attempt to load all SHDRs at once to catch any errors early */
104     for (i = 1; i < numShdr; i++) {
105       Elf_Scn *scn;
106       GElf_Shdr shdr;
107
108       scn = elf_getscn(e, i);
109       if (!scn) {
110         ELFU_WARN("elf_getscn() failed for #%d: %s\n", i, elf_errmsg(-1));
111       }
112
113       if (gelf_getshdr(scn, &shdr) != &shdr) {
114         ELFU_WARNELF("gelf_getshdr");
115         goto ERROR;
116       }
117
118       shdrs[i] = shdr;
119     }
120
121
122     /* Check that Section memory ranges do not overlap.
123      * NB: Section 0 is reserved and thus ignored. */
124     for (i = 1; i < numShdr; i++) {
125       /* Section should not overlap with EHDR. */
126       if (shdrs[i].sh_offset == 0) {
127         ELFU_WARN("elfu_eCheck: Section %d overlaps with EHDR.\n", i);
128         goto ERROR;
129       }
130
131       /* Section should not overlap with PHDRs. */
132       if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]),
133                       ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) {
134         ELFU_WARN("elfu_eCheck: Section %d overlaps with PHDR.\n", i);
135         goto ERROR;
136       }
137
138       /* Section should not overlap with SHDRs. */
139       if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]),
140                       ehdr.e_shoff, numShdr * ehdr.e_shentsize)) {
141         ELFU_WARN("elfu_eCheck: Section %d overlaps with SHDRs.\n", i);
142         goto ERROR;
143       }
144
145       for (j = 1; j < numShdr; j++) {
146         if (j == i) {
147           continue;
148         }
149
150         /* Sections must not overlap in memory. */
151         if (shdrs[i].sh_addr != 0
152             && shdrs[j].sh_addr != 0
153             && OVERLAPPING(shdrs[i].sh_addr, shdrs[i].sh_size,
154                            shdrs[j].sh_addr, shdrs[j].sh_size)) {
155           ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in memory.\n", i, j);
156           goto ERROR;
157         }
158
159         /* Sections must not overlap in file. */
160         if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]),
161                         shdrs[j].sh_offset, SCNFILESIZE(&shdrs[j]))) {
162           ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in file.\n", i, j);
163           goto ERROR;
164         }
165
166         /* We may not have more than one symbol table */
167         if (shdrs[i].sh_type == SHT_SYMTAB && shdrs[j].sh_type == SHT_SYMTAB) {
168           ELFU_WARN("elfu_eCheck: Found more than one SYMTAB section.\n");
169           goto ERROR;
170         }
171
172         /* We may not have more than one dynamic symbol table */
173         if (shdrs[i].sh_type == SHT_DYNSYM && shdrs[j].sh_type == SHT_DYNSYM) {
174           ELFU_WARN("elfu_eCheck: Found more than one DYNSYM section.\n");
175           goto ERROR;
176         }
177       }
178
179       /* Section addr/offset should match parent PHDR.
180        * Find parent PHDR: */
181       for (j = 0; j < numPhdr; j++) {
182         if (PHDR_CONTAINS_SCN_IN_MEMORY(&phdrs[j], &shdrs[i])) {
183           GElf_Off shoff = phdrs[j].p_offset + (shdrs[i].sh_addr - phdrs[j].p_vaddr);
184
185           if (shdrs[i].sh_offset != shoff
186               || !PHDR_CONTAINS_SCN_IN_FILE(&phdrs[j], &shdrs[i])) {
187             ELFU_WARN("elfu_eCheck: SHDR %d and PHDR %d report conflicting file/memory regions.\n", i, j);
188             goto ERROR;
189           }
190         }
191       }
192
193       /* sh_link members should not point to sections out of range. */
194       if (shdrs[i].sh_link >= numShdr) {
195         ELFU_WARN("elfu_eCheck: Bogus sh_link in SHDR %d.\n", i);
196       }
197     }
198   }
199
200
201   DONE:
202   if (phdrs) {
203     free(phdrs);
204   }
205   if (shdrs) {
206     free(shdrs);
207   }
208   return retval;
209
210   ERROR:
211   ELFU_WARN("elfu_eCheck: Errors found.\n");
212   retval = -1;
213   goto DONE;
214 }