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