From 4a066defabc39dafcb616ade6ee82c10212ac361 Mon Sep 17 00:00:00 2001 From: deraadt Date: Tue, 16 Jan 2024 19:07:31 +0000 Subject: [PATCH] Read PT_OPENBSD_SYSCALLS in libc.so, and convert it to a table for pinsyscalls(2). ok kettenis --- libexec/ld.so/library.c | 39 ++++++++++------ libexec/ld.so/library_mquery.c | 38 +++++++++------ libexec/ld.so/loader.c | 9 ++-- libexec/ld.so/resolve.c | 84 +++++++++++++++++++++++++++++++++- libexec/ld.so/resolve.h | 6 ++- 5 files changed, 142 insertions(+), 34 deletions(-) diff --git a/libexec/ld.so/library.c b/libexec/ld.so/library.c index b203d4fcef7..bfff01fe5ba 100644 --- a/libexec/ld.so/library.c +++ b/libexec/ld.so/library.c @@ -1,4 +1,4 @@ -/* $OpenBSD: library.c,v 1.93 2023/12/19 16:13:22 deraadt Exp $ */ +/* $OpenBSD: library.c,v 1.94 2024/01/16 19:07:31 deraadt Exp $ */ /* * Copyright (c) 2002 Dale Rahn @@ -99,7 +99,7 @@ elf_object_t * _dl_tryload_shlib(const char *libname, int type, int flags, int nodelete) { struct range_vector imut, mut; - int libfile, i; + int libfile, libc = -1, i; struct load_list *next_load, *load_list = NULL; Elf_Addr maxva = 0, minva = ELF_NO_ADDR; Elf_Addr libaddr, loff, align = _dl_pagesz - 1; @@ -109,8 +109,8 @@ _dl_tryload_shlib(const char *libname, int type, int flags, int nodelete) size_t exec_size = 0; Elf_Dyn *dynp = NULL; Elf_Ehdr *ehdr; - Elf_Phdr *phdp; - Elf_Phdr *ptls = NULL; + Elf_Phdr *phdp, *ptls = NULL; + Elf_Phdr *syscall_phdp = NULL; struct stat sb; #define powerof2(x) ((((x) - 1) & (x)) == 0) @@ -139,7 +139,6 @@ _dl_tryload_shlib(const char *libname, int type, int flags, int nodelete) if (flags & DF_1_NOOPEN) { _dl_close(libfile); return NULL; - } _dl_read(libfile, hbuf, sizeof(hbuf)); @@ -316,11 +315,30 @@ _dl_tryload_shlib(const char *libname, int type, int flags, int nodelete) _dl_push_range_size(&mut, phdp->p_vaddr + loff, phdp->p_memsz); break; + case PT_OPENBSD_SYSCALLS: + syscall_phdp = phdp; + break; default: break; } } + libc = _dl_islibc(dynp, loff); + if (libc) { + if (syscall_phdp) + _dl_pin(libfile, syscall_phdp, (void *)libaddr, + (size_t)((exec_start + exec_size) - libaddr), + exec_start, exec_size); + + /* + * XXX msyscall() can be removed once pinsyscalls() + * is fully operational + */ + /* Request permission for system calls in libc.so's text segment */ + if (_dl_msyscall(exec_start, exec_size) == -1) + _dl_printf("msyscall %lx %lx error\n", + exec_start, exec_size); + } _dl_close(libfile); dynp = (Elf_Dyn *)((unsigned long)dynp + loff); @@ -328,8 +346,6 @@ _dl_tryload_shlib(const char *libname, int type, int flags, int nodelete) (Elf_Phdr *)((char *)libaddr + ehdr->e_phoff), ehdr->e_phnum,type, libaddr, loff); if (object) { - char *soname = (char *)object->Dyn.info[DT_SONAME]; - object->load_size = maxva - minva; /*XXX*/ object->load_list = load_list; /* set inode, dev from stat info */ @@ -339,17 +355,10 @@ _dl_tryload_shlib(const char *libname, int type, int flags, int nodelete) object->nodelete = nodelete; object->relro_addr = relro_addr; object->relro_size = relro_size; + object->islibc = libc; _dl_set_sod(object->load_name, &object->sod); if (ptls != NULL && ptls->p_memsz) _dl_set_tls(object, ptls, libaddr, libname); - - /* Request permission for system calls in libc.so's text segment */ - if (soname != NULL && !_dl_traceld && - _dl_strncmp(soname, "libc.so.", 8) == 0) { - if (_dl_msyscall(exec_start, exec_size) == -1) - _dl_printf("msyscall %lx %lx error\n", - exec_start, exec_size); - } _dl_bcopy(&mut, &object->mut, sizeof mut); _dl_bcopy(&imut, &object->imut, sizeof imut); } else { diff --git a/libexec/ld.so/library_mquery.c b/libexec/ld.so/library_mquery.c index 74070a86a5f..7e85a5c0f96 100644 --- a/libexec/ld.so/library_mquery.c +++ b/libexec/ld.so/library_mquery.c @@ -1,4 +1,4 @@ -/* $OpenBSD: library_mquery.c,v 1.73 2023/12/19 16:13:22 deraadt Exp $ */ +/* $OpenBSD: library_mquery.c,v 1.74 2024/01/16 19:07:31 deraadt Exp $ */ /* * Copyright (c) 2002 Dale Rahn @@ -102,15 +102,15 @@ elf_object_t * _dl_tryload_shlib(const char *libname, int type, int flags, int nodelete) { struct range_vector imut, mut; - int libfile, i; + int libfile, libc = -1, i; struct load_list *ld, *lowld = NULL; elf_object_t *object; Elf_Dyn *dynp = NULL; Elf_Ehdr *ehdr; - Elf_Phdr *phdp; + Elf_Phdr *phdp, *ptls = NULL; + Elf_Phdr *syscall_phdp = NULL; Elf_Addr load_end = 0; Elf_Addr align = _dl_pagesz - 1, off, size; - Elf_Phdr *ptls = NULL; Elf_Addr relro_addr = 0, relro_size = 0; struct stat sb; char hbuf[4096], *exec_start; @@ -325,9 +325,28 @@ retry: _dl_push_range_size(&mut, phdp->p_vaddr + LOFF, phdp->p_memsz); break; + case PT_OPENBSD_SYSCALLS: + syscall_phdp = phdp; + break; } } + libc = _dl_islibc(dynp, LOFF); + if (libc) { + if (syscall_phdp) + _dl_pin(libfile, syscall_phdp, lowld->start, + (size_t)((exec_start + exec_size) - LOFF), + exec_start, exec_size); + + /* + * XXX msyscall() can be removed once pinsyscalls() + * is fully operational + */ + /* Request permission for system calls in libc.so's text segment */ + if (_dl_msyscall(exec_start, exec_size) == -1) + _dl_printf("msyscall %lx %lx error\n", + exec_start, exec_size); + } _dl_close(libfile); dynp = (Elf_Dyn *)((unsigned long)dynp + LOFF); @@ -335,8 +354,6 @@ retry: (Elf_Phdr *)((char *)lowld->start + ehdr->e_phoff), ehdr->e_phnum, type, (Elf_Addr)lowld->start, LOFF); if (object) { - char *soname = (char *)object->Dyn.info[DT_SONAME]; - object->load_size = (Elf_Addr)load_end - (Elf_Addr)lowld->start; object->load_list = lowld; /* set inode, dev from stat info */ @@ -346,18 +363,11 @@ retry: object->nodelete = nodelete; object->relro_addr = relro_addr; object->relro_size = relro_size; + object->islibc = libc; _dl_set_sod(object->load_name, &object->sod); if (ptls != NULL && ptls->p_memsz) _dl_set_tls(object, ptls, (Elf_Addr)lowld->start, libname); - - /* Request permission for system calls in libc.so's text segment */ - if (soname != NULL && !_dl_traceld && - _dl_strncmp(soname, "libc.so.", 8) == 0) { - if (_dl_msyscall(exec_start, exec_size) == -1) - _dl_printf("msyscall %lx %lx error\n", - exec_start, exec_size); - } _dl_bcopy(&mut, &object->mut, sizeof mut); _dl_bcopy(&imut, &object->imut, sizeof imut); } else { diff --git a/libexec/ld.so/loader.c b/libexec/ld.so/loader.c index de53dc85d58..07cd0d8e107 100644 --- a/libexec/ld.so/loader.c +++ b/libexec/ld.so/loader.c @@ -1,4 +1,4 @@ -/* $OpenBSD: loader.c,v 1.219 2024/01/14 09:39:03 kettenis Exp $ */ +/* $OpenBSD: loader.c,v 1.220 2024/01/16 19:07:31 deraadt Exp $ */ /* * Copyright (c) 1998 Per Fogelstrom, Opsycon AB @@ -440,11 +440,14 @@ _dl_load_dep_libs(elf_object_t *object, int flags, int booting) _dl_cache_grpsym_list_setup(object); + /* + * XXX pinsyscall(SYS_execve,...) can be removed once pinsyscalls() + * is fully operational + */ for (obj = _dl_objects; booting && obj != NULL; obj = obj->next) { - char *soname = (char *)obj->Dyn.info[DT_SONAME]; struct sym_res sr; - if (!soname || _dl_strncmp(soname, "libc.so.", 8)) + if (!obj->islibc) continue; sr = _dl_find_symbol("execve", SYM_SEARCH_SELF|SYM_PLT|SYM_WARNNOTFOUND, NULL, obj); diff --git a/libexec/ld.so/resolve.c b/libexec/ld.so/resolve.c index 1916341158c..b6e21f9d005 100644 --- a/libexec/ld.so/resolve.c +++ b/libexec/ld.so/resolve.c @@ -1,4 +1,4 @@ -/* $OpenBSD: resolve.c,v 1.100 2023/07/08 14:09:43 jasper Exp $ */ +/* $OpenBSD: resolve.c,v 1.101 2024/01/16 19:07:31 deraadt Exp $ */ /* * Copyright (c) 1998 Per Fogelstrom, Opsycon AB @@ -29,6 +29,8 @@ #define _DYN_LOADER #include +#include +#include #include #include @@ -36,6 +38,7 @@ #include "util.h" #include "path.h" #include "resolve.h" +#include "syscall.h" /* substitution types */ typedef enum { @@ -745,3 +748,82 @@ _dl_debug_state(void) { /* Debugger stub */ } + +/* + * Search for DT_SONAME, and check if this is libc + */ +int +_dl_islibc(Elf_Dyn *_dynp, Elf_Addr loff) +{ + Elf_Dyn *d, *dynp = (Elf_Dyn *)((unsigned long)_dynp + loff); + long base = 0; + + for (d = dynp; d->d_tag != DT_NULL; d++) + if (d->d_tag == DT_STRTAB) { + base = d->d_un.d_ptr + loff; + break; + } + if (base == 0) + return 0; + for (d = dynp; d->d_tag != DT_NULL; d++) + if (d->d_tag == DT_SONAME) { + if (_dl_strncmp((char *)(base + d->d_un.d_ptr), + "libc.so.", 8) == 0) + return 1; + break; + } + return 0; +} + +void +_dl_pin(int file, Elf_Phdr *phdp, void *base, size_t len, + void *exec_base, size_t exec_size) +{ + struct pinsyscalls { + u_int offset; + u_int sysno; + } *syscalls; + int npins = 0, nsyscalls, i; + u_int *pins = NULL; + vaddr_t offset; + + if (phdp->p_filesz > SYS_MAXSYSCALL * 2 * sizeof(*syscalls) || + phdp->p_filesz % sizeof(*syscalls) != 0 || + phdp->p_offset & 0x3) + return; + syscalls = _dl_mmap(NULL, phdp->p_filesz, PROT_READ, + MAP_PRIVATE|MAP_FILE, file, phdp->p_offset); + if (syscalls == MAP_FAILED) + return; + + /* Validate, and calculate pintable size */ + nsyscalls = phdp->p_filesz / sizeof(*syscalls); + for (i = 0; i < nsyscalls; i++) { + if (syscalls[i].sysno < 0 || + syscalls[i].sysno >= SYS_MAXSYSCALL || + syscalls[i].offset >= len) + goto bad; + npins = MAXIMUM(npins, syscalls[i].sysno); + } + npins++; + + /* + * Fill pintable: 0 = invalid, -1 = accept, else offset + * from base, rebase to text_start while at it + */ + pins = _dl_calloc(npins, sizeof(u_int)); + offset = exec_base - base; + for (i = 0; i < nsyscalls; i++) { + if (pins[syscalls[i].sysno]) + pins[syscalls[i].sysno] = (u_int)-1; /* duplicated */ + else + pins[syscalls[i].sysno] = syscalls[i].offset - offset; + } + base += offset; + len = len - offset; +bad: + _dl_munmap(syscalls, phdp->p_filesz); + if (pins) + _dl_pinsyscalls(base, len, pins, npins); + _dl_free(pins); +} diff --git a/libexec/ld.so/resolve.h b/libexec/ld.so/resolve.h index 8f3411dcfd1..0b3278e6f7a 100644 --- a/libexec/ld.so/resolve.h +++ b/libexec/ld.so/resolve.h @@ -1,4 +1,4 @@ -/* $OpenBSD: resolve.h,v 1.106 2023/12/19 16:13:22 deraadt Exp $ */ +/* $OpenBSD: resolve.h,v 1.107 2024/01/16 19:07:31 deraadt Exp $ */ /* * Copyright (c) 1998 Per Fogelstrom, Opsycon AB @@ -245,6 +245,7 @@ struct elf_object { struct range_vector imut; struct range_vector mut; + int islibc; }; struct dep_node { @@ -340,6 +341,9 @@ typedef void lock_cb(int); void _dl_thread_kern_go(lock_cb *); lock_cb *_dl_thread_kern_stop(void); +int _dl_islibc(Elf_Dyn *_dynp, Elf_Addr loff); +void _dl_pin(int, Elf_Phdr *, void *, size_t, void *, size_t); + char *_dl_getenv(const char *, char **) __boot; void _dl_unsetenv(const char *, char **) __boot; -- 2.20.1