1 /*- 2 * Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/elf.h> 32 #include <sys/exec.h> 33 #include <sys/user.h> 34 35 #include <assert.h> 36 #include <err.h> 37 #include <fcntl.h> 38 #include <gelf.h> 39 #include <libelf.h> 40 #include <stdbool.h> 41 #include <stdint.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 #include "core.h" 48 49 #define PROCSTAT_CORE_MAGIC 0x012DADB8 50 struct procstat_core 51 { 52 int pc_magic; 53 int pc_fd; 54 Elf *pc_elf; 55 GElf_Ehdr pc_ehdr; 56 GElf_Phdr pc_phdr; 57 }; 58 59 static bool core_offset(struct procstat_core *core, off_t offset); 60 static bool core_read(struct procstat_core *core, void *buf, size_t len); 61 static ssize_t core_read_mem(struct procstat_core *core, void *buf, 62 size_t len, vm_offset_t addr, bool readall); 63 static void *get_args(struct procstat_core *core, vm_offset_t psstrings, 64 enum psc_type type, void *buf, size_t *lenp); 65 66 struct procstat_core * 67 procstat_core_open(const char *filename) 68 { 69 struct procstat_core *core; 70 Elf *e; 71 GElf_Ehdr ehdr; 72 GElf_Phdr phdr; 73 size_t nph; 74 int fd, i; 75 76 if (elf_version(EV_CURRENT) == EV_NONE) { 77 warnx("ELF library too old"); 78 return (NULL); 79 } 80 fd = open(filename, O_RDONLY, 0); 81 if (fd == -1) { 82 warn("open(%s)", filename); 83 return (NULL); 84 } 85 e = elf_begin(fd, ELF_C_READ, NULL); 86 if (e == NULL) { 87 warnx("elf_begin: %s", elf_errmsg(-1)); 88 goto fail; 89 } 90 if (elf_kind(e) != ELF_K_ELF) { 91 warnx("%s is not an ELF object", filename); 92 goto fail; 93 } 94 if (gelf_getehdr(e, &ehdr) == NULL) { 95 warnx("gelf_getehdr: %s", elf_errmsg(-1)); 96 goto fail; 97 } 98 if (ehdr.e_type != ET_CORE) { 99 warnx("%s is not a CORE file", filename); 100 goto fail; 101 } 102 if (elf_getphnum(e, &nph) == 0) { 103 warnx("program headers not found"); 104 goto fail; 105 } 106 for (i = 0; i < ehdr.e_phnum; i++) { 107 if (gelf_getphdr(e, i, &phdr) != &phdr) { 108 warnx("gelf_getphdr: %s", elf_errmsg(-1)); 109 goto fail; 110 } 111 if (phdr.p_type == PT_NOTE) 112 break; 113 } 114 if (i == ehdr.e_phnum) { 115 warnx("NOTE program header not found"); 116 goto fail; 117 } 118 core = malloc(sizeof(struct procstat_core)); 119 if (core == NULL) { 120 warn("malloc(%zu)", sizeof(struct procstat_core)); 121 goto fail; 122 } 123 core->pc_magic = PROCSTAT_CORE_MAGIC; 124 core->pc_fd = fd; 125 core->pc_elf = e; 126 core->pc_ehdr = ehdr; 127 core->pc_phdr = phdr; 128 129 return (core); 130 fail: 131 if (e != NULL) 132 elf_end(e); 133 close(fd); 134 135 return (NULL); 136 } 137 138 void 139 procstat_core_close(struct procstat_core *core) 140 { 141 142 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 143 144 elf_end(core->pc_elf); 145 close(core->pc_fd); 146 free(core); 147 } 148 149 void * 150 procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf, 151 size_t *lenp) 152 { 153 Elf_Note nhdr; 154 off_t offset, eoffset; 155 vm_offset_t psstrings; 156 void *freebuf; 157 size_t len; 158 u_int32_t n_type; 159 int cstructsize, structsize; 160 char nbuf[8]; 161 162 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 163 164 switch(type) { 165 case PSC_TYPE_PROC: 166 n_type = NT_PROCSTAT_PROC; 167 structsize = sizeof(struct kinfo_proc); 168 break; 169 case PSC_TYPE_FILES: 170 n_type = NT_PROCSTAT_FILES; 171 structsize = sizeof(struct kinfo_file); 172 break; 173 case PSC_TYPE_VMMAP: 174 n_type = NT_PROCSTAT_VMMAP; 175 structsize = sizeof(struct kinfo_vmentry); 176 break; 177 case PSC_TYPE_GROUPS: 178 n_type = NT_PROCSTAT_GROUPS; 179 structsize = sizeof(gid_t); 180 break; 181 case PSC_TYPE_UMASK: 182 n_type = NT_PROCSTAT_UMASK; 183 structsize = sizeof(u_short); 184 break; 185 case PSC_TYPE_RLIMIT: 186 n_type = NT_PROCSTAT_RLIMIT; 187 structsize = sizeof(struct rlimit) * RLIM_NLIMITS; 188 break; 189 case PSC_TYPE_OSREL: 190 n_type = NT_PROCSTAT_OSREL; 191 structsize = sizeof(int); 192 break; 193 case PSC_TYPE_PSSTRINGS: 194 case PSC_TYPE_ARGV: 195 case PSC_TYPE_ENVV: 196 n_type = NT_PROCSTAT_PSSTRINGS; 197 structsize = sizeof(vm_offset_t); 198 break; 199 case PSC_TYPE_AUXV: 200 n_type = NT_PROCSTAT_AUXV; 201 structsize = sizeof(Elf_Auxinfo); 202 break; 203 default: 204 warnx("unknown core stat type: %d", type); 205 return (NULL); 206 } 207 208 offset = core->pc_phdr.p_offset; 209 eoffset = offset + core->pc_phdr.p_filesz; 210 211 while (offset < eoffset) { 212 if (!core_offset(core, offset)) 213 return (NULL); 214 if (!core_read(core, &nhdr, sizeof(nhdr))) 215 return (NULL); 216 217 offset += sizeof(nhdr) + 218 roundup2(nhdr.n_namesz, sizeof(Elf32_Size)) + 219 roundup2(nhdr.n_descsz, sizeof(Elf32_Size)); 220 221 if (nhdr.n_namesz == 0 && nhdr.n_descsz == 0) 222 break; 223 if (nhdr.n_type != n_type) 224 continue; 225 if (nhdr.n_namesz != 8) 226 continue; 227 if (!core_read(core, nbuf, sizeof(nbuf))) 228 return (NULL); 229 if (strcmp(nbuf, "FreeBSD") != 0) 230 continue; 231 if (nhdr.n_descsz < sizeof(cstructsize)) { 232 warnx("corrupted core file"); 233 return (NULL); 234 } 235 if (!core_read(core, &cstructsize, sizeof(cstructsize))) 236 return (NULL); 237 if (cstructsize != structsize) { 238 warnx("version mismatch"); 239 return (NULL); 240 } 241 len = nhdr.n_descsz - sizeof(cstructsize); 242 if (len == 0) 243 return (NULL); 244 if (buf != NULL) { 245 len = MIN(len, *lenp); 246 freebuf = NULL; 247 } else { 248 freebuf = buf = malloc(len); 249 if (buf == NULL) { 250 warn("malloc(%zu)", len); 251 return (NULL); 252 } 253 } 254 if (!core_read(core, buf, len)) { 255 free(freebuf); 256 return (NULL); 257 } 258 if (type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV) { 259 if (len < sizeof(psstrings)) { 260 free(freebuf); 261 return (NULL); 262 } 263 psstrings = *(vm_offset_t *)buf; 264 if (freebuf == NULL) 265 len = *lenp; 266 else 267 buf = NULL; 268 free(freebuf); 269 buf = get_args(core, psstrings, type, buf, &len); 270 } 271 *lenp = len; 272 return (buf); 273 } 274 275 return (NULL); 276 } 277 278 static bool 279 core_offset(struct procstat_core *core, off_t offset) 280 { 281 282 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 283 284 if (lseek(core->pc_fd, offset, SEEK_SET) == -1) { 285 warn("core: lseek(%jd)", (intmax_t)offset); 286 return (false); 287 } 288 return (true); 289 } 290 291 static bool 292 core_read(struct procstat_core *core, void *buf, size_t len) 293 { 294 ssize_t n; 295 296 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 297 298 n = read(core->pc_fd, buf, len); 299 if (n == -1) { 300 warn("core: read"); 301 return (false); 302 } 303 if (n < (ssize_t)len) { 304 warnx("core: short read"); 305 return (false); 306 } 307 return (true); 308 } 309 310 static ssize_t 311 core_read_mem(struct procstat_core *core, void *buf, size_t len, 312 vm_offset_t addr, bool readall) 313 { 314 GElf_Phdr phdr; 315 off_t offset; 316 int i; 317 318 assert(core->pc_magic == PROCSTAT_CORE_MAGIC); 319 320 for (i = 0; i < core->pc_ehdr.e_phnum; i++) { 321 if (gelf_getphdr(core->pc_elf, i, &phdr) != &phdr) { 322 warnx("gelf_getphdr: %s", elf_errmsg(-1)); 323 return (-1); 324 } 325 if (phdr.p_type != PT_LOAD) 326 continue; 327 if (addr < phdr.p_vaddr || addr > phdr.p_vaddr + phdr.p_memsz) 328 continue; 329 offset = phdr.p_offset + (addr - phdr.p_vaddr); 330 if ((phdr.p_vaddr + phdr.p_memsz) - addr < len) { 331 if (readall) { 332 warnx("format error: " 333 "attempt to read out of segment"); 334 return (-1); 335 } 336 len = (phdr.p_vaddr + phdr.p_memsz) - addr; 337 } 338 if (!core_offset(core, offset)) 339 return (-1); 340 if (!core_read(core, buf, len)) 341 return (-1); 342 return (len); 343 } 344 warnx("format error: address %ju not found", (uintmax_t)addr); 345 return (-1); 346 } 347 348 #define ARGS_CHUNK_SZ 256 /* Chunk size (bytes) for get_args operations. */ 349 350 static void * 351 get_args(struct procstat_core *core, vm_offset_t psstrings, enum psc_type type, 352 void *args, size_t *lenp) 353 { 354 struct ps_strings pss; 355 void *freeargs; 356 vm_offset_t addr; 357 char **argv, *p; 358 size_t chunksz, done, len, nchr, size; 359 ssize_t n; 360 u_int i, nstr; 361 362 assert(type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV); 363 364 if (core_read_mem(core, &pss, sizeof(pss), psstrings, true) == -1) 365 return (NULL); 366 if (type == PSC_TYPE_ARGV) { 367 addr = (vm_offset_t)pss.ps_argvstr; 368 nstr = pss.ps_nargvstr; 369 } else /* type == PSC_TYPE_ENVV */ { 370 addr = (vm_offset_t)pss.ps_envstr; 371 nstr = pss.ps_nenvstr; 372 } 373 if (addr == 0 || nstr == 0) 374 return (NULL); 375 if (nstr > ARG_MAX) { 376 warnx("format error"); 377 return (NULL); 378 } 379 size = nstr * sizeof(char *); 380 argv = malloc(size); 381 if (argv == NULL) { 382 warn("malloc(%zu)", size); 383 return (NULL); 384 } 385 done = 0; 386 freeargs = NULL; 387 if (core_read_mem(core, argv, size, addr, true) == -1) 388 goto fail; 389 if (args != NULL) { 390 nchr = MIN(ARG_MAX, *lenp); 391 } else { 392 nchr = ARG_MAX; 393 freeargs = args = malloc(nchr); 394 if (args == NULL) { 395 warn("malloc(%zu)", nchr); 396 goto fail; 397 } 398 } 399 p = args; 400 for (i = 0; ; i++) { 401 if (i == nstr) 402 goto done; 403 /* 404 * The program may have scribbled into its argv array, e.g. to 405 * remove some arguments. If that has happened, break out 406 * before trying to read from NULL. 407 */ 408 if (argv[i] == NULL) 409 goto done; 410 for (addr = (vm_offset_t)argv[i]; ; addr += chunksz) { 411 chunksz = MIN(ARGS_CHUNK_SZ, nchr - 1 - done); 412 if (chunksz <= 0) 413 goto done; 414 n = core_read_mem(core, p, chunksz, addr, false); 415 if (n == -1) 416 goto fail; 417 len = strnlen(p, chunksz); 418 p += len; 419 done += len; 420 if (len != chunksz) 421 break; 422 } 423 *p++ = '\0'; 424 done++; 425 } 426 fail: 427 free(freeargs); 428 args = NULL; 429 done: 430 *lenp = done; 431 free(argv); 432 return (args); 433 } 434