1 /* $NetBSD: readelf.c,v 1.27 2023/08/18 19:00:11 christos Exp $ */
2
3 /*
4 * Copyright (c) Christos Zoulas 2003.
5 * All Rights Reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice immediately at the beginning of the file, without modification,
12 * 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 AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21 * 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 #include "file.h"
30
31 #ifndef lint
32 #if 0
33 FILE_RCSID("@(#)$File: readelf.c,v 1.190 2023/07/27 19:39:06 christos Exp $")
34 #else
35 __RCSID("$NetBSD: readelf.c,v 1.27 2023/08/18 19:00:11 christos Exp $");
36 #endif
37 #endif
38
39 #ifdef BUILTIN_ELF
40 #include <string.h>
41 #include <ctype.h>
42 #include <stdlib.h>
43 #ifdef HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
46
47 #include "readelf.h"
48 #include "magic.h"
49
50 #ifdef ELFCORE
51 file_private int dophn_core(struct magic_set *, int, int, int, off_t, int,
52 size_t, off_t, int *, uint16_t *);
53 #endif
54 file_private int dophn_exec(struct magic_set *, int, int, int, off_t, int,
55 size_t, off_t, int, int *, uint16_t *);
56 file_private int doshn(struct magic_set *, int, int, int, off_t, int, size_t,
57 off_t, int, int, int *, uint16_t *);
58 file_private size_t donote(struct magic_set *, void *, size_t, size_t, int,
59 int, size_t, int *, uint16_t *, int, off_t, int, off_t);
60
61 #define ELF_ALIGN(a) ((((a) + align - 1) / align) * align)
62
63 #define isquote(c) (strchr("'\"`", (c)) != NULL)
64
65 file_private uint16_t getu16(int, uint16_t);
66 file_private uint32_t getu32(int, uint32_t);
67 file_private uint64_t getu64(int, uint64_t);
68
69 #define SIZE_UNKNOWN CAST(off_t, -1)
70
71 file_private int
toomany(struct magic_set * ms,const char * name,uint16_t num)72 toomany(struct magic_set *ms, const char *name, uint16_t num)
73 {
74 if (ms->flags & MAGIC_MIME)
75 return 1;
76 if (file_printf(ms, ", too many %s (%u)", name, num) == -1)
77 return -1;
78 return 1;
79 }
80
81 file_private uint16_t
getu16(int swap,uint16_t value)82 getu16(int swap, uint16_t value)
83 {
84 union {
85 uint16_t ui;
86 char c[2];
87 } retval, tmpval;
88
89 if (swap) {
90 tmpval.ui = value;
91
92 retval.c[0] = tmpval.c[1];
93 retval.c[1] = tmpval.c[0];
94
95 return retval.ui;
96 } else
97 return value;
98 }
99
100 file_private uint32_t
getu32(int swap,uint32_t value)101 getu32(int swap, uint32_t value)
102 {
103 union {
104 uint32_t ui;
105 char c[4];
106 } retval, tmpval;
107
108 if (swap) {
109 tmpval.ui = value;
110
111 retval.c[0] = tmpval.c[3];
112 retval.c[1] = tmpval.c[2];
113 retval.c[2] = tmpval.c[1];
114 retval.c[3] = tmpval.c[0];
115
116 return retval.ui;
117 } else
118 return value;
119 }
120
121 file_private uint64_t
getu64(int swap,uint64_t value)122 getu64(int swap, uint64_t value)
123 {
124 union {
125 uint64_t ui;
126 char c[8];
127 } retval, tmpval;
128
129 if (swap) {
130 tmpval.ui = value;
131
132 retval.c[0] = tmpval.c[7];
133 retval.c[1] = tmpval.c[6];
134 retval.c[2] = tmpval.c[5];
135 retval.c[3] = tmpval.c[4];
136 retval.c[4] = tmpval.c[3];
137 retval.c[5] = tmpval.c[2];
138 retval.c[6] = tmpval.c[1];
139 retval.c[7] = tmpval.c[0];
140
141 return retval.ui;
142 } else
143 return value;
144 }
145
146 #define elf_getu16(swap, value) getu16(swap, value)
147 #define elf_getu32(swap, value) getu32(swap, value)
148 #define elf_getu64(swap, value) getu64(swap, value)
149
150 #define xsh_addr (clazz == ELFCLASS32 \
151 ? CAST(void *, &sh32) \
152 : CAST(void *, &sh64))
153 #define xsh_sizeof (clazz == ELFCLASS32 \
154 ? sizeof(sh32) \
155 : sizeof(sh64))
156 #define xsh_size CAST(size_t, (clazz == ELFCLASS32 \
157 ? elf_getu32(swap, sh32.sh_size) \
158 : elf_getu64(swap, sh64.sh_size)))
159 #define xsh_offset CAST(off_t, (clazz == ELFCLASS32 \
160 ? elf_getu32(swap, sh32.sh_offset) \
161 : elf_getu64(swap, sh64.sh_offset)))
162 #define xsh_type (clazz == ELFCLASS32 \
163 ? elf_getu32(swap, sh32.sh_type) \
164 : elf_getu32(swap, sh64.sh_type))
165 #define xsh_name (clazz == ELFCLASS32 \
166 ? elf_getu32(swap, sh32.sh_name) \
167 : elf_getu32(swap, sh64.sh_name))
168
169 #define xph_addr (clazz == ELFCLASS32 \
170 ? CAST(void *, &ph32) \
171 : CAST(void *, &ph64))
172 #define xph_sizeof (clazz == ELFCLASS32 \
173 ? sizeof(ph32) \
174 : sizeof(ph64))
175 #define xph_type (clazz == ELFCLASS32 \
176 ? elf_getu32(swap, ph32.p_type) \
177 : elf_getu32(swap, ph64.p_type))
178 #define xph_offset CAST(off_t, (clazz == ELFCLASS32 \
179 ? elf_getu32(swap, ph32.p_offset) \
180 : elf_getu64(swap, ph64.p_offset)))
181 #define xph_align CAST(size_t, (clazz == ELFCLASS32 \
182 ? CAST(off_t, (ph32.p_align ? \
183 elf_getu32(swap, ph32.p_align) : 4))\
184 : CAST(off_t, (ph64.p_align ? \
185 elf_getu64(swap, ph64.p_align) : 4))))
186 #define xph_vaddr CAST(size_t, (clazz == ELFCLASS32 \
187 ? CAST(off_t, (ph32.p_vaddr ? \
188 elf_getu32(swap, ph32.p_vaddr) : 4))\
189 : CAST(off_t, (ph64.p_vaddr ? \
190 elf_getu64(swap, ph64.p_vaddr) : 4))))
191 #define xph_filesz CAST(size_t, (clazz == ELFCLASS32 \
192 ? elf_getu32(swap, ph32.p_filesz) \
193 : elf_getu64(swap, ph64.p_filesz)))
194 #define xph_memsz CAST(size_t, ((clazz == ELFCLASS32 \
195 ? elf_getu32(swap, ph32.p_memsz) \
196 : elf_getu64(swap, ph64.p_memsz))))
197 #define xnh_addr (clazz == ELFCLASS32 \
198 ? CAST(void *, &nh32) \
199 : CAST(void *, &nh64))
200 #define xnh_sizeof (clazz == ELFCLASS32 \
201 ? sizeof(nh32) \
202 : sizeof(nh64))
203 #define xnh_type (clazz == ELFCLASS32 \
204 ? elf_getu32(swap, nh32.n_type) \
205 : elf_getu32(swap, nh64.n_type))
206 #define xnh_namesz (clazz == ELFCLASS32 \
207 ? elf_getu32(swap, nh32.n_namesz) \
208 : elf_getu32(swap, nh64.n_namesz))
209 #define xnh_descsz (clazz == ELFCLASS32 \
210 ? elf_getu32(swap, nh32.n_descsz) \
211 : elf_getu32(swap, nh64.n_descsz))
212
213 #define xdh_addr (clazz == ELFCLASS32 \
214 ? CAST(void *, &dh32) \
215 : CAST(void *, &dh64))
216 #define xdh_sizeof (clazz == ELFCLASS32 \
217 ? sizeof(dh32) \
218 : sizeof(dh64))
219 #define xdh_tag (clazz == ELFCLASS32 \
220 ? elf_getu32(swap, dh32.d_tag) \
221 : elf_getu64(swap, dh64.d_tag))
222 #define xdh_val (clazz == ELFCLASS32 \
223 ? elf_getu32(swap, dh32.d_un.d_val) \
224 : elf_getu64(swap, dh64.d_un.d_val))
225
226 #define xcap_addr (clazz == ELFCLASS32 \
227 ? CAST(void *, &cap32) \
228 : CAST(void *, &cap64))
229 #define xcap_sizeof (clazz == ELFCLASS32 \
230 ? sizeof(cap32) \
231 : sizeof(cap64))
232 #define xcap_tag (clazz == ELFCLASS32 \
233 ? elf_getu32(swap, cap32.c_tag) \
234 : elf_getu64(swap, cap64.c_tag))
235 #define xcap_val (clazz == ELFCLASS32 \
236 ? elf_getu32(swap, cap32.c_un.c_val) \
237 : elf_getu64(swap, cap64.c_un.c_val))
238
239 #define xauxv_addr (clazz == ELFCLASS32 \
240 ? CAST(void *, &auxv32) \
241 : CAST(void *, &auxv64))
242 #define xauxv_sizeof (clazz == ELFCLASS32 \
243 ? sizeof(auxv32) \
244 : sizeof(auxv64))
245 #define xauxv_type (clazz == ELFCLASS32 \
246 ? elf_getu32(swap, auxv32.a_type) \
247 : elf_getu64(swap, auxv64.a_type))
248 #define xauxv_val (clazz == ELFCLASS32 \
249 ? elf_getu32(swap, auxv32.a_v) \
250 : elf_getu64(swap, auxv64.a_v))
251
252 #define prpsoffsets(i) (clazz == ELFCLASS32 \
253 ? prpsoffsets32[i] \
254 : prpsoffsets64[i])
255
256 #ifdef ELFCORE
257 /*
258 * Try larger offsets first to avoid false matches
259 * from earlier data that happen to look like strings.
260 */
261 static const size_t prpsoffsets32[] = {
262 #ifdef USE_NT_PSINFO
263 104, /* SunOS 5.x (command line) */
264 88, /* SunOS 5.x (short name) */
265 #endif /* USE_NT_PSINFO */
266
267 100, /* SunOS 5.x (command line) */
268 84, /* SunOS 5.x (short name) */
269
270 44, /* Linux (command line) */
271 28, /* Linux (short name) */
272
273 48, /* Linux PowerPC (command line) */
274 32, /* Linux PowerPC (short name) */
275
276 8, /* FreeBSD */
277 };
278
279 static const size_t prpsoffsets64[] = {
280 #ifdef USE_NT_PSINFO
281 152, /* SunOS 5.x (command line) */
282 136, /* SunOS 5.x (short name) */
283 #endif /* USE_NT_PSINFO */
284
285 136, /* SunOS 5.x, 64-bit (command line) */
286 120, /* SunOS 5.x, 64-bit (short name) */
287
288 56, /* Linux (command line) */
289 40, /* Linux (tested on core from 2.4.x, short name) */
290
291 16, /* FreeBSD, 64-bit */
292 };
293
294 #define NOFFSETS32 __arraycount(prpsoffsets32)
295 #define NOFFSETS64 __arraycount(prpsoffsets64)
296
297 #define NOFFSETS (clazz == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64)
298
299 /*
300 * Look through the program headers of an executable image, searching
301 * for a PT_NOTE section of type NT_PRPSINFO, with a name "CORE" or
302 * "FreeBSD"; if one is found, try looking in various places in its
303 * contents for a 16-character string containing only printable
304 * characters - if found, that string should be the name of the program
305 * that dropped core. Note: right after that 16-character string is,
306 * at least in SunOS 5.x (and possibly other SVR4-flavored systems) and
307 * Linux, a longer string (80 characters, in 5.x, probably other
308 * SVR4-flavored systems, and Linux) containing the start of the
309 * command line for that program.
310 *
311 * SunOS 5.x core files contain two PT_NOTE sections, with the types
312 * NT_PRPSINFO (old) and NT_PSINFO (new). These structs contain the
313 * same info about the command name and command line, so it probably
314 * isn't worthwhile to look for NT_PSINFO, but the offsets are provided
315 * above (see USE_NT_PSINFO), in case we ever decide to do so. The
316 * NT_PRPSINFO and NT_PSINFO sections are always in order and adjacent;
317 * the SunOS 5.x file command relies on this (and prefers the latter).
318 *
319 * The signal number probably appears in a section of type NT_PRSTATUS,
320 * but that's also rather OS-dependent, in ways that are harder to
321 * dissect with heuristics, so I'm not bothering with the signal number.
322 * (I suppose the signal number could be of interest in situations where
323 * you don't have the binary of the program that dropped core; if you
324 * *do* have that binary, the debugger will probably tell you what
325 * signal it was.)
326 */
327
328 #define OS_STYLE_SVR4 0
329 #define OS_STYLE_FREEBSD 1
330 #define OS_STYLE_NETBSD 2
331
332 file_private const char os_style_names[][8] = {
333 "SVR4",
334 "FreeBSD",
335 "NetBSD",
336 };
337
338 #define FLAGS_CORE_STYLE 0x0003
339
340 #define FLAGS_DID_CORE 0x0004
341 #define FLAGS_DID_OS_NOTE 0x0008
342 #define FLAGS_DID_BUILD_ID 0x0010
343 #define FLAGS_DID_CORE_STYLE 0x0020
344 #define FLAGS_DID_NETBSD_PAX 0x0040
345 #define FLAGS_DID_NETBSD_MARCH 0x0080
346 #define FLAGS_DID_NETBSD_CMODEL 0x0100
347 #define FLAGS_DID_NETBSD_EMULATION 0x0200
348 #define FLAGS_DID_NETBSD_UNKNOWN 0x0400
349 #define FLAGS_IS_CORE 0x0800
350 #define FLAGS_DID_AUXV 0x1000
351
352 file_private int
dophn_core(struct magic_set * ms,int clazz,int swap,int fd,off_t off,int num,size_t size,off_t fsize,int * flags,uint16_t * notecount)353 dophn_core(struct magic_set *ms, int clazz, int swap, int fd, off_t off,
354 int num, size_t size, off_t fsize, int *flags, uint16_t *notecount)
355 {
356 Elf32_Phdr ph32;
357 Elf64_Phdr ph64;
358 size_t offset, len;
359 unsigned char nbuf[BUFSIZ];
360 ssize_t bufsize;
361 off_t ph_off = off, offs;
362 int ph_num = num;
363
364 if (ms->flags & MAGIC_MIME)
365 return 0;
366
367 if (num == 0) {
368 if (file_printf(ms, ", no program header") == -1)
369 return -1;
370 return 0;
371 }
372 if (size != xph_sizeof) {
373 if (file_printf(ms, ", corrupted program header size") == -1)
374 return -1;
375 return 0;
376 }
377
378 /*
379 * Loop through all the program headers.
380 */
381 for ( ; num; num--) {
382 if (pread(fd, xph_addr, xph_sizeof, off) <
383 CAST(ssize_t, xph_sizeof)) {
384 if (file_printf(ms,
385 ", can't read elf program headers at %jd",
386 (intmax_t)off) == -1)
387 return -1;
388 return 0;
389 }
390 off += size;
391
392 if (fsize != SIZE_UNKNOWN && xph_offset > fsize) {
393 /* Perhaps warn here */
394 continue;
395 }
396
397 if (xph_type != PT_NOTE)
398 continue;
399
400 /*
401 * This is a PT_NOTE section; loop through all the notes
402 * in the section.
403 */
404 len = xph_filesz < sizeof(nbuf) ? xph_filesz : sizeof(nbuf);
405 offs = xph_offset;
406 if ((bufsize = pread(fd, nbuf, len, offs)) == -1) {
407 if (file_printf(ms, " can't read note section at %jd",
408 (intmax_t)offs) == -1)
409 return -1;
410 return 0;
411 }
412 offset = 0;
413 for (;;) {
414 if (offset >= CAST(size_t, bufsize))
415 break;
416 offset = donote(ms, nbuf, offset, CAST(size_t, bufsize),
417 clazz, swap, 4, flags, notecount, fd, ph_off,
418 ph_num, fsize);
419 if (offset == 0)
420 break;
421
422 }
423 }
424 return 0;
425 }
426 #endif
427
428 static int
do_note_netbsd_version(struct magic_set * ms,int swap,void * v)429 do_note_netbsd_version(struct magic_set *ms, int swap, void *v)
430 {
431 uint32_t desc;
432 memcpy(&desc, v, sizeof(desc));
433 desc = elf_getu32(swap, desc);
434
435 if (file_printf(ms, ", for NetBSD") == -1)
436 return -1;
437 /*
438 * The version number used to be stuck as 199905, and was thus
439 * basically content-free. Newer versions of NetBSD have fixed
440 * this and now use the encoding of __NetBSD_Version__:
441 *
442 * MMmmrrpp00
443 *
444 * M = major version
445 * m = minor version
446 * r = release ["",A-Z,Z[A-Z] but numeric]
447 * p = patchlevel
448 */
449 if (desc > 100000000U) {
450 uint32_t ver_patch = (desc / 100) % 100;
451 uint32_t ver_rel = (desc / 10000) % 100;
452 uint32_t ver_min = (desc / 1000000) % 100;
453 uint32_t ver_maj = desc / 100000000;
454
455 if (file_printf(ms, " %u.%u", ver_maj, ver_min) == -1)
456 return -1;
457 if (ver_maj >= 9) {
458 ver_patch += 100 * ver_rel;
459 ver_rel = 0;
460 }
461 if (ver_rel == 0 && ver_patch != 0) {
462 if (file_printf(ms, ".%u", ver_patch) == -1)
463 return -1;
464 } else if (ver_rel != 0) {
465 while (ver_rel > 26) {
466 if (file_printf(ms, "Z") == -1)
467 return -1;
468 ver_rel -= 26;
469 }
470 if (file_printf(ms, "%c", 'A' + ver_rel - 1) == -1)
471 return -1;
472 }
473 }
474 return 0;
475 }
476
477 static int
do_note_freebsd_version(struct magic_set * ms,int swap,void * v)478 do_note_freebsd_version(struct magic_set *ms, int swap, void *v)
479 {
480 uint32_t desc;
481
482 memcpy(&desc, v, sizeof(desc));
483 desc = elf_getu32(swap, desc);
484 if (file_printf(ms, ", for FreeBSD") == -1)
485 return -1;
486
487 /*
488 * Contents is __FreeBSD_version, whose relation to OS
489 * versions is defined by a huge table in the Porter's
490 * Handbook. This is the general scheme:
491 *
492 * Releases:
493 * Mmp000 (before 4.10)
494 * Mmi0p0 (before 5.0)
495 * Mmm0p0
496 *
497 * Development branches:
498 * Mmpxxx (before 4.6)
499 * Mmp1xx (before 4.10)
500 * Mmi1xx (before 5.0)
501 * M000xx (pre-M.0)
502 * Mmm1xx
503 *
504 * M = major version
505 * m = minor version
506 * i = minor version increment (491000 -> 4.10)
507 * p = patchlevel
508 * x = revision
509 *
510 * The first release of FreeBSD to use ELF by default
511 * was version 3.0.
512 */
513 if (desc == 460002) {
514 if (file_printf(ms, " 4.6.2") == -1)
515 return -1;
516 } else if (desc < 460100) {
517 if (file_printf(ms, " %d.%d", desc / 100000,
518 desc / 10000 % 10) == -1)
519 return -1;
520 if (desc / 1000 % 10 > 0)
521 if (file_printf(ms, ".%d", desc / 1000 % 10) == -1)
522 return -1;
523 if ((desc % 1000 > 0) || (desc % 100000 == 0))
524 if (file_printf(ms, " (%d)", desc) == -1)
525 return -1;
526 } else if (desc < 500000) {
527 if (file_printf(ms, " %d.%d", desc / 100000,
528 desc / 10000 % 10 + desc / 1000 % 10) == -1)
529 return -1;
530 if (desc / 100 % 10 > 0) {
531 if (file_printf(ms, " (%d)", desc) == -1)
532 return -1;
533 } else if (desc / 10 % 10 > 0) {
534 if (file_printf(ms, ".%d", desc / 10 % 10) == -1)
535 return -1;
536 }
537 } else {
538 if (file_printf(ms, " %d.%d", desc / 100000,
539 desc / 1000 % 100) == -1)
540 return -1;
541 if ((desc / 100 % 10 > 0) ||
542 (desc % 100000 / 100 == 0)) {
543 if (file_printf(ms, " (%d)", desc) == -1)
544 return -1;
545 } else if (desc / 10 % 10 > 0) {
546 if (file_printf(ms, ".%d", desc / 10 % 10) == -1)
547 return -1;
548 }
549 }
550 return 0;
551 }
552
553 file_private int
554 /*ARGSUSED*/
do_bid_note(struct magic_set * ms,unsigned char * nbuf,uint32_t type,int swap,uint32_t namesz,uint32_t descsz,size_t noff,size_t doff,int * flags)555 do_bid_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
556 int swap __attribute__((__unused__)), uint32_t namesz, uint32_t descsz,
557 size_t noff, size_t doff, int *flags)
558 {
559 if (namesz == 4 && strcmp(RCAST(char *, &nbuf[noff]), "GNU") == 0 &&
560 type == NT_GNU_BUILD_ID && (descsz >= 4 && descsz <= 20)) {
561 uint8_t desc[20];
562 const char *btype;
563 uint32_t i;
564 *flags |= FLAGS_DID_BUILD_ID;
565 switch (descsz) {
566 case 8:
567 btype = "xxHash";
568 break;
569 case 16:
570 btype = "md5/uuid";
571 break;
572 case 20:
573 btype = "sha1";
574 break;
575 default:
576 btype = "unknown";
577 break;
578 }
579 if (file_printf(ms, ", BuildID[%s]=", btype) == -1)
580 return -1;
581 memcpy(desc, &nbuf[doff], descsz);
582 for (i = 0; i < descsz; i++)
583 if (file_printf(ms, "%02x", desc[i]) == -1)
584 return -1;
585 return 1;
586 }
587 if (namesz == 4 && strcmp(RCAST(char *, &nbuf[noff]), "Go") == 0 &&
588 type == NT_GO_BUILD_ID && descsz < 128) {
589 char buf[256];
590 if (file_printf(ms, ", Go BuildID=%s",
591 file_copystr(buf, sizeof(buf), descsz,
592 RCAST(const char *, &nbuf[doff]))) == -1)
593 return -1;
594 return 1;
595 }
596 return 0;
597 }
598
599 file_private int
do_os_note(struct magic_set * ms,unsigned char * nbuf,uint32_t type,int swap,uint32_t namesz,uint32_t descsz,size_t noff,size_t doff,int * flags)600 do_os_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
601 int swap, uint32_t namesz, uint32_t descsz,
602 size_t noff, size_t doff, int *flags)
603 {
604 const char *name = RCAST(const char *, &nbuf[noff]);
605
606 if (namesz == 5 && strcmp(name, "SuSE") == 0 &&
607 type == NT_GNU_VERSION && descsz == 2) {
608 *flags |= FLAGS_DID_OS_NOTE;
609 if (file_printf(ms, ", for SuSE %d.%d", nbuf[doff],
610 nbuf[doff + 1]) == -1)
611 return -1;
612 return 1;
613 }
614
615 if (namesz == 4 && strcmp(name, "GNU") == 0 &&
616 type == NT_GNU_VERSION && descsz == 16) {
617 uint32_t desc[4];
618 memcpy(desc, &nbuf[doff], sizeof(desc));
619
620 *flags |= FLAGS_DID_OS_NOTE;
621 if (file_printf(ms, ", for GNU/") == -1)
622 return -1;
623 switch (elf_getu32(swap, desc[0])) {
624 case GNU_OS_LINUX:
625 if (file_printf(ms, "Linux") == -1)
626 return -1;
627 break;
628 case GNU_OS_HURD:
629 if (file_printf(ms, "Hurd") == -1)
630 return -1;
631 break;
632 case GNU_OS_SOLARIS:
633 if (file_printf(ms, "Solaris") == -1)
634 return -1;
635 break;
636 case GNU_OS_KFREEBSD:
637 if (file_printf(ms, "kFreeBSD") == -1)
638 return -1;
639 break;
640 case GNU_OS_KNETBSD:
641 if (file_printf(ms, "kNetBSD") == -1)
642 return -1;
643 break;
644 default:
645 if (file_printf(ms, "<unknown>") == -1)
646 return -1;
647 }
648 if (file_printf(ms, " %d.%d.%d", elf_getu32(swap, desc[1]),
649 elf_getu32(swap, desc[2]), elf_getu32(swap, desc[3])) == -1)
650 return -1;
651 return 1;
652 }
653
654 if (namesz == 7 && strcmp(name, "NetBSD") == 0) {
655 if (type == NT_NETBSD_VERSION && descsz == 4) {
656 *flags |= FLAGS_DID_OS_NOTE;
657 if (do_note_netbsd_version(ms, swap, &nbuf[doff]) == -1)
658 return -1;
659 return 1;
660 }
661 }
662
663 if (namesz == 8 && strcmp(name, "FreeBSD") == 0) {
664 if (type == NT_FREEBSD_VERSION && descsz == 4) {
665 *flags |= FLAGS_DID_OS_NOTE;
666 if (do_note_freebsd_version(ms, swap, &nbuf[doff])
667 == -1)
668 return -1;
669 return 1;
670 }
671 }
672
673 if (namesz == 8 && strcmp(name, "OpenBSD") == 0 &&
674 type == NT_OPENBSD_VERSION && descsz == 4) {
675 *flags |= FLAGS_DID_OS_NOTE;
676 if (file_printf(ms, ", for OpenBSD") == -1)
677 return -1;
678 /* Content of note is always 0 */
679 return 1;
680 }
681
682 if (namesz == 10 && strcmp(name, "DragonFly") == 0 &&
683 type == NT_DRAGONFLY_VERSION && descsz == 4) {
684 uint32_t desc;
685 *flags |= FLAGS_DID_OS_NOTE;
686 if (file_printf(ms, ", for DragonFly") == -1)
687 return -1;
688 memcpy(&desc, &nbuf[doff], sizeof(desc));
689 desc = elf_getu32(swap, desc);
690 if (file_printf(ms, " %d.%d.%d", desc / 100000,
691 desc / 10000 % 10, desc % 10000) == -1)
692 return -1;
693 return 1;
694 }
695 return 0;
696 }
697
698 file_private int
do_pax_note(struct magic_set * ms,unsigned char * nbuf,uint32_t type,int swap,uint32_t namesz,uint32_t descsz,size_t noff,size_t doff,int * flags)699 do_pax_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
700 int swap, uint32_t namesz, uint32_t descsz,
701 size_t noff, size_t doff, int *flags)
702 {
703 const char *name = RCAST(const char *, &nbuf[noff]);
704
705 if (namesz == 4 && strcmp(name, "PaX") == 0 &&
706 type == NT_NETBSD_PAX && descsz == 4) {
707 static const char *pax[] = {
708 "+mprotect",
709 "-mprotect",
710 "+segvguard",
711 "-segvguard",
712 "+ASLR",
713 "-ASLR",
714 };
715 uint32_t desc;
716 size_t i;
717 int did = 0;
718
719 *flags |= FLAGS_DID_NETBSD_PAX;
720 memcpy(&desc, &nbuf[doff], sizeof(desc));
721 desc = elf_getu32(swap, desc);
722
723 if (desc && file_printf(ms, ", PaX: ") == -1)
724 return -1;
725
726 for (i = 0; i < __arraycount(pax); i++) {
727 if (((1 << CAST(int, i)) & desc) == 0)
728 continue;
729 if (file_printf(ms, "%s%s", did++ ? "," : "",
730 pax[i]) == -1)
731 return -1;
732 }
733 return 1;
734 }
735 return 0;
736 }
737
738 file_private int
do_core_note(struct magic_set * ms,unsigned char * nbuf,uint32_t type,int swap,uint32_t namesz,uint32_t descsz,size_t noff,size_t doff,int * flags,size_t size,int clazz)739 do_core_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
740 int swap, uint32_t namesz, uint32_t descsz,
741 size_t noff, size_t doff, int *flags, size_t size, int clazz)
742 {
743 #ifdef ELFCORE
744 char buf[256];
745 const char *name = RCAST(const char *, &nbuf[noff]);
746
747 int os_style = -1;
748 /*
749 * Sigh. The 2.0.36 kernel in Debian 2.1, at
750 * least, doesn't correctly implement name
751 * sections, in core dumps, as specified by
752 * the "Program Linking" section of "UNIX(R) System
753 * V Release 4 Programmer's Guide: ANSI C and
754 * Programming Support Tools", because my copy
755 * clearly says "The first 'namesz' bytes in 'name'
756 * contain a *null-terminated* [emphasis mine]
757 * character representation of the entry's owner
758 * or originator", but the 2.0.36 kernel code
759 * doesn't include the terminating null in the
760 * name....
761 */
762 if ((namesz == 4 && strncmp(name, "CORE", 4) == 0) ||
763 (namesz == 5 && strcmp(name, "CORE") == 0)) {
764 os_style = OS_STYLE_SVR4;
765 }
766
767 if ((namesz == 8 && strcmp(name, "FreeBSD") == 0)) {
768 os_style = OS_STYLE_FREEBSD;
769 }
770
771 if ((namesz >= 11 && strncmp(name, "NetBSD-CORE", 11)
772 == 0)) {
773 os_style = OS_STYLE_NETBSD;
774 }
775
776 if (os_style != -1 && (*flags & FLAGS_DID_CORE_STYLE) == 0) {
777 if (file_printf(ms, ", %s-style", os_style_names[os_style])
778 == -1)
779 return -1;
780 *flags |= FLAGS_DID_CORE_STYLE;
781 *flags |= os_style;
782 }
783
784 switch (os_style) {
785 case OS_STYLE_NETBSD:
786 if (type == NT_NETBSD_CORE_PROCINFO) {
787 char sbuf[512];
788 struct NetBSD_elfcore_procinfo pi;
789 memset(&pi, 0, sizeof(pi));
790 memcpy(&pi, nbuf + doff, MIN(descsz, sizeof(pi)));
791
792 if (file_printf(ms, ", from '%.31s', pid=%u, uid=%u, "
793 "gid=%u, nlwps=%u, lwp=%u (signal %u/code %u)",
794 file_printable(ms, sbuf, sizeof(sbuf),
795 RCAST(char *, pi.cpi_name), sizeof(pi.cpi_name)),
796 elf_getu32(swap, CAST(uint32_t, pi.cpi_pid)),
797 elf_getu32(swap, pi.cpi_euid),
798 elf_getu32(swap, pi.cpi_egid),
799 elf_getu32(swap, pi.cpi_nlwps),
800 elf_getu32(swap, CAST(uint32_t, pi.cpi_siglwp)),
801 elf_getu32(swap, pi.cpi_signo),
802 elf_getu32(swap, pi.cpi_sigcode)) == -1)
803 return -1;
804
805 *flags |= FLAGS_DID_CORE;
806 return 1;
807 }
808 break;
809
810 case OS_STYLE_FREEBSD:
811 if (type == NT_PRPSINFO && *flags & FLAGS_IS_CORE) {
812 size_t argoff, pidoff;
813
814 if (clazz == ELFCLASS32)
815 argoff = 4 + 4 + 17;
816 else
817 argoff = 4 + 4 + 8 + 17;
818 if (file_printf(ms, ", from '%.80s'", nbuf + doff +
819 argoff) == -1)
820 return -1;
821 pidoff = argoff + 81 + 2;
822 if (doff + pidoff + 4 <= size) {
823 if (file_printf(ms, ", pid=%u",
824 elf_getu32(swap, *RCAST(uint32_t *, (nbuf +
825 doff + pidoff)))) == -1)
826 return -1;
827 }
828 *flags |= FLAGS_DID_CORE;
829 }
830 break;
831
832 default:
833 if (type == NT_PRPSINFO && *flags & FLAGS_IS_CORE) {
834 size_t i, j;
835 unsigned char c;
836 /*
837 * Extract the program name. We assume
838 * it to be 16 characters (that's what it
839 * is in SunOS 5.x and Linux).
840 *
841 * Unfortunately, it's at a different offset
842 * in various OSes, so try multiple offsets.
843 * If the characters aren't all printable,
844 * reject it.
845 */
846 for (i = 0; i < NOFFSETS; i++) {
847 unsigned char *cname, *cp;
848 size_t reloffset = prpsoffsets(i);
849 size_t noffset = doff + reloffset;
850 size_t k;
851 for (j = 0; j < 16; j++, noffset++,
852 reloffset++) {
853 /*
854 * Make sure we're not past
855 * the end of the buffer; if
856 * we are, just give up.
857 */
858 if (noffset >= size)
859 goto tryanother;
860
861 /*
862 * Make sure we're not past
863 * the end of the contents;
864 * if we are, this obviously
865 * isn't the right offset.
866 */
867 if (reloffset >= descsz)
868 goto tryanother;
869
870 c = nbuf[noffset];
871 if (c == '\0') {
872 /*
873 * A '\0' at the
874 * beginning is
875 * obviously wrong.
876 * Any other '\0'
877 * means we're done.
878 */
879 if (j == 0)
880 goto tryanother;
881 else
882 break;
883 } else {
884 /*
885 * A nonprintable
886 * character is also
887 * wrong.
888 */
889 if (!isprint(c) || isquote(c))
890 goto tryanother;
891 }
892 }
893 /*
894 * Well, that worked.
895 */
896
897 /*
898 * Try next offsets, in case this match is
899 * in the middle of a string.
900 */
901 for (k = i + 1 ; k < NOFFSETS; k++) {
902 size_t no;
903 int adjust = 1;
904 if (prpsoffsets(k) >= prpsoffsets(i))
905 continue;
906 /*
907 * pr_fname == pr_psargs - 16 &&
908 * non-nul-terminated fname (qemu)
909 */
910 if (prpsoffsets(k) ==
911 prpsoffsets(i) - 16 && j == 16)
912 continue;
913 for (no = doff + prpsoffsets(k);
914 no < doff + prpsoffsets(i); no++)
915 adjust = adjust
916 && isprint(nbuf[no]);
917 if (adjust)
918 i = k;
919 }
920
921 cname = CAST(unsigned char *,
922 &nbuf[doff + prpsoffsets(i)]);
923 for (cp = cname; cp < nbuf + size && *cp
924 && isprint(*cp); cp++)
925 continue;
926 /*
927 * Linux apparently appends a space at the end
928 * of the command line: remove it.
929 */
930 while (cp > cname && isspace(cp[-1]))
931 cp--;
932 if (file_printf(ms, ", from '%s'",
933 file_copystr(buf, sizeof(buf),
934 CAST(size_t, cp - cname),
935 RCAST(char *, cname))) == -1)
936 return -1;
937 *flags |= FLAGS_DID_CORE;
938 return 1;
939
940 tryanother:
941 ;
942 }
943 }
944 break;
945 }
946 #endif
947 return 0;
948 }
949
950 file_private off_t
get_offset_from_virtaddr(struct magic_set * ms,int swap,int clazz,int fd,off_t off,int num,off_t fsize,uint64_t virtaddr)951 get_offset_from_virtaddr(struct magic_set *ms, int swap, int clazz, int fd,
952 off_t off, int num, off_t fsize, uint64_t virtaddr)
953 {
954 Elf32_Phdr ph32;
955 Elf64_Phdr ph64;
956
957 /*
958 * Loop through all the program headers and find the header with
959 * virtual address in which the "virtaddr" belongs to.
960 */
961 for ( ; num; num--) {
962 if (pread(fd, xph_addr, xph_sizeof, off) <
963 CAST(ssize_t, xph_sizeof)) {
964 if (file_printf(ms,
965 ", can't read elf program header at %jd",
966 (intmax_t)off) == -1)
967 return -1;
968 return 0;
969
970 }
971 off += xph_sizeof;
972
973 if (fsize != SIZE_UNKNOWN && xph_offset > fsize) {
974 /* Perhaps warn here */
975 continue;
976 }
977
978 if (virtaddr >= xph_vaddr && virtaddr < xph_vaddr + xph_filesz)
979 return xph_offset + (virtaddr - xph_vaddr);
980 }
981 return 0;
982 }
983
984 file_private size_t
get_string_on_virtaddr(struct magic_set * ms,int swap,int clazz,int fd,off_t ph_off,int ph_num,off_t fsize,uint64_t virtaddr,char * buf,ssize_t buflen)985 get_string_on_virtaddr(struct magic_set *ms,
986 int swap, int clazz, int fd, off_t ph_off, int ph_num,
987 off_t fsize, uint64_t virtaddr, char *buf, ssize_t buflen)
988 {
989 char *bptr;
990 off_t offset;
991
992 if (buflen == 0)
993 return 0;
994
995 offset = get_offset_from_virtaddr(ms, swap, clazz, fd, ph_off, ph_num,
996 fsize, virtaddr);
997 if (offset < 0 ||
998 (buflen = pread(fd, buf, CAST(size_t, buflen), offset)) <= 0) {
999 (void)file_printf(ms, ", can't read elf string at %jd",
1000 (intmax_t)offset);
1001 return 0;
1002 }
1003
1004 buf[buflen - 1] = '\0';
1005
1006 /* We expect only printable characters, so return if buffer contains
1007 * non-printable character before the '\0' or just '\0'. */
1008 for (bptr = buf; *bptr && isprint(CAST(unsigned char, *bptr)); bptr++)
1009 continue;
1010 if (*bptr != '\0')
1011 return 0;
1012
1013 return bptr - buf;
1014 }
1015
1016
1017 /*ARGSUSED*/
1018 file_private int
do_auxv_note(struct magic_set * ms,unsigned char * nbuf,uint32_t type,int swap,uint32_t namesz,uint32_t descsz,size_t noff,size_t doff,int * flags,size_t size,int clazz,int fd,off_t ph_off,int ph_num,off_t fsize)1019 do_auxv_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
1020 int swap, uint32_t namesz __attribute__((__unused__)),
1021 uint32_t descsz __attribute__((__unused__)),
1022 size_t noff __attribute__((__unused__)), size_t doff,
1023 int *flags, size_t size __attribute__((__unused__)), int clazz,
1024 int fd, off_t ph_off, int ph_num, off_t fsize)
1025 {
1026 #ifdef ELFCORE
1027 Aux32Info auxv32;
1028 Aux64Info auxv64;
1029 size_t elsize = xauxv_sizeof;
1030 const char *tag;
1031 int is_string;
1032 size_t nval, off;
1033
1034 if ((*flags & (FLAGS_IS_CORE|FLAGS_DID_CORE_STYLE)) !=
1035 (FLAGS_IS_CORE|FLAGS_DID_CORE_STYLE))
1036 return 0;
1037
1038 switch (*flags & FLAGS_CORE_STYLE) {
1039 case OS_STYLE_SVR4:
1040 if (type != NT_AUXV)
1041 return 0;
1042 break;
1043 #ifdef notyet
1044 case OS_STYLE_NETBSD:
1045 if (type != NT_NETBSD_CORE_AUXV)
1046 return 0;
1047 break;
1048 case OS_STYLE_FREEBSD:
1049 if (type != NT_FREEBSD_PROCSTAT_AUXV)
1050 return 0;
1051 break;
1052 #endif
1053 default:
1054 return 0;
1055 }
1056
1057 *flags |= FLAGS_DID_AUXV;
1058
1059 nval = 0;
1060 for (off = 0; off + elsize <= descsz; off += elsize) {
1061 memcpy(xauxv_addr, &nbuf[doff + off], xauxv_sizeof);
1062 /* Limit processing to 50 vector entries to prevent DoS */
1063 if (nval++ >= 50) {
1064 file_error(ms, 0, "Too many ELF Auxv elements");
1065 return 1;
1066 }
1067
1068 switch(xauxv_type) {
1069 case AT_LINUX_EXECFN:
1070 is_string = 1;
1071 tag = "execfn";
1072 break;
1073 case AT_LINUX_PLATFORM:
1074 is_string = 1;
1075 tag = "platform";
1076 break;
1077 case AT_LINUX_UID:
1078 is_string = 0;
1079 tag = "real uid";
1080 break;
1081 case AT_LINUX_GID:
1082 is_string = 0;
1083 tag = "real gid";
1084 break;
1085 case AT_LINUX_EUID:
1086 is_string = 0;
1087 tag = "effective uid";
1088 break;
1089 case AT_LINUX_EGID:
1090 is_string = 0;
1091 tag = "effective gid";
1092 break;
1093 default:
1094 is_string = 0;
1095 tag = NULL;
1096 break;
1097 }
1098
1099 if (tag == NULL)
1100 continue;
1101
1102 if (is_string) {
1103 char buf[256];
1104 ssize_t buflen;
1105 buflen = get_string_on_virtaddr(ms, swap, clazz, fd,
1106 ph_off, ph_num, fsize, xauxv_val, buf, sizeof(buf));
1107
1108 if (buflen == 0)
1109 continue;
1110
1111 if (file_printf(ms, ", %s: '%s'", tag, buf) == -1)
1112 return -1;
1113 } else {
1114 if (file_printf(ms, ", %s: %d", tag,
1115 CAST(int, xauxv_val)) == -1)
1116 return -1;
1117 }
1118 }
1119 return 1;
1120 #else
1121 return 0;
1122 #endif
1123 }
1124
1125 file_private size_t
dodynamic(struct magic_set * ms,void * vbuf,size_t offset,size_t size,int clazz,int swap,int * pie,size_t * need)1126 dodynamic(struct magic_set *ms, void *vbuf, size_t offset, size_t size,
1127 int clazz, int swap, int *pie, size_t *need)
1128 {
1129 Elf32_Dyn dh32;
1130 Elf64_Dyn dh64;
1131 unsigned char *dbuf = CAST(unsigned char *, vbuf);
1132
1133 if (xdh_sizeof + offset > size) {
1134 /*
1135 * We're out of note headers.
1136 */
1137 return xdh_sizeof + offset;
1138 }
1139
1140 memcpy(xdh_addr, &dbuf[offset], xdh_sizeof);
1141 offset += xdh_sizeof;
1142
1143 switch (xdh_tag) {
1144 case DT_FLAGS_1:
1145 *pie = 1;
1146 if (xdh_val & DF_1_PIE)
1147 ms->mode |= 0111;
1148 else
1149 ms->mode &= ~0111;
1150 break;
1151 case DT_NEEDED:
1152 (*need)++;
1153 break;
1154 default:
1155 break;
1156 }
1157 return offset;
1158 }
1159
1160
1161 file_private size_t
donote(struct magic_set * ms,void * vbuf,size_t offset,size_t size,int clazz,int swap,size_t align,int * flags,uint16_t * notecount,int fd,off_t ph_off,int ph_num,off_t fsize)1162 donote(struct magic_set *ms, void *vbuf, size_t offset, size_t size,
1163 int clazz, int swap, size_t align, int *flags, uint16_t *notecount,
1164 int fd, off_t ph_off, int ph_num, off_t fsize)
1165 {
1166 Elf32_Nhdr nh32;
1167 Elf64_Nhdr nh64;
1168 size_t noff, doff;
1169 uint32_t namesz, descsz;
1170 char buf[256];
1171 unsigned char *nbuf = CAST(unsigned char *, vbuf);
1172
1173 if (*notecount == 0)
1174 return 0;
1175 --*notecount;
1176
1177 if (xnh_sizeof + offset > size) {
1178 /*
1179 * We're out of note headers.
1180 */
1181 return xnh_sizeof + offset;
1182 }
1183 /*XXX: GCC */
1184 memset(&nh32, 0, sizeof(nh32));
1185 memset(&nh64, 0, sizeof(nh64));
1186
1187 memcpy(xnh_addr, &nbuf[offset], xnh_sizeof);
1188 offset += xnh_sizeof;
1189
1190 namesz = xnh_namesz;
1191 descsz = xnh_descsz;
1192
1193 if ((namesz == 0) && (descsz == 0)) {
1194 /*
1195 * We're out of note headers.
1196 */
1197 return (offset >= size) ? offset : size;
1198 }
1199
1200 if (namesz & 0x80000000) {
1201 (void)file_printf(ms, ", bad note name size %#lx",
1202 CAST(unsigned long, namesz));
1203 return 0;
1204 }
1205
1206 if (descsz & 0x80000000) {
1207 (void)file_printf(ms, ", bad note description size %#lx",
1208 CAST(unsigned long, descsz));
1209 return 0;
1210 }
1211
1212 noff = offset;
1213 doff = ELF_ALIGN(offset + namesz);
1214
1215 if (offset + namesz > size) {
1216 /*
1217 * We're past the end of the buffer.
1218 */
1219 return doff;
1220 }
1221
1222 offset = ELF_ALIGN(doff + descsz);
1223 if (doff + descsz > size) {
1224 /*
1225 * We're past the end of the buffer.
1226 */
1227 return (offset >= size) ? offset : size;
1228 }
1229
1230
1231 if ((*flags & FLAGS_DID_OS_NOTE) == 0) {
1232 if (do_os_note(ms, nbuf, xnh_type, swap,
1233 namesz, descsz, noff, doff, flags))
1234 return offset;
1235 }
1236
1237 if ((*flags & FLAGS_DID_BUILD_ID) == 0) {
1238 if (do_bid_note(ms, nbuf, xnh_type, swap,
1239 namesz, descsz, noff, doff, flags))
1240 return offset;
1241 }
1242
1243 if ((*flags & FLAGS_DID_NETBSD_PAX) == 0) {
1244 if (do_pax_note(ms, nbuf, xnh_type, swap,
1245 namesz, descsz, noff, doff, flags))
1246 return offset;
1247 }
1248
1249 if ((*flags & FLAGS_DID_CORE) == 0) {
1250 if (do_core_note(ms, nbuf, xnh_type, swap,
1251 namesz, descsz, noff, doff, flags, size, clazz))
1252 return offset;
1253 }
1254
1255 if ((*flags & FLAGS_DID_AUXV) == 0) {
1256 if (do_auxv_note(ms, nbuf, xnh_type, swap,
1257 namesz, descsz, noff, doff, flags, size, clazz,
1258 fd, ph_off, ph_num, fsize))
1259 return offset;
1260 }
1261
1262 if (namesz == 7 && strcmp(RCAST(char *, &nbuf[noff]), "NetBSD") == 0) {
1263 int descw, flag;
1264 const char *str, *tag;
1265 if (descsz > 100)
1266 descsz = 100;
1267 switch (xnh_type) {
1268 case NT_NETBSD_VERSION:
1269 return offset;
1270 case NT_NETBSD_MARCH:
1271 flag = FLAGS_DID_NETBSD_MARCH;
1272 tag = "compiled for";
1273 break;
1274 case NT_NETBSD_CMODEL:
1275 flag = FLAGS_DID_NETBSD_CMODEL;
1276 tag = "compiler model";
1277 break;
1278 case NT_NETBSD_EMULATION:
1279 flag = FLAGS_DID_NETBSD_EMULATION;
1280 tag = "emulation:";
1281 break;
1282 default:
1283 if (*flags & FLAGS_DID_NETBSD_UNKNOWN)
1284 return offset;
1285 *flags |= FLAGS_DID_NETBSD_UNKNOWN;
1286 if (file_printf(ms, ", note=%u", xnh_type) == -1)
1287 return offset;
1288 return offset;
1289 }
1290
1291 if (*flags & flag)
1292 return offset;
1293 str = RCAST(const char *, &nbuf[doff]);
1294 descw = CAST(int, descsz);
1295 *flags |= flag;
1296 file_printf(ms, ", %s: %s", tag,
1297 file_copystr(buf, sizeof(buf), descw, str));
1298 return offset;
1299 }
1300
1301 return offset;
1302 }
1303
1304 /* SunOS 5.x hardware capability descriptions */
1305 typedef struct cap_desc {
1306 uint64_t cd_mask;
1307 const char *cd_name;
1308 } cap_desc_t;
1309
1310 static const cap_desc_t cap_desc_sparc[] = {
1311 { AV_SPARC_MUL32, "MUL32" },
1312 { AV_SPARC_DIV32, "DIV32" },
1313 { AV_SPARC_FSMULD, "FSMULD" },
1314 { AV_SPARC_V8PLUS, "V8PLUS" },
1315 { AV_SPARC_POPC, "POPC" },
1316 { AV_SPARC_VIS, "VIS" },
1317 { AV_SPARC_VIS2, "VIS2" },
1318 { AV_SPARC_ASI_BLK_INIT, "ASI_BLK_INIT" },
1319 { AV_SPARC_FMAF, "FMAF" },
1320 { AV_SPARC_FJFMAU, "FJFMAU" },
1321 { AV_SPARC_IMA, "IMA" },
1322 { 0, NULL }
1323 };
1324
1325 static const cap_desc_t cap_desc_386[] = {
1326 { AV_386_FPU, "FPU" },
1327 { AV_386_TSC, "TSC" },
1328 { AV_386_CX8, "CX8" },
1329 { AV_386_SEP, "SEP" },
1330 { AV_386_AMD_SYSC, "AMD_SYSC" },
1331 { AV_386_CMOV, "CMOV" },
1332 { AV_386_MMX, "MMX" },
1333 { AV_386_AMD_MMX, "AMD_MMX" },
1334 { AV_386_AMD_3DNow, "AMD_3DNow" },
1335 { AV_386_AMD_3DNowx, "AMD_3DNowx" },
1336 { AV_386_FXSR, "FXSR" },
1337 { AV_386_SSE, "SSE" },
1338 { AV_386_SSE2, "SSE2" },
1339 { AV_386_PAUSE, "PAUSE" },
1340 { AV_386_SSE3, "SSE3" },
1341 { AV_386_MON, "MON" },
1342 { AV_386_CX16, "CX16" },
1343 { AV_386_AHF, "AHF" },
1344 { AV_386_TSCP, "TSCP" },
1345 { AV_386_AMD_SSE4A, "AMD_SSE4A" },
1346 { AV_386_POPCNT, "POPCNT" },
1347 { AV_386_AMD_LZCNT, "AMD_LZCNT" },
1348 { AV_386_SSSE3, "SSSE3" },
1349 { AV_386_SSE4_1, "SSE4.1" },
1350 { AV_386_SSE4_2, "SSE4.2" },
1351 { 0, NULL }
1352 };
1353
1354 file_private int
doshn(struct magic_set * ms,int clazz,int swap,int fd,off_t off,int num,size_t size,off_t fsize,int mach,int strtab,int * flags,uint16_t * notecount)1355 doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num,
1356 size_t size, off_t fsize, int mach, int strtab, int *flags,
1357 uint16_t *notecount)
1358 {
1359 Elf32_Shdr sh32;
1360 Elf64_Shdr sh64;
1361 int stripped = 1, has_debug_info = 0;
1362 size_t nbadcap = 0;
1363 void *nbuf;
1364 off_t noff, coff, name_off, offs;
1365 uint64_t cap_hw1 = 0; /* SunOS 5.x hardware capabilities */
1366 uint64_t cap_sf1 = 0; /* SunOS 5.x software capabilities */
1367 char name[50];
1368 ssize_t namesize;
1369
1370 if (ms->flags & MAGIC_MIME)
1371 return 0;
1372
1373 if (num == 0) {
1374 if (file_printf(ms, ", no section header") == -1)
1375 return -1;
1376 return 0;
1377 }
1378 if (size != xsh_sizeof) {
1379 if (file_printf(ms, ", corrupted section header size") == -1)
1380 return -1;
1381 return 0;
1382 }
1383
1384 /* Read offset of name section to be able to read section names later */
1385 offs = CAST(off_t, (off + size * strtab));
1386 if (pread(fd, xsh_addr, xsh_sizeof, offs) < CAST(ssize_t, xsh_sizeof)) {
1387 if (file_printf(ms, ", missing section headers at %jd",
1388 (intmax_t)offs) == -1)
1389 return -1;
1390 return 0;
1391 }
1392 name_off = xsh_offset;
1393
1394 if (fsize != SIZE_UNKNOWN && fsize < name_off) {
1395 if (file_printf(ms, ", too large section header offset %jd",
1396 (intmax_t)name_off) == -1)
1397 return -1;
1398 return 0;
1399 }
1400
1401 for ( ; num; num--) {
1402 /* Read the name of this section. */
1403 offs = name_off + xsh_name;
1404 if ((namesize = pread(fd, name, sizeof(name) - 1, offs))
1405 == -1) {
1406 if (file_printf(ms,
1407 ", can't read name of elf section at %jd",
1408 (intmax_t)offs) == -1)
1409 return -1;
1410 return 0;
1411 }
1412 name[namesize] = '\0';
1413 if (strcmp(name, ".debug_info") == 0) {
1414 has_debug_info = 1;
1415 stripped = 0;
1416 }
1417
1418 if (pread(fd, xsh_addr, xsh_sizeof, off) <
1419 CAST(ssize_t, xsh_sizeof)) {
1420 if (file_printf(ms, ", can't read elf section at %jd",
1421 (intmax_t)off) == -1)
1422 return -1;
1423 return 0;
1424 }
1425 off += size;
1426
1427 /* Things we can determine before we seek */
1428 switch (xsh_type) {
1429 case SHT_SYMTAB:
1430 #if 0
1431 case SHT_DYNSYM:
1432 #endif
1433 stripped = 0;
1434 break;
1435 default:
1436 if (fsize != SIZE_UNKNOWN && xsh_offset > fsize) {
1437 /* Perhaps warn here */
1438 continue;
1439 }
1440 break;
1441 }
1442
1443
1444 /* Things we can determine when we seek */
1445 switch (xsh_type) {
1446 case SHT_NOTE:
1447 if (CAST(uintmax_t, (xsh_size + xsh_offset)) >
1448 CAST(uintmax_t, fsize)) {
1449 if (file_printf(ms,
1450 ", note offset/size %#" INTMAX_T_FORMAT
1451 "x+%#" INTMAX_T_FORMAT "x exceeds"
1452 " file size %#" INTMAX_T_FORMAT "x",
1453 CAST(uintmax_t, xsh_offset),
1454 CAST(uintmax_t, xsh_size),
1455 CAST(uintmax_t, fsize)) == -1)
1456 return -1;
1457 return 0;
1458 }
1459 if (xsh_size > ms->elf_shsize_max) {
1460 file_error(ms, errno, "Note section size too "
1461 "big (%ju > %zu)", (uintmax_t)xsh_size,
1462 ms->elf_shsize_max);
1463 return -1;
1464 }
1465 if ((nbuf = malloc(xsh_size)) == NULL) {
1466 file_error(ms, errno, "Cannot allocate memory"
1467 " for note");
1468 return -1;
1469 }
1470 offs = xsh_offset;
1471 if (pread(fd, nbuf, xsh_size, offs) <
1472 CAST(ssize_t, xsh_size)) {
1473 free(nbuf);
1474 if (file_printf(ms,
1475 ", can't read elf note at %jd",
1476 (intmax_t)offs) == -1)
1477 return -1;
1478 return 0;
1479 }
1480
1481 noff = 0;
1482 for (;;) {
1483 if (noff >= CAST(off_t, xsh_size))
1484 break;
1485 noff = donote(ms, nbuf, CAST(size_t, noff),
1486 xsh_size, clazz, swap, 4, flags, notecount,
1487 fd, 0, 0, 0);
1488 if (noff == 0)
1489 break;
1490 }
1491 free(nbuf);
1492 break;
1493 case SHT_SUNW_cap:
1494 switch (mach) {
1495 case EM_SPARC:
1496 case EM_SPARCV9:
1497 case EM_IA_64:
1498 case EM_386:
1499 case EM_AMD64:
1500 break;
1501 default:
1502 goto skip;
1503 }
1504
1505 if (nbadcap > 5)
1506 break;
1507 if (lseek(fd, xsh_offset, SEEK_SET)
1508 == CAST(off_t, -1)) {
1509 file_badseek(ms);
1510 return -1;
1511 }
1512 coff = 0;
1513 for (;;) {
1514 Elf32_Cap cap32;
1515 Elf64_Cap cap64;
1516 char cbuf[/*CONSTCOND*/
1517 MAX(sizeof(cap32), sizeof(cap64))];
1518 if ((coff += xcap_sizeof) >
1519 CAST(off_t, xsh_size))
1520 break;
1521 if (read(fd, cbuf, CAST(size_t, xcap_sizeof)) !=
1522 CAST(ssize_t, xcap_sizeof)) {
1523 file_badread(ms);
1524 return -1;
1525 }
1526 if (cbuf[0] == 'A') {
1527 #ifdef notyet
1528 char *p = cbuf + 1;
1529 uint32_t len, tag;
1530 memcpy(&len, p, sizeof(len));
1531 p += 4;
1532 len = getu32(swap, len);
1533 if (memcmp("gnu", p, 3) != 0) {
1534 if (file_printf(ms,
1535 ", unknown capability %.3s", p)
1536 == -1)
1537 return -1;
1538 break;
1539 }
1540 p += strlen(p) + 1;
1541 tag = *p++;
1542 memcpy(&len, p, sizeof(len));
1543 p += 4;
1544 len = getu32(swap, len);
1545 if (tag != 1) {
1546 if (file_printf(ms, ", unknown gnu"
1547 " capability tag %d", tag)
1548 == -1)
1549 return -1;
1550 break;
1551 }
1552 // gnu attributes
1553 #endif
1554 break;
1555 }
1556 memcpy(xcap_addr, cbuf, xcap_sizeof);
1557 switch (xcap_tag) {
1558 case CA_SUNW_NULL:
1559 break;
1560 case CA_SUNW_HW_1:
1561 cap_hw1 |= xcap_val;
1562 break;
1563 case CA_SUNW_SF_1:
1564 cap_sf1 |= xcap_val;
1565 break;
1566 default:
1567 if (file_printf(ms,
1568 ", with unknown capability "
1569 "%#" INT64_T_FORMAT "x = %#"
1570 INT64_T_FORMAT "x",
1571 CAST(unsigned long long, xcap_tag),
1572 CAST(unsigned long long, xcap_val))
1573 == -1)
1574 return -1;
1575 if (nbadcap++ > 2)
1576 coff = xsh_size;
1577 break;
1578 }
1579 }
1580 /*FALLTHROUGH*/
1581 skip:
1582 default:
1583 break;
1584 }
1585 }
1586
1587 if (has_debug_info) {
1588 if (file_printf(ms, ", with debug_info") == -1)
1589 return -1;
1590 }
1591 if (file_printf(ms, ", %sstripped", stripped ? "" : "not ") == -1)
1592 return -1;
1593 if (cap_hw1) {
1594 const cap_desc_t *cdp;
1595 switch (mach) {
1596 case EM_SPARC:
1597 case EM_SPARC32PLUS:
1598 case EM_SPARCV9:
1599 cdp = cap_desc_sparc;
1600 break;
1601 case EM_386:
1602 case EM_IA_64:
1603 case EM_AMD64:
1604 cdp = cap_desc_386;
1605 break;
1606 default:
1607 cdp = NULL;
1608 break;
1609 }
1610 if (file_printf(ms, ", uses") == -1)
1611 return -1;
1612 if (cdp) {
1613 while (cdp->cd_name) {
1614 if (cap_hw1 & cdp->cd_mask) {
1615 if (file_printf(ms,
1616 " %s", cdp->cd_name) == -1)
1617 return -1;
1618 cap_hw1 &= ~cdp->cd_mask;
1619 }
1620 ++cdp;
1621 }
1622 if (cap_hw1)
1623 if (file_printf(ms,
1624 " unknown hardware capability %#"
1625 INT64_T_FORMAT "x",
1626 CAST(unsigned long long, cap_hw1)) == -1)
1627 return -1;
1628 } else {
1629 if (file_printf(ms,
1630 " hardware capability %#" INT64_T_FORMAT "x",
1631 CAST(unsigned long long, cap_hw1)) == -1)
1632 return -1;
1633 }
1634 }
1635 if (cap_sf1) {
1636 if (cap_sf1 & SF1_SUNW_FPUSED) {
1637 if (file_printf(ms,
1638 (cap_sf1 & SF1_SUNW_FPKNWN)
1639 ? ", uses frame pointer"
1640 : ", not known to use frame pointer") == -1)
1641 return -1;
1642 }
1643 cap_sf1 &= ~SF1_SUNW_MASK;
1644 if (cap_sf1)
1645 if (file_printf(ms,
1646 ", with unknown software capability %#"
1647 INT64_T_FORMAT "x",
1648 CAST(unsigned long long, cap_sf1)) == -1)
1649 return -1;
1650 }
1651 return 0;
1652 }
1653
1654 /*
1655 * Look through the program headers of an executable image, to determine
1656 * if it is statically or dynamically linked. If it has a dynamic section,
1657 * it is pie, and does not have an interpreter or needed libraries, we
1658 * call it static pie.
1659 */
1660 file_private int
dophn_exec(struct magic_set * ms,int clazz,int swap,int fd,off_t off,int num,size_t size,off_t fsize,int sh_num,int * flags,uint16_t * notecount)1661 dophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off,
1662 int num, size_t size, off_t fsize, int sh_num, int *flags,
1663 uint16_t *notecount)
1664 {
1665 Elf32_Phdr ph32;
1666 Elf64_Phdr ph64;
1667 const char *linking_style;
1668 unsigned char nbuf[BUFSIZ];
1669 char ibuf[BUFSIZ];
1670 char interp[BUFSIZ];
1671 ssize_t bufsize;
1672 size_t offset, align, need = 0;
1673 int pie = 0, dynamic = 0;
1674
1675 if (num == 0) {
1676 if (file_printf(ms, ", no program header") == -1)
1677 return -1;
1678 return 0;
1679 }
1680 if (size != xph_sizeof) {
1681 if (file_printf(ms, ", corrupted program header size") == -1)
1682 return -1;
1683 return 0;
1684 }
1685
1686 interp[0] = '\0';
1687 for ( ; num; num--) {
1688 int doread;
1689 if (pread(fd, xph_addr, xph_sizeof, off) <
1690 CAST(ssize_t, xph_sizeof)) {
1691 if (file_printf(ms,
1692 ", can't read elf program headers at %jd",
1693 (intmax_t)off) == -1)
1694 return -1;
1695 return 0;
1696 }
1697
1698 off += size;
1699 bufsize = 0;
1700 align = 4;
1701
1702 /* Things we can determine before we seek */
1703 switch (xph_type) {
1704 case PT_DYNAMIC:
1705 doread = 1;
1706 break;
1707 case PT_NOTE:
1708 if (sh_num) /* Did this through section headers */
1709 continue;
1710 if (((align = xph_align) & 0x80000000UL) != 0 ||
1711 align < 4) {
1712 if (file_printf(ms,
1713 ", invalid note alignment %#lx",
1714 CAST(unsigned long, align)) == -1)
1715 return -1;
1716 align = 4;
1717 }
1718 /*FALLTHROUGH*/
1719 case PT_INTERP:
1720 doread = 1;
1721 break;
1722 default:
1723 doread = 0;
1724 if (fsize != SIZE_UNKNOWN && xph_offset > fsize) {
1725 /* Maybe warn here? */
1726 continue;
1727 }
1728 break;
1729 }
1730
1731 if (doread) {
1732 size_t len = xph_filesz < sizeof(nbuf) ? xph_filesz
1733 : sizeof(nbuf);
1734 off_t offs = xph_offset;
1735 bufsize = pread(fd, nbuf, len, offs);
1736 if (bufsize == -1) {
1737 if (file_printf(ms,
1738 ", can't read section at %jd",
1739 (intmax_t)offs) == -1)
1740 return -1;
1741 return 0;
1742 }
1743 }
1744
1745 /* Things we can determine when we seek */
1746 switch (xph_type) {
1747 case PT_DYNAMIC:
1748 dynamic = 1;
1749 offset = 0;
1750 // Let DF_1 determine if we are PIE or not.
1751 ms->mode &= ~0111;
1752 for (;;) {
1753 if (offset >= CAST(size_t, bufsize))
1754 break;
1755 offset = dodynamic(ms, nbuf, offset,
1756 CAST(size_t, bufsize), clazz, swap,
1757 &pie, &need);
1758 if (offset == 0)
1759 break;
1760 }
1761 if (ms->flags & MAGIC_MIME)
1762 continue;
1763 break;
1764
1765 case PT_INTERP:
1766 need++;
1767 if (ms->flags & MAGIC_MIME)
1768 continue;
1769 if (bufsize && nbuf[0]) {
1770 nbuf[bufsize - 1] = '\0';
1771 memcpy(interp, nbuf, CAST(size_t, bufsize));
1772 } else
1773 strlcpy(interp, "*empty*", sizeof(interp));
1774 break;
1775 case PT_NOTE:
1776 if (ms->flags & MAGIC_MIME)
1777 return 0;
1778 /*
1779 * This is a PT_NOTE section; loop through all the notes
1780 * in the section.
1781 */
1782 offset = 0;
1783 for (;;) {
1784 if (offset >= CAST(size_t, bufsize))
1785 break;
1786 offset = donote(ms, nbuf, offset,
1787 CAST(size_t, bufsize), clazz, swap, align,
1788 flags, notecount, fd, 0, 0, 0);
1789 if (offset == 0)
1790 break;
1791 }
1792 break;
1793 default:
1794 if (ms->flags & MAGIC_MIME)
1795 continue;
1796 break;
1797 }
1798 }
1799 if (ms->flags & MAGIC_MIME)
1800 return 0;
1801 if (dynamic) {
1802 if (pie && need == 0)
1803 linking_style = "static-pie";
1804 else
1805 linking_style = "dynamically";
1806 } else {
1807 linking_style = "statically";
1808 }
1809 if (file_printf(ms, ", %s linked", linking_style) == -1)
1810 return -1;
1811 if (interp[0])
1812 if (file_printf(ms, ", interpreter %s", file_printable(ms,
1813 ibuf, sizeof(ibuf), interp, sizeof(interp))) == -1)
1814 return -1;
1815 return 0;
1816 }
1817
1818
1819 file_protected int
file_tryelf(struct magic_set * ms,const struct buffer * b)1820 file_tryelf(struct magic_set *ms, const struct buffer *b)
1821 {
1822 int fd = b->fd;
1823 const unsigned char *buf = CAST(const unsigned char *, b->fbuf);
1824 size_t nbytes = b->flen;
1825 union {
1826 int32_t l;
1827 char c[sizeof(int32_t)];
1828 } u;
1829 int clazz;
1830 int swap;
1831 struct stat st;
1832 const struct stat *stp;
1833 off_t fsize;
1834 int flags = 0;
1835 Elf32_Ehdr elf32hdr;
1836 Elf64_Ehdr elf64hdr;
1837 uint16_t type, phnum, shnum, notecount;
1838
1839 if (ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION))
1840 return 0;
1841 /*
1842 * ELF executables have multiple section headers in arbitrary
1843 * file locations and thus file(1) cannot determine it from easily.
1844 * Instead we traverse thru all section headers until a symbol table
1845 * one is found or else the binary is stripped.
1846 * Return immediately if it's not ELF (so we avoid pipe2file unless
1847 * needed).
1848 */
1849 if (buf[EI_MAG0] != ELFMAG0
1850 || (buf[EI_MAG1] != ELFMAG1 && buf[EI_MAG1] != OLFMAG1)
1851 || buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3)
1852 return 0;
1853
1854 /*
1855 * If we cannot seek, it must be a pipe, socket or fifo.
1856 */
1857 if((lseek(fd, CAST(off_t, 0), SEEK_SET) == CAST(off_t, -1))
1858 && (errno == ESPIPE))
1859 fd = file_pipe2file(ms, fd, buf, nbytes);
1860
1861 if (fd == -1) {
1862 file_badread(ms);
1863 return -1;
1864 }
1865
1866 stp = &b->st;
1867 /*
1868 * b->st.st_size != 0 if previous fstat() succeeded,
1869 * which is likely, we can avoid extra stat() call.
1870 */
1871 if (b->st.st_size == 0) {
1872 stp = &st;
1873 if (fstat(fd, &st) == -1) {
1874 file_badread(ms);
1875 return -1;
1876 }
1877 }
1878 if (S_ISREG(stp->st_mode) || stp->st_size != 0)
1879 fsize = stp->st_size;
1880 else
1881 fsize = SIZE_UNKNOWN;
1882
1883 clazz = buf[EI_CLASS];
1884
1885 switch (clazz) {
1886 case ELFCLASS32:
1887 #undef elf_getu
1888 #define elf_getu(a, b) elf_getu32(a, b)
1889 #undef elfhdr
1890 #define elfhdr elf32hdr
1891 #include "elfclass.h"
1892 case ELFCLASS64:
1893 #undef elf_getu
1894 #define elf_getu(a, b) elf_getu64(a, b)
1895 #undef elfhdr
1896 #define elfhdr elf64hdr
1897 #include "elfclass.h"
1898 default:
1899 if (file_printf(ms, ", unknown class %d", clazz) == -1)
1900 return -1;
1901 break;
1902 }
1903 return 0;
1904 }
1905 #endif
1906