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