]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - lib/libprocstat/core.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / lib / libprocstat / core.c
1 /*-
2  * Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/elf.h>
32 #include <sys/exec.h>
33 #include <sys/user.h>
34
35 #include <assert.h>
36 #include <err.h>
37 #include <fcntl.h>
38 #include <gelf.h>
39 #include <libelf.h>
40 #include <stdbool.h>
41 #include <stdint.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #include "core.h"
48
49 #define PROCSTAT_CORE_MAGIC     0x012DADB8
50 struct procstat_core
51 {
52         int             pc_magic;
53         int             pc_fd;
54         Elf             *pc_elf;
55         GElf_Ehdr       pc_ehdr;
56         GElf_Phdr       pc_phdr;
57 };
58
59 static bool     core_offset(struct procstat_core *core, off_t offset);
60 static bool     core_read(struct procstat_core *core, void *buf, size_t len);
61 static ssize_t  core_read_mem(struct procstat_core *core, void *buf,
62     size_t len, vm_offset_t addr, bool readall);
63 static void     *get_args(struct procstat_core *core, vm_offset_t psstrings,
64     enum psc_type type, void *buf, size_t *lenp);
65
66 struct procstat_core *
67 procstat_core_open(const char *filename)
68 {
69         struct procstat_core *core;
70         Elf *e;
71         GElf_Ehdr ehdr;
72         GElf_Phdr phdr;
73         size_t nph;
74         int fd, i;
75
76         if (elf_version(EV_CURRENT) == EV_NONE) {
77                 warnx("ELF library too old");
78                 return (NULL);
79         }
80         fd = open(filename, O_RDONLY, 0);
81         if (fd == -1) {
82                 warn("open(%s)", filename);
83                 return (NULL);
84         }
85         e = elf_begin(fd, ELF_C_READ, NULL);
86         if (e == NULL) {
87                 warnx("elf_begin: %s", elf_errmsg(-1));
88                 goto fail;
89         }
90         if (elf_kind(e) != ELF_K_ELF) {
91                 warnx("%s is not an ELF object", filename);
92                 goto fail;
93         }
94         if (gelf_getehdr(e, &ehdr) == NULL) {
95                 warnx("gelf_getehdr: %s", elf_errmsg(-1));
96                 goto fail;
97         }
98         if (ehdr.e_type != ET_CORE) {
99                 warnx("%s is not a CORE file", filename);
100                 goto fail;
101         }
102         if (elf_getphnum(e, &nph) == 0) {
103                 warnx("program headers not found");
104                 goto fail;
105         }
106         for (i = 0; i < ehdr.e_phnum; i++) {
107                 if (gelf_getphdr(e, i, &phdr) != &phdr) {
108                         warnx("gelf_getphdr: %s", elf_errmsg(-1));
109                         goto fail;
110                 }
111                 if (phdr.p_type == PT_NOTE)
112                         break;
113         }
114         if (i == ehdr.e_phnum) {
115                 warnx("NOTE program header not found");
116                 goto fail;
117         }
118         core = malloc(sizeof(struct procstat_core));
119         if (core == NULL) {
120                 warn("malloc(%zu)", sizeof(struct procstat_core));
121                 goto fail;
122         }
123         core->pc_magic = PROCSTAT_CORE_MAGIC;
124         core->pc_fd = fd;
125         core->pc_elf = e;
126         core->pc_ehdr = ehdr;
127         core->pc_phdr = phdr;
128
129         return (core);
130 fail:
131         if (e != NULL)
132                 elf_end(e);
133         close(fd);
134
135         return (NULL);
136 }
137
138 void
139 procstat_core_close(struct procstat_core *core)
140 {
141
142         assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
143
144         elf_end(core->pc_elf);
145         close(core->pc_fd);
146         free(core);
147 }
148
149 void *
150 procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf,
151     size_t *lenp)
152 {
153         Elf_Note nhdr;
154         off_t offset, eoffset;
155         vm_offset_t psstrings;
156         void *freebuf;
157         size_t len;
158         u_int32_t n_type;
159         int cstructsize, structsize;
160         char nbuf[8];
161
162         assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
163
164         switch(type) {
165         case PSC_TYPE_PROC:
166                 n_type = NT_PROCSTAT_PROC;
167                 structsize = sizeof(struct kinfo_proc);
168                 break;
169         case PSC_TYPE_FILES:
170                 n_type = NT_PROCSTAT_FILES;
171                 structsize = sizeof(struct kinfo_file);
172                 break;
173         case PSC_TYPE_VMMAP:
174                 n_type = NT_PROCSTAT_VMMAP;
175                 structsize = sizeof(struct kinfo_vmentry);
176                 break;
177         case PSC_TYPE_GROUPS:
178                 n_type = NT_PROCSTAT_GROUPS;
179                 structsize = sizeof(gid_t);
180                 break;
181         case PSC_TYPE_UMASK:
182                 n_type = NT_PROCSTAT_UMASK;
183                 structsize = sizeof(u_short);
184                 break;
185         case PSC_TYPE_RLIMIT:
186                 n_type = NT_PROCSTAT_RLIMIT;
187                 structsize = sizeof(struct rlimit) * RLIM_NLIMITS;
188                 break;
189         case PSC_TYPE_OSREL:
190                 n_type = NT_PROCSTAT_OSREL;
191                 structsize = sizeof(int);
192                 break;
193         case PSC_TYPE_PSSTRINGS:
194         case PSC_TYPE_ARGV:
195         case PSC_TYPE_ENVV:
196                 n_type = NT_PROCSTAT_PSSTRINGS;
197                 structsize = sizeof(vm_offset_t);
198                 break;
199         case PSC_TYPE_AUXV:
200                 n_type = NT_PROCSTAT_AUXV;
201                 structsize = sizeof(Elf_Auxinfo);
202                 break;
203         default:
204                 warnx("unknown core stat type: %d", type);
205                 return (NULL);
206         }
207
208         offset = core->pc_phdr.p_offset;
209         eoffset = offset + core->pc_phdr.p_filesz;
210
211         while (offset < eoffset) {
212                 if (!core_offset(core, offset))
213                         return (NULL);
214                 if (!core_read(core, &nhdr, sizeof(nhdr)))
215                         return (NULL);
216
217                 offset += sizeof(nhdr) +
218                     roundup2(nhdr.n_namesz, sizeof(Elf32_Size)) +
219                     roundup2(nhdr.n_descsz, sizeof(Elf32_Size));
220
221                 if (nhdr.n_namesz == 0 && nhdr.n_descsz == 0)
222                         break;
223                 if (nhdr.n_type != n_type)
224                         continue;
225                 if (nhdr.n_namesz != 8)
226                         continue;
227                 if (!core_read(core, nbuf, sizeof(nbuf)))
228                         return (NULL);
229                 if (strcmp(nbuf, "FreeBSD") != 0)
230                         continue;
231                 if (nhdr.n_descsz < sizeof(cstructsize)) {
232                         warnx("corrupted core file");
233                         return (NULL);
234                 }
235                 if (!core_read(core, &cstructsize, sizeof(cstructsize)))
236                         return (NULL);
237                 if (cstructsize != structsize) {
238                         warnx("version mismatch");
239                         return (NULL);
240                 }
241                 len = nhdr.n_descsz - sizeof(cstructsize);
242                 if (len == 0)
243                         return (NULL);
244                 if (buf != NULL) {
245                         len = MIN(len, *lenp);
246                         freebuf = NULL;
247                 } else {
248                         freebuf = buf = malloc(len);
249                         if (buf == NULL) {
250                                 warn("malloc(%zu)", len);
251                                 return (NULL);
252                         }
253                 }
254                 if (!core_read(core, buf, len)) {
255                         free(freebuf);
256                         return (NULL);
257                 }
258                 if (type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV) {
259                         if (len < sizeof(psstrings)) {
260                                 free(freebuf);
261                                 return (NULL);
262                         }
263                         psstrings = *(vm_offset_t *)buf;
264                         if (freebuf == NULL)
265                                 len = *lenp;
266                         else
267                                 buf = NULL;
268                         free(freebuf);
269                         buf = get_args(core, psstrings, type, buf, &len);
270                 }
271                 *lenp = len;
272                 return (buf);
273         }
274
275         return (NULL);
276 }
277
278 static bool
279 core_offset(struct procstat_core *core, off_t offset)
280 {
281
282         assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
283
284         if (lseek(core->pc_fd, offset, SEEK_SET) == -1) {
285                 warn("core: lseek(%jd)", (intmax_t)offset);
286                 return (false);
287         }
288         return (true);
289 }
290
291 static bool
292 core_read(struct procstat_core *core, void *buf, size_t len)
293 {
294         ssize_t n;
295
296         assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
297
298         n = read(core->pc_fd, buf, len);
299         if (n == -1) {
300                 warn("core: read");
301                 return (false);
302         }
303         if (n < (ssize_t)len) {
304                 warnx("core: short read");
305                 return (false);
306         }
307         return (true);
308 }
309
310 static ssize_t
311 core_read_mem(struct procstat_core *core, void *buf, size_t len,
312     vm_offset_t addr, bool readall)
313 {
314         GElf_Phdr phdr;
315         off_t offset;
316         int i;
317
318         assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
319
320         for (i = 0; i < core->pc_ehdr.e_phnum; i++) {
321                 if (gelf_getphdr(core->pc_elf, i, &phdr) != &phdr) {
322                         warnx("gelf_getphdr: %s", elf_errmsg(-1));
323                         return (-1);
324                 }
325                 if (phdr.p_type != PT_LOAD)
326                         continue;
327                 if (addr < phdr.p_vaddr || addr > phdr.p_vaddr + phdr.p_memsz)
328                         continue;
329                 offset = phdr.p_offset + (addr - phdr.p_vaddr);
330                 if ((phdr.p_vaddr + phdr.p_memsz) - addr < len) {
331                         if (readall) {
332                                 warnx("format error: "
333                                     "attempt to read out of segment");
334                                 return (-1);
335                         }
336                         len = (phdr.p_vaddr + phdr.p_memsz) - addr;
337                 }
338                 if (!core_offset(core, offset))
339                         return (-1);
340                 if (!core_read(core, buf, len))
341                         return (-1);
342                 return (len);
343         }
344         warnx("format error: address %ju not found", (uintmax_t)addr);
345         return (-1);
346 }
347
348 #define ARGS_CHUNK_SZ   256     /* Chunk size (bytes) for get_args operations. */
349
350 static void *
351 get_args(struct procstat_core *core, vm_offset_t psstrings, enum psc_type type,
352      void *args, size_t *lenp)
353 {
354         struct ps_strings pss;
355         void *freeargs;
356         vm_offset_t addr;
357         char **argv, *p;
358         size_t chunksz, done, len, nchr, size;
359         ssize_t n;
360         u_int i, nstr;
361
362         assert(type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV);
363
364         if (core_read_mem(core, &pss, sizeof(pss), psstrings, true) == -1)
365                 return (NULL);
366         if (type == PSC_TYPE_ARGV) {
367                 addr = (vm_offset_t)pss.ps_argvstr;
368                 nstr = pss.ps_nargvstr;
369         } else /* type == PSC_TYPE_ENVV */ {
370                 addr = (vm_offset_t)pss.ps_envstr;
371                 nstr = pss.ps_nenvstr;
372         }
373         if (addr == 0 || nstr == 0)
374                 return (NULL);
375         if (nstr > ARG_MAX) {
376                 warnx("format error");
377                 return (NULL);
378         }
379         size = nstr * sizeof(char *);
380         argv = malloc(size);
381         if (argv == NULL) {
382                 warn("malloc(%zu)", size);
383                 return (NULL);
384         }
385         done = 0;
386         freeargs = NULL;
387         if (core_read_mem(core, argv, size, addr, true) == -1)
388                 goto fail;
389         if (args != NULL) {
390                 nchr = MIN(ARG_MAX, *lenp);
391         } else {
392                 nchr = ARG_MAX;
393                 freeargs = args = malloc(nchr);
394                 if (args == NULL) {
395                         warn("malloc(%zu)", nchr);
396                         goto fail;
397                 }
398         }
399         p = args;
400         for (i = 0; ; i++) {
401                 if (i == nstr)
402                         goto done;
403                 /*
404                  * The program may have scribbled into its argv array, e.g. to
405                  * remove some arguments.  If that has happened, break out
406                  * before trying to read from NULL.
407                  */
408                 if (argv[i] == NULL)
409                         goto done;
410                 for (addr = (vm_offset_t)argv[i]; ; addr += chunksz) {
411                         chunksz = MIN(ARGS_CHUNK_SZ, nchr - 1 - done);
412                         if (chunksz <= 0)
413                                 goto done;
414                         n = core_read_mem(core, p, chunksz, addr, false);
415                         if (n == -1)
416                                 goto fail;
417                         len = strnlen(p, chunksz);
418                         p += len;
419                         done += len;
420                         if (len != chunksz)
421                                 break;
422                 }
423                 *p++ = '\0';
424                 done++;
425         }
426 fail:
427         free(freeargs);
428         args = NULL;
429 done:
430         *lenp = done;
431         free(argv);
432         return (args);
433 }