acpi_timer: remove unused variable
[freebsd.git] / lib / libprocstat / core.c
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 }