xref: /freebsd/lib/libprocstat/core.c (revision d0b2dbfa0ecf2bbc9709efc5e20baf8e4b44bbbf)
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/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 *
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_getphnum(e, &nph) == 0) {
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
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 *
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
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
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
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 *
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
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