libdwfl: find_debuginfo_in_patch don't alloca/strdupa strings of unknown size.
[platform/upstream/elfutils.git] / libdwfl / find-debuginfo.c
1 /* Standard find_debuginfo callback for libdwfl.
2    Copyright (C) 2005-2010, 2014 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 #include "libdwflP.h"
30 #include <stdio.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <sys/stat.h>
34 #include "system.h"
35
36
37 /* Try to open64 [DIR/][SUBDIR/]DEBUGLINK, return file descriptor or -1.
38    On success, *DEBUGINFO_FILE_NAME has the malloc'd name of the open file.  */
39 static int
40 try_open (const struct stat64 *main_stat,
41           const char *dir, const char *subdir, const char *debuglink,
42           char **debuginfo_file_name)
43 {
44   char *fname;
45   if (dir == NULL && subdir == NULL)
46     {
47       fname = strdup (debuglink);
48       if (unlikely (fname == NULL))
49         return -1;
50     }
51   else if ((subdir == NULL ? asprintf (&fname, "%s/%s", dir, debuglink)
52             : dir == NULL ? asprintf (&fname, "%s/%s", subdir, debuglink)
53             : asprintf (&fname, "%s/%s/%s", dir, subdir, debuglink)) < 0)
54     return -1;
55
56   struct stat64 st;
57   int fd = TEMP_FAILURE_RETRY (open64 (fname, O_RDONLY));
58   if (fd < 0)
59     free (fname);
60   else if (fstat64 (fd, &st) == 0
61            && st.st_ino == main_stat->st_ino
62            && st.st_dev == main_stat->st_dev)
63     {
64       /* This is the main file by another name.  Don't look at it again.  */
65       close (fd);
66       errno = ENOENT;
67       fd = -1;
68     }
69   else
70     *debuginfo_file_name = fname;
71
72   return fd;
73 }
74
75 /* Return true iff the FD's contents CRC matches DEBUGLINK_CRC.  */
76 static inline bool
77 check_crc (int fd, GElf_Word debuglink_crc)
78 {
79   uint32_t file_crc;
80   return (__libdwfl_crc32_file (fd, &file_crc) == 0
81           && file_crc == debuglink_crc);
82 }
83
84 static bool
85 validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc)
86 {
87   /* For alt debug files always check the build-id from the Dwarf and alt.  */
88   if (mod->dw != NULL)
89     {
90       bool valid = false;
91       const void *build_id;
92       const char *altname;
93       ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw,
94                                                                    &altname,
95                                                                    &build_id);
96       if (build_id_len > 0)
97         {
98           /* We need to open an Elf handle on the file so we can check its
99              build ID note for validation.  Backdoor the handle into the
100              module data structure since we had to open it early anyway.  */
101           Dwfl_Error error = __libdw_open_file (&fd, &mod->alt_elf,
102                                                 false, false);
103           if (error != DWFL_E_NOERROR)
104             __libdwfl_seterrno (error);
105           else
106             {
107               const void *alt_build_id;
108               ssize_t alt_len = INTUSE(dwelf_elf_gnu_build_id) (mod->alt_elf,
109                                                                 &alt_build_id);
110               if (alt_len > 0 && alt_len == build_id_len
111                   && memcmp (build_id, alt_build_id, alt_len) == 0)
112                 valid = true;
113               else
114                 {
115                   /* A mismatch!  */
116                   elf_end (mod->alt_elf);
117                   mod->alt_elf = NULL;
118                   close (fd);
119                   fd = -1;
120                 }
121             }
122         }
123       return valid;
124     }
125
126   /* If we have a build ID, check only that.  */
127   if (mod->build_id_len > 0)
128     {
129       /* We need to open an Elf handle on the file so we can check its
130          build ID note for validation.  Backdoor the handle into the
131          module data structure since we had to open it early anyway.  */
132
133       mod->debug.valid = false;
134       Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, false, false);
135       if (error != DWFL_E_NOERROR)
136         __libdwfl_seterrno (error);
137       else if (likely (__libdwfl_find_build_id (mod, false,
138                                                 mod->debug.elf) == 2))
139         /* Also backdoor the gratuitous flag.  */
140         mod->debug.valid = true;
141       else
142         {
143           /* A mismatch!  */
144           elf_end (mod->debug.elf);
145           mod->debug.elf = NULL;
146           close (fd);
147           fd = -1;
148         }
149
150       return mod->debug.valid;
151     }
152
153   return !check || check_crc (fd, debuglink_crc);
154 }
155
156 static int
157 find_debuginfo_in_path (Dwfl_Module *mod, const char *file_name,
158                         const char *debuglink_file, GElf_Word debuglink_crc,
159                         char **debuginfo_file_name)
160 {
161   bool cancheck = debuglink_crc != (GElf_Word) 0;
162
163   const char *file_basename = file_name == NULL ? NULL : basename (file_name);
164   char *localname = NULL;
165   if (debuglink_file == NULL)
166     {
167       /* For a alt debug multi file we need a name, for a separate debug
168          name we may be able to fall back on file_basename.debug.  */
169       if (file_basename == NULL || mod->dw != NULL)
170         {
171           errno = 0;
172           return -1;
173         }
174
175       size_t len = strlen (file_basename);
176       localname = malloc (len + sizeof ".debug");
177       if (unlikely (localname == NULL))
178         return -1;
179       memcpy (localname, file_basename, len);
180       memcpy (&localname[len], ".debug", sizeof ".debug");
181       debuglink_file = localname;
182       cancheck = false;
183     }
184
185   /* Look for a file named DEBUGLINK_FILE in the directories
186      indicated by the debug directory path setting.  */
187
188   const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
189   char *localpath = strdup ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
190                             ?: DEFAULT_DEBUGINFO_PATH);
191   if (unlikely (localpath == NULL))
192     {
193       free (localname);
194       return -1;
195     }
196
197   /* A leading - or + in the whole path sets whether to check file CRCs.  */
198   bool defcheck = true;
199   char *path = localpath;
200   if (path[0] == '-' || path[0] == '+')
201     {
202       defcheck = path[0] == '+';
203       ++path;
204     }
205
206   /* XXX dev/ino should be cached in struct dwfl_file.  */
207   struct stat64 main_stat;
208   if (unlikely ((mod->main.fd != -1 ? fstat64 (mod->main.fd, &main_stat)
209                  : file_name != NULL ? stat64 (file_name, &main_stat)
210                  : -1) < 0))
211     {
212       main_stat.st_dev = 0;
213       main_stat.st_ino = 0;
214     }
215
216   char *file_dirname = (file_basename == file_name ? NULL
217                         : strndup (file_name, file_basename - 1 - file_name));
218   if (file_basename != file_name && file_dirname == NULL)
219     {
220       free (localpath);
221       free (localname);
222       return -1;
223     }
224   char *p;
225   while ((p = strsep (&path, ":")) != NULL)
226     {
227       /* A leading - or + says whether to check file CRCs for this element.  */
228       bool check = defcheck;
229       if (*p == '+' || *p == '-')
230         check = *p++ == '+';
231       check = check && cancheck;
232
233       const char *dir, *subdir, *file;
234       switch (p[0])
235         {
236         case '\0':
237           /* An empty entry says to try the main file's directory.  */
238           dir = file_dirname;
239           subdir = NULL;
240           file = debuglink_file;
241           break;
242         case '/':
243           /* An absolute path says to look there for a subdirectory
244              named by the main file's absolute directory.  This cannot
245              be applied to a relative file name.  For alt debug files
246              it means to look for the basename file in that dir or the
247              .dwz subdir (see below).  */
248           if (mod->dw == NULL
249               && (file_dirname == NULL || file_dirname[0] != '/'))
250             continue;
251           dir = p;
252           if (mod->dw == NULL)
253             {
254               subdir = file_dirname + 1;
255               file = debuglink_file;
256             }
257           else
258             {
259               subdir = NULL;
260               file = basename (debuglink_file);
261             }
262           break;
263         default:
264           /* A relative path says to try a subdirectory of that name
265              in the main file's directory.  */
266           dir = file_dirname;
267           subdir = p;
268           file = debuglink_file;
269           break;
270         }
271
272       char *fname = NULL;
273       int fd = try_open (&main_stat, dir, subdir, file, &fname);
274       if (fd < 0)
275         switch (errno)
276           {
277           case ENOENT:
278           case ENOTDIR:
279             /* If we are looking for the alt file also try the .dwz subdir.
280                But only if this is the empty or absolute path.  */
281             if (mod->dw != NULL && (p[0] == '\0' || p[0] == '/'))
282               {
283                 fd = try_open (&main_stat, dir, ".dwz",
284                                basename (file), &fname);
285                 if (fd < 0)
286                   {
287                     if (errno != ENOENT && errno != ENOTDIR)
288                       goto fail_free;
289                     else
290                       continue;
291                   }
292                 break;
293               }
294             continue;
295           default:
296             {
297             fail_free:
298               free (localpath);
299               free (localname);
300               free (file_dirname);
301               return -1;
302             }
303           }
304       free (localpath);
305       free (localname);
306       free (file_dirname);
307       if (validate (mod, fd, check, debuglink_crc))
308         {
309           *debuginfo_file_name = fname;
310           return fd;
311         }
312       free (fname);
313       close (fd);
314     }
315
316   /* No dice.  */
317   errno = 0;
318   return -1;
319 }
320
321 int
322 dwfl_standard_find_debuginfo (Dwfl_Module *mod,
323                               void **userdata __attribute__ ((unused)),
324                               const char *modname __attribute__ ((unused)),
325                               GElf_Addr base __attribute__ ((unused)),
326                               const char *file_name,
327                               const char *debuglink_file,
328                               GElf_Word debuglink_crc,
329                               char **debuginfo_file_name)
330 {
331   /* First try by build ID if we have one.  If that succeeds or fails
332      other than just by finding nothing, that's all we do.  */
333   const unsigned char *bits;
334   GElf_Addr vaddr;
335   if (INTUSE(dwfl_module_build_id) (mod, &bits, &vaddr) > 0)
336     {
337       /* Dropping most arguments means we cannot rely on them in
338          dwfl_build_id_find_debuginfo.  But leave it that way since
339          some user code out there also does this, so we'll have to
340          handle it anyway.  */
341       int fd = INTUSE(dwfl_build_id_find_debuginfo) (mod,
342                                                      NULL, NULL, 0,
343                                                      NULL, NULL, 0,
344                                                      debuginfo_file_name);
345
346       /* Did the build_id callback find something or report an error?
347          Then we are done.  Otherwise fallback on path based search.  */
348       if (fd >= 0
349           || (mod->dw == NULL && mod->debug.elf != NULL)
350           || (mod->dw != NULL && mod->alt_elf != NULL)
351           || errno != 0)
352         return fd;
353     }
354
355   /* Failing that, search the path by name.  */
356   int fd = find_debuginfo_in_path (mod, file_name,
357                                    debuglink_file, debuglink_crc,
358                                    debuginfo_file_name);
359
360   if (fd < 0 && errno == 0)
361     {
362       /* If FILE_NAME is a symlink, the debug file might be associated
363          with the symlink target name instead.  */
364
365       char *canon = canonicalize_file_name (file_name);
366       if (canon != NULL && strcmp (file_name, canon))
367         fd = find_debuginfo_in_path (mod, canon,
368                                      debuglink_file, debuglink_crc,
369                                      debuginfo_file_name);
370       free (canon);
371     }
372
373   return fd;
374 }
375 INTDEF (dwfl_standard_find_debuginfo)