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