]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/asf/asf.c
Fix kernel memory disclosure from /dev/midistat.
[FreeBSD/FreeBSD.git] / usr.sbin / asf / asf.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2002, 2003 Greg Lehey
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * This software is provided by the author ``as is'' and any express
17  * or implied warranties, including, but not limited to, the implied
18  * warranties of merchantability and fitness for a particular purpose
19  * are disclaimed.  In no event shall the author be liable for any
20  * direct, indirect, incidental, special, exemplary, or consequential
21  * damages (including, but not limited to, procurement of substitute
22  * goods or services; loss of use, data, or profits; or business
23  * interruption) however caused and on any theory of liability,
24  * whether in contract, strict liability, or tort (including
25  * negligence or otherwise) arising in any way out of the use of this
26  * software, even if advised of the possibility of such damage.
27  */
28 /* $Id: asf.c,v 1.4 2003/05/04 02:55:20 grog Exp grog $ */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/types.h>
34 #include <sys/queue.h>
35 #include <sys/stat.h>
36 #include <ctype.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <fts.h>
40 #include <inttypes.h>
41 #include <limits.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #include "asf.h"
48
49 struct kfile {
50     char               *name;
51     caddr_t             addr;
52     int                 seen;
53     STAILQ_ENTRY(kfile) link;
54 };
55
56 static STAILQ_HEAD(,kfile) kfile_head = STAILQ_HEAD_INITIALIZER(kfile_head);
57
58 void
59 kfile_add(const char *name, caddr_t addr)
60 {
61     struct kfile *kfp;
62
63     if ((kfp = malloc(sizeof(*kfp))) == NULL ||
64         (kfp->name = strdup(name)) == NULL)
65             errx(2, "out of memory");
66     kfp->addr = addr;
67     kfp->seen = 0;
68     STAILQ_INSERT_TAIL(&kfile_head, kfp, link);
69 }
70
71 static struct kfile *
72 kfile_find(const char *name)
73 {
74     struct kfile *kfp;
75
76     STAILQ_FOREACH(kfp, &kfile_head, link)
77         if (strcmp(kfp->name, name) == 0)
78             return (kfp);       /* found */
79
80     return (NULL);              /* not found */
81 }
82
83 static int
84 kfile_allseen(void)
85 {
86     struct kfile *kfp;
87
88     STAILQ_FOREACH(kfp, &kfile_head, link)
89         if (!kfp->seen)
90             return (0); /* at least one unseen */
91
92     return (1);         /* all seen */
93 }
94
95 static int
96 kfile_empty(void)
97 {
98     return (STAILQ_EMPTY(&kfile_head));
99 }
100
101 /*
102  * Take a blank separated list of tokens and turn it into a list of
103  * individual nul-delimited strings.  Build a list of pointers at
104  * token, which must have enough space for the tokens.  Return the
105  * number of tokens, or -1 on error (typically a missing string
106  * delimiter).
107  */
108 int
109 tokenize(char *cptr, char *token[], int maxtoken)
110 {
111     char delim;                         /* delimiter to search for */
112     int tokennr;                        /* index of this token */
113
114     for (tokennr = 0; tokennr < maxtoken;) {
115         while (isspace(*cptr))
116             cptr++;                     /* skip initial white space */
117         if ((*cptr == '\0') || (*cptr == '\n')
118             || (*cptr == '#'))          /* end of line */
119             return tokennr;             /* return number of tokens found */
120         delim = *cptr;
121         token[tokennr] = cptr;          /* point to it */
122         tokennr++;                      /* one more */
123         if (tokennr == maxtoken)        /* run off the end? */
124             return tokennr;
125         if ((delim == '\'') || (delim == '"')) { /* delimitered */
126             for (;;) {
127                 cptr++;
128                 if ((*cptr == delim)
129                     && (cptr[-1] != '\\')) { /* found the partner */
130                     cptr++;             /* move on past */
131                     if (!isspace(*cptr)) /* no space after closing quote */
132                         return -1;
133                     *cptr++ = '\0';     /* delimit */
134                 } else if ((*cptr == '\0')
135                     || (*cptr == '\n')) /* end of line */
136                     return -1;
137             }
138         } else {                        /* not quoted */
139             while ((*cptr != '\0') && (!isspace(*cptr)) && (*cptr != '\n'))
140                 cptr++;
141             if (*cptr != '\0')          /* not end of the line, */
142                 *cptr++ = '\0';         /* delimit and move to the next */
143         }
144     }
145     return maxtoken;                    /* can't get here */
146 }
147
148 static void
149 doobj(const char *path, caddr_t addr, FILE *out)
150 {
151     uintmax_t   base = (uintptr_t)addr;
152     uintmax_t   textaddr = 0;
153     uintmax_t   dataaddr = 0;
154     uintmax_t   bssaddr = 0;
155     uintmax_t  *up;
156     int         octokens;
157     char       *octoken[MAXTOKEN];
158     char        ocbuf[LINE_MAX + PATH_MAX];
159     FILE       *objcopy;
160
161     snprintf(ocbuf, sizeof(ocbuf),
162              "/usr/bin/objdump --section-headers %s", path);
163     if ((objcopy = popen(ocbuf, "r")) == NULL)
164         err(2, "can't start %s", ocbuf);
165     while (fgets(ocbuf, sizeof(ocbuf), objcopy)) {
166         octokens = tokenize(ocbuf, octoken, MAXTOKEN);
167         if (octokens <= 1)
168             continue;
169         up = NULL;
170         if (strcmp(octoken[1], ".text") == 0)
171             up = &textaddr;
172         else if (strcmp(octoken[1], ".data") == 0)
173             up = &dataaddr;
174         else if (strcmp(octoken[1], ".bss") == 0)
175             up = &bssaddr;
176         if (up == NULL)
177             continue;
178         *up = strtoumax(octoken[3], NULL, 16) + base;
179     }
180     if (textaddr) {     /* we must have a text address */
181         fprintf(out, "add-symbol-file %s 0x%jx", path, textaddr);
182         if (dataaddr)
183             fprintf(out, " -s .data 0x%jx", dataaddr);
184         if (bssaddr)
185             fprintf(out, " -s .bss 0x%jx", bssaddr);
186         fprintf(out, "\n");
187     }
188 }
189
190 static void
191 findmodules(char *path_argv[], const char *sfx[], FILE *out)
192 {
193     char               *p;
194     FTS                *fts;
195     FTSENT             *ftsent;
196     struct kfile       *kfp;
197     int                 i;
198     int                 sl;
199
200     /* Have to fts once per suffix to find preferred suffixes first */
201     do {
202         sl = *sfx ? strlen(*sfx) : 0;   /* current suffix length */
203         fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
204         if (fts == NULL)
205             err(2, "can't begin traversing module path");
206         while ((ftsent = fts_read(fts)) != NULL) {
207             if (ftsent->fts_info == FTS_DNR ||
208                 ftsent->fts_info == FTS_ERR ||
209                 ftsent->fts_info == FTS_NS) {
210                     errno = ftsent->fts_errno;
211                     err(2, "error while traversing path %s", ftsent->fts_path);
212             }
213             if (ftsent->fts_info != FTS_F)
214                 continue;                       /* not a plain file */
215
216             if (sl > 0) {
217                 /* non-blank suffix; see if file name has it */
218                 i = ftsent->fts_namelen - sl;
219                 if (i <= 0 || strcmp(ftsent->fts_name + i, *sfx) != 0)
220                     continue;           /* no such suffix */
221                 if ((p = strdup(ftsent->fts_name)) == NULL)
222                     errx(2, "out of memory");
223                 p[i] = '\0';            /* remove suffix in the copy */
224                 kfp = kfile_find(p);
225                 free(p);
226             } else
227                 kfp = kfile_find(ftsent->fts_name);
228
229             if (kfp && !kfp->seen) {
230                 doobj(ftsent->fts_path, kfp->addr, out);
231                 kfp->seen = 1;
232                 /* Optimization: stop fts as soon as seen all loaded modules */
233                 if (kfile_allseen()) {
234                     fts_close(fts);
235                     return;
236                 }
237             }
238         }
239         if (ftsent == NULL && errno != 0)
240             err(2, "couldn't complete traversing module path");
241         fts_close(fts);
242     } while (*sfx++);
243 }
244
245 static void
246 usage(const char *myname)
247 {
248     fprintf(stderr,
249         "Usage:\n"
250         "%s [-afKksVx] [-M core] [-N system] [-o outfile] [-X suffix]\n"
251         "%*s [modules-path [outfile]]\n\n"
252         "\t-a\tappend to outfile\n"
253         "\t-f\tfind the module in any subdirectory of modules-path\n"
254         "\t-K\tuse kld(2) to get the list of modules\n"
255         "\t-k\ttake input from kldstat(8)\n"
256         "\t-M\tspecify core name for kvm(3)\n"
257         "\t-N\tspecify system name for kvm(3)\n"
258         "\t-o\tuse outfile instead of \".asf\"\n"
259         "\t-s\tdon't prepend subdir for module path\n"
260         "\t-V\tuse kvm(3) to get the list of modules\n"
261         "\t-X\tappend suffix to list of possible module file name suffixes\n"
262         "\t-x\tclear list of possible module file name suffixes\n",
263         myname, (int)strlen(myname), "");
264     exit(2);
265 }
266
267 #define MAXPATHS        15
268 #define MAXSUFFIXES     15
269
270 /* KLD file names end in this */
271 static int         nsuffixes = 2;
272 static const char *suffixes[MAXSUFFIXES + 1] = {
273     ".debug",
274     ".symbols",
275     NULL
276 };
277
278 int
279 main(int argc, char *argv[])
280 {
281     char basename[PATH_MAX];
282     char path[PATH_MAX];
283     char *modules_argv[MAXPATHS + 1];
284     char *copy, *p;
285     char **ap;
286     const char *filemode = "w";         /* mode for outfile */
287     const char *modules_path = "modules"; /* path to kernel build directory */
288     const char *outfile = ".asf";       /* and where to write the output */
289     const char *corefile = NULL;        /* for kvm(3) */
290     const char *sysfile = NULL;         /* for kvm(3) */
291     const char **sfx;
292     struct kfile *kfp;
293     struct stat st;
294     FILE *out;                          /* output file */
295     int dofind = 0;
296     int dokld = 0;
297     int dokvm = 0;
298     int nosubdir = 0;
299     int runprog = 0;
300     int i;
301     const int sl = strlen(KLDSUFFIX);
302
303     while ((i = getopt(argc, argv, "afKkM:N:o:sVX:x")) != -1)
304         switch (i) {
305         case 'a':
306             filemode = "a";     /* append to outfile */
307             break;
308         case 'f':
309             dofind = 1;         /* find .ko (recursively) */
310             break;
311         case 'K':
312             dokld = 1;          /* use kld(2) interface */
313             break;
314         case 'k':
315             runprog = 1;        /* get input from kldstat(8) */
316             break;
317         case 'M':
318             corefile = optarg;  /* core file for kvm(3) */
319             break;
320         case 'N':
321             sysfile = optarg;   /* system file (kernel) for kvm(3) */
322             break;
323         case 'o':
324             outfile = optarg;   /* output file name */
325             break;
326         case 's':
327             nosubdir = 1;       /* don't descend into subdirs */
328             break;
329         case 'V':
330             dokvm = 1;          /* use kvm(3) interface */
331             break;
332         case 'X':
333             if (nsuffixes >= MAXSUFFIXES)
334                 errx(2, "only %d suffixes can be specified", MAXSUFFIXES);
335             suffixes[nsuffixes++] = optarg;
336             suffixes[nsuffixes] = NULL;
337             break;
338         case 'x':
339             nsuffixes = 0;
340             suffixes[0] = NULL;
341             break;
342         default:
343             usage(argv[0]);
344         }
345
346     argc -= optind;
347     argv += optind;
348
349     if (argc > 0) {
350         modules_path = argv[0];
351         argc--, argv++;
352     }
353     if (argc > 0) {
354         outfile = argv[0];
355         argc--, argv++;
356     }
357     if (argc > 0)
358         usage(argv[0]);
359
360     if (strcmp(outfile, "-") == 0)
361         out = stdout;
362     else
363         if ((out = fopen(outfile, filemode)) == NULL)
364             err(2, "can't open output file %s", outfile);
365
366     if (dokvm || corefile || sysfile) {
367         if (dokld || runprog)
368             warnx("using kvm(3) instead");
369         asf_kvm(sysfile, corefile);
370     } else if (dokld) {
371         if (runprog)
372             warnx("using kld(2) instead");
373         asf_kld();
374     } else
375         asf_prog(runprog);
376
377     /* Avoid long operations like module tree traversal when nothing to do */
378     if (kfile_empty()) {
379         warnx("no kernel modules loaded");
380         return (0);
381     }
382
383     if ((copy = strdup(modules_path)) == NULL)
384         errx(2, "out of memory");
385     for (
386         ap = modules_argv, p = copy;
387         (*ap = strsep(&p, ";")) != NULL && ap < &modules_argv[MAXPATHS];
388         ap++
389     );
390     if (*ap)
391         errx(2, "only %d module path elements can be specified", MAXPATHS);
392
393     if (!dofind)
394         STAILQ_FOREACH(kfp, &kfile_head, link) {
395             for (ap = modules_argv; *ap; ap++) {
396                 if (!nosubdir) {
397                     /* prepare basename of KLD, w/o suffix */
398                     strlcpy(basename, kfp->name, sizeof(basename) - 1);
399                     i = strlen(basename);
400                     if (i > sl && strcmp(basename + i - sl, KLDSUFFIX) == 0)
401                         i -= sl;
402                     basename[i] = '/';
403                     basename[i + 1] = '\0';
404                 }
405                 for (sfx = suffixes;; sfx++) {
406                     snprintf(path, sizeof(path),
407                              "%s/%s%s%s",
408                              *ap,
409                              nosubdir ? "" : basename,
410                              kfp->name,
411                              *sfx ? *sfx : "");
412                     if (stat(path, &st) == 0) {
413                         doobj(path, kfp->addr, out);
414                         goto found;
415                     }
416                     if (*sfx == NULL)
417                         break;
418                 }
419             }
420             warnx("module %s not found in search path", kfp->name);
421 found:
422             ;
423         }
424     else
425         findmodules(modules_argv, suffixes, out);
426
427     free(copy);
428     return (0);
429 }