diff options
author | 2012-08-20 23:25:07 +0000 | |
---|---|---|
committer | 2012-08-20 23:25:07 +0000 | |
commit | 5f7066906156ece39fb432bab1cea4415d608bfc (patch) | |
tree | c17c7fb24eb672c57598476e74a77656ffd85203 | |
parent | MAX_LINE_SIZE is supposed to define the max length of a SMTP line ... (diff) | |
download | wireguard-openbsd-5f7066906156ece39fb432bab1cea4415d608bfc.tar.xz wireguard-openbsd-5f7066906156ece39fb432bab1cea4415d608bfc.zip |
Add support for .openbsd.randomdata sections and PT_OPENBSD_RANDOMIZE
segments to the kernel, ld (2.15), and ld.so. Tested on alpha, amd64,
i386, macppc, and sparc64 (thanks naddy, mpi, and okan!).
Idea discussed for some time; committing now for further testing.
ok deraadt
-rw-r--r-- | gnu/usr.bin/binutils/bfd/elf.c | 31 | ||||
-rw-r--r-- | gnu/usr.bin/binutils/binutils/readelf.c | 1 | ||||
-rw-r--r-- | gnu/usr.bin/binutils/include/elf/common.h | 3 | ||||
-rw-r--r-- | gnu/usr.bin/binutils/ld/scripttempl/elf.sc | 7 | ||||
-rw-r--r-- | libexec/ld.so/SPECS.randomdata | 48 | ||||
-rw-r--r-- | libexec/ld.so/library.c | 15 | ||||
-rw-r--r-- | libexec/ld.so/library_mquery.c | 8 | ||||
-rw-r--r-- | sys/kern/exec_elf.c | 27 | ||||
-rw-r--r-- | sys/kern/exec_subr.c | 27 | ||||
-rw-r--r-- | sys/sys/exec.h | 3 | ||||
-rw-r--r-- | sys/sys/exec_elf.h | 7 |
11 files changed, 169 insertions, 8 deletions
diff --git a/gnu/usr.bin/binutils/bfd/elf.c b/gnu/usr.bin/binutils/bfd/elf.c index b0cadb6ddec..901fd2ca52d 100644 --- a/gnu/usr.bin/binutils/bfd/elf.c +++ b/gnu/usr.bin/binutils/bfd/elf.c @@ -968,6 +968,7 @@ _bfd_elf_print_private_bfd_data (bfd *abfd, void *farg) case PT_TLS: pt = "TLS"; break; case PT_GNU_EH_FRAME: pt = "EH_FRAME"; break; case PT_GNU_STACK: pt = "STACK"; break; + case PT_OPENBSD_RANDOMIZE: pt = "OPENBSD_RANDOMIZE"; break; default: sprintf (buf, "0x%lx", p->p_type); pt = buf; break; } fprintf (f, "%8s off 0x", pt); @@ -2291,6 +2292,10 @@ bfd_section_from_phdr (bfd *abfd, Elf_Internal_Phdr *hdr, int index) case PT_GNU_STACK: return _bfd_elf_make_section_from_phdr (abfd, hdr, index, "stack"); + case PT_OPENBSD_RANDOMIZE: + return _bfd_elf_make_section_from_phdr (abfd, hdr, index, + "openbsd_randomize"); + default: /* Check for any processor-specific program segment types. If no handler for them, default to making "segment" sections. */ @@ -3194,7 +3199,7 @@ map_sections_to_segments (bfd *abfd) bfd_boolean writable; int tls_count = 0; asection *first_tls = NULL; - asection *dynsec, *eh_frame_hdr; + asection *dynsec, *eh_frame_hdr, *randomdata; bfd_size_type amt; if (elf_tdata (abfd)->segment_map != NULL) @@ -3514,6 +3519,24 @@ map_sections_to_segments (bfd *abfd) pm = &m->next; } + /* If there is a .openbsd.randomdata section, throw in a PT_OPENBSD_RANDOMIZE + segment. */ + randomdata = bfd_get_section_by_name (abfd, ".openbsd.randomdata"); + if (randomdata != NULL && (randomdata->flags & SEC_LOAD) != 0) + { + amt = sizeof (struct elf_segment_map); + m = bfd_zalloc (abfd, amt); + if (m == NULL) + goto error_return; + m->next = NULL; + m->p_type = PT_OPENBSD_RANDOMIZE; + m->count = 1; + m->sections[0] = randomdata->output_section; + + *pm = m; + pm = &m->next; + } + free (sections); sections = NULL; @@ -4126,6 +4149,12 @@ get_program_header_size (bfd *abfd) ++segs; } + if (bfd_get_section_by_name (abfd, ".openbsd.randomdata") != NULL) + { + /* We need a PT_OPENBSD_RANDOMIZE segment. */ + ++segs; + } + if (elf_tdata (abfd)->eh_frame_hdr) { /* We need a PT_GNU_EH_FRAME segment. */ diff --git a/gnu/usr.bin/binutils/binutils/readelf.c b/gnu/usr.bin/binutils/binutils/readelf.c index e95a501dabb..2f9ecce43ee 100644 --- a/gnu/usr.bin/binutils/binutils/readelf.c +++ b/gnu/usr.bin/binutils/binutils/readelf.c @@ -2144,6 +2144,7 @@ get_segment_type (unsigned long p_type) case PT_GNU_EH_FRAME: return "GNU_EH_FRAME"; case PT_GNU_STACK: return "STACK"; + case PT_OPENBSD_RANDOMIZE: return "OPENBSD_RANDOMIZE"; default: if ((p_type >= PT_LOPROC) && (p_type <= PT_HIPROC)) diff --git a/gnu/usr.bin/binutils/include/elf/common.h b/gnu/usr.bin/binutils/include/elf/common.h index 84ef2b53723..562543338ce 100644 --- a/gnu/usr.bin/binutils/include/elf/common.h +++ b/gnu/usr.bin/binutils/include/elf/common.h @@ -290,6 +290,9 @@ #define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550) #define PT_GNU_STACK (PT_LOOS + 0x474e551) +#define PT_OPENBSD_RANDOMIZE 0x65a3dbe6 + + /* Program segment permissions, in program header p_flags field. */ #define PF_X (1 << 0) /* Segment is executable */ diff --git a/gnu/usr.bin/binutils/ld/scripttempl/elf.sc b/gnu/usr.bin/binutils/ld/scripttempl/elf.sc index 28a5a4fb2c7..031bab72c04 100644 --- a/gnu/usr.bin/binutils/ld/scripttempl/elf.sc +++ b/gnu/usr.bin/binutils/ld/scripttempl/elf.sc @@ -319,6 +319,13 @@ cat <<EOF .fini_array ${RELOCATING-0} : { *(.fini_array) } ${RELOCATING+${CREATE_SHLIB-PROVIDE (__fini_array_end = .);}} + ${RELOCATING+${CREATE_SHLIB-PROVIDE (__openbsd_randomdata_start = .);}} + .openbsd.randomdata ${RELOCATING-0} : + { + *(.openbsd.randomdata${RELOCATING+ .openbsd.randomdata.*}) + } + ${RELOCATING+${CREATE_SHLIB-PROVIDE (__openbsd_randomdata_end = .);}} + .data ${RELOCATING-0} : { ${RELOCATING+${DATA_START_SYMBOLS}} diff --git a/libexec/ld.so/SPECS.randomdata b/libexec/ld.so/SPECS.randomdata new file mode 100644 index 00000000000..434de2ee3de --- /dev/null +++ b/libexec/ld.so/SPECS.randomdata @@ -0,0 +1,48 @@ +$OpenBSD: SPECS.randomdata,v 1.1 2012/08/20 23:25:07 matthew Exp $ + +This document describes the OpenBSD operating system supplement for +adding "random data" sections to the ELF ABI. These sections can be +useful for holding values like GCC's stack-smashing protector cookies +and offer additional benefits like ensuring the data is initialized +before any constructor methods are called and allowing the dynamic +linker to mark the memory as read-only after initialization. + + +Program Header + +OpenBSD defines the following operating system-specific segment type: + + Name Value + PT_OPENBSD_RANDOMIZE 0x65a3dbe6 + + + PT_OPENBSD_RANDOMIZE + + The array element specifies the location and size of a random data + section. The system will initialize the specified memory range + with random data. The memory range must be separately mapped + (e.g., by use of a PT_LOAD segment). + + +Special Sections + +OpenBSD defines the following operating system-specific special +sections: + + Name Type Attributes + .openbsd.randomdata SHT_PROGBITS SHF_ALLOC + + + .openbsd.randomdata + + This section holds the random data section. + + +Implementation Notes + +On OpenBSD, PT_OPENBSD_RANDOMIZE segments are handled alongside +PT_LOAD segments: the kernel handles initializing random data segments +in executables and program interpreters (i.e., ld.so), while ld.so +handles initializing them in shared libraries. Additionally, the +kernel limits the total number of PT_OPENBSD_RANDOMIZE segment bytes +in an executable or interpreter to 1024 bytes. diff --git a/libexec/ld.so/library.c b/libexec/ld.so/library.c index 1dd2013e6df..60c69f370c6 100644 --- a/libexec/ld.so/library.c +++ b/libexec/ld.so/library.c @@ -1,4 +1,4 @@ -/* $OpenBSD: library.c,v 1.66 2012/06/12 20:32:17 matthew Exp $ */ +/* $OpenBSD: library.c,v 1.67 2012/08/20 23:25:07 matthew Exp $ */ /* * Copyright (c) 2002 Dale Rahn @@ -181,7 +181,8 @@ _dl_tryload_shlib(const char *libname, int type, int flags) phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff); for (i = 0; i < ehdr->e_phnum; i++, phdp++) { - if (phdp->p_type == PT_LOAD) { + switch (phdp->p_type) { + case PT_LOAD: { char *start = (char *)(TRUNC_PG(phdp->p_vaddr)) + loff; Elf_Addr off = (phdp->p_vaddr & align); Elf_Addr size = off + phdp->p_filesz; @@ -233,6 +234,16 @@ _dl_tryload_shlib(const char *libname, int type, int flags) return(0); } } + break; + } + + case PT_OPENBSD_RANDOMIZE: + _dl_randombuf((char *)(phdp->p_vaddr + loff), + phdp->p_memsz); + break; + + default: + break; } } diff --git a/libexec/ld.so/library_mquery.c b/libexec/ld.so/library_mquery.c index aedfd091f8b..abbde2e533d 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.43 2012/07/21 06:46:58 matthew Exp $ */ +/* $OpenBSD: library_mquery.c,v 1.44 2012/08/20 23:25:07 matthew Exp $ */ /* * Copyright (c) 2002 Dale Rahn @@ -266,6 +266,12 @@ retry: load_end = (Elf_Addr)ld->start + ROUND_PG(ld->size); } + phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff); + for (i = 0; i < ehdr->e_phnum; i++, phdp++) + if (phdp->p_type == PT_OPENBSD_RANDOMIZE) + _dl_randombuf((char *)(phdp->p_vaddr + LOFF), + phdp->p_memsz); + prebind_data = prebind_load_fd(libfile, libname); _dl_close(libfile); diff --git a/sys/kern/exec_elf.c b/sys/kern/exec_elf.c index 4e9f314965f..7cfa7d11ea6 100644 --- a/sys/kern/exec_elf.c +++ b/sys/kern/exec_elf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: exec_elf.c,v 1.86 2012/03/09 13:01:28 ariane Exp $ */ +/* $OpenBSD: exec_elf.c,v 1.87 2012/08/20 23:25:07 matthew Exp $ */ /* * Copyright (c) 1996 Per Fogelstrom @@ -130,6 +130,9 @@ extern char *syscallnames[]; */ #define ELF_MAX_VALID_PHDR 32 +/* Limit on total PT_OPENBSD_RANDOMIZE bytes. */ +#define ELF_RANDOMIZE_LIMIT 1024 + /* * This is the basic elf emul. elf_probe_funcs may change to other emuls. */ @@ -327,6 +330,7 @@ ELFNAME(load_file)(struct proc *p, char *path, struct exec_package *epp, Elf_Addr pos = *last; int file_align; int loop; + size_t randomizequota = ELF_RANDOMIZE_LIMIT; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, path, p); if ((error = namei(&nd)) != 0) { @@ -469,6 +473,16 @@ ELFNAME(load_file)(struct proc *p, char *path, struct exec_package *epp, case PT_NOTE: break; + case PT_OPENBSD_RANDOMIZE: + if (ph[i].p_memsz > randomizequota) { + error = ENOMEM; + goto bad1; + } + randomizequota -= ph[i].p_memsz; + NEW_VMCMD(&epp->ep_vmcmds, vmcmd_randomize, + ph[i].p_memsz, ph[i].p_vaddr + pos, NULLVP, 0, 0); + break; + default: break; } @@ -506,6 +520,7 @@ ELFNAME2(exec,makecmds)(struct proc *p, struct exec_package *epp) char *interp = NULL; u_long pos = 0, phsize; u_int8_t os = OOS_NULL; + size_t randomizequota = ELF_RANDOMIZE_LIMIT; if (epp->ep_hdrvalid < sizeof(Elf_Ehdr)) return (ENOEXEC); @@ -692,6 +707,16 @@ native: phdr = pp->p_vaddr; break; + case PT_OPENBSD_RANDOMIZE: + if (ph[i].p_memsz > randomizequota) { + error = ENOMEM; + goto bad; + } + randomizequota -= ph[i].p_memsz; + NEW_VMCMD(&epp->ep_vmcmds, vmcmd_randomize, + ph[i].p_memsz, ph[i].p_vaddr + exe_base, NULLVP, 0, 0); + break; + default: /* * Not fatal, we don't need to understand everything diff --git a/sys/kern/exec_subr.c b/sys/kern/exec_subr.c index b712404f3b3..301214722d6 100644 --- a/sys/kern/exec_subr.c +++ b/sys/kern/exec_subr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: exec_subr.c,v 1.29 2011/06/29 12:16:17 tedu Exp $ */ +/* $OpenBSD: exec_subr.c,v 1.30 2012/08/20 23:25:07 matthew Exp $ */ /* $NetBSD: exec_subr.c,v 1.9 1994/12/04 03:10:42 mycroft Exp $ */ /* @@ -42,6 +42,7 @@ #include <sys/resourcevar.h> #include <uvm/uvm.h> +#include <dev/rndvar.h> #ifdef DEBUG /* @@ -289,6 +290,30 @@ vmcmd_map_zero(struct proc *p, struct exec_vmcmd *cmd) } /* + * vmcmd_randomize(): + * handle vmcmd which specifies a randomized address space region. + */ + +int +vmcmd_randomize(struct proc *p, struct exec_vmcmd *cmd) +{ + char *buf; + int error; + + if (cmd->ev_len == 0) + return (0); + if (cmd->ev_len > 1024) + return (EINVAL); + + buf = malloc(cmd->ev_len, M_TEMP, M_WAITOK); + arc4random_buf(buf, cmd->ev_len); + error = copyout(buf, (void *)cmd->ev_addr, cmd->ev_len); + free(buf, M_TEMP); + + return (error); +} + +/* * exec_setup_stack(): Set up the stack segment for an a.out * executable. * diff --git a/sys/sys/exec.h b/sys/sys/exec.h index 6791d2bf167..ecb4239d40b 100644 --- a/sys/sys/exec.h +++ b/sys/sys/exec.h @@ -1,4 +1,4 @@ -/* $OpenBSD: exec.h,v 1.25 2009/07/09 22:29:56 thib Exp $ */ +/* $OpenBSD: exec.h,v 1.26 2012/08/20 23:25:07 matthew Exp $ */ /* $NetBSD: exec.h,v 1.59 1996/02/09 18:25:09 christos Exp $ */ /*- @@ -174,6 +174,7 @@ void kill_vmcmds(struct exec_vmcmd_set *evsp); int vmcmd_map_pagedvn(struct proc *, struct exec_vmcmd *); int vmcmd_map_readvn(struct proc *, struct exec_vmcmd *); int vmcmd_map_zero(struct proc *, struct exec_vmcmd *); +int vmcmd_randomize(struct proc *, struct exec_vmcmd *); void *copyargs(struct exec_package *, struct ps_strings *, void *, void *); diff --git a/sys/sys/exec_elf.h b/sys/sys/exec_elf.h index 530048b2fb2..17288b80d93 100644 --- a/sys/sys/exec_elf.h +++ b/sys/sys/exec_elf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: exec_elf.h,v 1.49 2011/09/20 16:42:01 pirofti Exp $ */ +/* $OpenBSD: exec_elf.h,v 1.50 2012/08/20 23:25:07 matthew Exp $ */ /* * Copyright (c) 1995, 1996 Erik Theisen. All rights reserved. * @@ -426,6 +426,9 @@ typedef struct { #define PT_LOPROC 0x70000000 /* reserved range for processor */ #define PT_HIPROC 0x7fffffff /* specific segment types */ +#define PT_OPENBSD_RANDOMIZE 0x65a3dbe6 /* fill with random data */ + + /* Segment flags - p_flags */ #define PF_X 0x1 /* Executable */ #define PF_W 0x2 /* Writable */ @@ -478,6 +481,8 @@ typedef struct { #define DT_JMPREL 23 /* add. of PLT's relocation entries */ #define DT_BIND_NOW 24 /* Bind now regardless of env setting */ #define DT_NUM 25 /* Number used. */ +#define DT_LOOS 0x6000000d /* reserved range for OS */ +#define DT_HIOS 0x6ffff000 /* specific dynamic array tags */ #define DT_LOPROC 0x70000000 /* reserved range for processor */ #define DT_HIPROC 0x7fffffff /* specific dynamic array tags */ |