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