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