1 |
commit: 95e5489534ac9e9324c5096286899b688e19ae00 |
2 |
Author: Mike Frysinger <vapier <AT> gentoo <DOT> org> |
3 |
AuthorDate: Tue Jan 24 02:25:20 2017 +0000 |
4 |
Commit: Mike Frysinger <vapier <AT> gentoo <DOT> org> |
5 |
CommitDate: Tue Jan 24 02:25:20 2017 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/pax-utils.git/commit/?id=95e54895 |
7 |
|
8 |
scanelf: add helper for walking dynamic tags |
9 |
|
10 |
We have many loops that operate on dynamic tags which all crash when |
11 |
given an ELF with a corrupt dynamic table. Add a helper to walk the |
12 |
table so we can centralize bounds checking in one place. |
13 |
|
14 |
Reported-by: Agostino Sarubbo <ago <AT> gentoo.org> |
15 |
|
16 |
scanelf.c | 131 +++++++++++++++++--------------------------------------------- |
17 |
1 file changed, 35 insertions(+), 96 deletions(-) |
18 |
|
19 |
diff --git a/scanelf.c b/scanelf.c |
20 |
index 52c436a..ceec26d 100644 |
21 |
--- a/scanelf.c |
22 |
+++ b/scanelf.c |
23 |
@@ -189,6 +189,14 @@ static void *scanelf_file_get_pt_dynamic(elfobj *elf) |
24 |
return NULL; |
25 |
} |
26 |
|
27 |
+#define scanelf_dt_for_each(B, elf, dyn) \ |
28 |
+ { \ |
29 |
+ Elf##B##_Phdr *_phdr = scanelf_file_get_pt_dynamic(elf); \ |
30 |
+ dyn = (_phdr == NULL) ? elf->data_end : DYN##B(elf->vdata + EGET(_phdr->p_offset)); \ |
31 |
+ } \ |
32 |
+ --dyn; \ |
33 |
+ while ((void *)++dyn < elf->data_end - sizeof(*dyn) && EGET(dyn->d_tag) != DT_NULL) |
34 |
+ |
35 |
/* sub-funcs for scanelf_fileat() */ |
36 |
static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **str) |
37 |
{ |
38 |
@@ -258,10 +266,9 @@ static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **str) |
39 |
size_t i; \ |
40 |
static Elf ## B ## _Shdr sym_shdr, str_shdr; \ |
41 |
Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ |
42 |
- Elf ## B ## _Phdr *phdr; \ |
43 |
+ Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ |
44 |
Elf ## B ## _Addr vsym, vstr, vhash, vgnu_hash; \ |
45 |
Elf ## B ## _Dyn *dyn; \ |
46 |
- Elf ## B ## _Off doffset; \ |
47 |
\ |
48 |
/* lookup symbols used at runtime with DT_SYMTAB / DT_STRTAB */ \ |
49 |
vsym = vstr = vhash = vgnu_hash = 0; \ |
50 |
@@ -269,13 +276,7 @@ static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **str) |
51 |
memset(&str_shdr, 0, sizeof(str_shdr)); \ |
52 |
\ |
53 |
/* Find the dynamic headers */ \ |
54 |
- phdr = scanelf_file_get_pt_dynamic(elf); \ |
55 |
- if (phdr == NULL) \ |
56 |
- break; \ |
57 |
- doffset = EGET(phdr->p_offset); \ |
58 |
- \ |
59 |
- dyn = DYN ## B (elf->vdata + doffset); \ |
60 |
- while (EGET(dyn->d_tag) != DT_NULL) { \ |
61 |
+ scanelf_dt_for_each(B, elf, dyn) { \ |
62 |
switch (EGET(dyn->d_tag)) { \ |
63 |
case DT_SYMTAB: vsym = EGET(dyn->d_un.d_val); break; \ |
64 |
case DT_SYMENT: sym_shdr.sh_entsize = dyn->d_un.d_val; break; \ |
65 |
@@ -284,13 +285,11 @@ static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **str) |
66 |
case DT_HASH: vhash = EGET(dyn->d_un.d_val); break; \ |
67 |
/*case DT_GNU_HASH: vgnu_hash = EGET(dyn->d_un.d_val); break;*/ \ |
68 |
} \ |
69 |
- ++dyn; \ |
70 |
} \ |
71 |
if (!vsym || !vstr || !(vhash || vgnu_hash)) \ |
72 |
return; \ |
73 |
\ |
74 |
/* calc offset into the ELF by finding the load addr of the syms */ \ |
75 |
- phdr = PHDR ## B (elf->phdr); \ |
76 |
for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ |
77 |
Elf ## B ## _Addr vaddr = EGET(phdr[i].p_vaddr); \ |
78 |
Elf ## B ## _Addr filesz = EGET(phdr[i].p_filesz); \ |
79 |
@@ -539,23 +538,13 @@ static const char *scanelf_file_textrel(elfobj *elf, char *found_textrel) |
80 |
|
81 |
#define SHOW_TEXTREL(B) \ |
82 |
Elf ## B ## _Dyn *dyn; \ |
83 |
- Elf ## B ## _Phdr *phdr; \ |
84 |
- Elf ## B ## _Off offset; \ |
85 |
- \ |
86 |
- /* Find the dynamic headers */ \ |
87 |
- phdr = scanelf_file_get_pt_dynamic(elf); \ |
88 |
- if (phdr == NULL) \ |
89 |
- break; \ |
90 |
- offset = EGET(phdr->p_offset); \ |
91 |
\ |
92 |
- dyn = DYN ## B (elf->vdata + offset); \ |
93 |
- while (EGET(dyn->d_tag) != DT_NULL) { \ |
94 |
+ scanelf_dt_for_each(B, elf, dyn) { \ |
95 |
if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \ |
96 |
*found_textrel = 1; \ |
97 |
/*if (dyn->d_un.d_val & DF_TEXTREL)*/ \ |
98 |
return (be_wewy_wewy_quiet ? NULL : ret); \ |
99 |
} \ |
100 |
- ++dyn; \ |
101 |
} |
102 |
if (elf->phdr) |
103 |
SCANELF_ELF_SIZED(SHOW_TEXTREL); |
104 |
@@ -588,7 +577,6 @@ static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *foun |
105 |
size_t i; \ |
106 |
Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ |
107 |
Elf ## B ## _Phdr *phdr; \ |
108 |
- Elf ## B ## _Off offset; \ |
109 |
Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \ |
110 |
Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \ |
111 |
Elf ## B ## _Rel *rel; \ |
112 |
@@ -596,16 +584,9 @@ static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *foun |
113 |
Elf ## B ## _Dyn *dyn, *drel, *drelsz, *drelent, *dpltrel; \ |
114 |
uint32_t pltrel; \ |
115 |
\ |
116 |
- /* Find the dynamic headers */ \ |
117 |
- phdr = scanelf_file_get_pt_dynamic(elf); \ |
118 |
- if (phdr == NULL) \ |
119 |
- break; \ |
120 |
- offset = EGET(phdr->p_offset); \ |
121 |
- \ |
122 |
/* Walk all the dynamic tags to find relocation info */ \ |
123 |
- dyn = DYN ## B (elf->vdata + offset); \ |
124 |
drel = drelsz = drelent = dpltrel = NULL; \ |
125 |
- while (EGET(dyn->d_tag) != DT_NULL) { \ |
126 |
+ scanelf_dt_for_each(B, elf, dyn) { \ |
127 |
switch (EGET(dyn->d_tag)) { \ |
128 |
case DT_REL: \ |
129 |
case DT_RELA: \ |
130 |
@@ -623,7 +604,6 @@ static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *foun |
131 |
dpltrel = dyn; \ |
132 |
break; \ |
133 |
} \ |
134 |
- ++dyn; \ |
135 |
} \ |
136 |
if (!drel || !drelsz || !drelent || !dpltrel) { \ |
137 |
warnf("ELF is missing relocation information"); \ |
138 |
@@ -796,26 +776,18 @@ static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_ |
139 |
|
140 |
#define SHOW_RPATH(B) \ |
141 |
Elf ## B ## _Dyn *dyn; \ |
142 |
- Elf ## B ## _Phdr *phdr; \ |
143 |
Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \ |
144 |
Elf ## B ## _Off offset; \ |
145 |
Elf ## B ## _Xword word; \ |
146 |
\ |
147 |
- /* Find the dynamic headers */ \ |
148 |
- phdr = scanelf_file_get_pt_dynamic(elf); \ |
149 |
- if (phdr == NULL) \ |
150 |
- break; \ |
151 |
- offset = EGET(phdr->p_offset); \ |
152 |
- \ |
153 |
/* Just scan dynamic RPATH/RUNPATH headers */ \ |
154 |
- dyn = DYN ## B (elf->vdata + offset); \ |
155 |
- while ((word=EGET(dyn->d_tag)) != DT_NULL) { \ |
156 |
+ scanelf_dt_for_each(B, elf, dyn) { \ |
157 |
+ word = EGET(dyn->d_tag); \ |
158 |
if (word == DT_RPATH) { \ |
159 |
r = &rpath; \ |
160 |
} else if (word == DT_RUNPATH) { \ |
161 |
r = &runpath; \ |
162 |
} else { \ |
163 |
- ++dyn; \ |
164 |
continue; \ |
165 |
} \ |
166 |
/* Verify the memory is somewhat sane */ \ |
167 |
@@ -900,7 +872,6 @@ static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_ |
168 |
*found_rpath = 1; \ |
169 |
} \ |
170 |
} \ |
171 |
- ++dyn; \ |
172 |
} |
173 |
if (elf->phdr && strtbl_void) |
174 |
SCANELF_ELF_SIZED(SHOW_RPATH); |
175 |
@@ -956,26 +927,15 @@ static const char *scanelf_file_needed_lib(elfobj *elf, char *found_needed, char |
176 |
|
177 |
#define SHOW_NEEDED(B) \ |
178 |
Elf ## B ## _Dyn *dyn; \ |
179 |
- Elf ## B ## _Phdr *phdr; \ |
180 |
Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \ |
181 |
- Elf ## B ## _Off offset; \ |
182 |
size_t matched = 0; \ |
183 |
\ |
184 |
- /* Find the dynamic headers */ \ |
185 |
- phdr = scanelf_file_get_pt_dynamic(elf); \ |
186 |
- if (phdr == NULL) \ |
187 |
- break; \ |
188 |
- offset = EGET(phdr->p_offset); \ |
189 |
- \ |
190 |
/* Walk all the dynamic tags to find NEEDED entries */ \ |
191 |
- dyn = DYN ## B (elf->vdata + offset); \ |
192 |
- while (EGET(dyn->d_tag) != DT_NULL) { \ |
193 |
+ scanelf_dt_for_each(B, elf, dyn) { \ |
194 |
if (EGET(dyn->d_tag) == DT_NEEDED) { \ |
195 |
- offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ |
196 |
- if (offset >= (Elf ## B ## _Off)elf->len) { \ |
197 |
- ++dyn; \ |
198 |
+ Elf ## B ## _Off offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ |
199 |
+ if (offset >= (Elf ## B ## _Off)elf->len) \ |
200 |
continue; \ |
201 |
- } \ |
202 |
needed = elf->data + offset; \ |
203 |
if (op == 0) { \ |
204 |
/* -n -> print all entries */ \ |
205 |
@@ -1010,7 +970,6 @@ static const char *scanelf_file_needed_lib(elfobj *elf, char *found_needed, char |
206 |
} \ |
207 |
} \ |
208 |
} \ |
209 |
- ++dyn; \ |
210 |
} |
211 |
if (elf->phdr && strtbl_void) { |
212 |
SCANELF_ELF_SIZED(SHOW_NEEDED); |
213 |
@@ -1066,7 +1025,6 @@ static char *scanelf_file_interp(elfobj *elf, char *found_interp) |
214 |
} |
215 |
static const char *scanelf_file_bind(elfobj *elf, char *found_bind) |
216 |
{ |
217 |
- unsigned long i; |
218 |
struct stat s; |
219 |
bool dynamic = false; |
220 |
|
221 |
@@ -1075,24 +1033,15 @@ static const char *scanelf_file_bind(elfobj *elf, char *found_bind) |
222 |
|
223 |
#define SHOW_BIND(B) \ |
224 |
Elf ## B ## _Dyn *dyn; \ |
225 |
- Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ |
226 |
- Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ |
227 |
- Elf ## B ## _Off offset; \ |
228 |
- for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ |
229 |
- if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \ |
230 |
+ \ |
231 |
+ scanelf_dt_for_each(B, elf, dyn) { \ |
232 |
dynamic = true; \ |
233 |
- offset = EGET(phdr[i].p_offset); \ |
234 |
- if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \ |
235 |
- dyn = DYN ## B (elf->vdata + offset); \ |
236 |
- while (EGET(dyn->d_tag) != DT_NULL) { \ |
237 |
- if (EGET(dyn->d_tag) == DT_BIND_NOW || \ |
238 |
- (EGET(dyn->d_tag) == DT_FLAGS && EGET(dyn->d_un.d_val) & DF_BIND_NOW)) \ |
239 |
- { \ |
240 |
- if (be_quiet) return NULL; \ |
241 |
- *found_bind = 1; \ |
242 |
- return (char *)(be_wewy_wewy_quiet ? NULL : "NOW"); \ |
243 |
- } \ |
244 |
- ++dyn; \ |
245 |
+ if (EGET(dyn->d_tag) == DT_BIND_NOW || \ |
246 |
+ (EGET(dyn->d_tag) == DT_FLAGS && EGET(dyn->d_un.d_val) & DF_BIND_NOW)) { \ |
247 |
+ if (be_quiet) \ |
248 |
+ return NULL; \ |
249 |
+ *found_bind = 1; \ |
250 |
+ return (char *)(be_wewy_wewy_quiet ? NULL : "NOW"); \ |
251 |
} \ |
252 |
} |
253 |
SCANELF_ELF_SIZED(SHOW_BIND); |
254 |
@@ -1109,7 +1058,6 @@ static const char *scanelf_file_bind(elfobj *elf, char *found_bind) |
255 |
} |
256 |
static char *scanelf_file_soname(elfobj *elf, char *found_soname) |
257 |
{ |
258 |
- unsigned long i; |
259 |
char *soname; |
260 |
void *strtbl_void; |
261 |
|
262 |
@@ -1120,29 +1068,20 @@ static char *scanelf_file_soname(elfobj *elf, char *found_soname) |
263 |
#define SHOW_SONAME(B) \ |
264 |
Elf ## B ## _Dyn *dyn; \ |
265 |
Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ |
266 |
- Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ |
267 |
Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \ |
268 |
- Elf ## B ## _Off offset; \ |
269 |
+ \ |
270 |
/* only look for soname in shared objects */ \ |
271 |
if (EGET(ehdr->e_type) != ET_DYN) \ |
272 |
return NULL; \ |
273 |
- for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ |
274 |
- if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \ |
275 |
- offset = EGET(phdr[i].p_offset); \ |
276 |
- if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \ |
277 |
- dyn = DYN ## B (elf->vdata + offset); \ |
278 |
- while (EGET(dyn->d_tag) != DT_NULL) { \ |
279 |
- if (EGET(dyn->d_tag) == DT_SONAME) { \ |
280 |
- offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ |
281 |
- if (offset >= (Elf ## B ## _Off)elf->len) { \ |
282 |
- ++dyn; \ |
283 |
- continue; \ |
284 |
- } \ |
285 |
- soname = elf->data + offset; \ |
286 |
- *found_soname = 1; \ |
287 |
- return (be_wewy_wewy_quiet ? NULL : soname); \ |
288 |
- } \ |
289 |
- ++dyn; \ |
290 |
+ \ |
291 |
+ scanelf_dt_for_each(B, elf, dyn) { \ |
292 |
+ if (EGET(dyn->d_tag) == DT_SONAME) { \ |
293 |
+ Elf ## B ## _Off offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ |
294 |
+ if (offset >= (Elf ## B ## _Off)elf->len) \ |
295 |
+ continue; \ |
296 |
+ soname = elf->data + offset; \ |
297 |
+ *found_soname = 1; \ |
298 |
+ return (be_wewy_wewy_quiet ? NULL : soname); \ |
299 |
} \ |
300 |
} |
301 |
if (elf->phdr && strtbl_void) |