sync with OpenBSD -current

This commit is contained in:
purplerain 2024-01-16 16:00:24 +00:00
parent 34a3d7e18a
commit ee68147dcd
Signed by: purplerain
GPG key ID: F42C07F07E2E35B7
30 changed files with 722 additions and 211 deletions

View file

@ -1,4 +1,4 @@
/* $OpenBSD: exec_elf.c,v 1.183 2023/07/12 19:34:14 jasper Exp $ */
/* $OpenBSD: exec_elf.c,v 1.184 2024/01/16 19:05:01 deraadt Exp $ */
/*
* Copyright (c) 1996 Per Fogelstrom
@ -81,6 +81,7 @@
#include <sys/ptrace.h>
#include <sys/signalvar.h>
#include <sys/pledge.h>
#include <sys/syscall.h>
#include <sys/mman.h>
@ -97,6 +98,8 @@ void elf_load_psection(struct exec_vmcmd_set *, struct vnode *,
Elf_Phdr *, Elf_Addr *, Elf_Addr *, int *, int);
int elf_os_pt_note_name(Elf_Note *);
int elf_os_pt_note(struct proc *, struct exec_package *, Elf_Ehdr *, int *);
int elf_read_pintable(struct proc *p, struct vnode *vp, Elf_Phdr *pp,
u_int **pinp, int is_ldso, size_t len);
/* round up and down to page boundaries. */
#define ELF_ROUND(a, b) (((a) + (b) - 1) & ~((b) - 1))
@ -265,6 +268,74 @@ elf_read_from(struct proc *p, struct vnode *vp, u_long off, void *buf,
return (0);
}
/*
* rebase the pin offsets inside a base,len window for the text segment only.
*/
void
elf_adjustpins(vaddr_t *basep, size_t *lenp, u_int *pins, int npins, u_int offset)
{
int i;
/* Adjust offsets, base, len */
for (i = 0; i < npins; i++) {
if (pins[i] == -1 || pins[i] == 0)
continue;
pins[i] -= offset;
}
*basep += offset;
*lenp -= offset;
}
int
elf_read_pintable(struct proc *p, struct vnode *vp, Elf_Phdr *pp,
u_int **pinp, int is_ldso, size_t len)
{
struct pinsyscalls {
u_int offset;
u_int sysno;
} *syscalls = NULL;
int i, nsyscalls = 0, npins = 0;
u_int *pins = NULL;
if (pp->p_filesz > SYS_MAXSYSCALL * 2 * sizeof(*syscalls) ||
pp->p_filesz % sizeof(*syscalls) != 0)
goto bad;
nsyscalls = pp->p_filesz / sizeof(*syscalls);
syscalls = malloc(pp->p_filesz, M_PINSYSCALL, M_WAITOK);
if (elf_read_from(p, vp, pp->p_offset, syscalls,
pp->p_filesz) != 0)
goto bad;
/* Validate, and calculate pintable size */
for (i = 0; i < nsyscalls; i++) {
if (syscalls[i].sysno <= 0 ||
syscalls[i].sysno >= SYS_MAXSYSCALL ||
syscalls[i].offset > len)
goto bad;
npins = MAX(npins, syscalls[i].sysno);
}
if (is_ldso)
npins = MAX(npins, SYS_kbind); /* XXX see ld.so/loader.c */
npins++;
/* Fill pintable: 0 = invalid, -1 = allowed, else offset from base */
pins = mallocarray(npins, sizeof(u_int), M_PINSYSCALL, M_WAITOK|M_ZERO);
for (i = 0; i < nsyscalls; i++) {
if (pins[syscalls[i].sysno])
pins[syscalls[i].sysno] = -1; /* duplicated */
else
pins[syscalls[i].sysno] = syscalls[i].offset;
}
if (is_ldso)
pins[SYS_kbind] = -1; /* XXX see ld.so/loader.c */
*pinp = pins;
pins = NULL;
bad:
free(syscalls, M_PINSYSCALL, nsyscalls * sizeof(*syscalls));
free(pins, M_PINSYSCALL, npins * sizeof(u_int));
return npins;
}
/*
* Load a file (interpreter/library) pointed to by path [stolen from
* coff_load_shlib()]. Made slightly generic so it might be used externally.
@ -276,7 +347,7 @@ elf_load_file(struct proc *p, char *path, struct exec_package *epp,
int error, i;
struct nameidata nd;
Elf_Ehdr eh;
Elf_Phdr *ph = NULL;
Elf_Phdr *ph = NULL, *syscall_ph = NULL;
u_long phsize = 0;
Elf_Addr addr;
struct vnode *vp;
@ -290,6 +361,7 @@ elf_load_file(struct proc *p, char *path, struct exec_package *epp,
int file_align;
int loop;
size_t randomizequota = ELF_RANDOMIZE_LIMIT;
vaddr_t text_start = -1, text_end = 0;
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, path, p);
nd.ni_pledge = PLEDGE_RPATH;
@ -432,6 +504,12 @@ elf_load_file(struct proc *p, char *path, struct exec_package *epp,
epp->ep_entry += pos;
ap->arg_interp = pos;
}
if (prot & PROT_EXEC) {
if (addr < text_start)
text_start = addr;
if (addr+size >= text_end)
text_end = addr + size;
}
addr += size;
break;
@ -461,12 +539,34 @@ elf_load_file(struct proc *p, char *path, struct exec_package *epp,
NEW_VMCMD(&epp->ep_vmcmds, vmcmd_mutable,
ph[i].p_memsz, ph[i].p_vaddr + pos, NULLVP, 0, 0);
break;
case PT_OPENBSD_SYSCALLS:
syscall_ph = &ph[i];
break;
default:
break;
}
}
if (syscall_ph) {
struct process *pr = p->p_p;
vaddr_t base = pos;
size_t len = text_end;
u_int *pins;
int npins;
npins = elf_read_pintable(p, nd.ni_vp, syscall_ph,
&pins, 1, len);
if (npins) {
elf_adjustpins(&base, &len, pins, npins,
text_start);
pr->ps_pin.pn_start = base;
pr->ps_pin.pn_end = base + len;
pr->ps_pin.pn_pins = pins;
pr->ps_pin.pn_npins = npins;
pr->ps_flags |= PS_PIN;
}
}
vn_marktext(nd.ni_vp);
bad1:
@ -491,8 +591,8 @@ int
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;
Elf_Phdr *ph, *pp, *base_ph = NULL, *syscall_ph = NULL;
Elf_Addr phdr = 0, exe_base = 0, exe_end = 0;
int error, i, has_phdr = 0, names = 0, textrel = 0;
char *interp = NULL;
u_long phsize;
@ -633,11 +733,13 @@ exec_elf_makecmds(struct proc *p, struct exec_package *epp)
/*
* Permit system calls in main-text static binaries.
* Also block the ld.so syscall-grant
* static binaries may not call msyscall() or
* pinsyscalls()
*/
if (interp == NULL) {
syscall = VMCMD_SYSCALL;
p->p_vmspace->vm_map.flags |= VM_MAP_SYSCALL_ONCE;
p->p_vmspace->vm_map.flags |= VM_MAP_PINSYSCALL_ONCE;
}
/*
@ -696,6 +798,9 @@ exec_elf_makecmds(struct proc *p, struct exec_package *epp)
epp->ep_tsize = addr+size -
epp->ep_taddr;
}
if (interp == NULL)
exe_end = epp->ep_taddr +
epp->ep_tsize; /* end of TEXT */
}
break;
@ -735,7 +840,10 @@ exec_elf_makecmds(struct proc *p, struct exec_package *epp)
NEW_VMCMD(&epp->ep_vmcmds, vmcmd_mutable,
ph[i].p_memsz, ph[i].p_vaddr + exe_base, NULLVP, 0, 0);
break;
case PT_OPENBSD_SYSCALLS:
if (interp == NULL)
syscall_ph = &ph[i];
break;
default:
/*
* Not fatal, we don't need to understand everything
@ -745,6 +853,25 @@ exec_elf_makecmds(struct proc *p, struct exec_package *epp)
}
}
if (syscall_ph) {
vaddr_t base = exe_base;
size_t len = exe_end - exe_base;
u_int *pins;
int npins;
npins = elf_read_pintable(p, epp->ep_vp, syscall_ph,
&pins, 0, len);
if (npins) {
elf_adjustpins(&base, &len, pins, npins,
epp->ep_taddr - exe_base);
epp->ep_pinstart = base;
epp->ep_pinend = base + len;
epp->ep_pins = pins;
epp->ep_npins = npins;
p->p_p->ps_flags |= PS_PIN;
}
}
phdr += exe_base;
/*