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 }; 113 114 static bool core_offset(struct procstat_core *core, off_t offset); 115 static bool core_read(struct procstat_core *core, void *buf, size_t len); 116 static ssize_t core_read_mem(struct procstat_core *core, void *buf, 117 size_t len, vm_offset_t addr, bool readall); 118 static void *get_args(struct procstat_core *core, vm_offset_t psstrings, 119 enum psc_type type, void *buf, size_t *lenp); 120 121 struct procstat_core * 122 procstat_core_open(const char *filename) 123 { 124 struct procstat_core *core; 125 Elf *e; 126 GElf_Ehdr ehdr; 127 GElf_Phdr phdr; 128 size_t nph; 129 int fd, i; 130 131 if (elf_version(EV_CURRENT) == EV_NONE) { 132 warnx("ELF library too old"); 133 return (NULL); 134 } 135 fd = open(filename, O_RDONLY, 0); 136 if (fd == -1) { 137 warn("open(%s)", filename); 138 return (NULL); 139 } 140 e = elf_begin(fd, ELF_C_READ, NULL); 141 if (e == NULL) { 142 warnx("elf_begin: %s", elf_errmsg(-1)); 143 goto fail; 144 } 145 if (elf_kind(e) != ELF_K_ELF) { 146 warnx("%s is not an ELF object", filename); 147 goto fail; 148 } 149 if (gelf_getehdr(e, &ehdr) == NULL) { 150 warnx("gelf_getehdr: %s", elf_errmsg(-1)); 151 goto fail; 152 } 153 if (ehdr.e_type != ET_CORE) { 154 warnx("%s is not a CORE file", filename); 155 goto fail; 156 } 157 if (elf_getphdrnum(e, &nph) == -1) { 158 warnx("program headers not found"); 159 goto fail; 160 } 161 for (i = 0; i < ehdr.e_phnum; i++) { 162 if (gelf_getphdr(e, i, &phdr) != &phdr) { 163 warnx("gelf_getphdr: %s", elf_errmsg(-1)); 164 goto fail; 165 } 166 if (phdr.p_type == PT_NOTE) 167 break; 168 } 169 if (i == ehdr.e_phnum) { 170 warnx("NOTE program header not found"); 171 goto fail; 172 } 173 core = malloc(sizeof(struct procstat_core)); 174 if (core == NULL) { 175 warn("malloc(%zu)", sizeof(struct procstat_core)); 176 goto fail; 177 } 178 core->pc_magic = PROCSTAT_CORE_MAGIC; 179 core->pc_fd = fd; 180 core->pc_elf = e; 181 core->pc_ehdr = ehdr; 182 core->pc_phdr = phdr; 183 184 return (core); 185 fail: 186 if (e != NULL) 187 elf_end(e); 188 close(fd); 189 190 return (NULL); 191 } 192 193 void 194 procstat_core_close(struct procstat_core *core) 195 { 196 197 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 198 199 elf_end(core->pc_elf); 200 close(core->pc_fd); 201 free(core); 202 } 203 204 void * 205 procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf, 206 size_t *lenp) 207 { 208 Elf_Note nhdr; 209 off_t offset, eoffset; 210 vm_offset_t psstrings; 211 void *freebuf; 212 size_t len, curlen; 213 int cstructsize; 214 char nbuf[8]; 215 216 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 217 218 if (type >= PSC_TYPE_MAX) { 219 warnx("unknown core stat type: %d", type); 220 return (NULL); 221 } 222 223 offset = core->pc_phdr.p_offset; 224 eoffset = offset + core->pc_phdr.p_filesz; 225 curlen = 0; 226 227 while (offset < eoffset) { 228 if (!core_offset(core, offset)) 229 return (NULL); 230 if (!core_read(core, &nhdr, sizeof(nhdr))) 231 return (NULL); 232 233 offset += sizeof(nhdr) + 234 roundup2(nhdr.n_namesz, sizeof(Elf32_Size)) + 235 roundup2(nhdr.n_descsz, sizeof(Elf32_Size)); 236 237 if (nhdr.n_namesz == 0 && nhdr.n_descsz == 0) 238 break; 239 if (nhdr.n_type != psc_type_info[type].n_type) 240 continue; 241 if (nhdr.n_namesz != 8) 242 continue; 243 if (!core_read(core, nbuf, sizeof(nbuf))) 244 return (NULL); 245 if (strcmp(nbuf, "FreeBSD") != 0) 246 continue; 247 if (nhdr.n_descsz < sizeof(cstructsize)) { 248 warnx("corrupted core file"); 249 return (NULL); 250 } 251 if (!core_read(core, &cstructsize, sizeof(cstructsize))) 252 return (NULL); 253 if (cstructsize != psc_type_info[type].structsize) { 254 warnx("version mismatch"); 255 return (NULL); 256 } 257 len = nhdr.n_descsz - sizeof(cstructsize); 258 if (len == 0) 259 return (NULL); 260 if (buf != NULL) { 261 len = MIN(len, *lenp); 262 freebuf = NULL; 263 } else { 264 freebuf = buf = malloc(len); 265 if (buf == NULL) { 266 warn("malloc(%zu)", len); 267 return (NULL); 268 } 269 } 270 if (!core_read(core, (char *)buf + curlen, len)) { 271 free(freebuf); 272 return (NULL); 273 } 274 if (type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV) { 275 if (len < sizeof(psstrings)) { 276 free(freebuf); 277 return (NULL); 278 } 279 psstrings = *(vm_offset_t *)buf; 280 if (freebuf == NULL) 281 len = *lenp; 282 else 283 buf = NULL; 284 free(freebuf); 285 buf = get_args(core, psstrings, type, buf, &len); 286 } else if (type == PSC_TYPE_PTLWPINFO) { 287 *lenp -= len; 288 curlen += len; 289 continue; 290 } 291 *lenp = len; 292 return (buf); 293 } 294 295 if (curlen != 0) { 296 *lenp = curlen; 297 return (buf); 298 } 299 300 return (NULL); 301 } 302 303 static bool 304 core_offset(struct procstat_core *core, off_t offset) 305 { 306 307 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 308 309 if (lseek(core->pc_fd, offset, SEEK_SET) == -1) { 310 warn("core: lseek(%jd)", (intmax_t)offset); 311 return (false); 312 } 313 return (true); 314 } 315 316 static bool 317 core_read(struct procstat_core *core, void *buf, size_t len) 318 { 319 ssize_t n; 320 321 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 322 323 n = read(core->pc_fd, buf, len); 324 if (n == -1) { 325 warn("core: read"); 326 return (false); 327 } 328 if (n < (ssize_t)len) { 329 warnx("core: short read"); 330 return (false); 331 } 332 return (true); 333 } 334 335 static ssize_t 336 core_read_mem(struct procstat_core *core, void *buf, size_t len, 337 vm_offset_t addr, bool readall) 338 { 339 GElf_Phdr phdr; 340 off_t offset; 341 int i; 342 343 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 344 345 for (i = 0; i < core->pc_ehdr.e_phnum; i++) { 346 if (gelf_getphdr(core->pc_elf, i, &phdr) != &phdr) { 347 warnx("gelf_getphdr: %s", elf_errmsg(-1)); 348 return (-1); 349 } 350 if (phdr.p_type != PT_LOAD) 351 continue; 352 if (addr < phdr.p_vaddr || addr > phdr.p_vaddr + phdr.p_memsz) 353 continue; 354 offset = phdr.p_offset + (addr - phdr.p_vaddr); 355 if ((phdr.p_vaddr + phdr.p_memsz) - addr < len) { 356 if (readall) { 357 warnx("format error: " 358 "attempt to read out of segment"); 359 return (-1); 360 } 361 len = (phdr.p_vaddr + phdr.p_memsz) - addr; 362 } 363 if (!core_offset(core, offset)) 364 return (-1); 365 if (!core_read(core, buf, len)) 366 return (-1); 367 return (len); 368 } 369 warnx("format error: address %ju not found", (uintmax_t)addr); 370 return (-1); 371 } 372 373 #define ARGS_CHUNK_SZ 256 /* Chunk size (bytes) for get_args operations. */ 374 375 static void * 376 get_args(struct procstat_core *core, vm_offset_t psstrings, enum psc_type type, 377 void *args, size_t *lenp) 378 { 379 struct ps_strings pss; 380 void *freeargs; 381 vm_offset_t addr; 382 char **argv, *p; 383 size_t chunksz, done, len, nchr, size; 384 ssize_t n; 385 u_int i, nstr; 386 387 assert(type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV); 388 389 if (core_read_mem(core, &pss, sizeof(pss), psstrings, true) == -1) 390 return (NULL); 391 if (type == PSC_TYPE_ARGV) { 392 addr = (vm_offset_t)pss.ps_argvstr; 393 nstr = pss.ps_nargvstr; 394 } else /* type == PSC_TYPE_ENVV */ { 395 addr = (vm_offset_t)pss.ps_envstr; 396 nstr = pss.ps_nenvstr; 397 } 398 if (addr == 0 || nstr == 0) 399 return (NULL); 400 if (nstr > ARG_MAX) { 401 warnx("format error"); 402 return (NULL); 403 } 404 size = nstr * sizeof(char *); 405 argv = malloc(size); 406 if (argv == NULL) { 407 warn("malloc(%zu)", size); 408 return (NULL); 409 } 410 done = 0; 411 freeargs = NULL; 412 if (core_read_mem(core, argv, size, addr, true) == -1) 413 goto fail; 414 if (args != NULL) { 415 nchr = MIN(ARG_MAX, *lenp); 416 } else { 417 nchr = ARG_MAX; 418 freeargs = args = malloc(nchr); 419 if (args == NULL) { 420 warn("malloc(%zu)", nchr); 421 goto fail; 422 } 423 } 424 p = args; 425 for (i = 0; ; i++) { 426 if (i == nstr) 427 goto done; 428 /* 429 * The program may have scribbled into its argv array, e.g. to 430 * remove some arguments. If that has happened, break out 431 * before trying to read from NULL. 432 */ 433 if (argv[i] == NULL) 434 goto done; 435 for (addr = (vm_offset_t)argv[i]; ; addr += chunksz) { 436 chunksz = MIN(ARGS_CHUNK_SZ, nchr - 1 - done); 437 if (chunksz <= 0) 438 goto done; 439 n = core_read_mem(core, p, chunksz, addr, false); 440 if (n == -1) 441 goto fail; 442 len = strnlen(p, chunksz); 443 p += len; 444 done += len; 445 if (len != chunksz) 446 break; 447 } 448 *p++ = '\0'; 449 done++; 450 } 451 fail: 452 free(freeargs); 453 args = NULL; 454 done: 455 *lenp = done; 456 free(argv); 457 return (args); 458 } 459 460 int 461 procstat_core_note_count(struct procstat_core *core, enum psc_type type) 462 { 463 Elf_Note nhdr; 464 off_t offset, eoffset; 465 int cstructsize; 466 char nbuf[8]; 467 int n; 468 469 if (type >= PSC_TYPE_MAX) { 470 warnx("unknown core stat type: %d", type); 471 return (0); 472 } 473 474 offset = core->pc_phdr.p_offset; 475 eoffset = offset + core->pc_phdr.p_filesz; 476 477 for (n = 0; offset < eoffset; n++) { 478 if (!core_offset(core, offset)) 479 return (0); 480 if (!core_read(core, &nhdr, sizeof(nhdr))) 481 return (0); 482 483 offset += sizeof(nhdr) + 484 roundup2(nhdr.n_namesz, sizeof(Elf32_Size)) + 485 roundup2(nhdr.n_descsz, sizeof(Elf32_Size)); 486 487 if (nhdr.n_namesz == 0 && nhdr.n_descsz == 0) 488 break; 489 if (nhdr.n_type != psc_type_info[type].n_type) 490 continue; 491 if (nhdr.n_namesz != 8) 492 continue; 493 if (!core_read(core, nbuf, sizeof(nbuf))) 494 return (0); 495 if (strcmp(nbuf, "FreeBSD") != 0) 496 continue; 497 if (nhdr.n_descsz < sizeof(cstructsize)) { 498 warnx("corrupted core file"); 499 return (0); 500 } 501 if (!core_read(core, &cstructsize, sizeof(cstructsize))) 502 return (0); 503 if (cstructsize != psc_type_info[type].structsize) { 504 warnx("version mismatch"); 505 return (0); 506 } 507 if (nhdr.n_descsz - sizeof(cstructsize) == 0) 508 return (0); 509 } 510 511 return (n); 512 } 513