]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/elfctl/elfctl.c
ssh: Update to OpenSSH 9.5p1
[FreeBSD/FreeBSD.git] / usr.bin / elfctl / elfctl.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2019 The FreeBSD Foundation.
5  *
6  * This software was developed by Bora Ozarslan under sponsorship from
7  * the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include <sys/param.h>
32 #include <sys/elf_common.h>
33 #include <sys/endian.h>
34 #include <sys/stat.h>
35
36 #include <ctype.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <gelf.h>
41 #include <getopt.h>
42 #include <libelf.h>
43 #include <stdbool.h>
44 #include <stdint.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #include "_elftc.h"
51 static bool convert_to_feature_val(const char *, uint32_t *);
52 static bool edit_file_features(Elf *, int, int, char *, bool);
53 static bool get_file_features(Elf *, int, int, uint32_t *, uint64_t *, bool);
54 static void print_features(void);
55 static bool print_file_features(Elf *, int, int, char *, bool);
56 static void usage(void) __dead2;
57
58 struct ControlFeatures {
59         const char *alias;
60         unsigned long value;
61         const char *desc;
62 };
63
64 static struct ControlFeatures featurelist[] = {
65         { "noaslr",     NT_FREEBSD_FCTL_ASLR_DISABLE,   "Disable ASLR" },
66         { "noprotmax",  NT_FREEBSD_FCTL_PROTMAX_DISABLE,
67             "Disable implicit PROT_MAX" },
68         { "nostackgap", NT_FREEBSD_FCTL_STKGAP_DISABLE, "Disable stack gap" },
69         { "wxneeded",   NT_FREEBSD_FCTL_WXNEEDED, "Requires W+X mappings" },
70         { "la48",       NT_FREEBSD_FCTL_LA48, "amd64: Limit user VA to 48bit" },
71 };
72
73 static struct option long_opts[] = {
74         { "help",       no_argument,    NULL,   'h' },
75         { NULL,         0,              NULL,   0 }
76 };
77
78 #if BYTE_ORDER == LITTLE_ENDIAN
79 #define HOST_ENDIAN     ELFDATA2LSB
80 #define SWAP_ENDIAN     ELFDATA2MSB
81 #else
82 #define HOST_ENDIAN     ELFDATA2MSB
83 #define SWAP_ENDIAN     ELFDATA2LSB
84 #endif
85
86 static bool iflag;
87
88 int
89 main(int argc, char **argv)
90 {
91         GElf_Ehdr ehdr;
92         Elf *elf;
93         Elf_Kind kind;
94         int ch, fd, retval;
95         char *features;
96         bool editfeatures, lflag, endian_swap;
97
98         lflag = 0;
99         editfeatures = false;
100         retval = 0;
101         features = NULL;
102
103         if (elf_version(EV_CURRENT) == EV_NONE)
104                 errx(EXIT_FAILURE, "elf_version error");
105
106         while ((ch = getopt_long(argc, argv, "hile:", long_opts, NULL)) != -1) {
107                 switch (ch) {
108                 case 'i':
109                         iflag = true;
110                         break;
111                 case 'l':
112                         print_features();
113                         lflag = true;
114                         break;
115                 case 'e':
116                         if (features != NULL)
117                                 errx(1, "-e may be specified only once");
118                         features = optarg;
119                         editfeatures = true;
120                         break;
121                 case 'h':
122                 default:
123                         usage();
124                 }
125         }
126         argc -= optind;
127         argv += optind;
128         if (argc == 0) {
129                 if (lflag)
130                         exit(0);
131                 else {
132                         warnx("no file(s) specified");
133                         usage();
134                 }
135         }
136
137         while (argc) {
138                 elf = NULL;
139
140                 if ((fd = open(argv[0],
141                     editfeatures ? O_RDWR : O_RDONLY, 0)) < 0) {
142                         warn("error opening file %s", argv[0]);
143                         retval = 1;
144                         goto fail;
145                 }
146
147                 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
148                         warnx("elf_begin failed: %s", elf_errmsg(-1));
149                         retval = 1;
150                         goto fail;
151                 }
152
153                 if ((kind = elf_kind(elf)) != ELF_K_ELF) {
154                         if (kind == ELF_K_AR)
155                                 warnx("file '%s' is an archive", argv[0]);
156                         else
157                                 warnx("file '%s' is not an ELF file", argv[0]);
158                         retval = 1;
159                         goto fail;
160                 }
161
162                 if (gelf_getehdr(elf, &ehdr) == NULL) {
163                         warnx("gelf_getehdr: %s", elf_errmsg(-1));
164                         retval = 1;
165                         goto fail;
166                 }
167
168                 if (ehdr.e_ident[EI_DATA] == HOST_ENDIAN) {
169                         endian_swap = false;
170                 } else if (ehdr.e_ident[EI_DATA] == SWAP_ENDIAN) {
171                         endian_swap = true;
172                 } else {
173                         warnx("file endianness unknown");
174                         retval = 1;
175                         goto fail;
176                 }
177
178                 if (!editfeatures) {
179                         if (!print_file_features(elf, ehdr.e_phnum, fd,
180                             argv[0], endian_swap)) {
181                                 retval = 1;
182                                 goto fail;
183                         }
184                 } else if (!edit_file_features(elf, ehdr.e_phnum, fd,
185                     features, endian_swap)) {
186                         retval = 1;
187                         goto fail;
188                 }
189 fail:
190                 if (elf != NULL)
191                         elf_end(elf);
192
193                 if (fd >= 0)
194                         close(fd);
195
196                 argc--;
197                 argv++;
198         }
199
200         return (retval);
201 }
202
203 #define USAGE_MESSAGE \
204         "\
205 Usage: %s [options] file...\n\
206   Set or display the control features for an ELF object.\n\n\
207   Supported options are:\n\
208   -l                        List known control features.\n\
209   -i                        Ignore unknown features.\n\
210   -e [+-=]feature,list      Edit features from a comma separated list.\n\
211   -h | --help               Print a usage message and exit.\n"
212
213 static void
214 usage(void)
215 {
216
217         fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
218         exit(1);
219 }
220
221 static bool
222 convert_to_feature_val(const char *feature_str, uint32_t *feature_val)
223 {
224         char *feature, *feature_tmp;
225         int i, len;
226         uint32_t input;
227         char operation;
228
229         input = 0;
230         operation = *feature_str;
231         feature_str++;
232
233         if (operation != '+' && operation != '-' && operation != '=')
234                 errx(1, "'%c' not an operator - use '+', '-', '='", operation);
235
236         if ((feature_tmp = strdup(feature_str)) == NULL)
237                 err(1, "strdup");
238         len = nitems(featurelist);
239         while ((feature = strsep(&feature_tmp, ",")) != NULL) {
240                 for (i = 0; i < len; ++i) {
241                         if (strcmp(featurelist[i].alias, feature) == 0) {
242                                 input |= featurelist[i].value;
243                                 break;
244                         }
245                         /* XXX Backwards compatibility for "no"-prefix flags. */
246                         if (strncmp(featurelist[i].alias, "no", 2) == 0 &&
247                             strcmp(featurelist[i].alias + 2, feature) == 0) {
248                                 input |= featurelist[i].value;
249                                 warnx(
250                                     "interpreting %s as %s; please specify %s",
251                                     feature, featurelist[i].alias,
252                                     featurelist[i].alias);
253                                 break;
254                         }
255                 }
256                 if (i == len) {
257                         if (isdigit(feature[0])) {
258                                 char *eptr;
259                                 unsigned long long val;
260
261                                 errno = 0;
262                                 val = strtoll(feature, &eptr, 0);
263                                 if (eptr == feature || *eptr != '\0')
264                                         errno = EINVAL;
265                                 else if (val > UINT32_MAX)
266                                         errno = ERANGE;
267                                 if (errno != 0) {
268                                         warn("%s invalid", feature);
269                                         free(feature_tmp);
270                                         return (false);
271                                 }
272                                 input |= val;
273                         } else {
274                                 warnx("%s is not a valid feature", feature);
275                                 if (!iflag) {
276                                         free(feature_tmp);
277                                         return (false);
278                                 }
279                         }
280                 }
281         }
282
283         if (operation == '+') {
284                 *feature_val |= input;
285         } else if (operation == '=') {
286                 *feature_val = input;
287         } else if (operation == '-') {
288                 *feature_val &= ~input;
289         }
290         free(feature_tmp);
291         return (true);
292 }
293
294 static bool
295 edit_file_features(Elf *elf, int phcount, int fd, char *val, bool endian_swap)
296 {
297         uint32_t features, prev_features;
298         uint64_t off;
299
300         if (!get_file_features(elf, phcount, fd, &features, &off,
301             endian_swap)) {
302                 warnx("NT_FREEBSD_FEATURE_CTL note not found");
303                 return (false);
304         }
305
306         prev_features = features;
307         if (!convert_to_feature_val(val, &features))
308                 return (false);
309         /* Avoid touching file if no change. */
310         if (features == prev_features)
311                 return (true);
312
313         if (endian_swap)
314                 features = bswap32(features);
315
316         if (lseek(fd, off, SEEK_SET) == -1 ||
317             write(fd, &features, sizeof(features)) <
318             (ssize_t)sizeof(features)) {
319                 warnx("error writing feature value");
320                 return (false);
321         }
322         return (true);
323 }
324
325 static void
326 print_features(void)
327 {
328         size_t i;
329
330         printf("Known features are:\n");
331         for (i = 0; i < nitems(featurelist); ++i)
332                 printf("%-16s%s\n", featurelist[i].alias,
333                     featurelist[i].desc);
334 }
335
336 static bool
337 print_file_features(Elf *elf, int phcount, int fd, char *filename,
338     bool endian_swap)
339 {
340         uint32_t features;
341         unsigned long i;
342
343         if (!get_file_features(elf, phcount, fd, &features, NULL,
344             endian_swap)) {
345                 return (false);
346         }
347
348         printf("File '%s' features:\n", filename);
349         for (i = 0; i < nitems(featurelist); ++i) {
350                 printf("%-16s'%s' is ", featurelist[i].alias,
351                     featurelist[i].desc);
352
353                 if ((featurelist[i].value & features) == 0)
354                         printf("un");
355
356                 printf("set.\n");
357         }
358         return (true);
359 }
360
361 static bool
362 get_file_features(Elf *elf, int phcount, int fd, uint32_t *features,
363     uint64_t *off, bool endian_swap)
364 {
365         GElf_Phdr phdr;
366         Elf_Note note;
367         unsigned long read_total;
368         int namesz, descsz, i;
369         char *name;
370
371         /*
372          * Go through each program header to find one that is of type PT_NOTE
373          * and has a note for feature control.
374          */
375         for (i = 0; i < phcount; ++i) {
376                 if (gelf_getphdr(elf, i, &phdr) == NULL) {
377                         warnx("gelf_getphdr failed: %s", elf_errmsg(-1));
378                         return (false);
379                 }
380
381                 if (phdr.p_type != PT_NOTE)
382                         continue;
383
384                 if (lseek(fd, phdr.p_offset, SEEK_SET) < 0) {
385                         warn("lseek() failed:");
386                         return (false);
387                 }
388
389                 read_total = 0;
390                 while (read_total < phdr.p_filesz) {
391                         if (read(fd, &note, sizeof(note)) <
392                             (ssize_t)sizeof(note)) {
393                                 warnx("elf note header too short");
394                                 return (false);
395                         }
396                         read_total += sizeof(note);
397
398                         if (endian_swap) {
399                                 note.n_namesz = bswap32(note.n_namesz);
400                                 note.n_descsz = bswap32(note.n_descsz);
401                                 note.n_type = bswap32(note.n_type);
402                         }
403
404                         /*
405                          * XXX: Name and descriptor are 4 byte aligned, however,
406                          * the size given doesn't include the padding.
407                          */
408                         namesz = roundup2(note.n_namesz, 4);
409                         name = malloc(namesz);
410                         if (name == NULL) {
411                                 warn("malloc() failed.");
412                                 return (false);
413                         }
414                         descsz = roundup2(note.n_descsz, 4);
415                         if (read(fd, name, namesz) < namesz) {
416                                 warnx("elf note name too short");
417                                 free(name);
418                                 return (false);
419                         }
420                         read_total += namesz;
421
422                         if (note.n_namesz != 8 ||
423                             strncmp("FreeBSD", name, 7) != 0 ||
424                             note.n_type != NT_FREEBSD_FEATURE_CTL) {
425                                 /* Not the right note. Skip the description */
426                                 if (lseek(fd, descsz, SEEK_CUR) < 0) {
427                                         warn("lseek() failed.");
428                                         free(name);
429                                         return (false);
430                                 }
431                                 read_total += descsz;
432                                 free(name);
433                                 continue;
434                         }
435
436                         if (note.n_descsz < sizeof(uint32_t)) {
437                                 warnx("Feature descriptor can't "
438                                     "be less than 4 bytes");
439                                 free(name);
440                                 return (false);
441                         }
442
443                         /*
444                          * XXX: For now we look at only 4 bytes of the
445                          * descriptor. This should respect descsz.
446                          */
447                         if (note.n_descsz > sizeof(uint32_t))
448                                 warnx("Feature note is bigger than expected");
449                         if (read(fd, features, sizeof(uint32_t)) <
450                             (ssize_t)sizeof(uint32_t)) {
451                                 warnx("feature note data too short");
452                                 free(name);
453                                 return (false);
454                         }
455                         if (endian_swap)
456                                 *features = bswap32(*features);
457                         if (off != NULL)
458                                 *off = phdr.p_offset + read_total;
459                         free(name);
460                         return (true);
461                 }
462         }
463
464         warnx("NT_FREEBSD_FEATURE_CTL note not found");
465         return (false);
466 }