mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
bitkeeper revision 1.1159.41.1 (41238c3fi_zBknIqLEcbst8cclEjAQ)
Remove binary rewriting from Linux for now. Replaced with a warning banner.
This commit is contained in:
1 parent
afe0649d91
commit
37dca5a201
1 file changed
+31
-769
@@ -5,6 +5,11 @@
|
||||
* Used to avoid repeated slow emulation of common instructions used by the
|
||||
* user-space TLS (Thread-Local Storage) libraries.
|
||||
*
|
||||
* **** NOTE ****
|
||||
* Issues with the binary rewriting have caused it to be removed. Instead
|
||||
* we rely on Xen's emulator to boot the kernel, and then print a banner
|
||||
* message recommending that the user disables /lib/tls.
|
||||
*
|
||||
* Copyright (c) 2004, K A Fraser
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -27,790 +32,47 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/mman.h>
|
||||
#include <asm/fixmap.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#if 1
|
||||
#define ASSERT(_p) \
|
||||
if ( !(_p) ) { printk("Assertion '%s' failed, line %d, file %s", #_p , \
|
||||
__LINE__, __FILE__); *(int*)0=0; }
|
||||
#define DPRINTK(_f, _a...) printk(KERN_ALERT \
|
||||
"(file=%s, line=%d) " _f "\n", \
|
||||
__FILE__ , __LINE__ , ## _a )
|
||||
#else
|
||||
#define ASSERT(_p) ((void)0)
|
||||
#define DPRINTK(_f, _a...) ((void)0)
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
|
||||
#define TestSetPageLocked(_p) TryLockPage(_p)
|
||||
#define PageAnon(_p) 0 /* no equivalent in 2.4 */
|
||||
#define pte_offset_kernel pte_offset
|
||||
#define remap_page_range(_a,_b,_c,_d,_e) remap_page_range(_b,_c,_d,_e)
|
||||
#define daemonize(_n) \
|
||||
do { \
|
||||
daemonize(); \
|
||||
strcpy(current->comm, _n); \
|
||||
sigfillset(¤t->blocked); \
|
||||
} while ( 0 )
|
||||
#endif
|
||||
|
||||
static unsigned char *fixup_buf;
|
||||
#define FIXUP_BUF_USER PAGE_SIZE
|
||||
#define FIXUP_BUF_ORDER 1
|
||||
#define FIXUP_BUF_SIZE (PAGE_SIZE<<FIXUP_BUF_ORDER)
|
||||
#define PATCH_LEN 5
|
||||
|
||||
struct fixup_entry {
|
||||
unsigned char patched_code[20];
|
||||
unsigned short patched_code_len;
|
||||
unsigned short fixup_idx;
|
||||
unsigned short return_idx;
|
||||
struct fixup_entry *next;
|
||||
};
|
||||
|
||||
#define FIXUP_HASHSZ 128
|
||||
static struct fixup_entry *fixup_hash[FIXUP_HASHSZ];
|
||||
static inline int FIXUP_HASH(char *b)
|
||||
{
|
||||
int i, j = 0;
|
||||
for ( i = 0; i < PATCH_LEN; i++ )
|
||||
j ^= b[i];
|
||||
return j & (FIXUP_HASHSZ-1);
|
||||
}
|
||||
|
||||
/* General instruction properties. */
|
||||
#define INSN_SUFFIX_BYTES (7)
|
||||
#define PREFIX_BYTE (1<<3)
|
||||
#define OPCODE_BYTE (1<<4)
|
||||
#define HAS_MODRM (1<<5)
|
||||
|
||||
/* Helpful codes for the main decode routine. */
|
||||
#define CODE_MASK (3<<6)
|
||||
#define PUSH (1<<6) /* PUSH onto stack */
|
||||
#define POP (2<<6) /* POP from stack */
|
||||
#define JMP (3<<6) /* 8-bit relative JMP */
|
||||
|
||||
/* Short forms for the table. */
|
||||
#define X 0 /* invalid for some random reason */
|
||||
#define S 0 /* invalid because it munges the stack */
|
||||
#define P PREFIX_BYTE
|
||||
#define O OPCODE_BYTE
|
||||
#define M HAS_MODRM
|
||||
|
||||
static unsigned char insn_decode[256] = {
|
||||
/* 0x00 - 0x0F */
|
||||
O|M, O|M, O|M, O|M, O|1, O|4, S, S,
|
||||
O|M, O|M, O|M, O|M, O|1, O|4, S, X,
|
||||
/* 0x10 - 0x1F */
|
||||
O|M, O|M, O|M, O|M, O|1, O|4, S, S,
|
||||
O|M, O|M, O|M, O|M, O|1, O|4, S, S,
|
||||
/* 0x20 - 0x2F */
|
||||
O|M, O|M, O|M, O|M, O|1, O|4, P, O,
|
||||
O|M, O|M, O|M, O|M, O|1, O|4, P, O,
|
||||
/* 0x30 - 0x3F */
|
||||
O|M, O|M, O|M, O|M, O|1, O|4, P, O,
|
||||
O|M, O|M, O|M, O|M, O|1, O|4, P, O,
|
||||
/* 0x40 - 0x4F */
|
||||
O, O, O, O, S, O, O, O,
|
||||
O, O, O, O, S, O, O, O,
|
||||
/* 0x50 - 0x5F */
|
||||
O|PUSH, O|PUSH, O|PUSH, O|PUSH, S, O|PUSH, O|PUSH, O|PUSH,
|
||||
O|POP, O|POP, O|POP, O|POP, S, O|POP, O|POP, O|POP,
|
||||
/* 0x60 - 0x6F */
|
||||
S, S, O|M, O|M, P, P, X, X,
|
||||
O|4|PUSH, O|M|4, O|1|PUSH, O|M|1, O, O, O, O,
|
||||
/* 0x70 - 0x7F */
|
||||
O|1|JMP, O|1|JMP, O|1|JMP, O|1|JMP, O|1|JMP, O|1|JMP, O|1|JMP, O|1|JMP,
|
||||
O|1|JMP, O|1|JMP, O|1|JMP, O|1|JMP, O|1|JMP, O|1|JMP, O|1|JMP, O|1|JMP,
|
||||
/* 0x80 - 0x8F */
|
||||
O|M|1, O|M|4, O|M|1, O|M|1, O|M, O|M, O|M, O|M,
|
||||
O|M, O|M, O|M, O|M, O|M, O|M, O|M, O|M|POP,
|
||||
/* 0x90 - 0x9F */
|
||||
O, O, O, O, S, O, O, O,
|
||||
O, O, X, O, O, O, O, O,
|
||||
/* 0xA0 - 0xAF */
|
||||
O|1, O|4, O|1, O|4, O, O, O, O,
|
||||
O|1, O|4, O, O, O, O, O, O,
|
||||
/* 0xB0 - 0xBF */
|
||||
O|1, O|1, O|1, O|1, O|1, O|1, O|1, O|1,
|
||||
O|4, O|4, O|4, O|4, O|4, O|4, O|4, O|4,
|
||||
/* 0xC0 - 0xCF */
|
||||
O|M|1, O|M|1, X, O, X, X, O|M|1, O|M|4,
|
||||
X, X, X, X, X, X, X, X,
|
||||
/* 0xD0 - 0xDF */
|
||||
O|M, O|M, O|M, O|M, O|1, O|1, X, X,
|
||||
X, X, X, X, X, X, X, X,
|
||||
/* 0xE0 - 0xEF */
|
||||
X, X, X, X, X, X, X, X,
|
||||
X, O|4, X, O|1|JMP, X, X, X, X,
|
||||
/* 0xF0 - 0xFF */
|
||||
P, X, P, P, O, O, O|M|1, O|M|4,
|
||||
O, O, O, O, O, O, O|M, X
|
||||
};
|
||||
|
||||
/* Bitmap of faulting instructions that we can handle. */
|
||||
static unsigned char handleable_code[32] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0x80-0x83, 0x89, 0x8B */
|
||||
0x0F, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0xC7 */
|
||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
/* Bitmap of opcodes that use a register operand specified by Mod/RM. */
|
||||
static unsigned char opcode_uses_reg[32] = {
|
||||
/* 0x00 - 0x3F */
|
||||
0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
|
||||
/* 0x40 - 0x7F */
|
||||
0x00, 0x00, 0x00, 0x00, 0x0C, 0x0A, 0x00, 0x00,
|
||||
/* 0x80 - 0xBF */
|
||||
0xF0, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* 0xC0 - 0xFF */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static unsigned int parse_insn(unsigned char *insn,
|
||||
unsigned char *p_opcode,
|
||||
unsigned char *p_decode)
|
||||
{
|
||||
unsigned char b, d, *pb, mod, rm;
|
||||
|
||||
/* 1. Step over the prefix bytes. */
|
||||
for ( pb = insn; (pb - insn) < 4; pb++ )
|
||||
{
|
||||
b = *pb;
|
||||
d = insn_decode[b];
|
||||
if ( !(d & PREFIX_BYTE) )
|
||||
break;
|
||||
}
|
||||
|
||||
*p_opcode = b;
|
||||
*p_decode = d;
|
||||
|
||||
/* 2. Ensure we have a valid opcode byte. */
|
||||
if ( !(d & OPCODE_BYTE) )
|
||||
return 0;
|
||||
|
||||
/* 3. Process Mod/RM if there is one. */
|
||||
if ( d & HAS_MODRM )
|
||||
{
|
||||
b = *(++pb);
|
||||
if ( (mod = (b >> 6) & 3) != 3 )
|
||||
{
|
||||
if ( (rm = (b >> 0) & 7) == 4 )
|
||||
pb += 1; /* SIB byte */
|
||||
switch ( mod )
|
||||
{
|
||||
case 0:
|
||||
if ( rm == 5 )
|
||||
pb += 4; /* disp32 */
|
||||
break;
|
||||
case 1:
|
||||
pb += 1; /* disp8 */
|
||||
break;
|
||||
case 2:
|
||||
pb += 4; /* disp32 */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 4. All done. Result is all bytes stepped over, plus any immediates. */
|
||||
return ((pb - insn) + 1 + (d & INSN_SUFFIX_BYTES));
|
||||
}
|
||||
|
||||
#define SUCCESS 1
|
||||
#define FAIL 0
|
||||
static int map_fixup_buf(struct mm_struct *mm)
|
||||
{
|
||||
struct vm_area_struct *vma;
|
||||
|
||||
/* Already mapped? This is a pretty safe check. */
|
||||
if ( ((vma = find_vma(current->mm, FIXUP_BUF_USER)) != NULL) &&
|
||||
(vma->vm_start <= FIXUP_BUF_USER) &&
|
||||
(vma->vm_flags == (VM_READ | VM_MAYREAD | VM_RESERVED)) &&
|
||||
(vma->vm_file == NULL) )
|
||||
return SUCCESS;
|
||||
|
||||
if ( (vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL)) == NULL )
|
||||
{
|
||||
DPRINTK("Cannot allocate VMA.");
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
memset(vma, 0, sizeof(*vma));
|
||||
|
||||
vma->vm_mm = mm;
|
||||
vma->vm_flags = VM_READ | VM_MAYREAD | VM_RESERVED;
|
||||
vma->vm_page_prot = PAGE_READONLY;
|
||||
|
||||
down_write(&mm->mmap_sem);
|
||||
|
||||
vma->vm_start = get_unmapped_area(
|
||||
NULL, FIXUP_BUF_USER, FIXUP_BUF_SIZE,
|
||||
0, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED);
|
||||
if ( vma->vm_start != FIXUP_BUF_USER )
|
||||
{
|
||||
DPRINTK("Cannot allocate low-memory-region VMA.");
|
||||
up_write(&mm->mmap_sem);
|
||||
kmem_cache_free(vm_area_cachep, vma);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
vma->vm_end = vma->vm_start + FIXUP_BUF_SIZE;
|
||||
|
||||
if ( remap_page_range(vma, vma->vm_start, __pa(fixup_buf),
|
||||
vma->vm_end - vma->vm_start, vma->vm_page_prot) )
|
||||
{
|
||||
DPRINTK("Cannot map low-memory-region VMA.");
|
||||
up_write(&mm->mmap_sem);
|
||||
kmem_cache_free(vm_area_cachep, vma);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
insert_vm_struct(mm, vma);
|
||||
|
||||
mm->total_vm += FIXUP_BUF_SIZE >> PAGE_SHIFT;
|
||||
|
||||
up_write(&mm->mmap_sem);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mainly this function checks that our patches can't erroneously get flushed
|
||||
* to a file on disc, which would screw us after reboot!
|
||||
*/
|
||||
#define SUCCESS 1
|
||||
#define FAIL 0
|
||||
static int safe_to_patch(struct mm_struct *mm, unsigned long addr)
|
||||
{
|
||||
struct vm_area_struct *vma;
|
||||
struct file *file;
|
||||
unsigned char _name[30], *name;
|
||||
|
||||
/* Always safe to patch the fixup buffer. */
|
||||
if ( addr <= (FIXUP_BUF_USER + FIXUP_BUF_SIZE) )
|
||||
return SUCCESS;
|
||||
|
||||
if ( ((vma = find_vma(current->mm, addr)) == NULL) ||
|
||||
(vma->vm_start > addr) )
|
||||
{
|
||||
DPRINTK("No VMA contains fault address.");
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
/* Only patch shared libraries. */
|
||||
if ( (file = vma->vm_file) == NULL )
|
||||
{
|
||||
DPRINTK("VMA is anonymous!");
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
/* No shared mappings => nobody can dirty the file. */
|
||||
/* XXX Note the assumption that noone will dirty the file in future! */
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
|
||||
if ( file->f_mapping->i_mmap_writable != 0 )
|
||||
#else
|
||||
if ( file->f_dentry->d_inode->i_mapping->i_mmap_shared != NULL )
|
||||
#endif
|
||||
{
|
||||
DPRINTK("Shared mappings exist.");
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Because of above dodgy assumption, we will only patch things in
|
||||
* /lib/tls. Our belief is that updates here will only ever occur by
|
||||
* unlinking the old files and installing completely fresh ones. :-)
|
||||
*/
|
||||
name = d_path(file->f_dentry, file->f_vfsmnt, _name, sizeof(_name));
|
||||
if ( IS_ERR(name) || (strncmp("/lib/tls", name, 8) != 0) )
|
||||
{
|
||||
DPRINTK("Backing file is not in /lib/tls");
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
#define DP(_f) printk(KERN_ALERT " " _f "\n")
|
||||
|
||||
asmlinkage void do_fixup_4gb_segment(struct pt_regs *regs, long error_code)
|
||||
{
|
||||
static unsigned int fixup_idx = 0;
|
||||
struct mm_struct *mm = current->mm;
|
||||
unsigned int fi;
|
||||
int save_indirect_reg, hash, i;
|
||||
unsigned int insn_len = (unsigned int)error_code, new_insn_len;
|
||||
unsigned char b[20], modrm, mod, reg, rm, sib, patch[20], opcode, decode;
|
||||
unsigned long eip = regs->eip - insn_len;
|
||||
struct fixup_entry *fe;
|
||||
struct page *page;
|
||||
pte_t *pte;
|
||||
pmd_t *pmd;
|
||||
pgd_t *pgd;
|
||||
void *veip;
|
||||
static unsigned long printed = 0;
|
||||
int i;
|
||||
|
||||
/* Easy check that code segment has base 0, max limit. */
|
||||
if ( unlikely(regs->xcs != __USER_CS) )
|
||||
if ( !test_and_set_bit(0, &printed) )
|
||||
{
|
||||
DPRINTK("Unexpected CS value.");
|
||||
return;
|
||||
}
|
||||
HYPERVISOR_vm_assist(VMASST_CMD_disable,
|
||||
VMASST_TYPE_4gb_segments_notify);
|
||||
|
||||
if ( unlikely(!map_fixup_buf(mm)) )
|
||||
goto out;
|
||||
DP("");
|
||||
DP("***************************************************************");
|
||||
DP("***************************************************************");
|
||||
DP("** WARNING: Currently emulating unsupported memory accesses **");
|
||||
DP("** in /lib/tls libraries. Although this emulation **");
|
||||
DP("** ensures correct execution, it is very slow! **");
|
||||
DP("** TO OBTAIN FULL PERFORMANCE, EXECUTE THE **");
|
||||
DP("** FOLLOWING AS ROOT: **");
|
||||
DP("** mv /lib/tls /lib/tls.disabled **");
|
||||
DP("***************************************************************");
|
||||
DP("***************************************************************");
|
||||
DP("");
|
||||
|
||||
/* Hold the mmap_sem to prevent the mapping from disappearing under us. */
|
||||
down_read(&mm->mmap_sem);
|
||||
|
||||
if ( unlikely(!safe_to_patch(mm, eip)) )
|
||||
goto out;
|
||||
|
||||
if ( unlikely(copy_from_user(b, (void *)eip, sizeof(b)) != 0) )
|
||||
{
|
||||
DPRINTK("Could not read instruction bytes from user space.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Already created a fixup for this code sequence? */
|
||||
hash = FIXUP_HASH(b);
|
||||
for ( fe = fixup_hash[hash]; fe != NULL; fe = fe->next )
|
||||
{
|
||||
if ( memcmp(fe->patched_code, b, fe->patched_code_len) == 0 )
|
||||
goto do_the_patch;
|
||||
}
|
||||
|
||||
/* Guaranteed enough room to patch? */
|
||||
if ( unlikely((fi = fixup_idx) > (FIXUP_BUF_SIZE-64)) )
|
||||
{
|
||||
static int printed = 0;
|
||||
if ( !printed )
|
||||
printk(KERN_ALERT "WARNING: Out of room in segment-fixup page.\n");
|
||||
printed = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Must be a handleable opcode with GS override. */
|
||||
if ( (b[0] != 0x65) ||
|
||||
!test_bit((unsigned int)b[1], (unsigned long *)handleable_code) )
|
||||
{
|
||||
DPRINTK("No GS override, or not a MOV (%02x %02x).", b[0], b[1]);
|
||||
goto out;
|
||||
}
|
||||
|
||||
modrm = b[2];
|
||||
mod = (modrm >> 6) & 3;
|
||||
reg = (modrm >> 3) & 7;
|
||||
rm = (modrm >> 0) & 7;
|
||||
|
||||
/* If indirect register isn't clobbered then we must push/pop it. */
|
||||
save_indirect_reg = !((b[1] == 0x8b) && (reg == rm));
|
||||
|
||||
/* We don't grok SIB bytes. */
|
||||
if ( rm == 4 )
|
||||
{
|
||||
DPRINTK("We don't grok SIB bytes.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Ensure Mod/RM specifies (r32) or disp8(r32). */
|
||||
switch ( mod )
|
||||
{
|
||||
case 0:
|
||||
if ( rm == 5 )
|
||||
for ( i = 5; i > 0; i-- )
|
||||
{
|
||||
DPRINTK("Unhandleable disp32 EA %d.", rm);
|
||||
goto out;
|
||||
printk("Pausing... %d", i);
|
||||
mdelay(1000);
|
||||
printk("\b\b\b\b\b\b\b\b\b\b\b\b");
|
||||
}
|
||||
break; /* m32 == (r32) */
|
||||
case 1:
|
||||
break; /* m32 == disp8(r32) */
|
||||
default:
|
||||
DPRINTK("Unhandleable Mod value %d.", mod);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Indirect jump pointer. */
|
||||
*(u32 *)&fixup_buf[fi] = FIXUP_BUF_USER + fi + 4;
|
||||
fi += 4;
|
||||
|
||||
/* push <r32> */
|
||||
if ( save_indirect_reg )
|
||||
fixup_buf[fi++] = 0x50 + rm;
|
||||
|
||||
/* pushf */
|
||||
fixup_buf[fi++] = 0x9c;
|
||||
|
||||
/* add %gs:0,<r32> */
|
||||
fixup_buf[fi++] = 0x65;
|
||||
fixup_buf[fi++] = 0x03;
|
||||
fixup_buf[fi++] = 0x05 | (rm << 3);
|
||||
*(unsigned long *)&fixup_buf[fi] = 0;
|
||||
fi += 4;
|
||||
|
||||
/* popf */
|
||||
fixup_buf[fi++] = 0x9d;
|
||||
|
||||
/* Relocate the faulting instruction, minus the GS override. */
|
||||
memcpy(&fixup_buf[fi], &b[1], error_code - 1);
|
||||
fi += error_code - 1;
|
||||
|
||||
/* pop <r32> */
|
||||
if ( save_indirect_reg )
|
||||
fixup_buf[fi++] = 0x58 + rm;
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
if ( insn_len >= PATCH_LEN )
|
||||
{
|
||||
/* ret */
|
||||
fixup_buf[fi++] = 0xc3;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Bail if can't decode the following instruction. */
|
||||
if ( unlikely((new_insn_len =
|
||||
parse_insn(&b[insn_len], &opcode, &decode)) == 0) )
|
||||
{
|
||||
DPRINTK("Could not decode following instruction.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( (decode & CODE_MASK) == JMP )
|
||||
{
|
||||
long off;
|
||||
|
||||
memcpy(&fixup_buf[fi], &b[insn_len], new_insn_len - 1);
|
||||
fi += new_insn_len - 1;
|
||||
|
||||
/* Patch the 8-bit relative offset. */
|
||||
fixup_buf[fi++] = 1;
|
||||
|
||||
insn_len += new_insn_len;
|
||||
ASSERT(insn_len >= PATCH_LEN);
|
||||
|
||||
/* ret */
|
||||
fixup_buf[fi++] = 0xc3;
|
||||
|
||||
/* pushf */
|
||||
fixup_buf[fi++] = 0x9c;
|
||||
|
||||
off = (insn_len - PATCH_LEN) + (long)(char)b[insn_len-1];
|
||||
if ( unlikely(off > 127) )
|
||||
{
|
||||
/* add <imm32>,4(%esp) */
|
||||
fixup_buf[fi++] = 0x81;
|
||||
fixup_buf[fi++] = 0x44;
|
||||
fixup_buf[fi++] = 0x24;
|
||||
fixup_buf[fi++] = 0x04;
|
||||
fi += 4;
|
||||
*(long *)&fixup_buf[fi-4] = off;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* add <imm8>,4(%esp) [sign-extended] */
|
||||
fixup_buf[fi++] = 0x83;
|
||||
fixup_buf[fi++] = 0x44;
|
||||
fixup_buf[fi++] = 0x24;
|
||||
fixup_buf[fi++] = 0x04;
|
||||
fixup_buf[fi++] = (char)(off & 0xff);
|
||||
}
|
||||
|
||||
/* popf */
|
||||
fixup_buf[fi++] = 0x9d;
|
||||
|
||||
/* ret */
|
||||
fixup_buf[fi++] = 0xc3;
|
||||
|
||||
break;
|
||||
}
|
||||
else if ( opcode == 0xe9 ) /* jmp <rel32> */
|
||||
{
|
||||
insn_len += new_insn_len;
|
||||
ASSERT(insn_len >= PATCH_LEN);
|
||||
|
||||
/* pushf */
|
||||
fixup_buf[fi++] = 0x9c;
|
||||
|
||||
/* add <imm32>,4(%esp) */
|
||||
fixup_buf[fi++] = 0x81;
|
||||
fixup_buf[fi++] = 0x44;
|
||||
fixup_buf[fi++] = 0x24;
|
||||
fixup_buf[fi++] = 0x04;
|
||||
fi += 4;
|
||||
*(long *)&fixup_buf[fi-4] =
|
||||
(insn_len - PATCH_LEN) + *(long *)&b[insn_len-4];
|
||||
|
||||
/* popf */
|
||||
fixup_buf[fi++] = 0x9d;
|
||||
|
||||
/* ret */
|
||||
fixup_buf[fi++] = 0xc3;
|
||||
|
||||
break;
|
||||
}
|
||||
else if ( opcode == 0xc3 ) /* ret */
|
||||
{
|
||||
/* pop -4(%esp) [doesn't affect EFLAGS] */
|
||||
fixup_buf[fi++] = 0x8f;
|
||||
fixup_buf[fi++] = 0x44;
|
||||
fixup_buf[fi++] = 0x24;
|
||||
fixup_buf[fi++] = 0xfc;
|
||||
}
|
||||
else
|
||||
{
|
||||
int stack_addon = 4;
|
||||
|
||||
if ( (decode & CODE_MASK) == PUSH )
|
||||
{
|
||||
stack_addon = 8;
|
||||
/* push (%esp) */
|
||||
fixup_buf[fi++] = 0xff;
|
||||
fixup_buf[fi++] = 0x34;
|
||||
fixup_buf[fi++] = 0x24;
|
||||
}
|
||||
else if ( (decode & CODE_MASK) == POP )
|
||||
{
|
||||
stack_addon = 8;
|
||||
/* push 4(%esp) */
|
||||
fixup_buf[fi++] = 0xff;
|
||||
fixup_buf[fi++] = 0x74;
|
||||
fixup_buf[fi++] = 0x24;
|
||||
fixup_buf[fi++] = 0x04;
|
||||
}
|
||||
|
||||
/* Check for EA calculations involving ESP, and skip return addr */
|
||||
if ( decode & HAS_MODRM )
|
||||
{
|
||||
do { new_insn_len--; }
|
||||
while ( (fixup_buf[fi++] = b[insn_len++]) != opcode );
|
||||
|
||||
modrm = fixup_buf[fi++] = b[insn_len++];
|
||||
new_insn_len--;
|
||||
mod = (modrm >> 6) & 3;
|
||||
reg = (modrm >> 3) & 7;
|
||||
rm = (modrm >> 0) & 7;
|
||||
|
||||
if ( (reg == 4) &&
|
||||
test_bit(opcode, (unsigned long *)opcode_uses_reg) )
|
||||
{
|
||||
DPRINTK("Data movement to ESP unsupported.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( rm == 4 )
|
||||
{
|
||||
if ( mod == 3 )
|
||||
{
|
||||
DPRINTK("Data movement to ESP is unsupported.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
sib = fixup_buf[fi++] = b[insn_len++];
|
||||
new_insn_len--;
|
||||
if ( (sib & 7) == 4 )
|
||||
{
|
||||
switch ( mod )
|
||||
{
|
||||
case 0:
|
||||
mod = 1;
|
||||
fixup_buf[fi-2] |= 0x40;
|
||||
fixup_buf[fi++] = stack_addon;
|
||||
break;
|
||||
case 1:
|
||||
fixup_buf[fi++] = b[insn_len++] + stack_addon;
|
||||
new_insn_len--;
|
||||
break;
|
||||
case 2:
|
||||
*(long *)&fixup_buf[fi] =
|
||||
*(long *)&b[insn_len] + stack_addon;
|
||||
fi += 4;
|
||||
insn_len += 4;
|
||||
new_insn_len -= 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Relocate the (remainder of) the instruction. */
|
||||
if ( new_insn_len != 0 )
|
||||
{
|
||||
memcpy(&fixup_buf[fi], &b[insn_len], new_insn_len);
|
||||
fi += new_insn_len;
|
||||
}
|
||||
|
||||
if ( (decode & CODE_MASK) == PUSH )
|
||||
{
|
||||
/* pop 4(%esp) */
|
||||
fixup_buf[fi++] = 0x8f;
|
||||
fixup_buf[fi++] = 0x44;
|
||||
fixup_buf[fi++] = 0x24;
|
||||
fixup_buf[fi++] = 0x04;
|
||||
}
|
||||
else if ( (decode & CODE_MASK) == POP )
|
||||
{
|
||||
/* pop (%esp) */
|
||||
fixup_buf[fi++] = 0x8f;
|
||||
fixup_buf[fi++] = 0x04;
|
||||
fixup_buf[fi++] = 0x24;
|
||||
}
|
||||
}
|
||||
|
||||
if ( (insn_len += new_insn_len) > 20 )
|
||||
{
|
||||
DPRINTK("Code to patch is too long!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Can't have a RET in the middle of a patch sequence. */
|
||||
if ( (opcode == 0xc3) && (insn_len < PATCH_LEN) )
|
||||
{
|
||||
DPRINTK("RET in middle of patch seq!\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create an entry for a new fixup patch. */
|
||||
fe = kmalloc(sizeof(struct fixup_entry), GFP_KERNEL);
|
||||
if ( unlikely(fe == NULL) )
|
||||
{
|
||||
DPRINTK("Not enough memory to allocate a fixup_entry.");
|
||||
goto out;
|
||||
}
|
||||
fe->patched_code_len = insn_len;
|
||||
memcpy(fe->patched_code, b, insn_len);
|
||||
fe->fixup_idx = fixup_idx;
|
||||
fe->return_idx =
|
||||
fixup_idx + error_code + (save_indirect_reg ? 14 : 12);
|
||||
fe->next = fixup_hash[hash];
|
||||
fixup_hash[hash] = fe;
|
||||
|
||||
/* Commit the patch. */
|
||||
fixup_idx = fi;
|
||||
|
||||
do_the_patch:
|
||||
|
||||
if ( unlikely(((eip ^ (eip + fe->patched_code_len)) & PAGE_MASK) != 0) )
|
||||
{
|
||||
DPRINTK("Patch instruction would straddle a page boundary.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ( put_user(eip + PATCH_LEN, (unsigned long *)regs->esp - 1) != 0 )
|
||||
{
|
||||
DPRINTK("Failed to place return address on user stack.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Create the patching instructions in a temporary buffer. */
|
||||
patch[0] = 0x67;
|
||||
patch[1] = 0xff;
|
||||
patch[2] = 0x16; /* call <r/m16> */
|
||||
*(u16 *)&patch[3] = FIXUP_BUF_USER + fe->fixup_idx;
|
||||
for ( i = 5; i < fe->patched_code_len; i++ )
|
||||
patch[i] = 0x90; /* nop */
|
||||
|
||||
spin_lock(&mm->page_table_lock);
|
||||
|
||||
/* Find the physical page that is to be patched. */
|
||||
pgd = pgd_offset(current->mm, eip);
|
||||
if ( unlikely(!pgd_present(*pgd)) )
|
||||
goto unlock_and_out;
|
||||
pmd = pmd_offset(pgd, eip);
|
||||
if ( unlikely(!pmd_present(*pmd)) )
|
||||
goto unlock_and_out;
|
||||
pte = pte_offset_kernel(pmd, eip);
|
||||
if ( unlikely(!pte_present(*pte)) )
|
||||
goto unlock_and_out;
|
||||
page = pte_page(*pte);
|
||||
|
||||
/*
|
||||
* We get lock to prevent page going AWOL on us. Also a locked page
|
||||
* might be getting flushed to disc!
|
||||
*/
|
||||
if ( unlikely(TestSetPageLocked(page)) )
|
||||
{
|
||||
DPRINTK("Page is locked.");
|
||||
goto unlock_and_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If page is dirty it will get flushed back to disc - bad news! An
|
||||
* anonymous page may be moulinexed under our feet by another thread.
|
||||
*/
|
||||
if ( unlikely(PageDirty(page)) || unlikely(PageAnon(page)) )
|
||||
{
|
||||
DPRINTK("Page is dirty or anonymous.");
|
||||
unlock_page(page);
|
||||
goto unlock_and_out;
|
||||
}
|
||||
|
||||
veip = kmap(page);
|
||||
memcpy((char *)veip + (eip & ~PAGE_MASK), patch, fe->patched_code_len);
|
||||
kunmap(page);
|
||||
|
||||
unlock_page(page);
|
||||
spin_unlock(&mm->page_table_lock);
|
||||
|
||||
/* Success! Return to user land to execute 2nd insn of the pair. */
|
||||
regs->esp -= 4;
|
||||
regs->eip = FIXUP_BUF_USER + fe->return_idx;
|
||||
|
||||
out:
|
||||
up_read(&mm->mmap_sem);
|
||||
return;
|
||||
|
||||
unlock_and_out:
|
||||
spin_unlock(&mm->page_table_lock);
|
||||
up_read(&mm->mmap_sem);
|
||||
return;
|
||||
}
|
||||
|
||||
static int fixup_thread(void *unused)
|
||||
{
|
||||
daemonize("segfixup");
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule();
|
||||
printk("Continuing...\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int nosegfixup = 0;
|
||||
|
||||
static int __init fixup_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
nosegfixup = 1; /* XXX */
|
||||
|
||||
if ( !nosegfixup )
|
||||
{
|
||||
HYPERVISOR_vm_assist(VMASST_CMD_enable,
|
||||
VMASST_TYPE_4gb_segments_notify);
|
||||
fixup_buf = (char *)__get_free_pages(GFP_ATOMIC, FIXUP_BUF_ORDER);
|
||||
for ( i = 0; i < (1 << FIXUP_BUF_ORDER); i++ )
|
||||
SetPageReserved(virt_to_page(fixup_buf) + i);
|
||||
memset(fixup_hash, 0, sizeof(fixup_hash));
|
||||
(void)kernel_thread(fixup_thread, NULL, CLONE_FS | CLONE_FILES);
|
||||
}
|
||||
|
||||
HYPERVISOR_vm_assist(VMASST_CMD_enable,
|
||||
VMASST_TYPE_4gb_segments_notify);
|
||||
return 0;
|
||||
}
|
||||
__initcall(fixup_init);
|
||||
|
||||
static int __init fixup_setup(char *str)
|
||||
{
|
||||
nosegfixup = 1;
|
||||
return 0;
|
||||
}
|
||||
__setup("nosegfixup", fixup_setup);
|
||||