1 /* $OpenBSD: rdsetroot.c,v 1.3 2021/10/24 21:24:19 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2019 Sunil Nimmagadda <sunil@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/mman.h> 20 #include <sys/stat.h> 21 22 #include <err.h> 23 #include <fcntl.h> 24 #include <gelf.h> 25 #include <libelf.h> 26 #include <stdio.h> 27 #include <stdint.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <unistd.h> 31 32 int find_rd_root_image(uint64_t *, uint64_t *, off_t *, size_t *); 33 int symbol_get_u64(const char *, uint64_t *); 34 __dead void usage(void); 35 36 Elf *e; 37 Elf_Scn *symtab; 38 size_t nsymb, strtabndx, strtabsz; 39 40 int 41 main(int argc, char **argv) 42 { 43 GElf_Shdr shdr; 44 Elf_Scn *scn; 45 char *dataseg, *kernel = NULL, *fs = NULL, *name; 46 off_t mmap_off, rd_root_size_val; 47 size_t shstrndx, mmap_size; 48 uint64_t rd_root_size_off, rd_root_image_off; 49 uint32_t *ip; 50 int ch, debug = 0, fsfd, kfd, n, xflag = 0; 51 52 while ((ch = getopt(argc, argv, "dx")) != -1) { 53 switch (ch) { 54 case 'd': 55 debug = 1; 56 break; 57 case 'x': 58 xflag = 1; 59 break; 60 default: 61 usage(); 62 } 63 } 64 argc -= optind; 65 argv += optind; 66 67 if (argc == 1) 68 kernel = argv[0]; 69 else if (argc == 2) { 70 kernel = argv[0]; 71 fs = argv[1]; 72 } else 73 usage(); 74 75 if ((kfd = open(kernel, xflag ? O_RDONLY : O_RDWR)) < 0) 76 err(1, "%s", kernel); 77 78 if (fs) { 79 if (xflag) 80 fsfd = open(fs, O_RDWR | O_CREAT | O_TRUNC, 0644); 81 else 82 fsfd = open(fs, O_RDONLY); 83 } else { 84 if (xflag) 85 fsfd = dup(STDOUT_FILENO); 86 else 87 fsfd = dup(STDIN_FILENO); 88 } 89 if (fsfd < 0) 90 err(1, "%s", fs); 91 92 if (pledge("stdio", NULL) == -1) 93 err(1, "pledge"); 94 95 if (elf_version(EV_CURRENT) == EV_NONE) 96 errx(1, "elf_version: %s", elf_errmsg(-1)); 97 98 if ((e = elf_begin(kfd, xflag ? ELF_C_READ : ELF_C_RDWR, NULL)) == NULL) 99 errx(1, "elf_begin: %s", elf_errmsg(-1)); 100 101 if (elf_kind(e) != ELF_K_ELF) 102 errx(1, "%s: not an elf", kernel); 103 104 if (gelf_getclass(e) == ELFCLASSNONE) 105 errx(1, "%s: invalid elf, not 32 or 64 bit", kernel); 106 107 /* Retrieve index of section name string table. */ 108 if (elf_getshdrstrndx(e, &shstrndx) != 0) 109 errx(1, "elf_getshdrstrndx: %s", elf_errmsg(-1)); 110 111 /* Find symbol table, string table. */ 112 scn = symtab = NULL; 113 while ((scn = elf_nextscn(e, scn)) != NULL) { 114 if (gelf_getshdr(scn, &shdr) != &shdr) 115 errx(1, "elf_getshdr: %s", elf_errmsg(-1)); 116 117 if ((name = elf_strptr(e, shstrndx, shdr.sh_name)) == NULL) 118 errx(1, "elf_strptr: %s", elf_errmsg(-1)); 119 120 if (strcmp(name, ELF_SYMTAB) == 0 && 121 shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) { 122 symtab = scn; 123 nsymb = shdr.sh_size / shdr.sh_entsize; 124 } 125 126 if (strcmp(name, ELF_STRTAB) == 0 && 127 shdr.sh_type == SHT_STRTAB) { 128 strtabndx = elf_ndxscn(scn); 129 strtabsz = shdr.sh_size; 130 } 131 } 132 133 if (symtab == NULL) 134 errx(1, "symbol table not found"); 135 136 if (strtabndx == 0) 137 errx(1, "string table not found"); 138 139 if (find_rd_root_image(&rd_root_size_off, &rd_root_image_off, 140 &mmap_off, &mmap_size) != 0) 141 errx(1, "can't locate space for rd_root_image!"); 142 143 if (debug) { 144 fprintf(stderr, "rd_root_size_off: 0x%llx\n", rd_root_size_off); 145 fprintf(stderr, "rd_root_image_off: 0x%llx\n", 146 rd_root_image_off); 147 } 148 149 /* 150 * Map in the whole data segment. 151 * The file offset needs to be page aligned. 152 */ 153 dataseg = mmap(NULL, mmap_size, 154 xflag ? PROT_READ : PROT_READ | PROT_WRITE, 155 MAP_SHARED, kfd, mmap_off); 156 if (dataseg == MAP_FAILED) 157 err(1, "%s: cannot map data seg", kernel); 158 159 /* 160 * Find value in the location: rd_root_size 161 */ 162 ip = (uint32_t *) (dataseg + rd_root_size_off); 163 rd_root_size_val = *ip; 164 if (debug) { 165 fprintf(stderr, "rd_root_size val: 0x%llx (%lld blocks)\n", 166 (unsigned long long)rd_root_size_val, 167 (unsigned long long)rd_root_size_val >> 9); 168 fprintf(stderr, "copying root image...\n"); 169 } 170 171 if (xflag) { 172 n = write(fsfd, dataseg + rd_root_image_off, 173 (size_t)rd_root_size_val); 174 if (n != rd_root_size_val) 175 err(1, "write"); 176 } else { 177 struct stat sstat; 178 179 if (fstat(fsfd, &sstat) == -1) 180 err(1, "fstat"); 181 if (S_ISREG(sstat.st_mode) && 182 sstat.st_size > rd_root_size_val) { 183 fprintf(stderr, "ramdisk too small 0x%llx 0x%llx\n", 184 (unsigned long long)sstat.st_size, 185 (unsigned long long)rd_root_size_val); 186 exit(1); 187 } 188 n = read(fsfd, dataseg + rd_root_image_off, 189 (size_t)rd_root_size_val); 190 if (n < 0) 191 err(1, "read"); 192 193 msync(dataseg, mmap_size, 0); 194 } 195 196 if (debug) 197 fprintf(stderr, "...copied %d bytes\n", n); 198 199 elf_end(e); 200 return 0; 201 } 202 203 int 204 find_rd_root_image(uint64_t *rd_root_size_off, uint64_t *rd_root_image_off, 205 off_t *pmmap_off, size_t *pmmap_size) 206 { 207 GElf_Phdr phdr; 208 size_t i, phdrnum; 209 unsigned long kernel_start, kernel_size; 210 uint64_t adiff, rd_root_size, rd_root_image, size_off, image_off; 211 int error = 1; 212 213 if (symbol_get_u64("rd_root_size", &rd_root_size) != 0) 214 errx(1, "no rd_root_image symbols?"); 215 216 if (symbol_get_u64("rd_root_image", &rd_root_image) != 0) 217 errx(1, "no rd_root_image symbols?"); 218 219 /* Retrieve number of program headers. */ 220 if (elf_getphdrnum(e, &phdrnum) != 0) 221 errx(1, "elf_getphdrnum: %s", elf_errmsg(-1)); 222 223 /* Locate the data segment. */ 224 for (i = 0; i < phdrnum; i++) { 225 if (gelf_getphdr(e, i, &phdr) != &phdr) 226 errx(1, "gelf_getphdr: %s", elf_errmsg(-1)); 227 228 if (phdr.p_type != PT_LOAD) 229 continue; 230 231 kernel_start = phdr.p_paddr; 232 kernel_size = phdr.p_filesz; 233 adiff = phdr.p_vaddr - phdr.p_paddr; 234 235 size_off = rd_root_size - kernel_start; 236 image_off = rd_root_image - kernel_start; 237 if (size_off < adiff || image_off < adiff) 238 continue; 239 240 size_off -= adiff; 241 image_off -= adiff; 242 if (image_off >= kernel_size) 243 continue; 244 if (size_off >= kernel_size) 245 errx(1, "rd_root_size not in data segment"); 246 247 *pmmap_off = phdr.p_offset; 248 *pmmap_size = kernel_size; 249 *rd_root_size_off = size_off; 250 *rd_root_image_off = image_off; 251 error = 0; 252 break; 253 } 254 255 return error; 256 } 257 258 int 259 symbol_get_u64(const char *symbol, uint64_t *result) 260 { 261 GElf_Sym sym; 262 Elf_Data *data; 263 const char *name; 264 size_t i; 265 int error = 1; 266 267 data = NULL; 268 while ((data = elf_rawdata(symtab, data)) != NULL) { 269 for (i = 0; i < nsymb; i++) { 270 if (gelf_getsym(data, i, &sym) != &sym) 271 continue; 272 273 if (sym.st_name >= strtabsz) 274 break; 275 276 if ((name = elf_strptr(e, strtabndx, 277 sym.st_name)) == NULL) 278 continue; 279 280 if (strcmp(name, symbol) == 0) { 281 if (result) 282 *result = sym.st_value; 283 error = 0; 284 break; 285 } 286 } 287 } 288 289 return error; 290 } 291 292 __dead void 293 usage(void) 294 { 295 extern char *__progname; 296 297 fprintf(stderr, "usage: %s [-dx] bsd [fs]\n", __progname); 298 exit(1); 299 } 300