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