main.c revision 1.25       1 /*	$NetBSD: main.c,v 1.25 2015/12/13 18:09:00 christos Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2002, 2003 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.25 2015/12/13 18:09:00 christos 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 
     52 #include "pmap.h"
     53 #include "main.h"
     54 
     55 struct cache_head lcache;
     56 struct nchashhead *nchashtbl;
     57 void *uvm_vnodeops, *uvm_deviceops, *aobj_pager, *ubc_pager;
     58 struct vm_map *kmem_map, *mb_map, *phys_map, *exec_map, *pager_map;
     59 struct vm_map *st_map, *pt_map, *lkm_map, *buf_map;
     60 u_long nchash_addr, nchashtbl_addr, kernel_map_addr;
     61 int debug, verbose, recurse, page_size;
     62 int print_all, print_map, print_maps, print_solaris, print_ddb;
     63 rlim_t maxssiz;
     64 
     65 struct nlist ksyms[] = {
     66 	{ "_maxsmap", 0, 0, 0, 0 },
     67 #define NL_MAXSSIZ		0
     68 	{ "_uvm_vnodeops", 0, 0, 0, 0 },
     69 #define NL_UVM_VNODEOPS		1
     70 	{ "_uvm_deviceops", 0, 0, 0, 0 },
     71 #define NL_UVM_DEVICEOPS	2
     72 	{ "_aobj_pager", 0, 0, 0, 0 },
     73 #define NL_AOBJ_PAGER		3
     74 	{ "_ubc_pager", 0, 0, 0, 0 },
     75 #define NL_UBC_PAGER		4
     76 	{ "_kernel_map", 0, 0, 0, 0 },
     77 #define NL_KERNEL_MAP		5
     78 	{ "_nchashtbl", 0, 0, 0, 0 },
     79 #define NL_NCHASHTBL		6
     80 	{ "_nchash", 0, 0, 0, 0 },
     81 #define NL_NCHASH		7
     82 	{ NULL, 0, 0, 0, 0 }
     83 };
     84 
     85 struct nlist kmaps[] = {
     86 	{ "_kmem_map", 0, 0, 0, 0 },
     87 #define NL_kmem_map		0
     88 	{ "_mb_map", 0, 0, 0, 0 },
     89 #define NL_mb_map		1
     90 	{ "_phys_map", 0, 0, 0, 0 },
     91 #define NL_phys_map		2
     92 	{ "_exec_map", 0, 0, 0, 0 },
     93 #define NL_exec_map		3
     94 	{ "_pager_map", 0, 0, 0, 0 },
     95 #define NL_pager_map		4
     96 	{ "_st_map", 0, 0, 0, 0 },
     97 #define NL_st_map		5
     98 	{ "_pt_map", 0, 0, 0, 0 },
     99 #define NL_pt_map		6
    100 	{ "_lkm_map", 0, 0, 0, 0 },
    101 #define NL_lkm_map		7
    102 	{ "_buf_map", 0, 0, 0, 0 },
    103 #define NL_buf_map		8
    104 	{ NULL, 0, 0, 0, 0 },
    105 };
    106 
    107 #define VMSPACE_ADDRESS		1
    108 #define VM_MAP_ADDRESS		2
    109 #define VM_MAP_ENTRY_ADDRESS	3
    110 #define AMAP_ADDRESS		4
    111 
    112 void check_fd(int);
    113 void load_symbols(kvm_t *);
    114 void cache_enter(u_long, struct namecache *);
    115 
    116 int
    117 main(int argc, char *argv[])
    118 {
    119 	kvm_t *kd;
    120 	pid_t pid;
    121 	uid_t uid;
    122 	int which, many, ch, rc;
    123 	char errbuf[_POSIX2_LINE_MAX + 1];
    124 	struct kinfo_proc2 *kproc;
    125 	char *kmem, *kernel, *t;
    126 	gid_t egid;
    127 	struct kbit kbit, *vmspace;
    128 	u_long address;
    129 
    130 	egid = getegid();
    131 	if (setegid(getgid()) == -1)
    132 		err(1, "failed to reset privileges");
    133 
    134 	check_fd(STDIN_FILENO);
    135 	check_fd(STDOUT_FILENO);
    136 	check_fd(STDERR_FILENO);
    137 
    138 	pid = -1;
    139 	which = verbose = debug = 0;
    140 	print_all = print_map = print_maps = print_solaris = print_ddb = 0;
    141 	recurse = 0;
    142 	kmem = kernel = NULL;
    143 	address = 0;
    144 	vmspace = &kbit;
    145 
    146 	while ((ch = getopt(argc, argv, "A:aD:dE:lM:mN:Pp:RrS:sV:vx")) != -1) {
    147 		switch (ch) {
    148 		case 'A':
    149 		case 'E':
    150 		case 'S':
    151 		case 'V':
    152 			if (which != 0)
    153 				errx(1, "use only one of -A, -E, -S, or -V");
    154 			errno = 0;
    155 			address = strtoul(optarg, &t, 0);
    156 			if (*t != '\0')
    157 				errx(1, "%s is not a valid address", optarg);
    158 			if (errno != 0)
    159 				err(1, "%s is not a valid address", optarg);
    160 			switch (ch) {
    161 			case 'A':	which = AMAP_ADDRESS;		break;
    162 			case 'E':	which = VM_MAP_ENTRY_ADDRESS;	break;
    163 			case 'S':	which = VMSPACE_ADDRESS;	break;
    164 			case 'V':	which = VM_MAP_ADDRESS;		break;
    165 			}
    166 			break;
    167 		case 'a':
    168 			print_all = 1;
    169 			break;
    170 		case 'd':
    171 			print_ddb = 1;
    172 			break;
    173 		case 'D':
    174 			errno = 0;
    175 			debug = strtoul(optarg, &t, 0);
    176 			if (*t != '\0')
    177 				errx(1, "%s is not a valid number", optarg);
    178 			if (errno != 0)
    179 				err(1, "%s is not a valid number", optarg);
    180 			break;
    181 		case 'l':
    182 			print_maps = 1;
    183 			break;
    184 		case 'm':
    185 			print_map = 1;
    186 			break;
    187 		case 'M':
    188 			kmem = optarg;
    189 			break;
    190 		case 'N':
    191 			kernel = optarg;
    192 			break;
    193 		case 'p':
    194 			errno = 0;
    195 			pid = strtol(optarg, &t, 0);
    196 			if (pid < 0)
    197 				errno = EINVAL;
    198 			if (*t != '\0')
    199 				errx(1, "%s is not a valid pid", optarg);
    200 			if (errno != 0)
    201 				err(1, "%s is not a valid pid", optarg);
    202 			break;
    203 		case 'P':
    204 			pid = getpid();
    205 			break;
    206 		case 'R':
    207 			recurse = 1;
    208 			break;
    209 		case 's':
    210 			print_solaris = 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 [-adlmPRsv] [-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 	/* get privs back if it appears to be safe, otherwise toss them */
    241 	if (kernel == NULL && kmem == NULL && address == 0)
    242 		rc = setegid(egid);
    243 	else
    244 		rc = setgid(getgid());
    245 	if (rc == -1)
    246 		err(1, "failed to reset privileges");
    247 
    248 	/* start by opening libkvm */
    249 	kd = kvm_openfiles(kernel, kmem, NULL, O_RDONLY, errbuf);
    250 
    251 	/* we're completely done with privileges now */
    252 	rc = setgid(getgid());
    253 	if (rc == -1)
    254 		err(1, "failed to reset privileges");
    255 
    256 	/* print the kvm_open error, if any */
    257 	errbuf[_POSIX2_LINE_MAX] = '\0';
    258 	if (kd == NULL)
    259 		errx(1, "%s", errbuf);
    260 
    261 	/* get "bootstrap" addresses from kernel */
    262 	load_symbols(kd);
    263 
    264 	if (address) {
    265 		struct kbit kbit2, *at = &kbit2;
    266 
    267 		memset(vmspace, 0, sizeof(*vmspace));
    268 		A(at) = address;
    269 		S(at) = (size_t)-1;
    270 
    271 		switch (which) {
    272 		    case VMSPACE_ADDRESS:
    273 			/* (kd, kproc, vmspace, thing) */
    274 			(*process_map)(kd, NULL, at, "vm_map");
    275 			break;
    276 		    case VM_MAP_ADDRESS:
    277 			/* (kd, proc, vmspace, vm_map, thing) */
    278 			(*dump_vm_map)(kd, NULL, vmspace, at, "vm_map");
    279 			break;
    280 		    case VM_MAP_ENTRY_ADDRESS:
    281 			/* (kd, proc, vmspace, vm_map_entry, 0) */
    282 			(*dump_vm_map_entry)(kd, NULL, vmspace, at, 0);
    283 			break;
    284 		    case AMAP_ADDRESS:
    285 			/* (kd, amap) */
    286 			(*dump_amap)(kd, at);
    287 			break;
    288 		}
    289 		exit(0);
    290 	}
    291 
    292 	uid = getuid();
    293 
    294 	do {
    295 		if (pid == -1) {
    296 			if (argc == 0)
    297 				pid = getppid();
    298 			else {
    299 				errno = 0;
    300 				pid = strtol(argv[0], &t, 0);
    301 				if (pid < 0)
    302 					errno = EINVAL;
    303 				if (*t != '\0')
    304 					errx(1, "%s is not a valid pid",
    305 					    argv[0]);
    306 				if (errno != 0)
    307 					err(1, "%s is not a valid pid",
    308 					    argv[0]);
    309 				argv++;
    310 				argc--;
    311 			}
    312 		}
    313 
    314 		errno = 0;
    315 		/* find the process id */
    316 		if (pid == 0) {
    317 			kproc = NULL;
    318 			if (uid != 0) {
    319 				/* only root can print kernel mappings */
    320 				errno = EPERM;
    321 			}
    322 		} else {
    323 			kproc = kvm_getproc2(kd, KERN_PROC_PID, pid,
    324 			    sizeof(struct kinfo_proc2), &rc);
    325 			if (kproc == NULL || rc == 0) {
    326 				errno = ESRCH;
    327 			} else if (uid != 0 && uid != kproc->p_uid) {
    328 				/*
    329 				 * only the real owner of the process and
    330 				 * root can print process mappings
    331 				 */
    332 				errno = EPERM;
    333 			}
    334 		}
    335 
    336 		if (errno != 0) {
    337 			warn("%d", pid);
    338 			pid = -1;
    339 			continue;
    340 		}
    341 
    342 		/* dump it */
    343 		if (many) {
    344 			if (kproc != NULL)
    345 				printf("process %d:\n", kproc->p_pid);
    346 			else
    347 				printf("kernel:\n");
    348 		}
    349 
    350 		(*process_map)(kd, kproc, vmspace, NULL);
    351 		pid = -1;
    352 	} while (argc > 0);
    353 
    354 	/* done.  go away. */
    355 	rc = kvm_close(kd);
    356 	if (rc == -1)
    357 		err(1, "kvm_close");
    358 
    359 	return (0);
    360 }
    361 
    362 void
    363 check_fd(int fd)
    364 {
    365 	struct stat st;
    366 	int n;
    367 
    368 	if (fstat(fd, &st) == -1) {
    369 		(void)close(fd);
    370 		n = open("/dev/null", O_RDWR);
    371 		if (n == fd || n == -1)
    372 			/* we're either done or we can do no more */
    373 			return;
    374 		/* if either of these fail, there's not much we can do */
    375 		(void)dup2(n, fd);
    376 		(void)close(n);
    377 		/* XXX should we exit if it fails? */
    378 	}
    379 }
    380 
    381 void
    382 load_symbols(kvm_t *kd)
    383 {
    384 	int rc, i, mib[2];
    385 	size_t sz;
    386 
    387 	rc = kvm_nlist(kd, &ksyms[0]);
    388 	if (rc != 0) {
    389 		for (i = 0; ksyms[i].n_name != NULL; i++)
    390 			if (ksyms[i].n_value == 0)
    391 				warnx("symbol %s: not found", ksyms[i].n_name);
    392 		exit(1);
    393 	}
    394 
    395 	uvm_vnodeops =	(void*)ksyms[NL_UVM_VNODEOPS].n_value;
    396 	uvm_deviceops =	(void*)ksyms[NL_UVM_DEVICEOPS].n_value;
    397 	aobj_pager =	(void*)ksyms[NL_AOBJ_PAGER].n_value;
    398 	ubc_pager =	(void*)ksyms[NL_UBC_PAGER].n_value;
    399 
    400 	nchash_addr =	ksyms[NL_NCHASH].n_value;
    401 
    402 	_KDEREF(kd, ksyms[NL_MAXSSIZ].n_value, &maxssiz,
    403 		sizeof(maxssiz));
    404 	_KDEREF(kd, ksyms[NL_NCHASHTBL].n_value, &nchashtbl_addr,
    405 	       sizeof(nchashtbl_addr));
    406 	_KDEREF(kd, ksyms[NL_KERNEL_MAP].n_value, &kernel_map_addr,
    407 		sizeof(kernel_map_addr));
    408 
    409 	/*
    410 	 * Some of these may be missing from some platforms, for
    411 	 * example sparc, sh3, and most powerpc platforms don't
    412 	 * have a "phys_map", etc.
    413 	 */
    414 	(void)kvm_nlist(kd, &kmaps[0]);
    415 
    416 #define get_map_address(m) do {\
    417 	if (kmaps[__CONCAT(NL_,m)].n_value != 0) \
    418 		_KDEREF(kd, kmaps[__CONCAT(NL_,m)].n_value, &m, sizeof(m)); \
    419 	} while (0/*CONSTCOND*/)
    420 
    421 	get_map_address(kmem_map);
    422 	get_map_address(mb_map);
    423 	get_map_address(phys_map);
    424 	get_map_address(exec_map);
    425 	get_map_address(pager_map);
    426 	get_map_address(st_map);
    427 	get_map_address(pt_map);
    428 	get_map_address(lkm_map);
    429 	get_map_address(buf_map);
    430 
    431 	mib[0] = CTL_HW;
    432 	mib[1] = HW_PAGESIZE;
    433 	sz = sizeof(page_size);
    434 	if (sysctl(&mib[0], 2, &page_size, &sz, NULL, 0) == -1)
    435 		err(1, "sysctl: hw.pagesize");
    436 }
    437 
    438 const char *
    439 mapname(void *addr)
    440 {
    441 
    442 	if (addr == (void*)kernel_map_addr)
    443 		return ("kernel_map");
    444 	else if (addr == kmem_map)
    445 		return ("kmem_map");
    446 	else if (addr == mb_map)
    447 		return ("mb_map");
    448 	else if (addr == phys_map)
    449 		return ("phys_map");
    450 	else if (addr == exec_map)
    451 		return ("exec_map");
    452 	else if (addr == pager_map)
    453 		return ("pager_map");
    454 	else if (addr == st_map)
    455 		return ("st_map");
    456 	else if (addr == pt_map)
    457 		return ("pt_map");
    458 	else if (addr == lkm_map)
    459 		return ("lkm_map");
    460 	else if (addr == buf_map)
    461 		return ("buf_map");
    462 	else
    463 		return (NULL);
    464 }
    465 
    466 void
    467 load_name_cache(kvm_t *kd)
    468 {
    469 	struct namecache _ncp, *ncp, *oncp;
    470 	struct nchashhead _ncpp, *ncpp;
    471 	u_long lnchash;
    472 	size_t nchash, i;
    473 
    474 	LIST_INIT(&lcache);
    475 
    476 	_KDEREF(kd, nchash_addr, &lnchash, sizeof(lnchash));
    477 	nchash = (size_t)lnchash + 1;
    478 	nchashtbl = malloc(nchash * sizeof(*nchashtbl));
    479 	_KDEREF(kd, nchashtbl_addr, nchashtbl, sizeof(*nchashtbl) * nchash);
    480 
    481 	ncpp = &_ncpp;
    482 
    483 	for (i = 0; i < nchash; i++) {
    484 		ncpp = &nchashtbl[i];
    485 		oncp = NULL;
    486 		LIST_FOREACH(ncp, ncpp, nc_hash) {
    487 			if (ncp == oncp ||
    488 			    ncp == (void*)0xdeadbeef)
    489 				break;
    490 			oncp = ncp;
    491 			_KDEREF(kd, (u_long)ncp, &_ncp, sizeof(*ncp));
    492 			ncp = &_ncp;
    493 			if (ncp->nc_nlen > 0) {
    494 				if (ncp->nc_nlen > 2 ||
    495 				    ncp->nc_name[0] != '.' ||
    496 				    (ncp->nc_name[1] != '.' &&
    497 				     ncp->nc_nlen != 1))
    498 					cache_enter(i, ncp);
    499 			}
    500 		}
    501 	}
    502 }
    503 
    504 void
    505 cache_enter(u_long i, struct namecache *ncp)
    506 {
    507 	struct cache_entry *ce;
    508 
    509 	if (debug & DUMP_NAMEI_CACHE)
    510 		printf("[%lu] ncp->nc_vp %10p, ncp->nc_dvp %10p, "
    511 		       "ncp->nc_nlen %3d [%.*s]\n",
    512 		       i, ncp->nc_vp, ncp->nc_dvp,
    513 		       ncp->nc_nlen, ncp->nc_nlen, ncp->nc_name);
    514 
    515 	ce = malloc(sizeof(struct cache_entry));
    516 
    517 	ce->ce_vp = ncp->nc_vp;
    518 	ce->ce_pvp = ncp->nc_dvp;
    519 	ce->ce_nlen = ncp->nc_nlen;
    520 	strncpy(ce->ce_name, ncp->nc_name, sizeof(ce->ce_name));
    521 	ce->ce_name[MIN(ce->ce_nlen, (int)(sizeof(ce->ce_name) - 1))] = '\0';
    522 
    523 	LIST_INSERT_HEAD(&lcache, ce, ce_next);
    524 }
    525