xref: /freebsd/lib/libprocstat/core.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
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 *
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
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 *
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
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
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
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 *
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
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