Print this page
10907 hot_patch_kernel_text() has no respect for boundaries
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
Reviewed by: Robert Mustacchi <rm@joyent.com>
*** 21,30 ****
--- 21,34 ----
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+ /*
+ * Copyright 2019 Joyent, Inc.
+ */
+
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#include <sys/types.h>
#include <sys/sysmacros.h>
*** 306,355 ****
90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
};
/*
* Hot-patch a single instruction in the kernel's text.
! * If you want to patch multiple instructions you must
! * arrange to do it so that all intermediate stages are
! * sane -- we don't stop other cpus while doing this.
* Size must be 1, 2, or 4 bytes with iaddr aligned accordingly.
*/
void
hot_patch_kernel_text(caddr_t iaddr, uint32_t new_instr, uint_t size)
{
caddr_t vaddr;
page_t **ppp;
- uintptr_t off = (uintptr_t)iaddr & PAGEOFFSET;
! vaddr = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
! (void) as_pagelock(&kas, &ppp, iaddr - off, PAGESIZE, S_WRITE);
hat_devload(kas.a_hat, vaddr, PAGESIZE,
! hat_getpfnum(kas.a_hat, iaddr - off),
PROT_READ | PROT_WRITE, HAT_LOAD_LOCK | HAT_LOAD_NOCONSIST);
switch (size) {
case 1:
! *(uint8_t *)(vaddr + off) = new_instr;
break;
case 2:
! *(uint16_t *)(vaddr + off) = new_instr;
break;
case 4:
! *(uint32_t *)(vaddr + off) = new_instr;
break;
default:
panic("illegal hot-patch");
}
membar_enter();
! sync_icache(vaddr + off, size);
sync_icache(iaddr, size);
! as_pageunlock(&kas, ppp, iaddr - off, PAGESIZE, S_WRITE);
! hat_unload(kas.a_hat, vaddr, PAGESIZE, HAT_UNLOAD_UNLOCK);
! vmem_free(heap_arena, vaddr, PAGESIZE);
}
/*
* Routine to report an attempt to execute non-executable data. If the
* address executed lies in the stack, explicitly say so.
--- 310,373 ----
90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
};
/*
* Hot-patch a single instruction in the kernel's text.
! *
! * If you want to patch multiple instructions you must arrange to do it so that
! * all intermediate stages are sane -- we don't stop other cpus while doing
! * this.
! *
* Size must be 1, 2, or 4 bytes with iaddr aligned accordingly.
+ *
+ * The instruction itself might straddle a page boundary, so we have to account
+ * for that.
*/
void
hot_patch_kernel_text(caddr_t iaddr, uint32_t new_instr, uint_t size)
{
+ const uintptr_t pageoff = (uintptr_t)iaddr & PAGEOFFSET;
+ const boolean_t straddles = (pageoff + size > PAGESIZE);
+ const size_t mapsize = straddles ? PAGESIZE * 2 : PAGESIZE;
+ caddr_t ipageaddr = iaddr - pageoff;
caddr_t vaddr;
page_t **ppp;
! vaddr = vmem_alloc(heap_arena, mapsize, VM_SLEEP);
! (void) as_pagelock(&kas, &ppp, ipageaddr, mapsize, S_WRITE);
hat_devload(kas.a_hat, vaddr, PAGESIZE,
! hat_getpfnum(kas.a_hat, ipageaddr), PROT_READ | PROT_WRITE,
! HAT_LOAD_LOCK | HAT_LOAD_NOCONSIST);
!
! if (straddles) {
! hat_devload(kas.a_hat, vaddr + PAGESIZE, PAGESIZE,
! hat_getpfnum(kas.a_hat, ipageaddr + PAGESIZE),
PROT_READ | PROT_WRITE, HAT_LOAD_LOCK | HAT_LOAD_NOCONSIST);
+ }
switch (size) {
case 1:
! *(uint8_t *)(vaddr + pageoff) = new_instr;
break;
case 2:
! *(uint16_t *)(vaddr + pageoff) = new_instr;
break;
case 4:
! *(uint32_t *)(vaddr + pageoff) = new_instr;
break;
default:
panic("illegal hot-patch");
}
membar_enter();
! sync_icache(vaddr + pageoff, size);
sync_icache(iaddr, size);
! as_pageunlock(&kas, ppp, ipageaddr, mapsize, S_WRITE);
! hat_unload(kas.a_hat, vaddr, mapsize, HAT_UNLOAD_UNLOCK);
! vmem_free(heap_arena, vaddr, mapsize);
}
/*
* Routine to report an attempt to execute non-executable data. If the
* address executed lies in the stack, explicitly say so.