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