2c7f040910dee5e28421e0a389867687ed867a86
[centaur.git] / src / 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 (elf_getphdrnum(e, &numPhdr)) {
28     ELFU_WARNELF("elf_getphdrnum");
29     goto ERROR;
30   }
31
32   if (elf_getshdrnum(e, &numShdr)) {
33     ELFU_WARNELF("elf_getshdrnum");
34     goto ERROR;
35   }
36
37
38   if (numPhdr > 0) {
39     phdrs = malloc(numPhdr * sizeof(GElf_Phdr));
40     if (!phdrs) {
41       ELFU_WARN("elfu_eCheck: malloc() failed for phdrs.\n");
42       goto ERROR;
43     }
44
45     /* Attempt to load all PHDRs at once to catch any errors early */
46     for (i = 0; i < numPhdr; i++) {
47       GElf_Phdr phdr;
48       if (gelf_getphdr(e, i, &phdr) != &phdr) {
49         ELFU_WARN("gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1));
50         goto ERROR;
51       }
52
53       phdrs[i] = phdr;
54     }
55
56     /* Check that LOAD PHDR memory ranges do not overlap, and that others
57      * are either fully contained in a LOAD range, or not at all. */
58     for (i = 0; i < numPhdr; i++) {
59       if (phdrs[i].p_type != PT_LOAD) {
60         continue;
61       }
62
63       for (j = 0; j < numPhdr; j++) {
64         if (j == i || phdrs[j].p_type != PT_LOAD) {
65           continue;
66         }
67
68         if (OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz,
69                         phdrs[j].p_vaddr, phdrs[j].p_memsz)) {
70           if (phdrs[j].p_type == PT_LOAD) {
71             ELFU_WARN("elfu_eCheck: Found LOAD PHDRs that overlap in memory.\n");
72             goto ERROR;
73           } else if (!FULLY_OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz,
74                                         phdrs[j].p_vaddr, phdrs[j].p_memsz)) {
75             ELFU_WARN("elfu_eCheck: PHDRs %d and %d partially overlap in memory.\n", i, j);
76             goto ERROR;
77           }
78         }
79       }
80     }
81   }
82
83
84   if (numShdr > 1) {
85     /* SHDRs should not overlap with PHDRs. */
86     if (OVERLAPPING(ehdr.e_shoff, numShdr * ehdr.e_shentsize,
87                     ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) {
88       ELFU_WARN("elfu_eCheck: SHDRs overlap with PHDRs.\n");
89       goto ERROR;
90     }
91
92     shdrs = malloc(numShdr * sizeof(GElf_Shdr));
93     if (!shdrs) {
94       ELFU_WARN("elfu_eCheck: malloc() failed for shdrs.\n");
95       goto ERROR;
96     }
97
98     /* Attempt to load all SHDRs at once to catch any errors early */
99     for (i = 1; i < numShdr; i++) {
100       Elf_Scn *scn;
101       GElf_Shdr shdr;
102
103       scn = elf_getscn(e, i);
104       if (!scn) {
105         ELFU_WARN("elf_getscn() failed for #%d: %s\n", i, elf_errmsg(-1));
106       }
107
108       if (gelf_getshdr(scn, &shdr) != &shdr) {
109         ELFU_WARNELF("gelf_getshdr");
110         goto ERROR;
111       }
112
113       shdrs[i] = shdr;
114     }
115
116
117     /* Check that Section memory ranges do not overlap.
118      * NB: Section 0 is reserved and thus ignored. */
119     for (i = 1; i < numShdr; i++) {
120       /* Section should not overlap with EHDR. */
121       if (shdrs[i].sh_offset == 0) {
122         ELFU_WARN("elfu_eCheck: Section %d overlaps with EHDR.\n", i);
123         goto ERROR;
124       }
125
126       /* Section should not overlap with PHDRs. */
127       if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]),
128                       ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) {
129         ELFU_WARN("elfu_eCheck: Section %d overlaps with PHDR.\n", i);
130         goto ERROR;
131       }
132
133       /* Section should not overlap with SHDRs. */
134       if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]),
135                       ehdr.e_shoff, numShdr * ehdr.e_shentsize)) {
136         ELFU_WARN("elfu_eCheck: Section %d overlaps with SHDRs.\n", i);
137         goto ERROR;
138       }
139
140       for (j = 1; j < numShdr; j++) {
141         if (j == i) {
142           continue;
143         }
144
145         /* Sections must not overlap in memory. */
146         if (shdrs[i].sh_addr != 0
147             && shdrs[j].sh_addr != 0
148             && OVERLAPPING(shdrs[i].sh_addr, shdrs[i].sh_size,
149                            shdrs[j].sh_addr, shdrs[j].sh_size)) {
150           ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in memory.\n", i, j);
151           goto ERROR;
152         }
153
154         /* Sections must not overlap in file. */
155         if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]),
156                         shdrs[j].sh_offset, SCNFILESIZE(&shdrs[j]))) {
157           ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in file.\n", i, j);
158           goto ERROR;
159         }
160       }
161
162       /* Section addr/offset should match parent PHDR.
163        * Find parent PHDR: */
164       for (j = 0; j < numPhdr; j++) {
165         if (PHDR_CONTAINS_SCN_IN_MEMORY(&phdrs[j], &shdrs[i])) {
166           GElf_Off shoff = phdrs[j].p_offset + (shdrs[i].sh_addr - phdrs[j].p_vaddr);
167
168           if (shdrs[i].sh_offset != shoff
169               || !PHDR_CONTAINS_SCN_IN_FILE(&phdrs[j], &shdrs[i])) {
170             ELFU_WARN("elfu_eCheck: Memory/file offsets/sizes are not congruent for SHDR %d, PHDR %d.\n", i, j);
171             goto ERROR;
172           }
173         }
174       }
175
176       /* sh_link members should not point to sections out of range. */
177       if (shdrs[i].sh_link >= numShdr) {
178         ELFU_WARN("elfu_eCheck: Bogus sh_link in SHDR %d.\n", i);
179       }
180     }
181   }
182
183
184   DONE:
185   if (phdrs) {
186     free(phdrs);
187   }
188   if (shdrs) {
189     free(shdrs);
190   }
191   return retval;
192
193   ERROR:
194   ELFU_WARN("elfu_eCheck: Errors found.\n");
195   retval = -1;
196   goto DONE;
197 }