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