libdwfl: Sanity check partial core file dyn data read.
[platform/upstream/elfutils.git] / libdw / dwarf_ranges.c
1 /* Enumerate the PC ranges covered by a DIE.
2    Copyright (C) 2005, 2007, 2009, 2018 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 "libdwP.h"
34 #include <dwarf.h>
35 #include <assert.h>
36
37 /* Read up begin/end pair and increment read pointer.
38     - If it's normal range record, set up `*beginp' and `*endp' and return 0.
39     - If it's a default location, set `*beginp' (0), `*endp' (-1) and return 0.
40     - If it's base address selection record, set up `*basep' and return 1.
41     - If it's end of rangelist, don't set anything and return 2
42     - If an error occurs, don't set anything and return -1.  */
43 internal_function int
44 __libdw_read_begin_end_pair_inc (Dwarf_CU *cu, int sec_index,
45                                  const unsigned char **addrp,
46                                  const unsigned char *addrend,
47                                  int width,
48                                  Dwarf_Addr *beginp, Dwarf_Addr *endp,
49                                  Dwarf_Addr *basep)
50 {
51   Dwarf *dbg = cu->dbg;
52   if (sec_index == IDX_debug_loc
53       && cu->version < 5
54       && cu->unit_type == DW_UT_split_compile)
55     {
56       /* GNU DebugFission.  */
57       const unsigned char *addr = *addrp;
58       if (addrend - addr < 1)
59         goto invalid;
60
61       const char code = *addr++;
62       uint64_t begin = 0, end = 0, base = *basep, addr_idx;
63       switch (code)
64         {
65         case DW_LLE_GNU_end_of_list_entry:
66           *addrp = addr;
67           return 2;
68
69         case DW_LLE_GNU_base_address_selection_entry:
70           if (addrend - addr < 1)
71             goto invalid;
72           get_uleb128 (addr_idx, addr, addrend);
73           if (__libdw_addrx (cu, addr_idx, &base) != 0)
74             return -1;
75           *basep = base;
76           *addrp = addr;
77           return 1;
78
79         case DW_LLE_GNU_start_end_entry:
80           if (addrend - addr < 1)
81             goto invalid;
82           get_uleb128 (addr_idx, addr, addrend);
83           if (__libdw_addrx (cu, addr_idx, &begin) != 0)
84             return -1;
85           if (addrend - addr < 1)
86             goto invalid;
87           get_uleb128 (addr_idx, addr, addrend);
88           if (__libdw_addrx (cu, addr_idx, &end) != 0)
89             return -1;
90
91           *beginp = begin;
92           *endp = end;
93           *addrp = addr;
94           return 0;
95
96         case DW_LLE_GNU_start_length_entry:
97           if (addrend - addr < 1)
98             goto invalid;
99           get_uleb128 (addr_idx, addr, addrend);
100           if (__libdw_addrx (cu, addr_idx, &begin) != 0)
101             return -1;
102           if (addrend - addr < 4)
103             goto invalid;
104           end = read_4ubyte_unaligned_inc (dbg, addr);
105
106           *beginp = begin;
107           *endp = begin + end;
108           *addrp = addr;
109           return 0;
110
111         default:
112           goto invalid;
113         }
114     }
115   else if (sec_index == IDX_debug_ranges || sec_index == IDX_debug_loc)
116     {
117       Dwarf_Addr escape = (width == 8 ? (Elf64_Addr) -1
118                            : (Elf64_Addr) (Elf32_Addr) -1);
119       Dwarf_Addr begin;
120       Dwarf_Addr end;
121
122       const unsigned char *addr = *addrp;
123       if (addrend - addr < width * 2)
124         {
125         invalid:
126           __libdw_seterrno (DWARF_E_INVALID_DWARF);
127           return -1;
128         }
129
130       bool begin_relocated = READ_AND_RELOCATE (__libdw_relocate_address,
131                                                 begin);
132       bool end_relocated = READ_AND_RELOCATE (__libdw_relocate_address,
133                                               end);
134       *addrp = addr;
135
136       /* Unrelocated escape for begin means base address selection.  */
137       if (begin == escape && !begin_relocated)
138         {
139           if (unlikely (end == escape))
140             goto invalid;
141
142           *basep = end;
143           return 1;
144         }
145
146       /* Unrelocated pair of zeroes means end of range list.  */
147       if (begin == 0 && end == 0 && !begin_relocated && !end_relocated)
148         return 2;
149
150       /* Don't check for begin_relocated == end_relocated.  Serve the data
151          to the client even though it may be buggy.  */
152       *beginp = begin + *basep;
153       *endp = end + *basep;
154
155       return 0;
156     }
157   else if (sec_index == IDX_debug_rnglists)
158     {
159       const unsigned char *addr = *addrp;
160       if (addrend - addr < 1)
161         goto invalid;
162
163       const char code = *addr++;
164       uint64_t begin = 0, end = 0, base = *basep, addr_idx;
165       switch (code)
166         {
167         case DW_RLE_end_of_list:
168           *addrp = addr;
169           return 2;
170
171         case DW_RLE_base_addressx:
172           if (addrend - addr < 1)
173             goto invalid;
174           get_uleb128 (addr_idx, addr, addrend);
175           if (__libdw_addrx (cu, addr_idx, &base) != 0)
176             return -1;
177
178           *basep = base;
179           *addrp = addr;
180           return 1;
181
182         case DW_RLE_startx_endx:
183           if (addrend - addr < 1)
184             goto invalid;
185           get_uleb128 (addr_idx, addr, addrend);
186           if (__libdw_addrx (cu, addr_idx, &begin) != 0)
187             return -1;
188           if (addrend - addr < 1)
189             goto invalid;
190           get_uleb128 (addr_idx, addr, addrend);
191           if (__libdw_addrx (cu, addr_idx, &end) != 0)
192             return -1;
193
194           *beginp = begin;
195           *endp = end;
196           *addrp = addr;
197           return 0;
198
199         case DW_RLE_startx_length:
200           if (addrend - addr < 1)
201             goto invalid;
202           get_uleb128 (addr_idx, addr, addrend);
203           if (__libdw_addrx (cu, addr_idx, &begin) != 0)
204             return -1;
205           if (addrend - addr < 1)
206             goto invalid;
207           get_uleb128 (end, addr, addrend);
208
209           *beginp = begin;
210           *endp = begin + end;
211           *addrp = addr;
212           return 0;
213
214         case DW_RLE_offset_pair:
215           if (addrend - addr < 1)
216             goto invalid;
217           get_uleb128 (begin, addr, addrend);
218           if (addrend - addr < 1)
219             goto invalid;
220           get_uleb128 (end, addr, addrend);
221
222           *beginp = begin + base;
223           *endp = end + base;
224           *addrp = addr;
225           return 0;
226
227         case DW_RLE_base_address:
228           if (addrend - addr < width)
229             goto invalid;
230           __libdw_read_address_inc (dbg, sec_index, &addr, width, &base);
231
232           *basep = base;
233           *addrp = addr;
234           return 1;
235
236         case DW_RLE_start_end:
237           if (addrend - addr < 2 * width)
238             goto invalid;
239           __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin);
240           __libdw_read_address_inc (dbg, sec_index, &addr, width, &end);
241
242           *beginp = begin;
243           *endp = end;
244           *addrp = addr;
245           return 0;
246
247         case DW_RLE_start_length:
248           if (addrend - addr < width)
249             goto invalid;
250           __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin);
251           if (addrend - addr < 1)
252             goto invalid;
253           get_uleb128 (end, addr, addrend);
254
255           *beginp = begin;
256           *endp = begin + end;
257           *addrp = addr;
258           return 0;
259
260         default:
261           goto invalid;
262         }
263     }
264   else if (sec_index == IDX_debug_loclists)
265     {
266       const unsigned char *addr = *addrp;
267       if (addrend - addr < 1)
268         goto invalid;
269
270       const char code = *addr++;
271       uint64_t begin = 0, end = 0, base = *basep, addr_idx;
272       switch (code)
273         {
274         case DW_LLE_end_of_list:
275           *addrp = addr;
276           return 2;
277
278         case DW_LLE_base_addressx:
279           if (addrend - addr < 1)
280             goto invalid;
281           get_uleb128 (addr_idx, addr, addrend);
282           if (__libdw_addrx (cu, addr_idx, &base) != 0)
283             return -1;
284
285           *basep = base;
286           *addrp = addr;
287           return 1;
288
289         case DW_LLE_startx_endx:
290           if (addrend - addr < 1)
291             goto invalid;
292           get_uleb128 (addr_idx, addr, addrend);
293           if (__libdw_addrx (cu, addr_idx, &begin) != 0)
294             return -1;
295           if (addrend - addr < 1)
296             goto invalid;
297           get_uleb128 (addr_idx, addr, addrend);
298           if (__libdw_addrx (cu, addr_idx, &end) != 0)
299             return -1;
300
301           *beginp = begin;
302           *endp = end;
303           *addrp = addr;
304           return 0;
305
306         case DW_LLE_startx_length:
307           if (addrend - addr < 1)
308             goto invalid;
309           get_uleb128 (addr_idx, addr, addrend);
310           if (__libdw_addrx (cu, addr_idx, &begin) != 0)
311             return -1;
312           if (addrend - addr < 1)
313             goto invalid;
314           get_uleb128 (end, addr, addrend);
315
316           *beginp = begin;
317           *endp = begin + end;
318           *addrp = addr;
319           return 0;
320
321         case DW_LLE_offset_pair:
322           if (addrend - addr < 1)
323             goto invalid;
324           get_uleb128 (begin, addr, addrend);
325           if (addrend - addr < 1)
326             goto invalid;
327           get_uleb128 (end, addr, addrend);
328
329           *beginp = begin + base;
330           *endp = end + base;
331           *addrp = addr;
332           return 0;
333
334         case DW_LLE_default_location:
335           *beginp = 0;
336           *endp = (Dwarf_Addr) -1;
337           *addrp = addr;
338           return 0;
339
340         case DW_LLE_base_address:
341           if (addrend - addr < width)
342             goto invalid;
343           __libdw_read_address_inc (dbg, sec_index, &addr, width, &base);
344
345           *basep = base;
346           *addrp = addr;
347           return 1;
348
349         case DW_LLE_start_end:
350           if (addrend - addr < 2 * width)
351             goto invalid;
352           __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin);
353           __libdw_read_address_inc (dbg, sec_index, &addr, width, &end);
354
355           *beginp = begin;
356           *endp = end;
357           *addrp = addr;
358           return 0;
359
360         case DW_LLE_start_length:
361           if (addrend - addr < width)
362             goto invalid;
363           __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin);
364           if (addrend - addr < 1)
365             goto invalid;
366           get_uleb128 (end, addr, addrend);
367
368           *beginp = begin;
369           *endp = begin + end;
370           *addrp = addr;
371           return 0;
372
373         default:
374           goto invalid;
375         }
376     }
377   else
378     {
379       __libdw_seterrno (DWARF_E_INVALID_DWARF);
380       return -1;
381     }
382 }
383
384 static int
385 initial_offset (Dwarf_Attribute *attr, ptrdiff_t *offset)
386 {
387   size_t secidx = (attr->cu->version < 5
388                    ? IDX_debug_ranges : IDX_debug_rnglists);
389
390   Dwarf_Word start_offset;
391   if (attr->form == DW_FORM_rnglistx)
392     {
393       Dwarf_Word idx;
394       Dwarf_CU *cu = attr->cu;
395       const unsigned char *datap = attr->valp;
396       const unsigned char *endp = cu->endp;
397       if (datap >= endp)
398         {
399           __libdw_seterrno (DWARF_E_INVALID_DWARF);
400           return -1;
401         }
402       get_uleb128 (idx, datap, endp);
403
404       Elf_Data *data = cu->dbg->sectiondata[secidx];
405       if (data == NULL && cu->unit_type == DW_UT_split_compile)
406         {
407           cu = __libdw_find_split_unit (cu);
408           if (cu != NULL)
409             data = cu->dbg->sectiondata[secidx];
410         }
411
412       if (data == NULL)
413         {
414           __libdw_seterrno (secidx == IDX_debug_ranges
415                             ? DWARF_E_NO_DEBUG_RANGES
416                             : DWARF_E_NO_DEBUG_RNGLISTS);
417           return -1;
418         }
419
420       Dwarf_Off range_base_off = __libdw_cu_ranges_base (cu);
421
422       /* The section should at least contain room for one offset.  */
423       size_t sec_size = cu->dbg->sectiondata[secidx]->d_size;
424       size_t offset_size = cu->offset_size;
425       if (offset_size > sec_size)
426         {
427         invalid_offset:
428           __libdw_seterrno (DWARF_E_INVALID_OFFSET);
429           return -1;
430         }
431
432       /* And the base offset should be at least inside the section.  */
433       if (range_base_off > (sec_size - offset_size))
434         goto invalid_offset;
435
436       size_t max_idx = (sec_size - offset_size - range_base_off) / offset_size;
437       if (idx > max_idx)
438         goto invalid_offset;
439
440       datap = (cu->dbg->sectiondata[secidx]->d_buf
441                + range_base_off + (idx * offset_size));
442       if (offset_size == 4)
443         start_offset = read_4ubyte_unaligned (cu->dbg, datap);
444       else
445         start_offset = read_8ubyte_unaligned (cu->dbg, datap);
446
447       start_offset += range_base_off;
448     }
449   else
450     {
451       if (__libdw_formptr (attr, secidx,
452                            (secidx == IDX_debug_ranges
453                             ? DWARF_E_NO_DEBUG_RANGES
454                             : DWARF_E_NO_DEBUG_RNGLISTS),
455                            NULL, &start_offset) == NULL)
456         return -1;
457     }
458
459   *offset = start_offset;
460   return 0;
461 }
462
463 ptrdiff_t
464 dwarf_ranges (Dwarf_Die *die, ptrdiff_t offset, Dwarf_Addr *basep,
465               Dwarf_Addr *startp, Dwarf_Addr *endp)
466 {
467   if (die == NULL)
468     return -1;
469
470   if (offset == 0
471       /* Usually there is a single contiguous range.  */
472       && INTUSE(dwarf_highpc) (die, endp) == 0
473       && INTUSE(dwarf_lowpc) (die, startp) == 0)
474     /* A offset into .debug_ranges will never be 1, it must be at least a
475        multiple of 4.  So we can return 1 as a special case value to mark
476        there are no ranges to look for on the next call.  */
477     return 1;
478
479   if (offset == 1)
480     return 0;
481
482   /* We have to look for a noncontiguous range.  */
483   Dwarf_CU *cu = die->cu;
484   if (cu == NULL)
485     {
486       __libdw_seterrno (DWARF_E_INVALID_DWARF);
487       return -1;
488     }
489
490   size_t secidx = (cu->version < 5 ? IDX_debug_ranges : IDX_debug_rnglists);
491   const Elf_Data *d = cu->dbg->sectiondata[secidx];
492   if (d == NULL && cu->unit_type == DW_UT_split_compile)
493     {
494       Dwarf_CU *skel = __libdw_find_split_unit (cu);
495       if (skel != NULL)
496         {
497           cu = skel;
498           d = cu->dbg->sectiondata[secidx];
499         }
500     }
501
502   const unsigned char *readp;
503   const unsigned char *readendp;
504   if (offset == 0)
505     {
506       Dwarf_Attribute attr_mem;
507       Dwarf_Attribute *attr = INTUSE(dwarf_attr) (die, DW_AT_ranges,
508                                                   &attr_mem);
509       if (attr == NULL
510           && is_cudie (die)
511           && die->cu->unit_type == DW_UT_split_compile)
512         attr = INTUSE(dwarf_attr_integrate) (die, DW_AT_ranges, &attr_mem);
513       if (attr == NULL)
514         /* No PC attributes in this DIE at all, so an empty range list.  */
515         return 0;
516
517       *basep = __libdw_cu_base_address (attr->cu);
518       if (*basep == (Dwarf_Addr) -1)
519         return -1;
520
521       if (initial_offset (attr, &offset) != 0)
522         return -1;
523     }
524   else
525     {
526       if (__libdw_offset_in_section (cu->dbg,
527                                      secidx, offset, 1))
528         return -1;
529     }
530
531   readp = d->d_buf + offset;
532   readendp = d->d_buf + d->d_size;
533
534   Dwarf_Addr begin;
535   Dwarf_Addr end;
536
537  next:
538   switch (__libdw_read_begin_end_pair_inc (cu, secidx,
539                                            &readp, readendp,
540                                            cu->address_size,
541                                            &begin, &end, basep))
542     {
543     case 0:
544       break;
545     case 1:
546       goto next;
547     case 2:
548       return 0;
549     default:
550       return -1;
551     }
552
553   *startp = begin;
554   *endp = end;
555   return readp - (unsigned char *) d->d_buf;
556 }
557 INTDEF (dwarf_ranges)