/root/src/xen/xen/common/libelf/libelf-tools.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * various helper functions to access elf structures |
3 | | * |
4 | | * This library is free software; you can redistribute it and/or |
5 | | * modify it under the terms of the GNU Lesser General Public |
6 | | * License as published by the Free Software Foundation; |
7 | | * version 2.1 of the License. |
8 | | * |
9 | | * This library is distributed in the hope that it will be useful, |
10 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | | * Lesser General Public License for more details. |
13 | | * |
14 | | * You should have received a copy of the GNU Lesser General Public |
15 | | * License along with this library; If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | |
18 | | #include "libelf-private.h" |
19 | | |
20 | | /* ------------------------------------------------------------------------ */ |
21 | | |
22 | | void elf_mark_broken(struct elf_binary *elf, const char *msg) |
23 | 0 | { |
24 | 0 | if ( elf->broken == NULL ) |
25 | 0 | elf->broken = msg; |
26 | 0 | } |
27 | | |
28 | | const char *elf_check_broken(const struct elf_binary *elf) |
29 | 0 | { |
30 | 0 | return elf->broken; |
31 | 0 | } |
32 | | |
33 | | static bool elf_ptrval_in_range(elf_ptrval ptrval, uint64_t size, |
34 | | const void *region, uint64_t regionsize) |
35 | | /* |
36 | | * Returns true if the putative memory area [ptrval,ptrval+size> |
37 | | * is completely inside the region [region,region+regionsize>. |
38 | | * |
39 | | * ptrval and size are the untrusted inputs to be checked. |
40 | | * region and regionsize are trusted and must be correct and valid, |
41 | | * although it is OK for region to perhaps be maliciously NULL |
42 | | * (but not some other malicious value). |
43 | | */ |
44 | 1.19k | { |
45 | 1.19k | elf_ptrval regionp = (elf_ptrval)region; |
46 | 1.19k | |
47 | 1.19k | if ( (region == NULL) || |
48 | 1.19k | (ptrval < regionp) || /* start is before region */ |
49 | 1.17k | (ptrval > regionp + regionsize) || /* start is after region */ |
50 | 1.14k | (size > regionsize - (ptrval - regionp)) ) /* too big */ |
51 | 52 | return 0; |
52 | 1.14k | return 1; |
53 | 1.19k | } |
54 | | |
55 | | bool elf_access_ok(struct elf_binary * elf, |
56 | | uint64_t ptrval, size_t size) |
57 | 1.14k | { |
58 | 1.14k | if ( elf_ptrval_in_range(ptrval, size, elf->image_base, elf->size) ) |
59 | 1.11k | return 1; |
60 | 26 | if ( elf_ptrval_in_range(ptrval, size, elf->dest_base, elf->dest_size) ) |
61 | 0 | return 1; |
62 | 26 | if ( elf_ptrval_in_range(ptrval, size, elf->xdest_base, elf->xdest_size) ) |
63 | 26 | return 1; |
64 | 0 | elf_mark_broken(elf, "out of range access"); |
65 | 0 | return 0; |
66 | 26 | } |
67 | | |
68 | | void elf_memcpy_safe(struct elf_binary *elf, elf_ptrval dst, |
69 | | elf_ptrval src, size_t size) |
70 | 3 | { |
71 | 3 | if ( elf_access_ok(elf, dst, size) && |
72 | 3 | elf_access_ok(elf, src, size) ) |
73 | 3 | { |
74 | 3 | /* use memmove because these checks do not prove that the |
75 | 3 | * regions don't overlap and overlapping regions grant |
76 | 3 | * permission for compiler malice */ |
77 | 3 | elf_memmove_unchecked(ELF_UNSAFE_PTR(dst), ELF_UNSAFE_PTR(src), size); |
78 | 3 | } |
79 | 3 | } |
80 | | |
81 | | void elf_memset_safe(struct elf_binary *elf, elf_ptrval dst, int c, size_t size) |
82 | 1 | { |
83 | 1 | if ( elf_access_ok(elf, dst, size) ) |
84 | 1 | { |
85 | 1 | elf_memset_unchecked(ELF_UNSAFE_PTR(dst), c, size); |
86 | 1 | } |
87 | 1 | } |
88 | | |
89 | | uint64_t elf_access_unsigned(struct elf_binary * elf, elf_ptrval base, |
90 | | uint64_t moreoffset, size_t size) |
91 | 799 | { |
92 | 799 | elf_ptrval ptrval = base + moreoffset; |
93 | 799 | bool need_swap = elf_swap(elf); |
94 | 799 | const uint8_t *u8; |
95 | 799 | const uint16_t *u16; |
96 | 799 | const uint32_t *u32; |
97 | 799 | const uint64_t *u64; |
98 | 799 | |
99 | 799 | if ( !elf_access_ok(elf, ptrval, size) ) |
100 | 0 | return 0; |
101 | 799 | |
102 | 799 | switch ( size ) |
103 | 799 | { |
104 | 197 | case 1: |
105 | 197 | u8 = (const void*)ptrval; |
106 | 197 | return *u8; |
107 | 249 | case 2: |
108 | 249 | u16 = (const void*)ptrval; |
109 | 249 | return need_swap ? bswap_16(*u16) : *u16; |
110 | 200 | case 4: |
111 | 200 | u32 = (const void*)ptrval; |
112 | 200 | return need_swap ? bswap_32(*u32) : *u32; |
113 | 153 | case 8: |
114 | 153 | u64 = (const void*)ptrval; |
115 | 153 | return need_swap ? bswap_64(*u64) : *u64; |
116 | 0 | default: |
117 | 0 | return 0; |
118 | 799 | } |
119 | 799 | } |
120 | | |
121 | | uint64_t elf_round_up(struct elf_binary *elf, uint64_t addr) |
122 | 6 | { |
123 | 6 | uint64_t elf_round = (elf_64bit(elf) ? 8 : 4) - 1; |
124 | 6 | |
125 | 6 | return (addr + elf_round) & ~elf_round; |
126 | 6 | } |
127 | | |
128 | | /* ------------------------------------------------------------------------ */ |
129 | | |
130 | | unsigned elf_shdr_count(struct elf_binary *elf) |
131 | 104 | { |
132 | 104 | unsigned count = elf_uval(elf, elf->ehdr, e_shnum); |
133 | 104 | uint64_t max = elf->size / sizeof(Elf32_Shdr); |
134 | 104 | |
135 | 104 | if ( max > UINT_MAX ) |
136 | 0 | max = UINT_MAX; |
137 | 104 | if ( count > max ) |
138 | 0 | { |
139 | 0 | elf_mark_broken(elf, "far too many section headers"); |
140 | 0 | count = max; |
141 | 0 | } |
142 | 104 | return count; |
143 | 104 | } |
144 | | |
145 | | unsigned elf_phdr_count(struct elf_binary *elf) |
146 | 22 | { |
147 | 22 | return elf_uval(elf, elf->ehdr, e_phnum); |
148 | 22 | } |
149 | | |
150 | | ELF_HANDLE_DECL(elf_shdr) elf_shdr_by_name(struct elf_binary *elf, const char *name) |
151 | 0 | { |
152 | 0 | unsigned i, count = elf_shdr_count(elf); |
153 | 0 | ELF_HANDLE_DECL(elf_shdr) shdr; |
154 | 0 | const char *sname; |
155 | 0 |
|
156 | 0 | for ( i = 1; i < count; i++ ) |
157 | 0 | { |
158 | 0 | shdr = elf_shdr_by_index(elf, i); |
159 | 0 | if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(shdr), 1) ) |
160 | 0 | /* input has an insane section header count field */ |
161 | 0 | break; |
162 | 0 | sname = elf_section_name(elf, shdr); |
163 | 0 | if ( sname && !strcmp(sname, name) ) |
164 | 0 | return shdr; |
165 | 0 | } |
166 | 0 | return ELF_INVALID_HANDLE(elf_shdr); |
167 | 0 | } |
168 | | |
169 | | ELF_HANDLE_DECL(elf_shdr) elf_shdr_by_index(struct elf_binary *elf, unsigned index) |
170 | 101 | { |
171 | 101 | unsigned count = elf_shdr_count(elf); |
172 | 101 | elf_ptrval ptr; |
173 | 101 | |
174 | 101 | if ( index >= count ) |
175 | 0 | return ELF_INVALID_HANDLE(elf_shdr); |
176 | 101 | |
177 | 101 | ptr = (ELF_IMAGE_BASE(elf) |
178 | 101 | + elf_uval(elf, elf->ehdr, e_shoff) |
179 | 101 | + elf_uval(elf, elf->ehdr, e_shentsize) * index); |
180 | 101 | return ELF_MAKE_HANDLE(elf_shdr, ptr); |
181 | 101 | } |
182 | | |
183 | | ELF_HANDLE_DECL(elf_phdr) elf_phdr_by_index(struct elf_binary *elf, unsigned index) |
184 | 18 | { |
185 | 18 | unsigned count = elf_phdr_count(elf); |
186 | 18 | elf_ptrval ptr; |
187 | 18 | |
188 | 18 | if ( index >= count ) |
189 | 0 | return ELF_INVALID_HANDLE(elf_phdr); |
190 | 18 | |
191 | 18 | ptr = (ELF_IMAGE_BASE(elf) |
192 | 18 | + elf_uval(elf, elf->ehdr, e_phoff) |
193 | 18 | + elf_uval(elf, elf->ehdr, e_phentsize) * index); |
194 | 18 | return ELF_MAKE_HANDLE(elf_phdr, ptr); |
195 | 18 | } |
196 | | |
197 | | |
198 | | const char *elf_section_name(struct elf_binary *elf, |
199 | | ELF_HANDLE_DECL(elf_shdr) shdr) |
200 | 0 | { |
201 | 0 | if ( ELF_PTRVAL_INVALID(elf->sec_strtab) ) |
202 | 0 | return "unknown"; |
203 | 0 |
|
204 | 0 | return elf_strval(elf, elf->sec_strtab + elf_uval(elf, shdr, sh_name)); |
205 | 0 | } |
206 | | |
207 | | const char *elf_strval(struct elf_binary *elf, elf_ptrval start) |
208 | 22 | { |
209 | 22 | uint64_t length; |
210 | 22 | |
211 | 195 | for ( length = 0; ; length++ ) { |
212 | 195 | if ( !elf_access_ok(elf, start + length, 1) ) |
213 | 0 | return NULL; |
214 | 195 | if ( !elf_access_unsigned(elf, start, length, 1) ) |
215 | 195 | /* ok */ |
216 | 22 | return ELF_UNSAFE_PTR(start); |
217 | 173 | if ( length >= ELF_MAX_STRING_LENGTH ) |
218 | 0 | { |
219 | 0 | elf_mark_broken(elf, "excessively long string"); |
220 | 0 | return NULL; |
221 | 0 | } |
222 | 173 | } |
223 | 22 | } |
224 | | |
225 | | const char *elf_strfmt(struct elf_binary *elf, elf_ptrval start) |
226 | 0 | { |
227 | 0 | const char *str = elf_strval(elf, start); |
228 | 0 |
|
229 | 0 | if ( str == NULL ) |
230 | 0 | return "(invalid)"; |
231 | 0 | return str; |
232 | 0 | } |
233 | | |
234 | | elf_ptrval elf_section_start(struct elf_binary *elf, ELF_HANDLE_DECL(elf_shdr) shdr) |
235 | 5 | { |
236 | 5 | return ELF_IMAGE_BASE(elf) + elf_uval(elf, shdr, sh_offset); |
237 | 5 | } |
238 | | |
239 | | elf_ptrval elf_section_end(struct elf_binary *elf, ELF_HANDLE_DECL(elf_shdr) shdr) |
240 | 1 | { |
241 | 1 | return ELF_IMAGE_BASE(elf) |
242 | 1 | + elf_uval(elf, shdr, sh_offset) + elf_uval(elf, shdr, sh_size); |
243 | 1 | } |
244 | | |
245 | | elf_ptrval elf_segment_start(struct elf_binary *elf, ELF_HANDLE_DECL(elf_phdr) phdr) |
246 | 0 | { |
247 | 0 | return ELF_IMAGE_BASE(elf) |
248 | 0 | + elf_uval(elf, phdr, p_offset); |
249 | 0 | } |
250 | | |
251 | | elf_ptrval elf_segment_end(struct elf_binary *elf, ELF_HANDLE_DECL(elf_phdr) phdr) |
252 | 0 | { |
253 | 0 | return ELF_IMAGE_BASE(elf) |
254 | 0 | + elf_uval(elf, phdr, p_offset) + elf_uval(elf, phdr, p_filesz); |
255 | 0 | } |
256 | | |
257 | | ELF_HANDLE_DECL(elf_sym) elf_sym_by_name(struct elf_binary *elf, const char *symbol) |
258 | 0 | { |
259 | 0 | elf_ptrval ptr = elf_section_start(elf, elf->sym_tab); |
260 | 0 | elf_ptrval end = elf_section_end(elf, elf->sym_tab); |
261 | 0 | ELF_HANDLE_DECL(elf_sym) sym; |
262 | 0 | uint64_t info, name; |
263 | 0 | const char *sym_name; |
264 | 0 |
|
265 | 0 | for ( ; ptr < end; ptr += elf_size(elf, sym) ) |
266 | 0 | { |
267 | 0 | sym = ELF_MAKE_HANDLE(elf_sym, ptr); |
268 | 0 | info = elf_uval(elf, sym, st_info); |
269 | 0 | name = elf_uval(elf, sym, st_name); |
270 | 0 | if ( ELF32_ST_BIND(info) != STB_GLOBAL ) |
271 | 0 | continue; |
272 | 0 | sym_name = elf_strval(elf, elf->sym_strtab + name); |
273 | 0 | if ( sym_name == NULL ) /* out of range, oops */ |
274 | 0 | return ELF_INVALID_HANDLE(elf_sym); |
275 | 0 | if ( strcmp(sym_name, symbol) ) |
276 | 0 | continue; |
277 | 0 | return sym; |
278 | 0 | } |
279 | 0 | return ELF_INVALID_HANDLE(elf_sym); |
280 | 0 | } |
281 | | |
282 | | ELF_HANDLE_DECL(elf_sym) elf_sym_by_index(struct elf_binary *elf, unsigned index) |
283 | 0 | { |
284 | 0 | elf_ptrval ptr = elf_section_start(elf, elf->sym_tab); |
285 | 0 | ELF_HANDLE_DECL(elf_sym) sym; |
286 | 0 |
|
287 | 0 | sym = ELF_MAKE_HANDLE(elf_sym, ptr + index * elf_size(elf, sym)); |
288 | 0 | return sym; |
289 | 0 | } |
290 | | |
291 | | const char *elf_note_name(struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) note) |
292 | 15 | { |
293 | 15 | return elf_strval(elf, ELF_HANDLE_PTRVAL(note) + elf_size(elf, note)); |
294 | 15 | } |
295 | | |
296 | | elf_ptrval elf_note_desc(struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) note) |
297 | 14 | { |
298 | 14 | unsigned namesz = (elf_uval(elf, note, namesz) + 3) & ~3; |
299 | 14 | |
300 | 14 | return ELF_HANDLE_PTRVAL(note) + elf_size(elf, note) + namesz; |
301 | 14 | } |
302 | | |
303 | | uint64_t elf_note_numeric(struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) note) |
304 | 7 | { |
305 | 7 | elf_ptrval desc = elf_note_desc(elf, note); |
306 | 7 | unsigned descsz = elf_uval(elf, note, descsz); |
307 | 7 | |
308 | 7 | switch (descsz) |
309 | 7 | { |
310 | 7 | case 1: |
311 | 7 | case 2: |
312 | 7 | case 4: |
313 | 7 | case 8: |
314 | 7 | return elf_access_unsigned(elf, desc, 0, descsz); |
315 | 0 | default: |
316 | 0 | return 0; |
317 | 7 | } |
318 | 7 | } |
319 | | |
320 | | uint64_t elf_note_numeric_array(struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) note, |
321 | | unsigned int unitsz, unsigned int idx) |
322 | 0 | { |
323 | 0 | elf_ptrval desc = elf_note_desc(elf, note); |
324 | 0 | unsigned descsz = elf_uval(elf, note, descsz); |
325 | 0 |
|
326 | 0 | if ( descsz % unitsz || idx >= descsz / unitsz ) |
327 | 0 | return 0; |
328 | 0 | switch (unitsz) |
329 | 0 | { |
330 | 0 | case 1: |
331 | 0 | case 2: |
332 | 0 | case 4: |
333 | 0 | case 8: |
334 | 0 | return elf_access_unsigned(elf, desc, idx * unitsz, unitsz); |
335 | 0 | default: |
336 | 0 | return 0; |
337 | 0 | } |
338 | 0 | } |
339 | | |
340 | | ELF_HANDLE_DECL(elf_note) elf_note_next(struct elf_binary *elf, ELF_HANDLE_DECL(elf_note) note) |
341 | 15 | { |
342 | 15 | unsigned namesz = (elf_uval(elf, note, namesz) + 3) & ~3; |
343 | 15 | unsigned descsz = (elf_uval(elf, note, descsz) + 3) & ~3; |
344 | 15 | |
345 | 15 | elf_ptrval ptrval = ELF_HANDLE_PTRVAL(note) |
346 | 15 | + elf_size(elf, note) + namesz + descsz; |
347 | 15 | |
348 | 15 | if ( ( ptrval <= ELF_HANDLE_PTRVAL(note) || /* wrapped or stuck */ |
349 | 15 | !elf_access_ok(elf, ELF_HANDLE_PTRVAL(note), 1) ) ) |
350 | 0 | ptrval = ELF_MAX_PTRVAL; /* terminate caller's loop */ |
351 | 15 | |
352 | 15 | return ELF_MAKE_HANDLE(elf_note, ptrval); |
353 | 15 | } |
354 | | |
355 | | /* ------------------------------------------------------------------------ */ |
356 | | |
357 | | bool elf_is_elfbinary(const void *image_start, size_t image_size) |
358 | 3 | { |
359 | 3 | const Elf32_Ehdr *ehdr = image_start; |
360 | 3 | |
361 | 3 | if ( image_size < sizeof(*ehdr) ) |
362 | 0 | return 0; |
363 | 3 | |
364 | 3 | return IS_ELF(*ehdr); |
365 | 3 | } |
366 | | |
367 | | bool elf_phdr_is_loadable(struct elf_binary *elf, ELF_HANDLE_DECL(elf_phdr) phdr) |
368 | 12 | { |
369 | 12 | uint64_t p_type = elf_uval(elf, phdr, p_type); |
370 | 12 | uint64_t p_flags = elf_uval(elf, phdr, p_flags); |
371 | 12 | |
372 | 12 | return ((p_type == PT_LOAD) && (p_flags & (PF_R | PF_W | PF_X)) != 0); |
373 | 12 | } |
374 | | |
375 | | void elf_set_xdest(struct elf_binary *elf, void *addr, uint64_t size) |
376 | 2 | { |
377 | 2 | elf->xdest_base = addr; |
378 | 2 | elf->xdest_size = size; |
379 | 2 | if ( addr != NULL ) |
380 | 1 | elf_memset_safe(elf, ELF_REALPTR2PTRVAL(addr), 0, size); |
381 | 2 | } |
382 | | |
383 | | /* |
384 | | * Local variables: |
385 | | * mode: C |
386 | | * c-file-style: "BSD" |
387 | | * c-basic-offset: 4 |
388 | | * tab-width: 4 |
389 | | * indent-tabs-mode: nil |
390 | | * End: |
391 | | */ |