libdwfl: Sanity check partial core file dyn data read.
[platform/upstream/elfutils.git] / libdw / dwarf_getcfi_elf.c
1 /* Get CFI from ELF file's exception-handling info.
2    Copyright (C) 2009-2010, 2014, 2015 Red Hat, Inc.
3    This file is part of elfutils.
4
5    This file is free software; you can redistribute it and/or modify
6    it under the terms of either
7
8      * the GNU Lesser General Public License as published by the Free
9        Software Foundation; either version 3 of the License, or (at
10        your option) any later version
11
12    or
13
14      * the GNU General Public License as published by the Free
15        Software Foundation; either version 2 of the License, or (at
16        your option) any later version
17
18    or both in parallel, as here.
19
20    elfutils is distributed in the hope that it will be useful, but
21    WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23    General Public License for more details.
24
25    You should have received copies of the GNU General Public License and
26    the GNU Lesser General Public License along with this program.  If
27    not, see <http://www.gnu.org/licenses/>.  */
28
29 #ifdef HAVE_CONFIG_H
30 # include <config.h>
31 #endif
32
33 #include <stdlib.h>
34 #include <string.h>
35 #include <assert.h>
36
37 #include "libdwP.h"
38 #include "cfi.h"
39 #include "encoded-value.h"
40 #include <dwarf.h>
41
42
43 static Dwarf_CFI *
44 allocate_cfi (Elf *elf, GElf_Addr vaddr)
45 {
46   Dwarf_CFI *cfi = calloc (1, sizeof *cfi);
47   if (cfi == NULL)
48     {
49       __libdw_seterrno (DWARF_E_NOMEM);
50       return NULL;
51     }
52
53   cfi->e_ident = (unsigned char *) elf_getident (elf, NULL);
54   if (cfi->e_ident == NULL)
55     {
56       free (cfi);
57       __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
58       return NULL;
59     }
60
61   if ((BYTE_ORDER == LITTLE_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2MSB)
62       || (BYTE_ORDER == BIG_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2LSB))
63     cfi->other_byte_order = true;
64
65   cfi->frame_vaddr = vaddr;
66   cfi->textrel = 0;             /* XXX ? */
67   cfi->datarel = 0;             /* XXX ? */
68
69   return cfi;
70 }
71
72 static const uint8_t *
73 parse_eh_frame_hdr (const uint8_t *hdr, size_t hdr_size, GElf_Addr hdr_vaddr,
74                     const GElf_Ehdr *ehdr, GElf_Addr *eh_frame_vaddr,
75                     size_t *table_entries, uint8_t *table_encoding)
76 {
77   const uint8_t *h = hdr;
78
79   if (hdr_size < 4 || *h++ != 1)                /* version */
80     return (void *) -1l;
81
82   uint8_t eh_frame_ptr_encoding = *h++;
83   uint8_t fde_count_encoding = *h++;
84   uint8_t fde_table_encoding = *h++;
85
86   if (eh_frame_ptr_encoding == DW_EH_PE_omit)
87     return (void *) -1l;
88
89   /* Dummy used by read_encoded_value.  */
90   Elf_Data_Scn dummy_cfi_hdr_data =
91     {
92       .d = { .d_buf = (void *) hdr, .d_size = hdr_size }
93     };
94   Dwarf_CFI dummy_cfi =
95     {
96       .e_ident = ehdr->e_ident,
97       .datarel = hdr_vaddr,
98       .frame_vaddr = hdr_vaddr,
99       .data = &dummy_cfi_hdr_data,
100     };
101
102   if (unlikely (read_encoded_value (&dummy_cfi, eh_frame_ptr_encoding, &h,
103                                     eh_frame_vaddr)))
104     return (void *) -1l;
105
106   if (fde_count_encoding != DW_EH_PE_omit)
107     {
108       Dwarf_Word fde_count;
109       if (unlikely (read_encoded_value (&dummy_cfi, fde_count_encoding, &h,
110                                         &fde_count)))
111         return (void *) -1l;
112       if (fde_count != 0 && (size_t) fde_count == fde_count
113           && fde_table_encoding != DW_EH_PE_omit
114           && (fde_table_encoding &~ DW_EH_PE_signed) != DW_EH_PE_uleb128)
115         {
116           *table_entries = fde_count;
117           *table_encoding = fde_table_encoding;
118           return h;
119         }
120     }
121
122   return NULL;
123 }
124
125 static Dwarf_CFI *
126 getcfi_gnu_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, const GElf_Phdr *phdr)
127 {
128   Elf_Data *data = elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz,
129                                          ELF_T_BYTE);
130   if (data == NULL || data->d_buf == NULL)
131     {
132     invalid_hdr:
133       /* XXX might be read error or corrupt phdr */
134       __libdw_seterrno (DWARF_E_INVALID_CFI);
135       return NULL;
136     }
137
138   size_t vsize, dmax;
139   Dwarf_Addr eh_frame_ptr;
140   size_t search_table_entries = 0;
141   uint8_t search_table_encoding = 0;
142   const uint8_t *search_table = parse_eh_frame_hdr (data->d_buf, phdr->p_filesz,
143                                                     phdr->p_vaddr, ehdr,
144                                                     &eh_frame_ptr,
145                                                     &search_table_entries,
146                                                     &search_table_encoding);
147
148   /* Make sure there is enough room for the entries in the table,
149      each entry consists of 2 encoded values.  */
150   vsize = encoded_value_size (data, ehdr->e_ident, search_table_encoding,
151                               NULL);
152   dmax = phdr->p_filesz - (search_table - (const uint8_t *) data->d_buf);
153   if (unlikely (search_table == (void *) -1l
154                 || vsize == 0
155                 || search_table_entries > (dmax / vsize) / 2))
156     goto invalid_hdr;
157
158   Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset;
159   Dwarf_Word eh_frame_size = 0;
160
161   /* XXX we have no way without section headers to know the size
162      of the .eh_frame data.  Calculate the largest it might possibly be.
163      This won't be wasteful if the file is already mmap'd, but if it isn't
164      it might be quite excessive.  */
165   size_t filesize;
166   if (elf_rawfile (elf, &filesize) != NULL)
167     eh_frame_size = filesize - eh_frame_offset;
168
169   data = elf_getdata_rawchunk (elf, eh_frame_offset, eh_frame_size, ELF_T_BYTE);
170   if (data == NULL)
171     {
172       __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */
173       return NULL;
174     }
175   Dwarf_CFI *cfi = allocate_cfi (elf, eh_frame_ptr);
176   if (cfi != NULL)
177     {
178       cfi->data = (Elf_Data_Scn *) data;
179
180       if (search_table != NULL)
181         {
182           cfi->search_table = search_table;
183           cfi->search_table_len = phdr->p_filesz;
184           cfi->search_table_vaddr = phdr->p_vaddr;
185           cfi->search_table_encoding = search_table_encoding;
186           cfi->search_table_entries = search_table_entries;
187         }
188     }
189   return cfi;
190 }
191
192 /* Search the phdrs for PT_GNU_EH_FRAME.  */
193 static Dwarf_CFI *
194 getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr)
195 {
196   size_t phnum;
197   if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
198     return NULL;
199
200   for (size_t i = 0; i < phnum; ++i)
201     {
202       GElf_Phdr phdr_mem;
203       GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
204       if (unlikely (phdr == NULL))
205         return NULL;
206       if (phdr->p_type == PT_GNU_EH_FRAME)
207         return getcfi_gnu_eh_frame (elf, ehdr, phdr);
208     }
209
210   __libdw_seterrno (DWARF_E_NO_DWARF);
211   return NULL;
212 }
213
214 static Dwarf_CFI *
215 getcfi_scn_eh_frame (Elf *elf, const GElf_Ehdr *ehdr,
216                      Elf_Scn *scn, GElf_Shdr *shdr,
217                      Elf_Scn *hdr_scn, GElf_Addr hdr_vaddr)
218 {
219   Elf_Data *data = elf_rawdata (scn, NULL);
220   if (data == NULL || data->d_buf == NULL)
221     {
222       __libdw_seterrno (DWARF_E_INVALID_ELF);
223       return NULL;
224     }
225   Dwarf_CFI *cfi = allocate_cfi (elf, shdr->sh_addr);
226   if (cfi != NULL)
227     {
228       cfi->data = (Elf_Data_Scn *) data;
229       if (hdr_scn != NULL)
230         {
231           Elf_Data *hdr_data = elf_rawdata (hdr_scn, NULL);
232           if (hdr_data != NULL && hdr_data->d_buf != NULL)
233             {
234               size_t vsize, dmax;
235               GElf_Addr eh_frame_vaddr;
236               cfi->search_table_vaddr = hdr_vaddr;
237               cfi->search_table
238                 = parse_eh_frame_hdr (hdr_data->d_buf, hdr_data->d_size,
239                                       hdr_vaddr, ehdr, &eh_frame_vaddr,
240                                       &cfi->search_table_entries,
241                                       &cfi->search_table_encoding);
242               cfi->search_table_len = hdr_data->d_size;
243
244               /* Make sure there is enough room for the entries in the table,
245                  each entry consists of 2 encoded values.  */
246               vsize = encoded_value_size (hdr_data, ehdr->e_ident,
247                                           cfi->search_table_encoding, NULL);
248               dmax = hdr_data->d_size - (cfi->search_table
249                                          - (const uint8_t *) hdr_data->d_buf);
250               if (unlikely (cfi->search_table == (void *) -1l
251                             || vsize == 0
252                             || cfi->search_table_entries > (dmax / vsize) / 2))
253                 {
254                   free (cfi);
255                   /* XXX might be read error or corrupt phdr */
256                   __libdw_seterrno (DWARF_E_INVALID_CFI);
257                   return NULL;
258                 }
259
260               /* Sanity check.  */
261               if (unlikely (eh_frame_vaddr != shdr->sh_addr))
262                 cfi->search_table = NULL;
263             }
264         }
265     }
266   return cfi;
267 }
268
269 /* Search for the sections named ".eh_frame" and ".eh_frame_hdr".  */
270 static Dwarf_CFI *
271 getcfi_shdr (Elf *elf, const GElf_Ehdr *ehdr)
272 {
273   size_t shstrndx;
274   if (elf_getshdrstrndx (elf, &shstrndx) != 0)
275     {
276       __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
277       return NULL;
278     }
279
280   if (shstrndx != 0)
281     {
282       Elf_Scn *hdr_scn = NULL;
283       GElf_Addr hdr_vaddr = 0;
284       Elf_Scn *scn = NULL;
285       while ((scn = elf_nextscn (elf, scn)) != NULL)
286         {
287           GElf_Shdr shdr_mem;
288           GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
289           if (shdr == NULL)
290             continue;
291           const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
292           if (name == NULL)
293             continue;
294           if (!strcmp (name, ".eh_frame_hdr"))
295             {
296               hdr_scn = scn;
297               hdr_vaddr = shdr->sh_addr;
298             }
299           else if (!strcmp (name, ".eh_frame"))
300             {
301               if (shdr->sh_type != SHT_NOBITS)
302                 return getcfi_scn_eh_frame (elf, ehdr, scn, shdr,
303                                             hdr_scn, hdr_vaddr);
304               else
305                 return NULL;
306             }
307         }
308     }
309
310   return (void *) -1l;
311 }
312
313 Dwarf_CFI *
314 dwarf_getcfi_elf (Elf *elf)
315 {
316   if (elf_kind (elf) != ELF_K_ELF)
317     {
318       __libdw_seterrno (DWARF_E_NOELF);
319       return NULL;
320     }
321
322   GElf_Ehdr ehdr_mem;
323   GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
324   if (unlikely (ehdr == NULL))
325     {
326       __libdw_seterrno (DWARF_E_INVALID_ELF);
327       return NULL;
328     }
329
330   Dwarf_CFI *result = getcfi_shdr (elf, ehdr);
331   if (result == (void *) -1l)
332     result = getcfi_phdr (elf, ehdr);
333
334   return result;
335 }
336 INTDEF (dwarf_getcfi_elf)