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/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/elf.h> 35 #include <sys/exec.h> 36 #include <sys/ptrace.h> 37 #include <sys/user.h> 38 39 #include <assert.h> 40 #include <err.h> 41 #include <fcntl.h> 42 #include <gelf.h> 43 #include <libelf.h> 44 #include <stdbool.h> 45 #include <stdint.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 51 #include "core.h" 52 53 #define PROCSTAT_CORE_MAGIC 0x012DADB8 54 struct procstat_core 55 { 56 int pc_magic; 57 int pc_fd; 58 Elf *pc_elf; 59 GElf_Ehdr pc_ehdr; 60 GElf_Phdr pc_phdr; 61 }; 62 63 static struct psc_type_info { 64 unsigned int n_type; 65 int structsize; 66 } psc_type_info[PSC_TYPE_MAX] = { 67 { .n_type = NT_PROCSTAT_PROC, .structsize = sizeof(struct kinfo_proc) }, 68 { .n_type = NT_PROCSTAT_FILES, .structsize = sizeof(struct kinfo_file) }, 69 { .n_type = NT_PROCSTAT_VMMAP, .structsize = sizeof(struct kinfo_vmentry) }, 70 { .n_type = NT_PROCSTAT_GROUPS, .structsize = sizeof(gid_t) }, 71 { .n_type = NT_PROCSTAT_UMASK, .structsize = sizeof(u_short) }, 72 { .n_type = NT_PROCSTAT_RLIMIT, .structsize = sizeof(struct rlimit) * RLIM_NLIMITS }, 73 { .n_type = NT_PROCSTAT_OSREL, .structsize = sizeof(int) }, 74 { .n_type = NT_PROCSTAT_PSSTRINGS, .structsize = sizeof(vm_offset_t) }, 75 { .n_type = NT_PROCSTAT_PSSTRINGS, .structsize = sizeof(vm_offset_t) }, 76 { .n_type = NT_PROCSTAT_PSSTRINGS, .structsize = sizeof(vm_offset_t) }, 77 { .n_type = NT_PROCSTAT_AUXV, .structsize = sizeof(Elf_Auxinfo) }, 78 { .n_type = NT_PTLWPINFO, .structsize = sizeof(struct ptrace_lwpinfo) }, 79 }; 80 81 static bool core_offset(struct procstat_core *core, off_t offset); 82 static bool core_read(struct procstat_core *core, void *buf, size_t len); 83 static ssize_t core_read_mem(struct procstat_core *core, void *buf, 84 size_t len, vm_offset_t addr, bool readall); 85 static void *get_args(struct procstat_core *core, vm_offset_t psstrings, 86 enum psc_type type, void *buf, size_t *lenp); 87 88 struct procstat_core * 89 procstat_core_open(const char *filename) 90 { 91 struct procstat_core *core; 92 Elf *e; 93 GElf_Ehdr ehdr; 94 GElf_Phdr phdr; 95 size_t nph; 96 int fd, i; 97 98 if (elf_version(EV_CURRENT) == EV_NONE) { 99 warnx("ELF library too old"); 100 return (NULL); 101 } 102 fd = open(filename, O_RDONLY, 0); 103 if (fd == -1) { 104 warn("open(%s)", filename); 105 return (NULL); 106 } 107 e = elf_begin(fd, ELF_C_READ, NULL); 108 if (e == NULL) { 109 warnx("elf_begin: %s", elf_errmsg(-1)); 110 goto fail; 111 } 112 if (elf_kind(e) != ELF_K_ELF) { 113 warnx("%s is not an ELF object", filename); 114 goto fail; 115 } 116 if (gelf_getehdr(e, &ehdr) == NULL) { 117 warnx("gelf_getehdr: %s", elf_errmsg(-1)); 118 goto fail; 119 } 120 if (ehdr.e_type != ET_CORE) { 121 warnx("%s is not a CORE file", filename); 122 goto fail; 123 } 124 if (elf_getphnum(e, &nph) == 0) { 125 warnx("program headers not found"); 126 goto fail; 127 } 128 for (i = 0; i < ehdr.e_phnum; i++) { 129 if (gelf_getphdr(e, i, &phdr) != &phdr) { 130 warnx("gelf_getphdr: %s", elf_errmsg(-1)); 131 goto fail; 132 } 133 if (phdr.p_type == PT_NOTE) 134 break; 135 } 136 if (i == ehdr.e_phnum) { 137 warnx("NOTE program header not found"); 138 goto fail; 139 } 140 core = malloc(sizeof(struct procstat_core)); 141 if (core == NULL) { 142 warn("malloc(%zu)", sizeof(struct procstat_core)); 143 goto fail; 144 } 145 core->pc_magic = PROCSTAT_CORE_MAGIC; 146 core->pc_fd = fd; 147 core->pc_elf = e; 148 core->pc_ehdr = ehdr; 149 core->pc_phdr = phdr; 150 151 return (core); 152 fail: 153 if (e != NULL) 154 elf_end(e); 155 close(fd); 156 157 return (NULL); 158 } 159 160 void 161 procstat_core_close(struct procstat_core *core) 162 { 163 164 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 165 166 elf_end(core->pc_elf); 167 close(core->pc_fd); 168 free(core); 169 } 170 171 void * 172 procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf, 173 size_t *lenp) 174 { 175 Elf_Note nhdr; 176 off_t offset, eoffset; 177 vm_offset_t psstrings; 178 void *freebuf; 179 size_t len, curlen; 180 int cstructsize; 181 char nbuf[8]; 182 183 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 184 185 if (type >= PSC_TYPE_MAX) { 186 warnx("unknown core stat type: %d", type); 187 return (NULL); 188 } 189 190 offset = core->pc_phdr.p_offset; 191 eoffset = offset + core->pc_phdr.p_filesz; 192 curlen = 0; 193 194 while (offset < eoffset) { 195 if (!core_offset(core, offset)) 196 return (NULL); 197 if (!core_read(core, &nhdr, sizeof(nhdr))) 198 return (NULL); 199 200 offset += sizeof(nhdr) + 201 roundup2(nhdr.n_namesz, sizeof(Elf32_Size)) + 202 roundup2(nhdr.n_descsz, sizeof(Elf32_Size)); 203 204 if (nhdr.n_namesz == 0 && nhdr.n_descsz == 0) 205 break; 206 if (nhdr.n_type != psc_type_info[type].n_type) 207 continue; 208 if (nhdr.n_namesz != 8) 209 continue; 210 if (!core_read(core, nbuf, sizeof(nbuf))) 211 return (NULL); 212 if (strcmp(nbuf, "FreeBSD") != 0) 213 continue; 214 if (nhdr.n_descsz < sizeof(cstructsize)) { 215 warnx("corrupted core file"); 216 return (NULL); 217 } 218 if (!core_read(core, &cstructsize, sizeof(cstructsize))) 219 return (NULL); 220 if (cstructsize != psc_type_info[type].structsize) { 221 warnx("version mismatch"); 222 return (NULL); 223 } 224 len = nhdr.n_descsz - sizeof(cstructsize); 225 if (len == 0) 226 return (NULL); 227 if (buf != NULL) { 228 len = MIN(len, *lenp); 229 freebuf = NULL; 230 } else { 231 freebuf = buf = malloc(len); 232 if (buf == NULL) { 233 warn("malloc(%zu)", len); 234 return (NULL); 235 } 236 } 237 if (!core_read(core, (char *)buf + curlen, len)) { 238 free(freebuf); 239 return (NULL); 240 } 241 if (type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV) { 242 if (len < sizeof(psstrings)) { 243 free(freebuf); 244 return (NULL); 245 } 246 psstrings = *(vm_offset_t *)buf; 247 if (freebuf == NULL) 248 len = *lenp; 249 else 250 buf = NULL; 251 free(freebuf); 252 buf = get_args(core, psstrings, type, buf, &len); 253 } else if (type == PSC_TYPE_PTLWPINFO) { 254 *lenp -= len; 255 curlen += len; 256 continue; 257 } 258 *lenp = len; 259 return (buf); 260 } 261 262 if (curlen != 0) { 263 *lenp = curlen; 264 return (buf); 265 } 266 267 return (NULL); 268 } 269 270 static bool 271 core_offset(struct procstat_core *core, off_t offset) 272 { 273 274 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 275 276 if (lseek(core->pc_fd, offset, SEEK_SET) == -1) { 277 warn("core: lseek(%jd)", (intmax_t)offset); 278 return (false); 279 } 280 return (true); 281 } 282 283 static bool 284 core_read(struct procstat_core *core, void *buf, size_t len) 285 { 286 ssize_t n; 287 288 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 289 290 n = read(core->pc_fd, buf, len); 291 if (n == -1) { 292 warn("core: read"); 293 return (false); 294 } 295 if (n < (ssize_t)len) { 296 warnx("core: short read"); 297 return (false); 298 } 299 return (true); 300 } 301 302 static ssize_t 303 core_read_mem(struct procstat_core *core, void *buf, size_t len, 304 vm_offset_t addr, bool readall) 305 { 306 GElf_Phdr phdr; 307 off_t offset; 308 int i; 309 310 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 311 312 for (i = 0; i < core->pc_ehdr.e_phnum; i++) { 313 if (gelf_getphdr(core->pc_elf, i, &phdr) != &phdr) { 314 warnx("gelf_getphdr: %s", elf_errmsg(-1)); 315 return (-1); 316 } 317 if (phdr.p_type != PT_LOAD) 318 continue; 319 if (addr < phdr.p_vaddr || addr > phdr.p_vaddr + phdr.p_memsz) 320 continue; 321 offset = phdr.p_offset + (addr - phdr.p_vaddr); 322 if ((phdr.p_vaddr + phdr.p_memsz) - addr < len) { 323 if (readall) { 324 warnx("format error: " 325 "attempt to read out of segment"); 326 return (-1); 327 } 328 len = (phdr.p_vaddr + phdr.p_memsz) - addr; 329 } 330 if (!core_offset(core, offset)) 331 return (-1); 332 if (!core_read(core, buf, len)) 333 return (-1); 334 return (len); 335 } 336 warnx("format error: address %ju not found", (uintmax_t)addr); 337 return (-1); 338 } 339 340 #define ARGS_CHUNK_SZ 256 /* Chunk size (bytes) for get_args operations. */ 341 342 static void * 343 get_args(struct procstat_core *core, vm_offset_t psstrings, enum psc_type type, 344 void *args, size_t *lenp) 345 { 346 struct ps_strings pss; 347 void *freeargs; 348 vm_offset_t addr; 349 char **argv, *p; 350 size_t chunksz, done, len, nchr, size; 351 ssize_t n; 352 u_int i, nstr; 353 354 assert(type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV); 355 356 if (core_read_mem(core, &pss, sizeof(pss), psstrings, true) == -1) 357 return (NULL); 358 if (type == PSC_TYPE_ARGV) { 359 addr = (vm_offset_t)pss.ps_argvstr; 360 nstr = pss.ps_nargvstr; 361 } else /* type == PSC_TYPE_ENVV */ { 362 addr = (vm_offset_t)pss.ps_envstr; 363 nstr = pss.ps_nenvstr; 364 } 365 if (addr == 0 || nstr == 0) 366 return (NULL); 367 if (nstr > ARG_MAX) { 368 warnx("format error"); 369 return (NULL); 370 } 371 size = nstr * sizeof(char *); 372 argv = malloc(size); 373 if (argv == NULL) { 374 warn("malloc(%zu)", size); 375 return (NULL); 376 } 377 done = 0; 378 freeargs = NULL; 379 if (core_read_mem(core, argv, size, addr, true) == -1) 380 goto fail; 381 if (args != NULL) { 382 nchr = MIN(ARG_MAX, *lenp); 383 } else { 384 nchr = ARG_MAX; 385 freeargs = args = malloc(nchr); 386 if (args == NULL) { 387 warn("malloc(%zu)", nchr); 388 goto fail; 389 } 390 } 391 p = args; 392 for (i = 0; ; i++) { 393 if (i == nstr) 394 goto done; 395 /* 396 * The program may have scribbled into its argv array, e.g. to 397 * remove some arguments. If that has happened, break out 398 * before trying to read from NULL. 399 */ 400 if (argv[i] == NULL) 401 goto done; 402 for (addr = (vm_offset_t)argv[i]; ; addr += chunksz) { 403 chunksz = MIN(ARGS_CHUNK_SZ, nchr - 1 - done); 404 if (chunksz <= 0) 405 goto done; 406 n = core_read_mem(core, p, chunksz, addr, false); 407 if (n == -1) 408 goto fail; 409 len = strnlen(p, chunksz); 410 p += len; 411 done += len; 412 if (len != chunksz) 413 break; 414 } 415 *p++ = '\0'; 416 done++; 417 } 418 fail: 419 free(freeargs); 420 args = NULL; 421 done: 422 *lenp = done; 423 free(argv); 424 return (args); 425 } 426 427 int 428 procstat_core_note_count(struct procstat_core *core, enum psc_type type) 429 { 430 Elf_Note nhdr; 431 off_t offset, eoffset; 432 int cstructsize; 433 char nbuf[8]; 434 int n; 435 436 if (type >= PSC_TYPE_MAX) { 437 warnx("unknown core stat type: %d", type); 438 return (0); 439 } 440 441 offset = core->pc_phdr.p_offset; 442 eoffset = offset + core->pc_phdr.p_filesz; 443 444 for (n = 0; offset < eoffset; n++) { 445 if (!core_offset(core, offset)) 446 return (0); 447 if (!core_read(core, &nhdr, sizeof(nhdr))) 448 return (0); 449 450 offset += sizeof(nhdr) + 451 roundup2(nhdr.n_namesz, sizeof(Elf32_Size)) + 452 roundup2(nhdr.n_descsz, sizeof(Elf32_Size)); 453 454 if (nhdr.n_namesz == 0 && nhdr.n_descsz == 0) 455 break; 456 if (nhdr.n_type != psc_type_info[type].n_type) 457 continue; 458 if (nhdr.n_namesz != 8) 459 continue; 460 if (!core_read(core, nbuf, sizeof(nbuf))) 461 return (0); 462 if (strcmp(nbuf, "FreeBSD") != 0) 463 continue; 464 if (nhdr.n_descsz < sizeof(cstructsize)) { 465 warnx("corrupted core file"); 466 return (0); 467 } 468 if (!core_read(core, &cstructsize, sizeof(cstructsize))) 469 return (0); 470 if (cstructsize != psc_type_info[type].structsize) { 471 warnx("version mismatch"); 472 return (0); 473 } 474 if (nhdr.n_descsz - sizeof(cstructsize) == 0) 475 return (0); 476 } 477 478 return (n); 479 } 480