1 /* $OpenBSD: library.c,v 1.85 2019/12/09 22:15:15 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2002 Dale Rahn 5 * Copyright (c) 1998 Per Fogelstrom, Opsycon AB 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30 #define _DYN_LOADER 31 32 #include <sys/types.h> 33 #include <fcntl.h> 34 #include <sys/mman.h> 35 36 #include "syscall.h" 37 #include "archdep.h" 38 #include "resolve.h" 39 #include "sod.h" 40 41 #define PFLAGS(X) ((((X) & PF_R) ? PROT_READ : 0) | \ 42 (((X) & PF_W) ? PROT_WRITE : 0) | \ 43 (((X) & PF_X) ? PROT_EXEC : 0)) 44 45 void 46 _dl_load_list_free(struct load_list *load_list) 47 { 48 struct load_list *next; 49 50 while (load_list != NULL) { 51 next = load_list->next; 52 _dl_free(load_list); 53 load_list = next; 54 } 55 } 56 57 void 58 _dl_unload_shlib(elf_object_t *object) 59 { 60 struct dep_node *n; 61 elf_object_t *load_object = object->load_object; 62 63 /* 64 * If our load object has become unreferenced then we lost the 65 * last group reference to it, so the entire group should be taken 66 * down. The current object is somewhere below load_object in 67 * the child_vec tree, so it'll get cleaned up by the recursion. 68 * That means we can just switch here to the load object. 69 */ 70 if (load_object != object && OBJECT_REF_CNT(load_object) == 0 && 71 (load_object->status & STAT_UNLOADED) == 0) { 72 DL_DEB(("unload_shlib switched from %s to %s\n", 73 object->load_name, load_object->load_name)); 74 object = load_object; 75 goto unload; 76 } 77 78 DL_DEB(("unload_shlib called on %s\n", object->load_name)); 79 if (OBJECT_REF_CNT(object) == 0 && 80 (object->status & STAT_UNLOADED) == 0) { 81 struct object_vector vec; 82 int i; 83 unload: 84 object->status |= STAT_UNLOADED; 85 for (vec = object->child_vec, i = 0; i < vec.len; i++) 86 _dl_unload_shlib(vec.vec[i]); 87 TAILQ_FOREACH(n, &object->grpref_list, next_sib) 88 _dl_unload_shlib(n->data); 89 DL_DEB(("unload_shlib unloading on %s\n", object->load_name)); 90 _dl_load_list_free(object->load_list); 91 _dl_munmap((void *)object->load_base, object->load_size); 92 _dl_remove_object(object); 93 } 94 } 95 96 elf_object_t * 97 _dl_tryload_shlib(const char *libname, int type, int flags) 98 { 99 int libfile, i; 100 struct load_list *next_load, *load_list = NULL; 101 Elf_Addr maxva = 0, minva = ELF_NO_ADDR; 102 Elf_Addr libaddr, loff, align = _dl_pagesz - 1; 103 Elf_Addr relro_addr = 0, relro_size = 0; 104 elf_object_t *object; 105 char hbuf[4096], *exec_start = 0; 106 size_t exec_size = 0; 107 Elf_Dyn *dynp = NULL; 108 Elf_Ehdr *ehdr; 109 Elf_Phdr *phdp; 110 Elf_Phdr *ptls = NULL; 111 struct stat sb; 112 113 #define ROUND_PG(x) (((x) + align) & ~(align)) 114 #define TRUNC_PG(x) ((x) & ~(align)) 115 116 libfile = _dl_open(libname, O_RDONLY | O_CLOEXEC); 117 if (libfile < 0) { 118 _dl_errno = DL_CANT_OPEN; 119 return(0); 120 } 121 122 if ( _dl_fstat(libfile, &sb) < 0) { 123 _dl_errno = DL_CANT_OPEN; 124 return(0); 125 } 126 127 for (object = _dl_objects; object != NULL; object = object->next) { 128 if (object->dev == sb.st_dev && 129 object->inode == sb.st_ino) { 130 object->obj_flags |= flags & DF_1_GLOBAL; 131 _dl_close(libfile); 132 if (_dl_loading_object == NULL) 133 _dl_loading_object = object; 134 if (object->load_object != _dl_objects && 135 object->load_object != _dl_loading_object) { 136 _dl_link_grpref(object->load_object, 137 _dl_loading_object); 138 } 139 return(object); 140 } 141 } 142 143 _dl_read(libfile, hbuf, sizeof(hbuf)); 144 ehdr = (Elf_Ehdr *)hbuf; 145 if (ehdr->e_ident[0] != ELFMAG0 || ehdr->e_ident[1] != ELFMAG1 || 146 ehdr->e_ident[2] != ELFMAG2 || ehdr->e_ident[3] != ELFMAG3 || 147 ehdr->e_type != ET_DYN || ehdr->e_machine != MACHID) { 148 _dl_close(libfile); 149 _dl_errno = DL_NOT_ELF; 150 return(0); 151 } 152 153 /* 154 * Alright, we might have a winner! 155 * Figure out how much VM space we need. 156 */ 157 phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff); 158 for (i = 0; i < ehdr->e_phnum; i++, phdp++) { 159 switch (phdp->p_type) { 160 case PT_LOAD: 161 if (phdp->p_vaddr < minva) 162 minva = phdp->p_vaddr; 163 if (phdp->p_vaddr + phdp->p_memsz > maxva) 164 maxva = phdp->p_vaddr + phdp->p_memsz; 165 break; 166 case PT_DYNAMIC: 167 dynp = (Elf_Dyn *)phdp->p_vaddr; 168 break; 169 case PT_TLS: 170 if (phdp->p_filesz > phdp->p_memsz) { 171 _dl_printf("%s: invalid tls data in %s.\n", 172 __progname, libname); 173 _dl_close(libfile); 174 _dl_errno = DL_CANT_LOAD_OBJ; 175 return(0); 176 } 177 if (!_dl_tib_static_done) { 178 ptls = phdp; 179 break; 180 } 181 _dl_printf("%s: unsupported TLS program header in %s\n", 182 __progname, libname); 183 _dl_close(libfile); 184 _dl_errno = DL_CANT_LOAD_OBJ; 185 return(0); 186 default: 187 break; 188 } 189 } 190 minva = TRUNC_PG(minva); 191 maxva = ROUND_PG(maxva); 192 193 /* 194 * We map the entire area to see that we can get the VM 195 * space required. Map it unaccessible to start with. 196 * 197 * We must map the file we'll map later otherwise the VM 198 * system won't be able to align the mapping properly 199 * on VAC architectures. 200 */ 201 libaddr = (Elf_Addr)_dl_mmap(0, maxva - minva, PROT_NONE, 202 MAP_PRIVATE|MAP_FILE, libfile, 0); 203 if (_dl_mmap_error(libaddr)) { 204 _dl_printf("%s: ld.so mmap failed mapping %s.\n", 205 __progname, libname); 206 _dl_close(libfile); 207 _dl_errno = DL_CANT_MMAP; 208 return(0); 209 } 210 211 loff = libaddr - minva; 212 phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff); 213 214 for (i = 0; i < ehdr->e_phnum; i++, phdp++) { 215 switch (phdp->p_type) { 216 case PT_LOAD: { 217 char *start = (char *)(TRUNC_PG(phdp->p_vaddr)) + loff; 218 Elf_Addr off = (phdp->p_vaddr & align); 219 Elf_Addr size = off + phdp->p_filesz; 220 int flags = PFLAGS(phdp->p_flags); 221 void *res; 222 223 /* 224 * Initially map W|X segments without X 225 * permission. After we're done with the 226 * initial relocation processing, we will make 227 * these segments read-only and add back the X 228 * permission. This way we maintain W^X at 229 * all times. 230 */ 231 if ((flags & PROT_WRITE) && (flags & PROT_EXEC)) 232 flags &= ~PROT_EXEC; 233 234 if (size != 0) { 235 res = _dl_mmap(start, ROUND_PG(size), flags, 236 MAP_FIXED|MAP_PRIVATE, libfile, 237 TRUNC_PG(phdp->p_offset)); 238 } else 239 res = NULL; /* silence gcc */ 240 next_load = _dl_calloc(1, sizeof(struct load_list)); 241 if (next_load == NULL) 242 _dl_oom(); 243 next_load->next = load_list; 244 load_list = next_load; 245 next_load->start = start; 246 next_load->size = size; 247 next_load->prot = PFLAGS(phdp->p_flags); 248 if (size != 0 && _dl_mmap_error(res)) { 249 _dl_printf("%s: ld.so mmap failed mapping %s.\n", 250 __progname, libname); 251 _dl_close(libfile); 252 _dl_errno = DL_CANT_MMAP; 253 _dl_munmap((void *)libaddr, maxva - minva); 254 _dl_load_list_free(load_list); 255 return(0); 256 } 257 if ((flags & PROT_EXEC) && exec_start == 0) { 258 exec_start = start; 259 exec_size = ROUND_PG(size); 260 } 261 262 if (phdp->p_flags & PF_W) { 263 /* Zero out everything past the EOF */ 264 if ((size & align) != 0) 265 _dl_memset(start + size, 0, 266 _dl_pagesz - (size & align)); 267 if (ROUND_PG(size) == 268 ROUND_PG(off + phdp->p_memsz)) 269 continue; 270 start = start + ROUND_PG(size); 271 size = ROUND_PG(off + phdp->p_memsz) - 272 ROUND_PG(size); 273 res = _dl_mmap(start, size, flags, 274 MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0); 275 if (_dl_mmap_error(res)) { 276 _dl_printf("%s: ld.so mmap failed mapping %s.\n", 277 __progname, libname); 278 _dl_close(libfile); 279 _dl_errno = DL_CANT_MMAP; 280 _dl_munmap((void *)libaddr, maxva - minva); 281 _dl_load_list_free(load_list); 282 return(0); 283 } 284 } 285 break; 286 } 287 288 case PT_OPENBSD_RANDOMIZE: 289 _dl_arc4randombuf((char *)(phdp->p_vaddr + loff), 290 phdp->p_memsz); 291 break; 292 293 case PT_GNU_RELRO: 294 relro_addr = phdp->p_vaddr + loff; 295 relro_size = phdp->p_memsz; 296 break; 297 298 default: 299 break; 300 } 301 } 302 303 _dl_close(libfile); 304 305 dynp = (Elf_Dyn *)((unsigned long)dynp + loff); 306 object = _dl_finalize_object(libname, dynp, 307 (Elf_Phdr *)((char *)libaddr + ehdr->e_phoff), ehdr->e_phnum,type, 308 libaddr, loff); 309 if (object) { 310 char *soname = (char *)object->Dyn.info[DT_SONAME]; 311 312 object->load_size = maxva - minva; /*XXX*/ 313 object->load_list = load_list; 314 /* set inode, dev from stat info */ 315 object->dev = sb.st_dev; 316 object->inode = sb.st_ino; 317 object->obj_flags |= flags; 318 object->relro_addr = relro_addr; 319 object->relro_size = relro_size; 320 _dl_set_sod(object->load_name, &object->sod); 321 if (ptls != NULL && ptls->p_memsz) 322 _dl_set_tls(object, ptls, libaddr, libname); 323 324 /* Request permission for system calls in libc.so's text segment */ 325 if (soname != NULL && 326 _dl_strncmp(soname, "libc.so.", 8) == 0) { 327 if (_dl_msyscall(exec_start, exec_size) == -1) 328 _dl_printf("msyscall %lx %lx error\n", 329 exec_start, exec_size); 330 } 331 } else { 332 _dl_munmap((void *)libaddr, maxva - minva); 333 _dl_load_list_free(load_list); 334 } 335 return(object); 336 } 337