1 /*        $NetBSD: main.c,v 1.30 2022/08/21 07:46:52 mlelstv Exp $ */
2 
3 /*
4  * Copyright (c) 2002, 2003, 2020 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Andrew Brown.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __RCSID("$NetBSD: main.c,v 1.30 2022/08/21 07:46:52 mlelstv Exp $");
35 #endif
36 
37 #include <sys/param.h>
38 
39 #ifndef __NetBSD_Version__
40 #error go away, you fool
41 #elif (__NetBSD_Version__ < 105000000)
42 #error only works with uvm
43 #endif
44 
45 #include <fcntl.h>
46 #include <errno.h>
47 #include <unistd.h>
48 #include <limits.h>
49 #include <string.h>
50 #include <signal.h>
51 #include <util.h>
52 
53 #include "pmap.h"
54 #include "main.h"
55 
56 struct cache_head lcache;
57 void *uvm_vnodeops, *uvm_deviceops, *aobj_pager, *ubc_pager;
58 struct vm_map *kmem_map, *phys_map, *exec_map, *pager_map;
59 struct vm_map *st_map, *pt_map, *module_map, *buf_map;
60 u_long kernel_map_addr;
61 int debug, verbose, recurse, page_size;
62 int print_all, print_map, print_maps, print_solaris, print_ddb;
63 int tree;
64 rlim_t maxssiz;
65 
66 struct nlist ksyms[] = {
67           { "_maxsmap", 0, 0, 0, 0 },
68 #define NL_MAXSSIZ            0
69           { "_uvm_vnodeops", 0, 0, 0, 0 },
70 #define NL_UVM_VNODEOPS                 1
71           { "_uvm_deviceops", 0, 0, 0, 0 },
72 #define NL_UVM_DEVICEOPS      2
73           { "_aobj_pager", 0, 0, 0, 0 },
74 #define NL_AOBJ_PAGER                   3
75           { "_ubc_pager", 0, 0, 0, 0 },
76 #define NL_UBC_PAGER                    4
77           { "_kernel_map", 0, 0, 0, 0 },
78 #define NL_KERNEL_MAP                   5
79           { NULL, 0, 0, 0, 0 }
80 };
81 
82 struct nlist kmaps[] = {
83           { "_kmem_map", 0, 0, 0, 0 },
84 #define NL_kmem_map           0
85           { "_phys_map", 0, 0, 0, 0 },
86 #define NL_phys_map           1
87           { "_exec_map", 0, 0, 0, 0 },
88 #define NL_exec_map           2
89           { "_pager_map", 0, 0, 0, 0 },
90 #define NL_pager_map                    3
91           { "_st_map", 0, 0, 0, 0 },
92 #define NL_st_map             4
93           { "_pt_map", 0, 0, 0, 0 },
94 #define NL_pt_map             5
95           { "_module_map", 0, 0, 0, 0 },
96 #define NL_module_map                   6
97           { "_buf_map", 0, 0, 0, 0 },
98 #define NL_buf_map            7
99           { NULL, 0, 0, 0, 0 },
100 };
101 
102 #define VMSPACE_ADDRESS                 1
103 #define VM_MAP_ADDRESS                  2
104 #define VM_MAP_ENTRY_ADDRESS  3
105 #define AMAP_ADDRESS                    4
106 
107 void check_fd(int);
108 void load_symbols(kvm_t *);
109 void cache_enter(u_long, struct namecache *);
110 
111 int
main(int argc,char * argv[])112 main(int argc, char *argv[])
113 {
114           kvm_t *kd;
115           pid_t pid;
116           uid_t uid;
117           int which, many, ch, rc;
118           char errbuf[_POSIX2_LINE_MAX + 1];
119           struct kinfo_proc2 *kproc;
120           char *kmem, *kernel, *t;
121           gid_t egid;
122           struct kbit kbit, *vmspace;
123           u_long address;
124 
125           uid = getuid();
126           egid = getegid();
127           if (setegid(getgid()) == -1)
128                     err(1, "failed to reset privileges");
129 
130           check_fd(STDIN_FILENO);
131           check_fd(STDOUT_FILENO);
132           check_fd(STDERR_FILENO);
133 
134           pid = -1;
135           which = verbose = debug = 0;
136           print_all = print_map = print_maps = print_solaris = print_ddb = 0;
137           tree = 0;
138           recurse = 0;
139           kmem = kernel = NULL;
140           address = 0;
141           vmspace = &kbit;
142 
143           while ((ch = getopt(argc, argv, "A:aD:dE:lM:mN:Pp:RrS:stV:vx")) != -1) {
144                     switch (ch) {
145                     case 'A':
146                     case 'E':
147                     case 'S':
148                     case 'V':
149                               if (which != 0)
150                                         errx(1, "use only one of -A, -E, -S, or -V");
151                               errno = 0;
152                               address = strtoul(optarg, &t, 0);
153                               if (*t != '\0')
154                                         errx(1, "%s is not a valid address", optarg);
155                               if (errno != 0)
156                                         err(1, "%s is not a valid address", optarg);
157                               switch (ch) {
158                               case 'A': which = AMAP_ADDRESS;                   break;
159                               case 'E': which = VM_MAP_ENTRY_ADDRESS; break;
160                               case 'S': which = VMSPACE_ADDRESS;      break;
161                               case 'V': which = VM_MAP_ADDRESS;                 break;
162                               }
163                               break;
164                     case 'a':
165                               print_all = 1;
166                               break;
167                     case 'd':
168                               print_ddb = 1;
169                               break;
170                     case 'D':
171                               errno = 0;
172                               debug = strtoul(optarg, &t, 0);
173                               if (*t != '\0')
174                                         errx(1, "%s is not a valid number", optarg);
175                               if (errno != 0)
176                                         err(1, "%s is not a valid number", optarg);
177                               break;
178                     case 'l':
179                               print_maps = 1;
180                               break;
181                     case 'm':
182                               print_map = 1;
183                               break;
184                     case 'M':
185                               kmem = optarg;
186                               break;
187                     case 'N':
188                               kernel = optarg;
189                               break;
190                     case 'p':
191                               errno = 0;
192                               pid = strtol(optarg, &t, 0);
193                               if (pid < 0)
194                                         errno = EINVAL;
195                               if (*t != '\0')
196                                         errx(1, "%s is not a valid pid", optarg);
197                               if (errno != 0)
198                                         err(1, "%s is not a valid pid", optarg);
199                               break;
200                     case 'P':
201                               pid = getpid();
202                               break;
203                     case 'R':
204                               recurse = 1;
205                               break;
206                     case 's':
207                               print_solaris = 1;
208                               break;
209                     case 't':
210                               tree = 1;
211                               break;
212                     case 'v':
213                               verbose++;
214                               break;
215                     case 'r':
216                     case 'x':
217                               errx(1, "-%c option not implemented, sorry", optopt);
218                               /*NOTREACHED*/
219                     case '?':
220                     default:
221                               fprintf(stderr, "usage: %s [-adlmPRstv] [-A address] "
222                                         "[-D number] [-E address] [-M core]\n"
223                                         "\t[-N system] [-p pid] [-S address] "
224                                         "[-V address] [pid ...]\n",
225                                         getprogname());
226                               exit(1);
227                     }
228           }
229           argc -= optind;
230           argv += optind;
231 
232           /* more than one "process" to dump? */
233           many = (argc > 1 - (pid == -1 ? 0 : 1)) ? 1 : 0;
234 
235           /* apply default */
236           if (print_all + print_map + print_maps + print_solaris +
237               print_ddb == 0)
238                     print_solaris = 1;
239 
240           if ((kernel != NULL || kmem != NULL || address != 0 ||
241                print_ddb || debug) && uid != 0)
242                     errx(1, "one or more options specified is restricted to root");
243 
244           /* get privs back since it appears to be safe. */
245           rc = setegid(egid);
246           if (rc == -1)
247                     err(1, "failed to reset privileges");
248 
249           /* start by opening libkvm */
250           kd = kvm_openfiles(kernel, kmem, NULL, O_RDONLY, errbuf);
251 
252           /* we're completely done with privileges now */
253           rc = setgid(getgid());
254           if (rc == -1)
255                     err(1, "failed to reset privileges");
256 
257           /* print the kvm_open error, if any */
258           errbuf[_POSIX2_LINE_MAX] = '\0';
259           if (kd == NULL)
260                     errx(1, "%s", errbuf);
261 
262           /* get "bootstrap" addresses from kernel */
263           load_symbols(kd);
264 
265           if (address) {
266                     struct kbit kbit2, *at = &kbit2;
267 
268                     memset(vmspace, 0, sizeof(*vmspace));
269                     A(at) = address;
270                     S(at) = (size_t)-1;
271 
272                     switch (which) {
273                         case VMSPACE_ADDRESS:
274                               /* (kd, kproc, vmspace, thing) */
275                               (*process_map)(kd, NULL, at, "vm_map");
276                               break;
277                         case VM_MAP_ADDRESS:
278                               /* (kd, proc, vmspace, vm_map, thing) */
279                               (*dump_vm_map)(kd, NULL, vmspace, at, "vm_map");
280                               break;
281                         case VM_MAP_ENTRY_ADDRESS:
282                               /* (kd, proc, vmspace, vm_map_entry, 0) */
283                               (*dump_vm_map_entry)(kd, NULL, vmspace, at, 0);
284                               break;
285                         case AMAP_ADDRESS:
286                               /* (kd, amap) */
287                               (*dump_amap)(kd, at);
288                               break;
289                     }
290                     exit(0);
291           }
292 
293           do {
294                     if (pid == -1) {
295                               if (argc == 0)
296                                         pid = getppid();
297                               else {
298                                         errno = 0;
299                                         pid = strtol(argv[0], &t, 0);
300                                         if (pid < 0)
301                                                   errno = EINVAL;
302                                         if (*t != '\0')
303                                                   errx(1, "%s is not a valid pid",
304                                                       argv[0]);
305                                         if (errno != 0)
306                                                   err(1, "%s is not a valid pid",
307                                                       argv[0]);
308                                         argv++;
309                                         argc--;
310                               }
311                     }
312 
313                     errno = 0;
314                     /* find the process id */
315                     if (pid == 0) {
316                               kproc = NULL;
317                               if (uid != 0) {
318                                         /* only root can print kernel mappings */
319                                         errno = EPERM;
320                               }
321                     } else {
322                               kproc = kvm_getproc2(kd, KERN_PROC_PID, pid,
323                                   sizeof(struct kinfo_proc2), &rc);
324                               if (kproc == NULL || rc == 0) {
325                                         errno = ESRCH;
326                               } else if (uid != 0 && uid != kproc->p_uid) {
327                                         /*
328                                          * only the real owner of the process and
329                                          * root can print process mappings
330                                          */
331                                         errno = EPERM;
332                               }
333                     }
334 
335                     if (errno != 0) {
336                               warn("%d", pid);
337                               pid = -1;
338                               continue;
339                     }
340 
341                     /* dump it */
342                     if (many) {
343                               if (kproc != NULL)
344                                         printf("process %d:\n", kproc->p_pid);
345                               else
346                                         printf("kernel:\n");
347                     }
348 
349                     (*process_map)(kd, kproc, vmspace, NULL);
350                     pid = -1;
351           } while (argc > 0);
352 
353           /* done.  go away. */
354           rc = kvm_close(kd);
355           if (rc == -1)
356                     err(1, "kvm_close");
357 
358           return (0);
359 }
360 
361 void
check_fd(int fd)362 check_fd(int fd)
363 {
364           struct stat st;
365           int n;
366 
367           if (fstat(fd, &st) == -1) {
368                     (void)close(fd);
369                     n = open("/dev/null", O_RDWR);
370                     if (n == fd || n == -1)
371                               /* we're either done or we can do no more */
372                               return;
373                     /* if either of these fail, there's not much we can do */
374                     (void)dup2(n, fd);
375                     (void)close(n);
376                     /* XXX should we exit if it fails? */
377           }
378 }
379 
380 void
load_symbols(kvm_t * kd)381 load_symbols(kvm_t *kd)
382 {
383           int rc, i, mib[2];
384           size_t sz;
385 
386           rc = kvm_nlist(kd, &ksyms[0]);
387           if (rc != 0) {
388                     for (i = 0; ksyms[i].n_name != NULL; i++)
389                               if (ksyms[i].n_value == 0)
390                                         warnx("symbol %s: not found", ksyms[i].n_name);
391                     exit(1);
392           }
393 
394           uvm_vnodeops =      (void*)ksyms[NL_UVM_VNODEOPS].n_value;
395           uvm_deviceops =     (void*)ksyms[NL_UVM_DEVICEOPS].n_value;
396           aobj_pager =        (void*)ksyms[NL_AOBJ_PAGER].n_value;
397           ubc_pager =         (void*)ksyms[NL_UBC_PAGER].n_value;
398 
399           _KDEREF(kd, ksyms[NL_MAXSSIZ].n_value, &maxssiz,
400                     sizeof(maxssiz));
401           _KDEREF(kd, ksyms[NL_KERNEL_MAP].n_value, &kernel_map_addr,
402                     sizeof(kernel_map_addr));
403 
404           /*
405            * Some of these may be missing from some platforms, for
406            * example sparc, sh3, and most powerpc platforms don't
407            * have a "phys_map", etc.
408            */
409           (void)kvm_nlist(kd, &kmaps[0]);
410 
411 #define get_map_address(m) do {\
412           if (kmaps[__CONCAT(NL_,m)].n_value != 0) \
413                     _KDEREF(kd, kmaps[__CONCAT(NL_,m)].n_value, &m, sizeof(m)); \
414           } while (0/*CONSTCOND*/)
415 
416           get_map_address(kmem_map);
417           get_map_address(phys_map);
418           get_map_address(exec_map);
419           get_map_address(pager_map);
420           get_map_address(st_map);
421           get_map_address(pt_map);
422           get_map_address(module_map);
423           get_map_address(buf_map);
424 
425           mib[0] = CTL_HW;
426           mib[1] = HW_PAGESIZE;
427           sz = sizeof(page_size);
428           if (sysctl(&mib[0], 2, &page_size, &sz, NULL, 0) == -1)
429                     err(1, "sysctl: hw.pagesize");
430 }
431 
432 const char *
mapname(void * addr)433 mapname(void *addr)
434 {
435 
436           if (addr == (void*)kernel_map_addr)
437                     return ("kernel_map");
438           else if (addr == kmem_map)
439                     return ("kmem_map");
440           else if (addr == phys_map)
441                     return ("phys_map");
442           else if (addr == exec_map)
443                     return ("exec_map");
444           else if (addr == pager_map)
445                     return ("pager_map");
446           else if (addr == st_map)
447                     return ("st_map");
448           else if (addr == pt_map)
449                     return ("pt_map");
450           else if (addr == module_map)
451                     return ("module_map");
452           else if (addr == buf_map)
453                     return ("buf_map");
454           else
455                     return (NULL);
456 }
457