Read PT_OPENBSD_SYSCALLS in libc.so, and convert it to a table for
authorderaadt <deraadt@openbsd.org>
Tue, 16 Jan 2024 19:07:31 +0000 (19:07 +0000)
committerderaadt <deraadt@openbsd.org>
Tue, 16 Jan 2024 19:07:31 +0000 (19:07 +0000)
pinsyscalls(2).
ok kettenis

libexec/ld.so/library.c
libexec/ld.so/library_mquery.c
libexec/ld.so/loader.c
libexec/ld.so/resolve.c
libexec/ld.so/resolve.h

index b203d4f..bfff01f 100644 (file)
@@ -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 {
index 74070a8..7e85a5c 100644 (file)
@@ -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 {
index de53dc8..07cd0d8 100644 (file)
@@ -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);
index 1916341..b6e21f9 100644 (file)
@@ -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 <sys/types.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
 
 #include <limits.h>
 #include <link.h>
@@ -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);
+}
index 8f3411d..0b3278e 100644 (file)
@@ -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;