Unfortunately there are still ugly text-relocation binaries in the wild.
authorderaadt <deraadt@openbsd.org>
Thu, 27 Oct 2022 22:48:17 +0000 (22:48 +0000)
committerderaadt <deraadt@openbsd.org>
Thu, 27 Oct 2022 22:48:17 +0000 (22:48 +0000)
Libraries are less of a concern, because ld.so can fix them in the right
order.  So we must scan DYNAMIC for the TEXTREL marker, and not make
X LOADs immutable.  ld.so will apply changes to the text segment.  In
upcoming diff, crt0 and ld.so will then apply immutability.
ok kettenis

sys/kern/exec_elf.c
sys/sys/exec.h

index 0c2d081..c521c79 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: exec_elf.c,v 1.172 2022/10/27 16:01:18 deraadt Exp $  */
+/*     $OpenBSD: exec_elf.c,v 1.173 2022/10/27 22:48:17 deraadt Exp $  */
 
 /*
  * Copyright (c) 1996 Per Fogelstrom
@@ -189,17 +189,21 @@ elf_load_psection(struct exec_vmcmd_set *vcset, struct vnode *vp,
         * initially.  The dynamic linker will make these read-only
         * and add back X permission after relocation processing.
         * Static executables with W|X segments will probably crash.
-        * Apply immutability as much as possible, but not for RELRO
-        * or PT_OPENBSD_MUTABLE sections, or LOADS marked
-        * PF_OPENBSD_MUTABLE, or LOADS which violate W^X. Userland
-        * (meaning crt0 or ld.so) will repair those regions.
         */
        *prot |= (ph->p_flags & PF_R) ? PROT_READ : 0;
        *prot |= (ph->p_flags & PF_W) ? PROT_WRITE : 0;
        if ((ph->p_flags & PF_W) == 0)
                *prot |= (ph->p_flags & PF_X) ? PROT_EXEC : 0;
+
+       /*
+        * Apply immutability as much as possible, but not text-segments
+        * of textrel binaries, or RELRO or PT_OPENBSD_MUTABLE sections,
+        * or LOADS marked PF_OPENBSD_MUTABLE, or LOADS which violate W^X.
+        * Userland (meaning crt0 or ld.so) will repair those regions.
+        */
        if ((ph->p_flags & (PF_X | PF_W)) != (PF_X | PF_W) &&
-           (ph->p_flags & PF_OPENBSD_MUTABLE) == 0)
+           ((ph->p_flags & PF_OPENBSD_MUTABLE) == 0) &&
+           ((flags & VMCMD_TEXTREL) == 0 && (ph->p_flags & PF_X) == 0))
                flags |= VMCMD_IMMUTABLE;
 
        msize = ph->p_memsz + diff;
@@ -482,7 +486,7 @@ exec_elf_makecmds(struct proc *p, struct exec_package *epp)
        Elf_Ehdr *eh = epp->ep_hdr;
        Elf_Phdr *ph, *pp, *base_ph = NULL;
        Elf_Addr phdr = 0, exe_base = 0;
-       int error, i, has_phdr = 0, names = 0;
+       int error, i, has_phdr = 0, names = 0, textrel = 0;
        char *interp = NULL;
        u_long phsize;
        size_t randomizequota = ELF_RANDOMIZE_LIMIT;
@@ -543,6 +547,15 @@ exec_elf_makecmds(struct proc *p, struct exec_package *epp)
                }
        }
 
+       /*
+        * Verify this is an OpenBSD executable.  If it's marked that way
+        * via a PT_NOTE then also check for a PT_OPENBSD_WXNEEDED segment.
+        */
+       if ((error = elf_os_pt_note(p, epp, epp->ep_hdr, &names)) != 0)
+               goto bad;
+       if (eh->e_ident[EI_OSABI] == ELFOSABI_OPENBSD)
+               names |= ELF_NOTE_NAME_OPENBSD;
+
        if (eh->e_type == ET_DYN) {
                /* need phdr and load sections for PIE */
                if (!has_phdr || base_ph == NULL) {
@@ -551,16 +564,39 @@ exec_elf_makecmds(struct proc *p, struct exec_package *epp)
                }
                /* randomize exe_base for PIE */
                exe_base = uvm_map_pie(base_ph->p_align);
-       }
 
-       /*
-        * Verify this is an OpenBSD executable.  If it's marked that way
-        * via a PT_NOTE then also check for a PT_OPENBSD_WXNEEDED segment.
-        */
-       if ((error = elf_os_pt_note(p, epp, epp->ep_hdr, &names)) != 0)
-               goto bad;
-       if (eh->e_ident[EI_OSABI] == ELFOSABI_OPENBSD)
-               names |= ELF_NOTE_NAME_OPENBSD;
+               /*
+                * Check if DYNAMIC contains DT_TEXTREL
+                */
+               for (i = 0, pp = ph; i < eh->e_phnum; i++, pp++) {
+                       Elf32_Dyn *dt;
+                       int j;
+
+                       switch (pp->p_type) {
+                       case PT_DYNAMIC:
+                               if (pp->p_filesz > 64*1024)
+                                       break;
+                               dt = malloc(pp->p_filesz, M_TEMP, M_WAITOK);
+                               error = vn_rdwr(UIO_READ, epp->ep_vp,
+                                   (caddr_t)dt, pp->p_filesz, pp->p_offset,
+                                   UIO_SYSSPACE, IO_UNIT, p->p_ucred, NULL, p);
+                               if (error) {
+                                       free(dt, M_TEMP, pp->p_filesz);
+                                       break;
+                               }
+                               for (j = 0; j * sizeof(*dt) < pp->p_filesz; j++) {
+                                       if (dt[j].d_tag == DT_TEXTREL) {
+                                               textrel = VMCMD_TEXTREL;
+                                               break;
+                                       }
+                               }
+                               free(dt, M_TEMP, pp->p_filesz);
+                               break;
+                       default:
+                               break;
+                       }
+               }
+       }
 
        /*
         * Load all the necessary sections
@@ -598,7 +634,7 @@ exec_elf_makecmds(struct proc *p, struct exec_package *epp)
                         * for DATA_PLT, is fine for TEXT_PLT.
                         */
                        elf_load_psection(&epp->ep_vmcmds, epp->ep_vp,
-                           pp, &addr, &size, &prot, flags | syscall);
+                           pp, &addr, &size, &prot, flags | textrel | syscall);
 
                        /*
                         * Update exe_base in case alignment was off.
index 2e410c0..40c7173 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: exec.h,v 1.49 2022/10/21 18:10:52 deraadt Exp $       */
+/*     $OpenBSD: exec.h,v 1.50 2022/10/27 22:48:17 deraadt Exp $       */
 /*     $NetBSD: exec.h,v 1.59 1996/02/09 18:25:09 christos Exp $       */
 
 /*-
@@ -94,6 +94,7 @@ struct exec_vmcmd {
 #define VMCMD_STACK     0x0004  /* create with UVM_FLAG_STACK */
 #define VMCMD_SYSCALL   0x0008  /* create with UVM_FLAG_SYSCALL */
 #define VMCMD_IMMUTABLE        0x0010  /* create with UVM_ET_IMMUTABLE */
+#define VMCMD_TEXTREL  0x0020  /* terrible binary contains terrible textrel */
 };
 
 #define        EXEC_DEFAULT_VMCMD_SETSIZE      12      /* # of cmds in set to start */