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 *); 56 static bool get_file_features(Elf *, int, int, uint32_t *, uint64_t *); 57 static void print_features(void); 58 static bool print_file_features(Elf *, int, int, char *); 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 SUPPORTED_ENDIAN ELFDATA2LSB 85 #else 86 #define SUPPORTED_ENDIAN ELFDATA2MSB 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; 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 * XXX need to support cross-endian operation, but for now 170 * exit on error rather than misbehaving. 171 */ 172 if (ehdr.e_ident[EI_DATA] != SUPPORTED_ENDIAN) { 173 warnx("file endianness must match host"); 174 retval = 1; 175 goto fail; 176 } 177 178 if (!editfeatures) { 179 if (!print_file_features(elf, ehdr.e_phnum, fd, 180 argv[0])) { 181 retval = 1; 182 goto fail; 183 } 184 } else if (!edit_file_features(elf, ehdr.e_phnum, fd, 185 features)) { 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(char *feature_str, uint32_t *feature_val) 223 { 224 char *feature; 225 int i, len; 226 uint32_t input; 227 char operation; 228 229 input = 0; 230 operation = *feature_str; 231 feature_str++; 232 len = nitems(featurelist); 233 while ((feature = strsep(&feature_str, ",")) != NULL) { 234 for (i = 0; i < len; ++i) { 235 if (strcmp(featurelist[i].alias, feature) == 0) { 236 input |= featurelist[i].value; 237 break; 238 } 239 /* XXX Backwards compatibility for "no"-prefix flags. */ 240 if (strncmp(featurelist[i].alias, "no", 2) == 0 && 241 strcmp(featurelist[i].alias + 2, feature) == 0) { 242 input |= featurelist[i].value; 243 warnx( 244 "interpreting %s as %s; please specify %s", 245 feature, featurelist[i].alias, 246 featurelist[i].alias); 247 break; 248 } 249 } 250 if (i == len) { 251 if (isdigit(feature[0])) { 252 char *eptr; 253 unsigned long long val; 254 255 errno = 0; 256 val = strtoll(feature, &eptr, 0); 257 if (eptr == feature || *eptr != '\0') 258 errno = EINVAL; 259 else if (val > UINT32_MAX) 260 errno = ERANGE; 261 if (errno != 0) { 262 warn("%s invalid", feature); 263 return (false); 264 } 265 input |= val; 266 } else { 267 warnx("%s is not a valid feature", feature); 268 if (!iflag) 269 return (false); 270 } 271 } 272 } 273 274 if (operation == '+') { 275 *feature_val |= input; 276 } else if (operation == '=') { 277 *feature_val = input; 278 } else if (operation == '-') { 279 *feature_val &= ~input; 280 } else { 281 warnx("'%c' not an operator - use '+', '-', '='", 282 feature_str[0]); 283 return (false); 284 } 285 return (true); 286 } 287 288 static bool 289 edit_file_features(Elf *elf, int phcount, int fd, char *val) 290 { 291 uint32_t features; 292 uint64_t off; 293 294 if (!get_file_features(elf, phcount, fd, &features, &off)) { 295 warnx("NT_FREEBSD_FEATURE_CTL note not found"); 296 return (false); 297 } 298 299 if (!convert_to_feature_val(val, &features)) 300 return (false); 301 302 if (lseek(fd, off, SEEK_SET) == -1 || 303 write(fd, &features, sizeof(features)) < 304 (ssize_t)sizeof(features)) { 305 warnx("error writing feature value"); 306 return (false); 307 } 308 return (true); 309 } 310 311 static void 312 print_features(void) 313 { 314 size_t i; 315 316 printf("Known features are:\n"); 317 for (i = 0; i < nitems(featurelist); ++i) 318 printf("%-16s%s\n", featurelist[i].alias, 319 featurelist[i].desc); 320 } 321 322 static bool 323 print_file_features(Elf *elf, int phcount, int fd, char *filename) 324 { 325 uint32_t features; 326 unsigned long i; 327 328 if (!get_file_features(elf, phcount, fd, &features, NULL)) { 329 return (false); 330 } 331 332 printf("File '%s' features:\n", filename); 333 for (i = 0; i < nitems(featurelist); ++i) { 334 printf("%-16s'%s' is ", featurelist[i].alias, 335 featurelist[i].desc); 336 337 if ((featurelist[i].value & features) == 0) 338 printf("un"); 339 340 printf("set.\n"); 341 } 342 return (true); 343 } 344 345 static bool 346 get_file_features(Elf *elf, int phcount, int fd, uint32_t *features, 347 uint64_t *off) 348 { 349 GElf_Phdr phdr; 350 Elf_Note note; 351 unsigned long read_total; 352 int namesz, descsz, i; 353 char *name; 354 355 /* 356 * Go through each program header to find one that is of type PT_NOTE 357 * and has a note for feature control. 358 */ 359 for (i = 0; i < phcount; ++i) { 360 if (gelf_getphdr(elf, i, &phdr) == NULL) { 361 warnx("gelf_getphdr failed: %s", elf_errmsg(-1)); 362 return (false); 363 } 364 365 if (phdr.p_type != PT_NOTE) 366 continue; 367 368 if (lseek(fd, phdr.p_offset, SEEK_SET) < 0) { 369 warn("lseek() failed:"); 370 return (false); 371 } 372 373 read_total = 0; 374 while (read_total < phdr.p_filesz) { 375 if (read(fd, ¬e, sizeof(note)) < 376 (ssize_t)sizeof(note)) { 377 warnx("elf note header too short"); 378 return (false); 379 } 380 read_total += sizeof(note); 381 382 /* 383 * XXX: Name and descriptor are 4 byte aligned, however, 384 * the size given doesn't include the padding. 385 */ 386 namesz = roundup2(note.n_namesz, 4); 387 name = malloc(namesz); 388 if (name == NULL) { 389 warn("malloc() failed."); 390 return (false); 391 } 392 descsz = roundup2(note.n_descsz, 4); 393 if (read(fd, name, namesz) < namesz) { 394 warnx("elf note name too short"); 395 free(name); 396 return (false); 397 } 398 read_total += namesz; 399 400 if (note.n_namesz != 8 || 401 strncmp("FreeBSD", name, 7) != 0 || 402 note.n_type != NT_FREEBSD_FEATURE_CTL) { 403 /* Not the right note. Skip the description */ 404 if (lseek(fd, descsz, SEEK_CUR) < 0) { 405 warn("lseek() failed."); 406 free(name); 407 return (false); 408 } 409 read_total += descsz; 410 free(name); 411 continue; 412 } 413 414 if (note.n_descsz < sizeof(uint32_t)) { 415 warnx("Feature descriptor can't " 416 "be less than 4 bytes"); 417 free(name); 418 return (false); 419 } 420 421 /* 422 * XXX: For now we look at only 4 bytes of the 423 * descriptor. This should respect descsz. 424 */ 425 if (note.n_descsz > sizeof(uint32_t)) 426 warnx("Feature note is bigger than expected"); 427 if (read(fd, features, sizeof(uint32_t)) < 428 (ssize_t)sizeof(uint32_t)) { 429 warnx("feature note data too short"); 430 free(name); 431 return (false); 432 } 433 if (off != NULL) 434 *off = phdr.p_offset + read_total; 435 free(name); 436 return (true); 437 } 438 } 439 440 warnx("NT_FREEBSD_FEATURE_CTL note not found"); 441 return (false); 442 } 443