mtx: unlock before traversing threads to wake up
[freebsd.git] / lib / libprocstat / core.c
1 /*-
2  * Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org>
3  * Copyright (c) 2017 Dell EMC
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
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 }