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