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