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