1249666Strociny/*- 2249666Strociny * Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org> 3249666Strociny * All rights reserved. 4249666Strociny * 5249666Strociny * Redistribution and use in source and binary forms, with or without 6249666Strociny * modification, are permitted provided that the following conditions 7249666Strociny * are met: 8249666Strociny * 1. Redistributions of source code must retain the above copyright 9249666Strociny * notice, this list of conditions and the following disclaimer. 10249666Strociny * 2. Redistributions in binary form must reproduce the above copyright 11249666Strociny * notice, this list of conditions and the following disclaimer in the 12249666Strociny * documentation and/or other materials provided with the distribution. 13249666Strociny * 14249666Strociny * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 15249666Strociny * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16249666Strociny * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17249666Strociny * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 18249666Strociny * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19249666Strociny * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20249666Strociny * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21249666Strociny * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22249666Strociny * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23249666Strociny * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24249666Strociny * SUCH DAMAGE. 25249666Strociny */ 26249666Strociny 27249731Strociny#include <sys/cdefs.h> 28249731Strociny__FBSDID("$FreeBSD$"); 29249731Strociny 30249666Strociny#include <sys/param.h> 31249666Strociny#include <sys/elf.h> 32249679Strociny#include <sys/exec.h> 33249666Strociny#include <sys/user.h> 34249666Strociny 35249666Strociny#include <assert.h> 36249666Strociny#include <err.h> 37249666Strociny#include <fcntl.h> 38249666Strociny#include <gelf.h> 39249666Strociny#include <libelf.h> 40249666Strociny#include <stdbool.h> 41249666Strociny#include <stdint.h> 42249666Strociny#include <stdio.h> 43249666Strociny#include <stdlib.h> 44249666Strociny#include <string.h> 45249666Strociny#include <unistd.h> 46249666Strociny 47249666Strociny#include "core.h" 48249666Strociny 49249666Strociny#define PROCSTAT_CORE_MAGIC 0x012DADB8 50249666Strocinystruct procstat_core 51249666Strociny{ 52249666Strociny int pc_magic; 53249666Strociny int pc_fd; 54249666Strociny Elf *pc_elf; 55249666Strociny GElf_Ehdr pc_ehdr; 56249666Strociny GElf_Phdr pc_phdr; 57249666Strociny}; 58249666Strociny 59249666Strocinystatic bool core_offset(struct procstat_core *core, off_t offset); 60249666Strocinystatic bool core_read(struct procstat_core *core, void *buf, size_t len); 61249679Strocinystatic ssize_t core_read_mem(struct procstat_core *core, void *buf, 62249679Strociny size_t len, vm_offset_t addr, bool readall); 63249679Strocinystatic void *get_args(struct procstat_core *core, vm_offset_t psstrings, 64249679Strociny enum psc_type type, void *buf, size_t *lenp); 65249666Strociny 66249666Strocinystruct procstat_core * 67249666Strocinyprocstat_core_open(const char *filename) 68249666Strociny{ 69249666Strociny struct procstat_core *core; 70249666Strociny Elf *e; 71249666Strociny GElf_Ehdr ehdr; 72249666Strociny GElf_Phdr phdr; 73249666Strociny size_t nph; 74249666Strociny int fd, i; 75249666Strociny 76249666Strociny if (elf_version(EV_CURRENT) == EV_NONE) { 77249666Strociny warnx("ELF library too old"); 78249666Strociny return (NULL); 79249666Strociny } 80249666Strociny fd = open(filename, O_RDONLY, 0); 81249666Strociny if (fd == -1) { 82249666Strociny warn("open(%s)", filename); 83249666Strociny return (NULL); 84249666Strociny } 85249666Strociny e = elf_begin(fd, ELF_C_READ, NULL); 86249666Strociny if (e == NULL) { 87249666Strociny warnx("elf_begin: %s", elf_errmsg(-1)); 88249666Strociny goto fail; 89249666Strociny } 90249666Strociny if (elf_kind(e) != ELF_K_ELF) { 91249666Strociny warnx("%s is not an ELF object", filename); 92249666Strociny goto fail; 93249666Strociny } 94249666Strociny if (gelf_getehdr(e, &ehdr) == NULL) { 95249666Strociny warnx("gelf_getehdr: %s", elf_errmsg(-1)); 96249666Strociny goto fail; 97249666Strociny } 98249666Strociny if (ehdr.e_type != ET_CORE) { 99249666Strociny warnx("%s is not a CORE file", filename); 100249666Strociny goto fail; 101249666Strociny } 102249666Strociny if (elf_getphnum(e, &nph) == 0) { 103249666Strociny warnx("program headers not found"); 104249666Strociny goto fail; 105249666Strociny } 106249666Strociny for (i = 0; i < ehdr.e_phnum; i++) { 107249666Strociny if (gelf_getphdr(e, i, &phdr) != &phdr) { 108249666Strociny warnx("gelf_getphdr: %s", elf_errmsg(-1)); 109249666Strociny goto fail; 110249666Strociny } 111249666Strociny if (phdr.p_type == PT_NOTE) 112249666Strociny break; 113249666Strociny } 114249666Strociny if (i == ehdr.e_phnum) { 115249666Strociny warnx("NOTE program header not found"); 116249666Strociny goto fail; 117249666Strociny } 118249666Strociny core = malloc(sizeof(struct procstat_core)); 119249666Strociny if (core == NULL) { 120249666Strociny warn("malloc(%zu)", sizeof(struct procstat_core)); 121249666Strociny goto fail; 122249666Strociny } 123249666Strociny core->pc_magic = PROCSTAT_CORE_MAGIC; 124249666Strociny core->pc_fd = fd; 125249666Strociny core->pc_elf = e; 126249666Strociny core->pc_ehdr = ehdr; 127249666Strociny core->pc_phdr = phdr; 128249666Strociny 129249666Strociny return (core); 130249666Strocinyfail: 131249666Strociny if (e != NULL) 132249666Strociny elf_end(e); 133249666Strociny close(fd); 134249666Strociny 135249666Strociny return (NULL); 136249666Strociny} 137249666Strociny 138249666Strocinyvoid 139249666Strocinyprocstat_core_close(struct procstat_core *core) 140249666Strociny{ 141249666Strociny 142249666Strociny assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 143249666Strociny 144249666Strociny elf_end(core->pc_elf); 145249666Strociny close(core->pc_fd); 146249666Strociny free(core); 147249666Strociny} 148249666Strociny 149249666Strocinyvoid * 150249666Strocinyprocstat_core_get(struct procstat_core *core, enum psc_type type, void *buf, 151249666Strociny size_t *lenp) 152249666Strociny{ 153249666Strociny Elf_Note nhdr; 154249666Strociny off_t offset, eoffset; 155249679Strociny vm_offset_t psstrings; 156249666Strociny void *freebuf; 157249666Strociny size_t len; 158249666Strociny u_int32_t n_type; 159249666Strociny int cstructsize, structsize; 160249666Strociny char nbuf[8]; 161249666Strociny 162249666Strociny assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 163249666Strociny 164249666Strociny switch(type) { 165249666Strociny case PSC_TYPE_PROC: 166249666Strociny n_type = NT_PROCSTAT_PROC; 167249666Strociny structsize = sizeof(struct kinfo_proc); 168249666Strociny break; 169249666Strociny case PSC_TYPE_FILES: 170249666Strociny n_type = NT_PROCSTAT_FILES; 171249666Strociny structsize = sizeof(struct kinfo_file); 172249666Strociny break; 173249666Strociny case PSC_TYPE_VMMAP: 174249666Strociny n_type = NT_PROCSTAT_VMMAP; 175249666Strociny structsize = sizeof(struct kinfo_vmentry); 176249666Strociny break; 177249670Strociny case PSC_TYPE_GROUPS: 178249670Strociny n_type = NT_PROCSTAT_GROUPS; 179249670Strociny structsize = sizeof(gid_t); 180249670Strociny break; 181249672Strociny case PSC_TYPE_UMASK: 182249672Strociny n_type = NT_PROCSTAT_UMASK; 183249672Strociny structsize = sizeof(u_short); 184249672Strociny break; 185249674Strociny case PSC_TYPE_RLIMIT: 186249674Strociny n_type = NT_PROCSTAT_RLIMIT; 187249674Strociny structsize = sizeof(struct rlimit) * RLIM_NLIMITS; 188249674Strociny break; 189249677Strociny case PSC_TYPE_OSREL: 190249677Strociny n_type = NT_PROCSTAT_OSREL; 191249677Strociny structsize = sizeof(int); 192249677Strociny break; 193249679Strociny case PSC_TYPE_PSSTRINGS: 194249679Strociny case PSC_TYPE_ARGV: 195249679Strociny case PSC_TYPE_ENVV: 196249679Strociny n_type = NT_PROCSTAT_PSSTRINGS; 197249679Strociny structsize = sizeof(vm_offset_t); 198249679Strociny break; 199249681Strociny case PSC_TYPE_AUXV: 200249681Strociny n_type = NT_PROCSTAT_AUXV; 201249681Strociny structsize = sizeof(Elf_Auxinfo); 202249681Strociny break; 203249666Strociny default: 204249666Strociny warnx("unknown core stat type: %d", type); 205249666Strociny return (NULL); 206249666Strociny } 207249666Strociny 208249666Strociny offset = core->pc_phdr.p_offset; 209249666Strociny eoffset = offset + core->pc_phdr.p_filesz; 210249666Strociny 211249666Strociny while (offset < eoffset) { 212249666Strociny if (!core_offset(core, offset)) 213249666Strociny return (NULL); 214249666Strociny if (!core_read(core, &nhdr, sizeof(nhdr))) 215249666Strociny return (NULL); 216249666Strociny 217249666Strociny offset += sizeof(nhdr) + 218249666Strociny roundup2(nhdr.n_namesz, sizeof(Elf32_Size)) + 219249666Strociny roundup2(nhdr.n_descsz, sizeof(Elf32_Size)); 220249666Strociny 221249666Strociny if (nhdr.n_namesz == 0 && nhdr.n_descsz == 0) 222249666Strociny break; 223249666Strociny if (nhdr.n_type != n_type) 224249666Strociny continue; 225249666Strociny if (nhdr.n_namesz != 8) 226249666Strociny continue; 227249666Strociny if (!core_read(core, nbuf, sizeof(nbuf))) 228249666Strociny return (NULL); 229249666Strociny if (strcmp(nbuf, "FreeBSD") != 0) 230249666Strociny continue; 231249666Strociny if (nhdr.n_descsz < sizeof(cstructsize)) { 232249666Strociny warnx("corrupted core file"); 233249666Strociny return (NULL); 234249666Strociny } 235249666Strociny if (!core_read(core, &cstructsize, sizeof(cstructsize))) 236249666Strociny return (NULL); 237249666Strociny if (cstructsize != structsize) { 238249666Strociny warnx("version mismatch"); 239249666Strociny return (NULL); 240249666Strociny } 241249666Strociny len = nhdr.n_descsz - sizeof(cstructsize); 242249666Strociny if (len == 0) 243249666Strociny return (NULL); 244249666Strociny if (buf != NULL) { 245249666Strociny len = MIN(len, *lenp); 246249666Strociny freebuf = NULL; 247249666Strociny } else { 248249666Strociny freebuf = buf = malloc(len); 249249666Strociny if (buf == NULL) { 250249666Strociny warn("malloc(%zu)", len); 251249666Strociny return (NULL); 252249666Strociny } 253249666Strociny } 254249666Strociny if (!core_read(core, buf, len)) { 255249666Strociny free(freebuf); 256249666Strociny return (NULL); 257249666Strociny } 258249679Strociny if (type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV) { 259249679Strociny if (len < sizeof(psstrings)) { 260249679Strociny free(freebuf); 261249679Strociny return (NULL); 262249679Strociny } 263249679Strociny psstrings = *(vm_offset_t *)buf; 264249679Strociny if (freebuf == NULL) 265249679Strociny len = *lenp; 266249679Strociny else 267249679Strociny buf = NULL; 268249679Strociny free(freebuf); 269249679Strociny buf = get_args(core, psstrings, type, buf, &len); 270249679Strociny } 271249666Strociny *lenp = len; 272249666Strociny return (buf); 273249666Strociny } 274249666Strociny 275249666Strociny return (NULL); 276249666Strociny} 277249666Strociny 278249666Strocinystatic bool 279249666Strocinycore_offset(struct procstat_core *core, off_t offset) 280249666Strociny{ 281249666Strociny 282249666Strociny assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 283249666Strociny 284249666Strociny if (lseek(core->pc_fd, offset, SEEK_SET) == -1) { 285249666Strociny warn("core: lseek(%jd)", (intmax_t)offset); 286249666Strociny return (false); 287249666Strociny } 288249666Strociny return (true); 289249666Strociny} 290249666Strociny 291249666Strocinystatic bool 292249666Strocinycore_read(struct procstat_core *core, void *buf, size_t len) 293249666Strociny{ 294249666Strociny ssize_t n; 295249666Strociny 296249666Strociny assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 297249666Strociny 298249666Strociny n = read(core->pc_fd, buf, len); 299249666Strociny if (n == -1) { 300249666Strociny warn("core: read"); 301249666Strociny return (false); 302249666Strociny } 303249666Strociny if (n < (ssize_t)len) { 304249666Strociny warnx("core: short read"); 305249666Strociny return (false); 306249666Strociny } 307249666Strociny return (true); 308249666Strociny} 309249679Strociny 310249679Strocinystatic ssize_t 311249679Strocinycore_read_mem(struct procstat_core *core, void *buf, size_t len, 312249679Strociny vm_offset_t addr, bool readall) 313249679Strociny{ 314249679Strociny GElf_Phdr phdr; 315249679Strociny off_t offset; 316249679Strociny int i; 317249679Strociny 318249679Strociny assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 319249679Strociny 320249679Strociny for (i = 0; i < core->pc_ehdr.e_phnum; i++) { 321249679Strociny if (gelf_getphdr(core->pc_elf, i, &phdr) != &phdr) { 322249679Strociny warnx("gelf_getphdr: %s", elf_errmsg(-1)); 323249679Strociny return (-1); 324249679Strociny } 325249679Strociny if (phdr.p_type != PT_LOAD) 326249679Strociny continue; 327249679Strociny if (addr < phdr.p_vaddr || addr > phdr.p_vaddr + phdr.p_memsz) 328249679Strociny continue; 329249679Strociny offset = phdr.p_offset + (addr - phdr.p_vaddr); 330249679Strociny if ((phdr.p_vaddr + phdr.p_memsz) - addr < len) { 331249679Strociny if (readall) { 332249679Strociny warnx("format error: " 333249679Strociny "attempt to read out of segment"); 334249679Strociny return (-1); 335249679Strociny } 336249679Strociny len = (phdr.p_vaddr + phdr.p_memsz) - addr; 337249679Strociny } 338249679Strociny if (!core_offset(core, offset)) 339249679Strociny return (-1); 340249679Strociny if (!core_read(core, buf, len)) 341249679Strociny return (-1); 342249679Strociny return (len); 343249679Strociny } 344249679Strociny warnx("format error: address %ju not found", (uintmax_t)addr); 345249679Strociny return (-1); 346249679Strociny} 347249679Strociny 348249679Strociny#define ARGS_CHUNK_SZ 256 /* Chunk size (bytes) for get_args operations. */ 349249679Strociny 350249679Strocinystatic void * 351249679Strocinyget_args(struct procstat_core *core, vm_offset_t psstrings, enum psc_type type, 352249679Strociny void *args, size_t *lenp) 353249679Strociny{ 354249679Strociny struct ps_strings pss; 355249679Strociny void *freeargs; 356249679Strociny vm_offset_t addr; 357249679Strociny char **argv, *p; 358249679Strociny size_t chunksz, done, len, nchr, size; 359249679Strociny ssize_t n; 360249679Strociny u_int i, nstr; 361249679Strociny 362249679Strociny assert(type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV); 363249679Strociny 364249679Strociny if (core_read_mem(core, &pss, sizeof(pss), psstrings, true) == -1) 365249679Strociny return (NULL); 366249679Strociny if (type == PSC_TYPE_ARGV) { 367249679Strociny addr = (vm_offset_t)pss.ps_argvstr; 368249679Strociny nstr = pss.ps_nargvstr; 369249679Strociny } else /* type == PSC_TYPE_ENVV */ { 370249679Strociny addr = (vm_offset_t)pss.ps_envstr; 371249679Strociny nstr = pss.ps_nenvstr; 372249679Strociny } 373249679Strociny if (addr == 0 || nstr == 0) 374249679Strociny return (NULL); 375249679Strociny if (nstr > ARG_MAX) { 376249679Strociny warnx("format error"); 377249679Strociny return (NULL); 378249679Strociny } 379249679Strociny size = nstr * sizeof(char *); 380249679Strociny argv = malloc(size); 381249679Strociny if (argv == NULL) { 382249679Strociny warn("malloc(%zu)", size); 383249679Strociny return (NULL); 384249679Strociny } 385249679Strociny done = 0; 386249679Strociny freeargs = NULL; 387249679Strociny if (core_read_mem(core, argv, size, addr, true) == -1) 388249679Strociny goto fail; 389249679Strociny if (args != NULL) { 390249679Strociny nchr = MIN(ARG_MAX, *lenp); 391249679Strociny } else { 392249679Strociny nchr = ARG_MAX; 393249679Strociny freeargs = args = malloc(nchr); 394249679Strociny if (args == NULL) { 395249679Strociny warn("malloc(%zu)", nchr); 396249679Strociny goto fail; 397249679Strociny } 398249679Strociny } 399249679Strociny p = args; 400249679Strociny for (i = 0; ; i++) { 401249679Strociny if (i == nstr) 402249679Strociny goto done; 403249679Strociny /* 404249679Strociny * The program may have scribbled into its argv array, e.g. to 405249679Strociny * remove some arguments. If that has happened, break out 406249679Strociny * before trying to read from NULL. 407249679Strociny */ 408249679Strociny if (argv[i] == NULL) 409249679Strociny goto done; 410249679Strociny for (addr = (vm_offset_t)argv[i]; ; addr += chunksz) { 411249679Strociny chunksz = MIN(ARGS_CHUNK_SZ, nchr - 1 - done); 412249679Strociny if (chunksz <= 0) 413249679Strociny goto done; 414249679Strociny n = core_read_mem(core, p, chunksz, addr, false); 415249679Strociny if (n == -1) 416249679Strociny goto fail; 417249679Strociny len = strnlen(p, chunksz); 418249679Strociny p += len; 419249679Strociny done += len; 420249679Strociny if (len != chunksz) 421249679Strociny break; 422249679Strociny } 423249679Strociny *p++ = '\0'; 424249679Strociny done++; 425249679Strociny } 426249679Strocinyfail: 427249679Strociny free(freeargs); 428249679Strociny args = NULL; 429249679Strocinydone: 430249679Strociny *lenp = done; 431249679Strociny free(argv); 432249679Strociny return (args); 433249679Strociny} 434