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