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