]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/elfctl/elfctl.c
elfctl: allow features to be specified by value
[FreeBSD/FreeBSD.git] / usr.bin / elfctl / elfctl.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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
52 __FBSDID("$FreeBSD$");
53
54 static bool convert_to_feature_val(char *, uint32_t *);
55 static bool edit_file_features(Elf *, int, int, char *);
56 static bool get_file_features(Elf *, int, int, uint32_t *, uint64_t *);
57 static void print_features(void);
58 static bool print_file_features(Elf *, int, int, char *);
59 static void usage(void);
60
61 struct ControlFeatures {
62         const char *alias;
63         unsigned long value;
64         const char *desc;
65 };
66
67 static struct ControlFeatures featurelist[] = {
68         { "aslr",       NT_FREEBSD_FCTL_ASLR_DISABLE,   "Disable ASLR" },
69         { "protmax",    NT_FREEBSD_FCTL_PROTMAX_DISABLE,
70             "Disable implicit PROT_MAX" },
71         { "stackgap",   NT_FREEBSD_FCTL_STKGAP_DISABLE, "Disable stack gap" },
72         { "wxneeded",   NT_FREEBSD_FCTL_WXNEEDED, "Requires W+X mappings" },
73 #ifdef NT_FREEBSD_FCTL_LA48
74         { "la48",       NT_FREEBSD_FCTL_LA48, "amd64: Limit user VA to 48bit" },
75 #endif
76         { "aslrstkgap", NT_FREEBSD_FCTL_ASG_DISABLE, "Disable ASLR stack gap" },
77 };
78
79 static struct option long_opts[] = {
80         { "help",       no_argument,    NULL,   'h' },
81         { NULL,         0,              NULL,   0 }
82 };
83
84 #if BYTE_ORDER == LITTLE_ENDIAN
85 #define SUPPORTED_ENDIAN ELFDATA2LSB
86 #else
87 #define SUPPORTED_ENDIAN ELFDATA2MSB
88 #endif
89
90 static bool iflag;
91
92 int
93 main(int argc, char **argv)
94 {
95         GElf_Ehdr ehdr;
96         Elf *elf;
97         Elf_Kind kind;
98         int ch, fd, retval;
99         char *features;
100         bool editfeatures, lflag;
101
102         lflag = 0;
103         editfeatures = false;
104         retval = 0;
105         features = NULL;
106
107         if (elf_version(EV_CURRENT) == EV_NONE)
108                 errx(EXIT_FAILURE, "elf_version error");
109
110         while ((ch = getopt_long(argc, argv, "hile:", long_opts, NULL)) != -1) {
111                 switch (ch) {
112                 case 'i':
113                         iflag = true;
114                         break;
115                 case 'l':
116                         print_features();
117                         lflag = true;
118                         break;
119                 case 'e':
120                         features = optarg;
121                         editfeatures = true;
122                         break;
123                 case 'h':
124                 default:
125                         usage();
126                 }
127         }
128         argc -= optind;
129         argv += optind;
130         if (argc == 0) {
131                 if (lflag)
132                         exit(0);
133                 else {
134                         warnx("no file(s) specified");
135                         usage();
136                 }
137         }
138
139         while (argc) {
140                 elf = NULL;
141
142                 if ((fd = open(argv[0],
143                     editfeatures ? O_RDWR : O_RDONLY, 0)) < 0) {
144                         warn("error opening file %s", argv[0]);
145                         retval = 1;
146                         goto fail;
147                 }
148
149                 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
150                         warnx("elf_begin failed: %s", elf_errmsg(-1));
151                         retval = 1;
152                         goto fail;
153                 }
154
155                 if ((kind = elf_kind(elf)) != ELF_K_ELF) {
156                         if (kind == ELF_K_AR)
157                                 warnx("file '%s' is an archive", argv[0]);
158                         else
159                                 warnx("file '%s' is not an ELF file", argv[0]);
160                         retval = 1;
161                         goto fail;
162                 }
163
164                 if (gelf_getehdr(elf, &ehdr) == NULL) {
165                         warnx("gelf_getehdr: %s", elf_errmsg(-1));
166                         retval = 1;
167                         goto fail;
168                 }
169                 /*
170                  * XXX need to support cross-endian operation, but for now
171                  * exit on error rather than misbehaving.
172                  */
173                 if (ehdr.e_ident[EI_DATA] != SUPPORTED_ENDIAN) {
174                         warnx("file endianness must match host");
175                         retval = 1;
176                         goto fail;
177                 }
178
179                 if (!editfeatures) {
180                         if (!print_file_features(elf, ehdr.e_phnum, fd,
181                             argv[0])) {
182                                 retval = 1;
183                                 goto fail;
184                         }
185                 } else if (!edit_file_features(elf, ehdr.e_phnum, fd,
186                     features)) {
187                         retval = 1;
188                         goto fail;
189                 }
190 fail:
191                 if (elf != NULL)
192                         elf_end(elf);
193
194                 if (fd >= 0)
195                         close(fd);
196
197                 argc--;
198                 argv++;
199         }
200
201         return (retval);
202 }
203
204 #define USAGE_MESSAGE \
205         "\
206 Usage: %s [options] file...\n\
207   Set or display the control features for an ELF object.\n\n\
208   Supported options are:\n\
209   -l                        List known control features.\n\
210   -i                        Ignore unknown features.\n\
211   -e [+-=]feature,list      Edit features from a comma separated list.\n\
212   -h | --help               Print a usage message and exit.\n"
213
214 static void
215 usage(void)
216 {
217
218         fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
219         exit(1);
220 }
221
222 static bool
223 convert_to_feature_val(char *feature_str, uint32_t *feature_val)
224 {
225         char *feature;
226         int i, len;
227         uint32_t input;
228         char operation;
229
230         input = 0;
231         operation = *feature_str;
232         feature_str++;
233         len = nitems(featurelist);
234         while ((feature = strsep(&feature_str, ",")) != NULL) {
235                 for (i = 0; i < len; ++i) {
236                         if (strcmp(featurelist[i].alias, feature) == 0) {
237                                 input |= featurelist[i].value;
238                                 break;
239                         }
240                         /* XXX Backwards compatibility for "no"-prefix flags. */
241                         if (strncmp(featurelist[i].alias, "no", 2) == 0 &&
242                             strcmp(featurelist[i].alias + 2, feature) == 0) {
243                                 input |= featurelist[i].value;
244                                 warnx(
245                                     "interpreting %s as %s; please specify %s",
246                                     feature, featurelist[i].alias,
247                                     featurelist[i].alias);
248                                 break;
249                         }
250                 }
251                 if (i == len) {
252                         if (isdigit(feature[0])) {
253                                 char *eptr;
254                                 unsigned long long val;
255
256                                 errno = 0;
257                                 val = strtoll(feature, &eptr, 0);
258                                 if (eptr == feature || *eptr != '\0')
259                                         errno = EINVAL;
260                                 else if (val > UINT32_MAX)
261                                         errno = ERANGE;
262                                 if (errno != 0) {
263                                         warn("%s invalid", feature);
264                                         return (false);
265                                 }
266                                 input |= val;
267                         } else {
268                                 warnx("%s is not a valid feature", feature);
269                                 if (!iflag)
270                                         return (false);
271                         }
272                 }
273         }
274
275         if (operation == '+') {
276                 *feature_val |= input;
277         } else if (operation == '=') {
278                 *feature_val = input;
279         } else if (operation == '-') {
280                 *feature_val &= ~input;
281         } else {
282                 warnx("'%c' not an operator - use '+', '-', '='",
283                     feature_str[0]);
284                 return (false);
285         }
286         return (true);
287 }
288
289 static bool
290 edit_file_features(Elf *elf, int phcount, int fd, char *val)
291 {
292         uint32_t features;
293         uint64_t off;
294
295         if (!get_file_features(elf, phcount, fd, &features, &off)) {
296                 warnx("NT_FREEBSD_FEATURE_CTL note not found");
297                 return (false);
298         }
299
300         if (!convert_to_feature_val(val, &features))
301                 return (false);
302
303         if (lseek(fd, off, SEEK_SET) == -1 ||
304             write(fd, &features, sizeof(features)) <
305             (ssize_t)sizeof(features)) {
306                 warnx("error writing feature value");
307                 return (false);
308         }
309         return (true);
310 }
311
312 static void
313 print_features(void)
314 {
315         size_t i;
316
317         printf("Known features are:\n");
318         for (i = 0; i < nitems(featurelist); ++i)
319                 printf("%-16s%s\n", featurelist[i].alias,
320                     featurelist[i].desc);
321 }
322
323 static bool
324 print_file_features(Elf *elf, int phcount, int fd, char *filename)
325 {
326         uint32_t features;
327         unsigned long i;
328
329         if (!get_file_features(elf, phcount, fd, &features, NULL)) {
330                 return (false);
331         }
332
333         printf("File '%s' features:\n", filename);
334         for (i = 0; i < nitems(featurelist); ++i) {
335                 printf("%-16s'%s' is ", featurelist[i].alias,
336                     featurelist[i].desc);
337
338                 if ((featurelist[i].value & features) == 0)
339                         printf("un");
340
341                 printf("set.\n");
342         }
343         return (true);
344 }
345
346 static bool
347 get_file_features(Elf *elf, int phcount, int fd, uint32_t *features,
348     uint64_t *off)
349 {
350         GElf_Phdr phdr;
351         Elf_Note note;
352         unsigned long read_total;
353         int namesz, descsz, i;
354         char *name;
355
356         /*
357          * Go through each program header to find one that is of type PT_NOTE
358          * and has a note for feature control.
359          */
360         for (i = 0; i < phcount; ++i) {
361                 if (gelf_getphdr(elf, i, &phdr) == NULL) {
362                         warnx("gelf_getphdr failed: %s", elf_errmsg(-1));
363                         return (false);
364                 }
365
366                 if (phdr.p_type != PT_NOTE)
367                         continue;
368
369                 if (lseek(fd, phdr.p_offset, SEEK_SET) < 0) {
370                         warn("lseek() failed:");
371                         return (false);
372                 }
373
374                 read_total = 0;
375                 while (read_total < phdr.p_filesz) {
376                         if (read(fd, &note, sizeof(note)) <
377                             (ssize_t)sizeof(note)) {
378                                 warnx("elf note header too short");
379                                 return (false);
380                         }
381                         read_total += sizeof(note);
382
383                         /*
384                          * XXX: Name and descriptor are 4 byte aligned, however,
385                          *      the size given doesn't include the padding.
386                          */
387                         namesz = roundup2(note.n_namesz, 4);
388                         name = malloc(namesz);
389                         if (name == NULL) {
390                                 warn("malloc() failed.");
391                                 return (false);
392                         }
393                         descsz = roundup2(note.n_descsz, 4);
394                         if (read(fd, name, namesz) < namesz) {
395                                 warnx("elf note name too short");
396                                 free(name);
397                                 return (false);
398                         }
399                         read_total += namesz;
400
401                         if (note.n_namesz != 8 ||
402                             strncmp("FreeBSD", name, 7) != 0 ||
403                             note.n_type != NT_FREEBSD_FEATURE_CTL) {
404                                 /* Not the right note. Skip the description */
405                                 if (lseek(fd, descsz, SEEK_CUR) < 0) {
406                                         warn("lseek() failed.");
407                                         free(name);
408                                         return (false);
409                                 }
410                                 read_total += descsz;
411                                 free(name);
412                                 continue;
413                         }
414
415                         if (note.n_descsz < sizeof(uint32_t)) {
416                                 warnx("Feature descriptor can't "
417                                     "be less than 4 bytes");
418                                 free(name);
419                                 return (false);
420                         }
421
422                         /*
423                          * XXX: For now we look at only 4 bytes of the
424                          *      descriptor. This should respect descsz.
425                          */
426                         if (note.n_descsz > sizeof(uint32_t))
427                                 warnx("Feature note is bigger than expected");
428                         if (read(fd, features, sizeof(uint32_t)) <
429                             (ssize_t)sizeof(uint32_t)) {
430                                 warnx("feature note data too short");
431                                 free(name);
432                                 return (false);
433                         }
434                         if (off != NULL)
435                                 *off = phdr.p_offset + read_total;
436                         free(name);
437                         return (true);
438                 }
439         }
440
441         warnx("NT_FREEBSD_FEATURE_CTL note not found");
442         return (false);
443 }