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