1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org> 5 * Copyright (c) 2017 Dell EMC 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/elf.h> 32 #include <sys/exec.h> 33 #include <sys/ptrace.h> 34 #include <sys/user.h> 35 36 #include <assert.h> 37 #include <err.h> 38 #include <fcntl.h> 39 #include <gelf.h> 40 #include <libelf.h> 41 #include <stdbool.h> 42 #include <stdint.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 48 #include "core.h" 49 50 #define PROCSTAT_CORE_MAGIC 0x012DADB8 51 struct procstat_core 52 { 53 int pc_magic; 54 int pc_fd; 55 Elf *pc_elf; 56 GElf_Ehdr pc_ehdr; 57 GElf_Phdr pc_phdr; 58 }; 59 60 static const struct psc_type_info { 61 unsigned int n_type; 62 int structsize; 63 } psc_type_info[PSC_TYPE_MAX] = { 64 [PSC_TYPE_PROC] = { 65 .n_type = NT_PROCSTAT_PROC, 66 .structsize = sizeof(struct kinfo_proc) 67 }, 68 [PSC_TYPE_FILES] = { 69 .n_type = NT_PROCSTAT_FILES, 70 .structsize = sizeof(struct kinfo_file) 71 }, 72 [PSC_TYPE_VMMAP] = { 73 .n_type = NT_PROCSTAT_VMMAP, 74 .structsize = sizeof(struct kinfo_vmentry) 75 }, 76 [PSC_TYPE_GROUPS] = { 77 .n_type = NT_PROCSTAT_GROUPS, 78 .structsize = sizeof(gid_t) 79 }, 80 [PSC_TYPE_UMASK] = { 81 .n_type = NT_PROCSTAT_UMASK, 82 .structsize = sizeof(u_short) 83 }, 84 [PSC_TYPE_RLIMIT] = { 85 .n_type = NT_PROCSTAT_RLIMIT, 86 .structsize = sizeof(struct rlimit) * RLIM_NLIMITS 87 }, 88 [PSC_TYPE_OSREL] = { 89 .n_type = NT_PROCSTAT_OSREL, 90 .structsize = sizeof(int) 91 }, 92 [PSC_TYPE_PSSTRINGS] = { 93 .n_type = NT_PROCSTAT_PSSTRINGS, 94 .structsize = sizeof(vm_offset_t) 95 }, 96 [PSC_TYPE_ARGV] = { 97 .n_type = NT_PROCSTAT_PSSTRINGS, 98 .structsize = sizeof(vm_offset_t) 99 }, 100 [PSC_TYPE_ENVV] = { 101 .n_type = NT_PROCSTAT_PSSTRINGS, 102 .structsize = sizeof(vm_offset_t) 103 }, 104 [PSC_TYPE_AUXV] = { 105 .n_type = NT_PROCSTAT_AUXV, 106 .structsize = sizeof(Elf_Auxinfo) 107 }, 108 [PSC_TYPE_PTLWPINFO] = { 109 .n_type = NT_PTLWPINFO, 110 .structsize = sizeof(struct ptrace_lwpinfo) 111 }, 112 [PSC_TYPE_KQUEUES] = { 113 .n_type = NT_PROCSTAT_KQUEUES, 114 .structsize = sizeof(struct kinfo_knote) 115 }, 116 }; 117 118 static bool core_offset(struct procstat_core *core, off_t offset); 119 static bool core_read(struct procstat_core *core, void *buf, size_t len); 120 static ssize_t core_read_mem(struct procstat_core *core, void *buf, 121 size_t len, vm_offset_t addr, bool readall); 122 static void *get_args(struct procstat_core *core, vm_offset_t psstrings, 123 enum psc_type type, void *buf, size_t *lenp); 124 125 struct procstat_core * 126 procstat_core_open(const char *filename) 127 { 128 struct procstat_core *core; 129 Elf *e; 130 GElf_Ehdr ehdr; 131 GElf_Phdr phdr; 132 size_t nph; 133 int fd, i; 134 135 if (elf_version(EV_CURRENT) == EV_NONE) { 136 warnx("ELF library too old"); 137 return (NULL); 138 } 139 fd = open(filename, O_RDONLY, 0); 140 if (fd == -1) { 141 warn("open(%s)", filename); 142 return (NULL); 143 } 144 e = elf_begin(fd, ELF_C_READ, NULL); 145 if (e == NULL) { 146 warnx("elf_begin: %s", elf_errmsg(-1)); 147 goto fail; 148 } 149 if (elf_kind(e) != ELF_K_ELF) { 150 warnx("%s is not an ELF object", filename); 151 goto fail; 152 } 153 if (gelf_getehdr(e, &ehdr) == NULL) { 154 warnx("gelf_getehdr: %s", elf_errmsg(-1)); 155 goto fail; 156 } 157 if (ehdr.e_type != ET_CORE) { 158 warnx("%s is not a CORE file", filename); 159 goto fail; 160 } 161 if (elf_getphdrnum(e, &nph) == -1) { 162 warnx("program headers not found"); 163 goto fail; 164 } 165 for (i = 0; i < ehdr.e_phnum; i++) { 166 if (gelf_getphdr(e, i, &phdr) != &phdr) { 167 warnx("gelf_getphdr: %s", elf_errmsg(-1)); 168 goto fail; 169 } 170 if (phdr.p_type == PT_NOTE) 171 break; 172 } 173 if (i == ehdr.e_phnum) { 174 warnx("NOTE program header not found"); 175 goto fail; 176 } 177 core = malloc(sizeof(struct procstat_core)); 178 if (core == NULL) { 179 warn("malloc(%zu)", sizeof(struct procstat_core)); 180 goto fail; 181 } 182 core->pc_magic = PROCSTAT_CORE_MAGIC; 183 core->pc_fd = fd; 184 core->pc_elf = e; 185 core->pc_ehdr = ehdr; 186 core->pc_phdr = phdr; 187 188 return (core); 189 fail: 190 if (e != NULL) 191 elf_end(e); 192 close(fd); 193 194 return (NULL); 195 } 196 197 void 198 procstat_core_close(struct procstat_core *core) 199 { 200 201 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 202 203 elf_end(core->pc_elf); 204 close(core->pc_fd); 205 free(core); 206 } 207 208 void * 209 procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf, 210 size_t *lenp) 211 { 212 Elf_Note nhdr; 213 off_t offset, eoffset; 214 vm_offset_t psstrings; 215 void *freebuf; 216 size_t len, curlen; 217 int cstructsize; 218 char nbuf[8]; 219 220 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 221 222 if (type >= PSC_TYPE_MAX) { 223 warnx("unknown core stat type: %d", type); 224 return (NULL); 225 } 226 227 offset = core->pc_phdr.p_offset; 228 eoffset = offset + core->pc_phdr.p_filesz; 229 curlen = 0; 230 231 while (offset < eoffset) { 232 if (!core_offset(core, offset)) 233 return (NULL); 234 if (!core_read(core, &nhdr, sizeof(nhdr))) 235 return (NULL); 236 237 offset += sizeof(nhdr) + 238 roundup2(nhdr.n_namesz, sizeof(Elf32_Size)) + 239 roundup2(nhdr.n_descsz, sizeof(Elf32_Size)); 240 241 if (nhdr.n_namesz == 0 && nhdr.n_descsz == 0) 242 break; 243 if (nhdr.n_type != psc_type_info[type].n_type) 244 continue; 245 if (nhdr.n_namesz != 8) 246 continue; 247 if (!core_read(core, nbuf, sizeof(nbuf))) 248 return (NULL); 249 if (strcmp(nbuf, "FreeBSD") != 0) 250 continue; 251 if (nhdr.n_descsz < sizeof(cstructsize)) { 252 warnx("corrupted core file"); 253 return (NULL); 254 } 255 if (!core_read(core, &cstructsize, sizeof(cstructsize))) 256 return (NULL); 257 if (cstructsize != psc_type_info[type].structsize) { 258 warnx("version mismatch"); 259 return (NULL); 260 } 261 len = nhdr.n_descsz - sizeof(cstructsize); 262 if (len == 0) 263 return (NULL); 264 if (buf != NULL) { 265 len = MIN(len, *lenp); 266 freebuf = NULL; 267 } else { 268 freebuf = buf = malloc(len); 269 if (buf == NULL) { 270 warn("malloc(%zu)", len); 271 return (NULL); 272 } 273 } 274 if (!core_read(core, (char *)buf + curlen, len)) { 275 free(freebuf); 276 return (NULL); 277 } 278 if (type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV) { 279 if (len < sizeof(psstrings)) { 280 free(freebuf); 281 return (NULL); 282 } 283 psstrings = *(vm_offset_t *)buf; 284 if (freebuf == NULL) 285 len = *lenp; 286 else 287 buf = NULL; 288 free(freebuf); 289 buf = get_args(core, psstrings, type, buf, &len); 290 } else if (type == PSC_TYPE_PTLWPINFO) { 291 *lenp -= len; 292 curlen += len; 293 continue; 294 } 295 *lenp = len; 296 return (buf); 297 } 298 299 if (curlen != 0) { 300 *lenp = curlen; 301 return (buf); 302 } 303 304 return (NULL); 305 } 306 307 static bool 308 core_offset(struct procstat_core *core, off_t offset) 309 { 310 311 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 312 313 if (lseek(core->pc_fd, offset, SEEK_SET) == -1) { 314 warn("core: lseek(%jd)", (intmax_t)offset); 315 return (false); 316 } 317 return (true); 318 } 319 320 static bool 321 core_read(struct procstat_core *core, void *buf, size_t len) 322 { 323 ssize_t n; 324 325 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 326 327 n = read(core->pc_fd, buf, len); 328 if (n == -1) { 329 warn("core: read"); 330 return (false); 331 } 332 if (n < (ssize_t)len) { 333 warnx("core: short read"); 334 return (false); 335 } 336 return (true); 337 } 338 339 static ssize_t 340 core_read_mem(struct procstat_core *core, void *buf, size_t len, 341 vm_offset_t addr, bool readall) 342 { 343 GElf_Phdr phdr; 344 off_t offset; 345 int i; 346 347 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 348 349 for (i = 0; i < core->pc_ehdr.e_phnum; i++) { 350 if (gelf_getphdr(core->pc_elf, i, &phdr) != &phdr) { 351 warnx("gelf_getphdr: %s", elf_errmsg(-1)); 352 return (-1); 353 } 354 if (phdr.p_type != PT_LOAD) 355 continue; 356 if (addr < phdr.p_vaddr || addr > phdr.p_vaddr + phdr.p_memsz) 357 continue; 358 offset = phdr.p_offset + (addr - phdr.p_vaddr); 359 if ((phdr.p_vaddr + phdr.p_memsz) - addr < len) { 360 if (readall) { 361 warnx("format error: " 362 "attempt to read out of segment"); 363 return (-1); 364 } 365 len = (phdr.p_vaddr + phdr.p_memsz) - addr; 366 } 367 if (!core_offset(core, offset)) 368 return (-1); 369 if (!core_read(core, buf, len)) 370 return (-1); 371 return (len); 372 } 373 warnx("format error: address %ju not found", (uintmax_t)addr); 374 return (-1); 375 } 376 377 #define ARGS_CHUNK_SZ 256 /* Chunk size (bytes) for get_args operations. */ 378 379 static void * 380 get_args(struct procstat_core *core, vm_offset_t psstrings, enum psc_type type, 381 void *args, size_t *lenp) 382 { 383 struct ps_strings pss; 384 void *freeargs; 385 vm_offset_t addr; 386 char **argv, *p; 387 size_t chunksz, done, len, nchr, size; 388 ssize_t n; 389 u_int i, nstr; 390 391 assert(type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV); 392 393 if (core_read_mem(core, &pss, sizeof(pss), psstrings, true) == -1) 394 return (NULL); 395 if (type == PSC_TYPE_ARGV) { 396 addr = (vm_offset_t)pss.ps_argvstr; 397 nstr = pss.ps_nargvstr; 398 } else /* type == PSC_TYPE_ENVV */ { 399 addr = (vm_offset_t)pss.ps_envstr; 400 nstr = pss.ps_nenvstr; 401 } 402 if (addr == 0 || nstr == 0) 403 return (NULL); 404 if (nstr > ARG_MAX) { 405 warnx("format error"); 406 return (NULL); 407 } 408 size = nstr * sizeof(char *); 409 argv = malloc(size); 410 if (argv == NULL) { 411 warn("malloc(%zu)", size); 412 return (NULL); 413 } 414 done = 0; 415 freeargs = NULL; 416 if (core_read_mem(core, argv, size, addr, true) == -1) 417 goto fail; 418 if (args != NULL) { 419 nchr = MIN(ARG_MAX, *lenp); 420 } else { 421 nchr = ARG_MAX; 422 freeargs = args = malloc(nchr); 423 if (args == NULL) { 424 warn("malloc(%zu)", nchr); 425 goto fail; 426 } 427 } 428 p = args; 429 for (i = 0; ; i++) { 430 if (i == nstr) 431 goto done; 432 /* 433 * The program may have scribbled into its argv array, e.g. to 434 * remove some arguments. If that has happened, break out 435 * before trying to read from NULL. 436 */ 437 if (argv[i] == NULL) 438 goto done; 439 for (addr = (vm_offset_t)argv[i]; ; addr += chunksz) { 440 chunksz = MIN(ARGS_CHUNK_SZ, nchr - 1 - done); 441 if (chunksz <= 0) 442 goto done; 443 n = core_read_mem(core, p, chunksz, addr, false); 444 if (n == -1) 445 goto fail; 446 len = strnlen(p, chunksz); 447 p += len; 448 done += len; 449 if (len != chunksz) 450 break; 451 } 452 *p++ = '\0'; 453 done++; 454 } 455 fail: 456 free(freeargs); 457 args = NULL; 458 done: 459 *lenp = done; 460 free(argv); 461 return (args); 462 } 463 464 int 465 procstat_core_note_count(struct procstat_core *core, enum psc_type type) 466 { 467 Elf_Note nhdr; 468 off_t offset, eoffset; 469 int cstructsize; 470 char nbuf[8]; 471 int n; 472 473 if (type >= PSC_TYPE_MAX) { 474 warnx("unknown core stat type: %d", type); 475 return (0); 476 } 477 478 offset = core->pc_phdr.p_offset; 479 eoffset = offset + core->pc_phdr.p_filesz; 480 481 for (n = 0; offset < eoffset; n++) { 482 if (!core_offset(core, offset)) 483 return (0); 484 if (!core_read(core, &nhdr, sizeof(nhdr))) 485 return (0); 486 487 offset += sizeof(nhdr) + 488 roundup2(nhdr.n_namesz, sizeof(Elf32_Size)) + 489 roundup2(nhdr.n_descsz, sizeof(Elf32_Size)); 490 491 if (nhdr.n_namesz == 0 && nhdr.n_descsz == 0) 492 break; 493 if (nhdr.n_type != psc_type_info[type].n_type) 494 continue; 495 if (nhdr.n_namesz != 8) 496 continue; 497 if (!core_read(core, nbuf, sizeof(nbuf))) 498 return (0); 499 if (strcmp(nbuf, "FreeBSD") != 0) 500 continue; 501 if (nhdr.n_descsz < sizeof(cstructsize)) { 502 warnx("corrupted core file"); 503 return (0); 504 } 505 if (!core_read(core, &cstructsize, sizeof(cstructsize))) 506 return (0); 507 if (cstructsize != psc_type_info[type].structsize) { 508 warnx("version mismatch"); 509 return (0); 510 } 511 if (nhdr.n_descsz - sizeof(cstructsize) == 0) 512 return (0); 513 } 514 515 return (n); 516 } 517