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