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 *
procstat_core_open(const char * filename)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
procstat_core_close(struct procstat_core * core)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 *
procstat_core_get(struct procstat_core * core,enum psc_type type,void * buf,size_t * lenp)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
core_offset(struct procstat_core * core,off_t offset)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
core_read(struct procstat_core * core,void * buf,size_t len)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
core_read_mem(struct procstat_core * core,void * buf,size_t len,vm_offset_t addr,bool readall)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 *
get_args(struct procstat_core * core,vm_offset_t psstrings,enum psc_type type,void * args,size_t * lenp)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
procstat_core_note_count(struct procstat_core * core,enum psc_type type)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