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