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 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 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 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 * 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