xref: /openbsd/usr.sbin/rdsetroot/rdsetroot.c (revision 73471bf0)
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