From: sf Date: Fri, 16 Jan 2015 10:17:51 +0000 (+0000) Subject: Binary code patching on amd64 X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=61d6df422f0f5a89aceb8b55fd2d03b24dfe19a1;p=openbsd Binary code patching on amd64 This commit adds generic infrastructure to do binary code patching on amd64. The existing code patching for SMAP is converted to the new infrastruture. More consumers and support for i386 will follow later. This version of the diff has some simplifications in codepatch_fill_nop() compared to a version that was: OK @kettenis @mlarkin @jsg --- diff --git a/sys/arch/amd64/amd64/codepatch.c b/sys/arch/amd64/amd64/codepatch.c new file mode 100644 index 00000000000..4e9ed6b55a0 --- /dev/null +++ b/sys/arch/amd64/amd64/codepatch.c @@ -0,0 +1,178 @@ +/* $OpenBSD: codepatch.c,v 1.1 2015/01/16 10:17:51 sf Exp $ */ +/* + * Copyright (c) 2014-2015 Stefan Fritsch + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#ifdef CODEPATCH_DEBUG +#define DBGPRINT(fmt, args...) printf("%s: " fmt "\n", __func__, ## args) +#else +#define DBGPRINT(fmt, args...) do {} while (0) +#endif + +struct codepatch { + uint32_t offset; + uint16_t len; + uint16_t tag; +}; + +extern struct codepatch codepatch_begin; +extern struct codepatch codepatch_end; + +#define NOP_LEN_MAX 9 + +static const unsigned char nops[][NOP_LEN_MAX] = { + { 0x90 }, + { 0x66, 0x90 }, + { 0x0F, 0x1F, 0x00 }, + { 0x0F, 0x1F, 0x40, 0x00 }, + { 0x0F, 0x1F, 0x44, 0x00, 0x00 }, + { 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00 }, + { 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00 }, + { 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, +}; + +void +codepatch_fill_nop(void *caddr, uint16_t len) +{ + unsigned char *addr = caddr; + uint16_t nop_len; + + while (len > 0) { + if (len <= NOP_LEN_MAX) + nop_len = len; + else + nop_len = NOP_LEN_MAX; + memcpy(addr, nops[nop_len-1], nop_len); + addr += nop_len; + len -= nop_len; + } +} + +/* + * Create writeable aliases of memory we need + * to write to as kernel is mapped read-only + */ +void *codepatch_maprw(vaddr_t *nva, vaddr_t dest) +{ + paddr_t kva = trunc_page((paddr_t)dest); + paddr_t po = (paddr_t)dest & PAGE_MASK; + paddr_t pa1, pa2; + + if (*nva == 0) + *nva = (vaddr_t)km_alloc(2 * PAGE_SIZE, &kv_any, &kp_none, + &kd_waitok); + + pmap_extract(pmap_kernel(), kva, &pa1); + pmap_extract(pmap_kernel(), kva + PAGE_SIZE, &pa2); + pmap_kenter_pa(*nva, pa1, PROT_READ | PROT_WRITE); + pmap_kenter_pa(*nva + PAGE_SIZE, pa2, PROT_READ | PROT_WRITE); + pmap_update(pmap_kernel()); + + return (void *)(*nva + po); +} + +void codepatch_unmaprw(vaddr_t nva) +{ + if (nva != 0) + km_free((void *)nva, 2 * PAGE_SIZE, &kv_any, &kp_none); +} + +/* Patch with NOPs */ +void +codepatch_nop(uint16_t tag) +{ + struct codepatch *patch; + unsigned char *rwaddr; + vaddr_t addr, rwmap = 0; + int i = 0; + + DBGPRINT("patching tag %u", tag); + + for (patch = &codepatch_begin; patch < &codepatch_end; patch++) { + if (patch->tag != tag) + continue; + addr = KERNBASE + patch->offset; + rwaddr = codepatch_maprw(&rwmap, addr); + codepatch_fill_nop(rwaddr, patch->len); + i++; + } + codepatch_unmaprw(rwmap); + DBGPRINT("patched %d places", i); +} + +/* Patch with alternative code */ +void +codepatch_replace(uint16_t tag, void *code, size_t len) +{ + struct codepatch *patch; + unsigned char *rwaddr; + vaddr_t addr, rwmap = 0; + int i = 0; + + DBGPRINT("patching tag %u with %p", tag, code); + + for (patch = &codepatch_begin; patch < &codepatch_end; patch++) { + if (patch->tag != tag) + continue; + addr = KERNBASE + patch->offset; + + if (len > patch->len) { + panic("%s: can't replace len %u with %zu at %#lx", + __func__, patch->len, len, addr); + } + rwaddr = codepatch_maprw(&rwmap, addr); + memcpy(rwaddr, code, len); + codepatch_fill_nop(rwaddr + len, patch->len - len); + i++; + } + codepatch_unmaprw(rwmap); + DBGPRINT("patched %d places", i); +} + +/* Patch with calls to func */ +void +codepatch_call(uint16_t tag, void *func) +{ + struct codepatch *patch; + unsigned char *rwaddr; + int32_t offset; + int i = 0; + vaddr_t addr, rwmap = 0; + + DBGPRINT("patching tag %u with call %p", tag, func); + + for (patch = &codepatch_begin; patch < &codepatch_end; patch++) { + if (patch->tag != tag) + continue; + addr = KERNBASE + patch->offset; + if (patch->len < 5) + panic("%s: can't replace len %u with call at %#lx", + __func__, patch->len, addr); + + offset = (vaddr_t)func - (addr + 5); + rwaddr = codepatch_maprw(&rwmap, addr); + rwaddr[0] = 0xe8; /* call near */ + memcpy(rwaddr + 1, &offset, sizeof(offset)); + codepatch_fill_nop(rwaddr + 5, patch->len - 5); + i++; + } + codepatch_unmaprw(rwmap); + DBGPRINT("patched %d places", i); +} diff --git a/sys/arch/amd64/amd64/copy.S b/sys/arch/amd64/amd64/copy.S index 32e18fc5b56..c638e2d1683 100644 --- a/sys/arch/amd64/amd64/copy.S +++ b/sys/arch/amd64/amd64/copy.S @@ -1,4 +1,4 @@ -/* $OpenBSD: copy.S,v 1.5 2012/10/31 03:30:22 jsg Exp $ */ +/* $OpenBSD: copy.S,v 1.6 2015/01/16 10:17:51 sf Exp $ */ /* $NetBSD: copy.S,v 1.1 2003/04/26 18:39:26 fvdl Exp $ */ /* @@ -42,6 +42,7 @@ #include #include +#include /* * As stac/clac SMAP instructions are 3 bytes, we want the fastest @@ -53,6 +54,12 @@ * on all family 0x6 and 0xf processors (ie 686+) */ #define SMAP_NOP .byte 0x0f, 0x1f, 0x00 +#define SMAP_STAC CODEPATCH_START ;\ + SMAP_NOP ;\ + CODEPATCH_END(CPTAG_STAC) +#define SMAP_CLAC CODEPATCH_START ;\ + SMAP_NOP ;\ + CODEPATCH_END(CPTAG_CLAC) /* * Copy routines from and to userland, plus a few more. See the @@ -110,7 +117,6 @@ ENTRY(kcopy) xorq %rax,%rax ret -.globl _C_LABEL(_copyout_stac), _C_LABEL(_copyout_clac) ENTRY(copyout) pushq $0 @@ -127,9 +133,7 @@ ENTRY(copyout) movq CPUVAR(CURPCB),%rdx leaq _C_LABEL(copy_fault)(%rip),%r11 movq %r11,PCB_ONFAULT(%rdx) -_C_LABEL(_copyout_stac): - SMAP_NOP - + SMAP_STAC cld movq %rax,%rcx shrq $3,%rcx @@ -139,22 +143,17 @@ _C_LABEL(_copyout_stac): andb $7,%cl rep movsb - -_C_LABEL(_copyout_clac): - SMAP_NOP + SMAP_CLAC popq PCB_ONFAULT(%rdx) xorl %eax,%eax ret -.globl _C_LABEL(_copyin_stac), _C_LABEL(_copyin_clac) ENTRY(copyin) movq CPUVAR(CURPCB),%rax pushq $0 leaq _C_LABEL(copy_fault)(%rip),%r11 movq %r11,PCB_ONFAULT(%rax) -_C_LABEL(_copyin_stac): - SMAP_NOP - + SMAP_STAC xchgq %rdi,%rsi movq %rdx,%rax @@ -176,8 +175,7 @@ _C_LABEL(_copyin_stac): rep movsb -_C_LABEL(_copyin_clac): - SMAP_NOP + SMAP_CLAC movq CPUVAR(CURPCB),%rdx popq PCB_ONFAULT(%rdx) xorl %eax,%eax @@ -186,15 +184,12 @@ _C_LABEL(_copyin_clac): NENTRY(copy_efault) movq $EFAULT,%rax -.globl _C_LABEL(_copy_fault_clac) NENTRY(copy_fault) -_C_LABEL(_copy_fault_clac): - SMAP_NOP + SMAP_CLAC movq CPUVAR(CURPCB),%rdx popq PCB_ONFAULT(%rdx) ret -.globl _C_LABEL(_copyoutstr_stac) ENTRY(copyoutstr) xchgq %rdi,%rsi movq %rdx,%r8 @@ -203,8 +198,7 @@ ENTRY(copyoutstr) 5: movq CPUVAR(CURPCB),%rax leaq _C_LABEL(copystr_fault)(%rip),%r11 movq %r11,PCB_ONFAULT(%rax) -_C_LABEL(_copyoutstr_stac): - SMAP_NOP + SMAP_STAC /* * Get min(%rdx, VM_MAXUSER_ADDRESS-%rdi). */ @@ -238,7 +232,6 @@ _C_LABEL(_copyoutstr_stac): movq $ENAMETOOLONG,%rax jmp copystr_return -.globl _C_LABEL(_copyinstr_stac) ENTRY(copyinstr) xchgq %rdi,%rsi movq %rdx,%r8 @@ -247,8 +240,7 @@ ENTRY(copyinstr) movq CPUVAR(CURPCB),%rcx leaq _C_LABEL(copystr_fault)(%rip),%r11 movq %r11,PCB_ONFAULT(%rcx) -_C_LABEL(_copyinstr_stac): - SMAP_NOP + SMAP_STAC /* * Get min(%rdx, VM_MAXUSER_ADDRESS-%rsi). @@ -286,11 +278,9 @@ _C_LABEL(_copyinstr_stac): ENTRY(copystr_efault) movl $EFAULT,%eax -.globl _C_LABEL(_copystr_fault_clac) ENTRY(copystr_fault) copystr_return: -_C_LABEL(_copystr_fault_clac): - SMAP_NOP + SMAP_CLAC /* Set *lencopied and return %eax. */ movq CPUVAR(CURPCB),%rcx movq $0,PCB_ONFAULT(%rcx) diff --git a/sys/arch/amd64/amd64/cpu.c b/sys/arch/amd64/amd64/cpu.c index 6bebf804076..6a47542c9e8 100644 --- a/sys/arch/amd64/amd64/cpu.c +++ b/sys/arch/amd64/amd64/cpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.c,v 1.77 2015/01/06 12:50:47 dlg Exp $ */ +/* $OpenBSD: cpu.c,v 1.78 2015/01/16 10:17:51 sf Exp $ */ /* $NetBSD: cpu.c,v 1.1 2003/04/26 18:39:26 fvdl Exp $ */ /*- @@ -77,6 +77,7 @@ #include +#include #include #include #include @@ -121,65 +122,23 @@ struct cpu_softc { #ifndef SMALL_KERNEL void replacesmap(void); -extern long _copyout_stac; -extern long _copyout_clac; -extern long _copyin_stac; -extern long _copyin_clac; -extern long _copy_fault_clac; -extern long _copyoutstr_stac; -extern long _copyinstr_stac; -extern long _copystr_fault_clac; extern long _stac; extern long _clac; -static const struct { - void *daddr; - void *saddr; -} ireplace[] = { - { &_copyout_stac, &_stac }, - { &_copyout_clac, &_clac }, - { &_copyin_stac, &_stac }, - { &_copyin_clac, &_clac }, - { &_copy_fault_clac, &_clac }, - { &_copyoutstr_stac, &_stac }, - { &_copyinstr_stac, &_stac }, - { &_copystr_fault_clac, &_clac }, -}; - void replacesmap(void) { static int replacedone = 0; - int i, s; - vaddr_t nva; + int s; if (replacedone) return; replacedone = 1; s = splhigh(); - /* - * Create writeable aliases of memory we need - * to write to as kernel is mapped read-only - */ - nva = (vaddr_t)km_alloc(2 * PAGE_SIZE, &kv_any, &kp_none, &kd_waitok); - - for (i = 0; i < nitems(ireplace); i++) { - paddr_t kva = trunc_page((paddr_t)ireplace[i].daddr); - paddr_t po = (paddr_t)ireplace[i].daddr & PAGE_MASK; - paddr_t pa1, pa2; - - pmap_extract(pmap_kernel(), kva, &pa1); - pmap_extract(pmap_kernel(), kva + PAGE_SIZE, &pa2); - pmap_kenter_pa(nva, pa1, PROT_READ | PROT_WRITE); - pmap_kenter_pa(nva + PAGE_SIZE, pa2, PROT_READ | PROT_WRITE); - pmap_update(pmap_kernel()); - - /* replace 3 byte nops with stac/clac instructions */ - memcpy((void *)(nva + po), ireplace[i].saddr, 3); - } - km_free((void *)nva, 2 * PAGE_SIZE, &kv_any, &kp_none); + codepatch_replace(CPTAG_STAC, &_stac, 3); + codepatch_replace(CPTAG_CLAC, &_clac, 3); splx(s); } diff --git a/sys/arch/amd64/amd64/locore.S b/sys/arch/amd64/amd64/locore.S index 022cd07ef38..46203642817 100644 --- a/sys/arch/amd64/amd64/locore.S +++ b/sys/arch/amd64/amd64/locore.S @@ -1,4 +1,4 @@ -/* $OpenBSD: locore.S,v 1.61 2014/12/21 16:27:07 mlarkin Exp $ */ +/* $OpenBSD: locore.S,v 1.62 2015/01/16 10:17:51 sf Exp $ */ /* $NetBSD: locore.S,v 1.13 2004/03/25 18:33:17 drochner Exp $ */ /* @@ -1168,3 +1168,13 @@ ENTRY(pagezero) sfence ret + .section .codepatch,"a" + .align 8 + .globl _C_LABEL(codepatch_begin) +_C_LABEL(codepatch_begin): + .previous + + .section .codepatchend,"a" + .globl _C_LABEL(codepatch_end) +_C_LABEL(codepatch_end): + .previous diff --git a/sys/arch/amd64/conf/files.amd64 b/sys/arch/amd64/conf/files.amd64 index d5a1c6e0c43..a39ed72c176 100644 --- a/sys/arch/amd64/conf/files.amd64 +++ b/sys/arch/amd64/conf/files.amd64 @@ -1,4 +1,4 @@ -# $OpenBSD: files.amd64,v 1.76 2014/12/10 05:42:25 jsg Exp $ +# $OpenBSD: files.amd64,v 1.77 2015/01/16 10:17:51 sf Exp $ maxpartitions 16 maxusers 2 16 128 @@ -80,6 +80,8 @@ device mainbus: isabus, pcibus, mainbus attach mainbus at root file arch/amd64/amd64/mainbus.c mainbus +file arch/amd64/amd64/codepatch.c + device bios {} attach bios at mainbus file arch/amd64/amd64/bios.c bios needs-flag diff --git a/sys/arch/amd64/include/codepatch.h b/sys/arch/amd64/include/codepatch.h new file mode 100644 index 00000000000..7ff3216ffa0 --- /dev/null +++ b/sys/arch/amd64/include/codepatch.h @@ -0,0 +1,52 @@ +/* $OpenBSD: codepatch.h,v 1.1 2015/01/16 10:17:51 sf Exp $ */ +/* + * Copyright (c) 2014-2015 Stefan Fritsch + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _MACHINE_CODEPATCH_H_ +#define _MACHINE_CODEPATCH_H_ + +#include + +#ifndef _LOCORE + +void *codepatch_maprw(vaddr_t *nva, vaddr_t dest); +void codepatch_unmaprw(vaddr_t nva); +void codepatch_fill_nop(void *caddr, uint16_t len); +void codepatch_nop(uint16_t tag); +void codepatch_replace(uint16_t tag, void *code, size_t len); +void codepatch_call(uint16_t tag, void *func); + +#endif /* !_LOCORE */ + +/* + * Mark the start of some code snippet to be patched. + */ +#define CODEPATCH_START 998: +/* + * Mark the end of some code to be patched, and assign the given tag. + */ +#define CODEPATCH_END(tag) \ + 999: \ + .section .codepatch, "a" ;\ + .int (998b - KERNBASE) ;\ + .short (999b - 998b) ;\ + .short tag ;\ + .previous + +#define CPTAG_STAC 1 +#define CPTAG_CLAC 2 + +#endif /* _MACHINE_CODEPATCH_H_ */