libdwfl: Sanity check partial core file dyn data read.
[platform/upstream/elfutils.git] / libdw / encoded-value.h
1 /* DW_EH_PE_* support for libdw unwinder.
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 #ifndef _ENCODED_VALUE_H
30 #define _ENCODED_VALUE_H 1
31
32 #include <dwarf.h>
33 #include <stdlib.h>
34 #include "libdwP.h"
35 #include "../libelf/common.h"
36
37
38 /* Returns zero if the value is omitted, the encoding is unknown or
39    the (leb128) size cannot be determined.  */
40 static size_t __attribute__ ((unused))
41 encoded_value_size (const Elf_Data *data, const unsigned char e_ident[],
42                     uint8_t encoding, const uint8_t *p)
43 {
44   if (encoding == DW_EH_PE_omit)
45     return 0;
46
47   switch (encoding & 0x07)
48     {
49     case DW_EH_PE_udata2:
50       return 2;
51     case DW_EH_PE_udata4:
52       return 4;
53     case DW_EH_PE_udata8:
54       return 8;
55
56     case DW_EH_PE_absptr:
57       return e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
58
59     case DW_EH_PE_uleb128:
60       if (p != NULL)
61         {
62           const uint8_t *end = p;
63           while (end < (uint8_t *) data->d_buf + data->d_size)
64             if (*end++ & 0x80u)
65               return end - p;
66         }
67       return 0;
68
69     default:
70       return 0;
71     }
72 }
73
74 /* Returns zero when value was read successfully, minus one otherwise.  */
75 static inline int __attribute__ ((unused))
76 __libdw_cfi_read_address_inc (const Dwarf_CFI *cache,
77                               const unsigned char **addrp,
78                               int width, Dwarf_Addr *ret)
79 {
80   width = width ?: cache->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
81
82   if (cache->dbg != NULL)
83     return __libdw_read_address_inc (cache->dbg, IDX_debug_frame,
84                                      addrp, width, ret);
85
86   /* Only .debug_frame might have relocation to consider.
87      Read plain values from .eh_frame data.  */
88
89   const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size;
90   Dwarf eh_dbg = { .other_byte_order = MY_ELFDATA != cache->e_ident[EI_DATA] };
91
92   if (width == 4)
93     {
94       if (unlikely (*addrp + 4 > endp))
95         {
96         invalid_data:
97           __libdw_seterrno (DWARF_E_INVALID_CFI);
98           return -1;
99         }
100       *ret = read_4ubyte_unaligned_inc (&eh_dbg, *addrp);
101     }
102   else
103     {
104       if (unlikely (*addrp + 8 > endp))
105         goto invalid_data;
106       *ret = read_8ubyte_unaligned_inc (&eh_dbg, *addrp);
107     }
108   return 0;
109 }
110
111 /* Returns true on error, false otherwise. */
112 static bool __attribute__ ((unused))
113 read_encoded_value (const Dwarf_CFI *cache, uint8_t encoding,
114                     const uint8_t **p, Dwarf_Addr *result)
115 {
116   *result = 0;
117   switch (encoding & 0x70)
118     {
119     case DW_EH_PE_absptr:
120       break;
121     case DW_EH_PE_pcrel:
122       *result = (cache->frame_vaddr
123                  + (*p - (const uint8_t *) cache->data->d.d_buf));
124       break;
125     case DW_EH_PE_textrel:
126       // ia64: segrel
127       *result = cache->textrel;
128       break;
129     case DW_EH_PE_datarel:
130       // i386: GOTOFF
131       // ia64: gprel
132       *result = cache->datarel;
133       break;
134     case DW_EH_PE_funcrel:      /* XXX */
135       break;
136     case DW_EH_PE_aligned:
137       {
138         const size_t size = encoded_value_size (&cache->data->d,
139                                                 cache->e_ident,
140                                                 encoding, *p);
141         if (unlikely (size == 0))
142           return true;
143         size_t align = ((cache->frame_vaddr
144                          + (*p - (const uint8_t *) cache->data->d.d_buf))
145                         & (size - 1));
146         if (align != 0)
147           *p += size - align;
148         break;
149       }
150
151     default:
152       __libdw_seterrno (DWARF_E_INVALID_CFI);
153       return true;
154     }
155
156   Dwarf_Addr value = 0;
157   const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size;
158   switch (encoding & 0x0f)
159     {
160     case DW_EH_PE_udata2:
161       if (unlikely (*p + 2 > endp))
162         {
163         invalid_data:
164           __libdw_seterrno (DWARF_E_INVALID_CFI);
165           return true;
166         }
167       value = read_2ubyte_unaligned_inc (cache, *p);
168       break;
169
170     case DW_EH_PE_sdata2:
171       if (unlikely (*p + 2 > endp))
172         goto invalid_data;
173       value = read_2sbyte_unaligned_inc (cache, *p);
174       break;
175
176     case DW_EH_PE_udata4:
177       if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0))
178         return true;
179       break;
180
181     case DW_EH_PE_sdata4:
182       if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0))
183         return true;
184       value = (Dwarf_Sword) (Elf32_Sword) value; /* Sign-extend.  */
185       break;
186
187     case DW_EH_PE_udata8:
188     case DW_EH_PE_sdata8:
189       if (unlikely (__libdw_cfi_read_address_inc (cache, p, 8, &value) != 0))
190         return true;
191       break;
192
193     case DW_EH_PE_absptr:
194       if (unlikely (__libdw_cfi_read_address_inc (cache, p, 0, &value) != 0))
195         return true;
196       break;
197
198     case DW_EH_PE_uleb128:
199       get_uleb128 (value, *p, endp);
200       break;
201
202     case DW_EH_PE_sleb128:
203       get_sleb128 (value, *p, endp);
204       break;
205
206     default:
207       __libdw_seterrno (DWARF_E_INVALID_CFI);
208       return true;
209     }
210
211   *result += value;
212
213   if (encoding & DW_EH_PE_indirect)
214     {
215       if (unlikely (*result < cache->frame_vaddr))
216         return true;
217       *result -= cache->frame_vaddr;
218       size_t ptrsize = encoded_value_size (NULL, cache->e_ident,
219                                            DW_EH_PE_absptr, NULL);
220       if (unlikely (cache->data->d.d_size < ptrsize
221                     || *result > (cache->data->d.d_size - ptrsize)))
222         return true;
223       const uint8_t *ptr = cache->data->d.d_buf + *result;
224       if (unlikely (__libdw_cfi_read_address_inc (cache, &ptr, 0, result)
225                     != 0))
226         return true;
227     }
228
229   return false;
230 }
231
232 #endif  /* encoded-value.h */