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