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