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