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 *, 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); 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 { "noaslrstkgap", NT_FREEBSD_FCTL_ASG_DISABLE, 75 "Disable ASLR stack gap" }, 76 }; 77 78 static struct option long_opts[] = { 79 { "help", no_argument, NULL, 'h' }, 80 { NULL, 0, NULL, 0 } 81 }; 82 83 #if BYTE_ORDER == LITTLE_ENDIAN 84 #define HOST_ENDIAN ELFDATA2LSB 85 #define SWAP_ENDIAN ELFDATA2MSB 86 #else 87 #define HOST_ENDIAN ELFDATA2MSB 88 #define SWAP_ENDIAN ELFDATA2LSB 89 #endif 90 91 static bool iflag; 92 93 int 94 main(int argc, char **argv) 95 { 96 GElf_Ehdr ehdr; 97 Elf *elf; 98 Elf_Kind kind; 99 int ch, fd, retval; 100 char *features; 101 bool editfeatures, lflag, endian_swap; 102 103 lflag = 0; 104 editfeatures = false; 105 retval = 0; 106 features = NULL; 107 108 if (elf_version(EV_CURRENT) == EV_NONE) 109 errx(EXIT_FAILURE, "elf_version error"); 110 111 while ((ch = getopt_long(argc, argv, "hile:", long_opts, NULL)) != -1) { 112 switch (ch) { 113 case 'i': 114 iflag = true; 115 break; 116 case 'l': 117 print_features(); 118 lflag = true; 119 break; 120 case 'e': 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(char *feature_str, uint32_t *feature_val) 226 { 227 char *feature; 228 int i, len; 229 uint32_t input; 230 char operation; 231 232 input = 0; 233 operation = *feature_str; 234 feature_str++; 235 len = nitems(featurelist); 236 while ((feature = strsep(&feature_str, ",")) != NULL) { 237 for (i = 0; i < len; ++i) { 238 if (strcmp(featurelist[i].alias, feature) == 0) { 239 input |= featurelist[i].value; 240 break; 241 } 242 /* XXX Backwards compatibility for "no"-prefix flags. */ 243 if (strncmp(featurelist[i].alias, "no", 2) == 0 && 244 strcmp(featurelist[i].alias + 2, feature) == 0) { 245 input |= featurelist[i].value; 246 warnx( 247 "interpreting %s as %s; please specify %s", 248 feature, featurelist[i].alias, 249 featurelist[i].alias); 250 break; 251 } 252 } 253 if (i == len) { 254 if (isdigit(feature[0])) { 255 char *eptr; 256 unsigned long long val; 257 258 errno = 0; 259 val = strtoll(feature, &eptr, 0); 260 if (eptr == feature || *eptr != '\0') 261 errno = EINVAL; 262 else if (val > UINT32_MAX) 263 errno = ERANGE; 264 if (errno != 0) { 265 warn("%s invalid", feature); 266 return (false); 267 } 268 input |= val; 269 } else { 270 warnx("%s is not a valid feature", feature); 271 if (!iflag) 272 return (false); 273 } 274 } 275 } 276 277 if (operation == '+') { 278 *feature_val |= input; 279 } else if (operation == '=') { 280 *feature_val = input; 281 } else if (operation == '-') { 282 *feature_val &= ~input; 283 } else { 284 warnx("'%c' not an operator - use '+', '-', '='", 285 feature_str[0]); 286 return (false); 287 } 288 return (true); 289 } 290 291 static bool 292 edit_file_features(Elf *elf, int phcount, int fd, char *val, bool endian_swap) 293 { 294 uint32_t features, prev_features; 295 uint64_t off; 296 297 if (!get_file_features(elf, phcount, fd, &features, &off, 298 endian_swap)) { 299 warnx("NT_FREEBSD_FEATURE_CTL note not found"); 300 return (false); 301 } 302 303 prev_features = features; 304 if (!convert_to_feature_val(val, &features)) 305 return (false); 306 /* Avoid touching file if no change. */ 307 if (features == prev_features) 308 return (true); 309 310 if (endian_swap) 311 features = bswap32(features); 312 313 if (lseek(fd, off, SEEK_SET) == -1 || 314 write(fd, &features, sizeof(features)) < 315 (ssize_t)sizeof(features)) { 316 warnx("error writing feature value"); 317 return (false); 318 } 319 return (true); 320 } 321 322 static void 323 print_features(void) 324 { 325 size_t i; 326 327 printf("Known features are:\n"); 328 for (i = 0; i < nitems(featurelist); ++i) 329 printf("%-16s%s\n", featurelist[i].alias, 330 featurelist[i].desc); 331 } 332 333 static bool 334 print_file_features(Elf *elf, int phcount, int fd, char *filename, 335 bool endian_swap) 336 { 337 uint32_t features; 338 unsigned long i; 339 340 if (!get_file_features(elf, phcount, fd, &features, NULL, 341 endian_swap)) { 342 return (false); 343 } 344 345 printf("File '%s' features:\n", filename); 346 for (i = 0; i < nitems(featurelist); ++i) { 347 printf("%-16s'%s' is ", featurelist[i].alias, 348 featurelist[i].desc); 349 350 if ((featurelist[i].value & features) == 0) 351 printf("un"); 352 353 printf("set.\n"); 354 } 355 return (true); 356 } 357 358 static bool 359 get_file_features(Elf *elf, int phcount, int fd, uint32_t *features, 360 uint64_t *off, bool endian_swap) 361 { 362 GElf_Phdr phdr; 363 Elf_Note note; 364 unsigned long read_total; 365 int namesz, descsz, i; 366 char *name; 367 368 /* 369 * Go through each program header to find one that is of type PT_NOTE 370 * and has a note for feature control. 371 */ 372 for (i = 0; i < phcount; ++i) { 373 if (gelf_getphdr(elf, i, &phdr) == NULL) { 374 warnx("gelf_getphdr failed: %s", elf_errmsg(-1)); 375 return (false); 376 } 377 378 if (phdr.p_type != PT_NOTE) 379 continue; 380 381 if (lseek(fd, phdr.p_offset, SEEK_SET) < 0) { 382 warn("lseek() failed:"); 383 return (false); 384 } 385 386 read_total = 0; 387 while (read_total < phdr.p_filesz) { 388 if (read(fd, ¬e, sizeof(note)) < 389 (ssize_t)sizeof(note)) { 390 warnx("elf note header too short"); 391 return (false); 392 } 393 read_total += sizeof(note); 394 395 if (endian_swap) { 396 note.n_namesz = bswap32(note.n_namesz); 397 note.n_descsz = bswap32(note.n_descsz); 398 note.n_type = bswap32(note.n_type); 399 } 400 401 /* 402 * XXX: Name and descriptor are 4 byte aligned, however, 403 * the size given doesn't include the padding. 404 */ 405 namesz = roundup2(note.n_namesz, 4); 406 name = malloc(namesz); 407 if (name == NULL) { 408 warn("malloc() failed."); 409 return (false); 410 } 411 descsz = roundup2(note.n_descsz, 4); 412 if (read(fd, name, namesz) < namesz) { 413 warnx("elf note name too short"); 414 free(name); 415 return (false); 416 } 417 read_total += namesz; 418 419 if (note.n_namesz != 8 || 420 strncmp("FreeBSD", name, 7) != 0 || 421 note.n_type != NT_FREEBSD_FEATURE_CTL) { 422 /* Not the right note. Skip the description */ 423 if (lseek(fd, descsz, SEEK_CUR) < 0) { 424 warn("lseek() failed."); 425 free(name); 426 return (false); 427 } 428 read_total += descsz; 429 free(name); 430 continue; 431 } 432 433 if (note.n_descsz < sizeof(uint32_t)) { 434 warnx("Feature descriptor can't " 435 "be less than 4 bytes"); 436 free(name); 437 return (false); 438 } 439 440 /* 441 * XXX: For now we look at only 4 bytes of the 442 * descriptor. This should respect descsz. 443 */ 444 if (note.n_descsz > sizeof(uint32_t)) 445 warnx("Feature note is bigger than expected"); 446 if (read(fd, features, sizeof(uint32_t)) < 447 (ssize_t)sizeof(uint32_t)) { 448 warnx("feature note data too short"); 449 free(name); 450 return (false); 451 } 452 if (endian_swap) 453 *features = bswap32(*features); 454 if (off != NULL) 455 *off = phdr.p_offset + read_total; 456 free(name); 457 return (true); 458 } 459 } 460 461 warnx("NT_FREEBSD_FEATURE_CTL note not found"); 462 return (false); 463 } 464