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