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
51struct 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
60static 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
78static bool	core_offset(struct procstat_core *core, off_t offset);
79static bool	core_read(struct procstat_core *core, void *buf, size_t len);
80static ssize_t	core_read_mem(struct procstat_core *core, void *buf,
81    size_t len, vm_offset_t addr, bool readall);
82static void	*get_args(struct procstat_core *core, vm_offset_t psstrings,
83    enum psc_type type, void *buf, size_t *lenp);
84
85struct procstat_core *
86procstat_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);
149fail:
150	if (e != NULL)
151		elf_end(e);
152	close(fd);
153
154	return (NULL);
155}
156
157void
158procstat_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
168void *
169procstat_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
267static bool
268core_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
280static bool
281core_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
299static ssize_t
300core_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
339static void *
340get_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	}
415fail:
416	free(freeargs);
417	args = NULL;
418done:
419	*lenp = done;
420	free(argv);
421	return (args);
422}
423
424int
425procstat_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