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