xref: /freebsd/lib/libprocstat/core.c (revision 580744621f33383027108364dcadad718df46ffe)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 __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 *
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
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 *
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
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
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
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 *
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
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