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