diff -Nrup linux-2.6.23/include/linux/kaho.h linux-2.6.23.kaho/include/linux/kaho.h --- linux-2.6.23/include/linux/kaho.h 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.6.23.kaho/include/linux/kaho.h 2008-09-16 15:07:24.000000000 +0900 @@ -0,0 +1,104 @@ +#ifndef _kaho_mod_h +#define _kaho_mod_h + +#include + +#define KAHO_IOC_MAGIC 'k' + +struct kaho_make_patch { + pid_t target_pid; + int nr_sub_patch; /* number of the patch */ + int handle; +}; + +struct kaho_map_opt { + int handle; + const char *filename; + unsigned long addr; + unsigned long length; + unsigned long offset; + unsigned long prot; + unsigned long flag; + unsigned long act_addr; +}; + +struct kaho_load_patch { + int handle; + int sub_patch_id; + int patch_type; + unsigned long src_addr; /* address of the patch in kaho command */ + unsigned long patch_addr; /* address to store the patch in the target */ + unsigned long patch_size; /* size of the patch code */ + unsigned long target_addr;/* address to be patched in the target */ +}; + +struct kaho_std_opt { + int handle; + unsigned int flags; +}; + +struct kaho_get_info { + int handle; + int sub_patch_id; + pid_t pid; + int nr_sub_patch; + int state; + int patch_type; + unsigned long patch_addr; + unsigned long patch_size; + unsigned long target_addr; + int next_handle; +}; + + +#define KAHOCTL_MAKE_PATCH _IOWR(KAHO_IOC_MAGIC, 1, struct kaho_make_patch) +#define KAHOCTL_MAP_PATCH _IOWR(KAHO_IOC_MAGIC, 2, struct kaho_map_opt) +#define KAHOCTL_LOAD_PATCH _IOW (KAHO_IOC_MAGIC, 3, struct kaho_load_patch) +#define KAHOCTL_ACTIVATE _IOW (KAHO_IOC_MAGIC, 4, struct kaho_std_opt) +#define KAHOCTL_ACTIVATE_RT _IOW (KAHO_IOC_MAGIC, 5, struct kaho_std_opt) +#define KAHOCTL_DEACTIVATE _IOW (KAHO_IOC_MAGIC, 6, struct kaho_std_opt) +#define KAHOCTL_UNMAP_PATCH _IOWR(KAHO_IOC_MAGIC, 7, struct kaho_map_opt) +#define KAHOCTL_DEL_PATCH _IOW (KAHO_IOC_MAGIC, 8, struct kaho_std_opt) +#define KAHOCTL_GET_INFO _IOWR(KAHO_IOC_MAGIC, 9, struct kaho_get_info) +#define KAHOCTL_CANCEL _IOW (KAHO_IOC_MAGIC, 10, struct kaho_std_opt) + +#define KAHO_STATE_ENTRY 0 +#define KAHO_STATE_MAPPING 1 +#define KAHO_STATE_MAPPED 2 +#define KAHO_STATE_LOADING 3 +#define KAHO_STATE_LOADED 4 +#define KAHO_STATE_IN_ACT 5 +//#define KAHO_STATE_IN_ACT_RT 6 +#define KAHO_STATE_ACTIVE 7 +#define KAHO_STATE_IN_DEACT 8 +//#define KAHO_STATE_IN_DEACT_RT 9 +#define KAHO_STATE_DEACTIVE 10 +#define KAHO_STATE_UNMAPPING 11 +#define KAHO_STATE_UNMAPPED 12 +#define KAHO_STATE_IN_DEL_PATCH 13 +#define KAHO_STATE_IN_ACT_RT0 14 +#define KAHO_STATE_IN_ACT_RT1 15 +#define KAHO_STATE_IN_ACT_RT2 16 +#define KAHO_STATE_IN_DEACT_RT0 17 +#define KAHO_STATE_IN_DEACT_RT1 18 +#define KAHO_STATE_IN_DEACT_RT2 19 + +#define KAHO_STATE_ERROR -1 + +#define KAHO_MAX_NR_PATCH 0x1000 +#define KAHO_MAX_NR_SUB 0x100 + + +/* definition of patch type */ +#define KAHO_PTYPE_UNKNOWN 0 +#define KAHO_PTYPE_FUNC 1 +#define KAHO_PTYPE_VAR 2 +#define KAHO_PTYPE_POINTER 3 +#define KAHO_PTYPE_NONE 4 +#define KAHO_PTYPE_MAX 5 + +/* flags for kaho_patch_item */ +#define KAHO_X86_ABS_JMP 0x0001 +#define KAHO_RT_PATCH 0x0002 + +#endif diff -Nrup linux-2.6.23/kernel/Makefile linux-2.6.23.kaho/kernel/Makefile --- linux-2.6.23/kernel/Makefile 2008-09-15 14:45:13.000000000 +0900 +++ linux-2.6.23.kaho/kernel/Makefile 2008-09-16 15:07:45.000000000 +0900 @@ -9,7 +9,7 @@ obj-y = sched.o fork.o exec_domain.o rcupdate.o extable.o params.o posix-timers.o \ kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ hrtimer.o rwsem.o latency.o nsproxy.o srcu.o die_notifier.o \ - utsname.o + utsname.o kaho.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += time/ diff -Nrup linux-2.6.23/kernel/kaho.c linux-2.6.23.kaho/kernel/kaho.c --- linux-2.6.23/kernel/kaho.c 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.6.23.kaho/kernel/kaho.c 2008-09-16 15:07:52.000000000 +0900 @@ -0,0 +1,2409 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) +#include +#endif + +#define CONFIG_KAHO_RT + +#include +#include "kaho_def.h" + +//#define DEBUG +#define MSG_HEAD KERN_ERR "kaho: " +#ifdef DEBUG +#define DPRINT(format, args...) printk(MSG_HEAD format, ## args) +#else +#define DPRINT(format, args...) +#endif + +#define MSG_MES "kaho[bench]: " + +/* ------------------------------------------------------------------ + * Basic variables and structures + * ---------------------------------------------------------------- */ +#define KAHO_VER "1.5.4" +static struct class *kaho_class; +static int kaho_major = 0; +static int kaho_minor = 0; +static const char kaho_dev_name[] = "kaho"; + +/* ------------------------------------------------------------------*/ +/* List of kaho_patch_info */ +LIST_HEAD(kaho_patch_list_head); +DEFINE_SPINLOCK(kaho_patch_list_lock); + +#ifdef CONFIG_KAHO_RT +/* List of kaho_patch_info that is waiting for realtime activation */ +LIST_HEAD(kaho_trap_list_head); +DEFINE_SPINLOCK(kaho_trap_list_lock); +#endif + +/* Number of patches included in kaho_patch_list_head */ +static atomic_t nr_patch; + +/* Array and spinlock for handle management */ +static char handle_tbl[KAHO_MAX_NR_PATCH]; +DEFINE_SPINLOCK(handle_tbl_lock); + +/* ------------------------------------------------------------------ + * Architecture depend codes + * ---------------------------------------------------------------- */ +#if defined(__i386__) || defined(__x86_64__) +#if 0 +#define __access_mem(tsk, dst, src, size, exec_id, err_msg, err_code, dir) \ +do { \ + struct mm_struct *mm = get_task_mm(tsk); \ + if (!mm) { \ + printk(KERN_ERR MSG_HEAD \ + "Failed to access_proccess_vm: " \ + "get_task_mm() returned NULL: " err_msg); \ + err_code; \ + } \ + down_read(&mm->mmap_sem); \ + if (tsk->self_exec_id != exec_id) { \ + printk(KERN_ERR MSG_HEAD \ + "Failed to access_proccess_vm: exec_id has changed " \ + "%d->%d " err_msg, exec_id, tsk->self_exec_id); \ + up_read(&mm->mmap_sem); \ + mmput(mm); \ + err_code; \ + } \ + ret = _kaho_access_process_vm(tsk, dst, src, size, dir); \ + up_read(&mm->mmap_sem); \ + mmput(mm); \ + if (ret != size) { \ + printk(KERN_ERR MSG_HEAD \ + "Failed to access_proccess_vm ret: %d, size: %d" \ + err_msg, ret, (int)size); \ + err_code; \ + } \ +} while(0) +#endif +//#if 0 +#define __access_mem(tsk, dst, src, size, exec_id, err_msg, err_code, dir) \ +do { \ + if (tsk->self_exec_id != exec_id) { \ + printk(KERN_ERR MSG_HEAD \ + "Failed to access_proccess_vm: exec_id has changed " \ + "%d->%d " err_msg, exec_id, tsk->self_exec_id); \ + err_code; \ + } \ + ret = access_process_vm(tsk, dst, src, size, dir); \ + if (ret != size) { \ + printk(KERN_ERR MSG_HEAD \ + "Failed to access_proccess_vm ret: %d, size: %d" \ + err_msg, ret, (int)size); \ + err_code; \ + } \ +} while(0) +//#endif + +#define __WRITE_MEM(tsk, dst, src, size, exec_id, err_msg, err_code) \ + __access_mem(tsk, dst, src, size, exec_id, err_msg, err_code, 1) + +#define __READ_MEM(tsk, dst, src, size, exec_id, err_msg, err_code) \ + __access_mem(tsk, dst, src, size, exec_id, err_msg, err_code, 0) + + +static int arch_overwrite_code(struct kaho_patch_info* kpi, int no, + unsigned long target_addr, + unsigned char* ovwr_code, int jmp_code_size) +{ + int ret; + +#ifdef CONFIG_KAHO_RT + if (kpi->items[no].flags & KAHO_RT_PATCH) { + __WRITE_MEM(kpi->target_task, target_addr+trap_code_size, + (void*)&ovwr_code[trap_code_size], + jmp_code_size-trap_code_size, kpi->exec_id, + "(arch_overwrite_code 1)\n", + ret = -EFAULT; return ret); + __WRITE_MEM(kpi->target_task, target_addr, + (void*)&ovwr_code[0], trap_code_size, kpi->exec_id, + "(arch_overwrite_code 0)\n", + ret = -EFAULT; return ret); + } else { +#endif + __WRITE_MEM(kpi->target_task, target_addr, (void*)ovwr_code, + jmp_code_size, kpi->exec_id, + "(arch_overwrite_code)\n", + ret = -EFAULT; return ret); +#ifdef CONFIG_KAHO_RT + } +#endif + return 0; +} + +static int arch_get_jump_code_size(struct kaho_patch_info* kpi, int no) +{ + int ret; +#if defined(__x86_64__) + if (kpi->items[no].flags & KAHO_X86_ABS_JMP) + ret = jmp_code_size_abs; + else +#endif + ret = jmp_code_size_rel; + return ret; +} + +#if defined(__x86_64__) +static int arch_func_patch_abs(struct kaho_patch_info* kpi, int no) +{ + unsigned long patch_addr = kpi->items[no].patch_addr; + unsigned long target_addr = kpi->items[no].target_addr; + unsigned char jmp_code[jmp_code_size_abs]; + + memcpy(jmp_code, jmp_code_abs, 6); + *((unsigned long*)&jmp_code[6]) = patch_addr; + return arch_overwrite_code(kpi, no, target_addr, jmp_code, + jmp_code_size_abs); +} +#endif + +#ifdef CONFIG_KAHO_RT +static int arch_trap_addr_check( + struct die_args* args, unsigned long target_addr) +{ + return arch_get_pc(args->regs) == (target_addr + trap_code_size); +} + +static int arch_implant_trap(struct kaho_patch_info* kpi, int i, int bkup) +{ + int ret; + struct kaho_patch_item* pitem = &(kpi->items[i]); + + /* backup */ + DPRINT("arch_implant_trap(entry): target_addr: %lx\n", + pitem->target_addr); + + if (bkup == CODE_BKUP) { + __READ_MEM(kpi->target_task, pitem->target_addr, + (void*)pitem->backup, trap_code_size, kpi->exec_id, + "(backup trap)\n", + return -EFAULT); + atomic_set(&pitem->backup_done, 1); + } + + /* Implant */ + if (bkup == CODE_RESTORE) { + __WRITE_MEM(kpi->target_task, pitem->target_addr, + (void*)pitem->backup, trap_code_size, + kpi->exec_id, "(implant trap)\n", + return -EFAULT); + return 0; + } + __WRITE_MEM(kpi->target_task, pitem->target_addr, (void*)&trap_code, + trap_code_size, kpi->exec_id, "(implant trap)\n", + return -EFAULT); + + DPRINT("arch_implant_trap(exit)\n"); + return 0; +} +#endif + +static int arch_calc_rel_addr(unsigned long patch_addr, + unsigned long target_addr, int32_t* prel) +{ +#if defined(__x86_64__) + int64_t rel_addr_64; + + rel_addr_64 = patch_addr - target_addr - jmp_code_size_rel; + if (rel_addr_64 > 0x7fffffffL || -rel_addr_64 > 0x80000000L) + return -EINVAL; + + *prel = (int32_t)rel_addr_64; +#endif + +#if defined(__i386__) + *prel = patch_addr - target_addr - jmp_code_size_rel; +#endif + return 0; +} + +static int arch_func_patch_rel(struct kaho_patch_info* kpi, int no) +{ + int ret; + unsigned long patch_addr = kpi->items[no].patch_addr; + unsigned long target_addr = kpi->items[no].target_addr; + unsigned char jmp_code[jmp_code_size_rel]; + int32_t rel_addr; + + /* Calculation of the relative address */ + ret = arch_calc_rel_addr(patch_addr, target_addr, &rel_addr); + if (ret < 0) { + printk(KERN_ERR MSG_HEAD "Cannot jump: patch_addr: %lx, " + "target_addr: %lx\n", + patch_addr, target_addr); + return ret; + } + + /* Overwirte jump code */ + jmp_code[0] = jmp_code_rel[0]; + *((int32_t*)&jmp_code[1]) = rel_addr; + return arch_overwrite_code(kpi, no, target_addr, (void*)jmp_code, + jmp_code_size_rel); +} + +static int arch_func_patch(struct kaho_patch_info* kpi, int no) +{ + int backup_len; + int ret; + unsigned long target; + void* backup; + + /* Get length to backup orig data */ + backup_len = arch_get_jump_code_size(kpi, no); + + /* Set addresses */ + target = (unsigned long) kpi->items[no].target_addr; + backup = (void*) kpi->items[no].backup; + +#ifdef CONFIG_KAHO_RT + /* Backup size and backup address are adjusted, because the region + that was overwrited by trap code has already backuped */ + if (kpi->items[no].flags & KAHO_RT_PATCH) { + target += trap_code_size; + backup = &(kpi->items[no].backup[trap_code_size]); + backup_len -= trap_code_size; + } +#endif + + __READ_MEM(kpi->target_task, target, backup, backup_len, + kpi->exec_id, "(backup code)\n", + ret = -EFAULT; return ret); + + /* Write jump command */ +#if defined(__x86_64__) + if (kpi->items[no].flags & KAHO_X86_ABS_JMP) + ret = arch_func_patch_abs(kpi, no); + else +#endif + ret = arch_func_patch_rel(kpi, no); + + return ret; +} + +static int arch_data_patch(struct kaho_patch_info* kpi, int no, int ptype) +{ + int ret; + int patch_size; + unsigned long target_addr = kpi->items[no].target_addr; + void* saddr; + void* backup_addr; + + if (ptype == KAHO_PTYPE_VAR) { + patch_size = kpi->items[no].patch_size; + saddr = (void*)kpi->items[no].patch_addr; + backup_addr = &((char*)saddr)[patch_size]; + DPRINT("KAHO_PTYPE_VAR: patch_size: %d, saddr: %p, " + "target: %lx, pid: %d\n", patch_size, saddr, + target_addr, current->pid); + } + else if (ptype == KAHO_PTYPE_POINTER) { + patch_size = sizeof(void *); + saddr = (void*)&(kpi->items[no].patch_addr); + backup_addr = (void*)kpi->items[no].backup; + DPRINT("KAHO_PTYPE_POINTER: patch_size: %d, saddr: %p, " + "*saddr: %lx, target: %lx, pid: %d\n", + patch_size, saddr, *((unsigned long*)saddr), + target_addr, current->pid); + } + else{ + printk(KERN_ERR MSG_HEAD + "Unknown ptype: %d @arch_data_patch\n", ptype); + return -EFAULT; + } + + /* Backup orignal data */ + __READ_MEM(kpi->target_task, target_addr, backup_addr, patch_size, + kpi->exec_id, "(backup data)\n", + ret = -EFAULT; return ret); + + /* Write new data */ + __WRITE_MEM(kpi->target_task, target_addr, saddr, patch_size, + kpi->exec_id, "(write data)\n", + ret = -EFAULT; return ret); + + return 0; +} + +static int arch_activate(struct kaho_patch_info* kpi, int i) +{ + int ret = -EINVAL; + struct kaho_patch_item* pitem = &(kpi->items[i]); + + if (pitem->patch_type == KAHO_PTYPE_FUNC) + ret = arch_func_patch(kpi, i); + else if (pitem->patch_type == KAHO_PTYPE_VAR || + pitem->patch_type == KAHO_PTYPE_POINTER) + ret = arch_data_patch(kpi, i, pitem->patch_type); + + return ret; +} + +static int arch_deactivate(struct kaho_patch_info* kpi, + unsigned long dst, unsigned char* src, int len, + int no) +{ + return arch_overwrite_code(kpi, no, dst, src, len); +} +#endif + +/* ------------------------------------------------------------------ + * Functions for handle management + * ---------------------------------------------------------------- */ +static void init_handle_tbl(void) +{ + int i; + for (i = 0; i < KAHO_MAX_NR_PATCH; i++) + handle_tbl[i] = HANDLE_NOT_USED; +} + +static int get_handle(void) +{ + static int prev_assign_handle = 0; + int i; + int handle = 0; + + spin_lock(&handle_tbl_lock); + for (i = prev_assign_handle + 1; i <= KAHO_MAX_NR_PATCH; i++) { + if (handle_tbl[i-1] == HANDLE_NOT_USED) { + handle = i; + break; + } + } + if (handle <= 0) { + for (i = 1; i <= prev_assign_handle; i++) { + if (handle_tbl[i-1] == HANDLE_NOT_USED) { + handle = i; + break; + } + } + } + + if (handle > 0) { + /* if available handle is detected */ + prev_assign_handle = handle; + handle_tbl[handle-1] = HANDLE_USED; + } + spin_unlock(&handle_tbl_lock); + + return handle; +} + +static void put_handle(int handle) +{ + spin_lock(&handle_tbl_lock); + handle_tbl[handle-1] = HANDLE_NOT_USED; + spin_unlock(&handle_tbl_lock); +} + +/* ------------------------------------------------------------------ + * State check functions + * ---------------------------------------------------------------- */ +static int chk_stat_map_ope_alloc(int stat) +{ + if (stat == KAHO_STATE_ENTRY) + return 1; + if (stat == KAHO_STATE_MAPPED) + return 1; + if (stat == KAHO_STATE_UNMAPPED) + return 1; + return 0; +} + +static int chk_stat_map_ope_free(int stat) +{ + if (stat == KAHO_STATE_MAPPED) + return 1; + if (stat == KAHO_STATE_UNMAPPED) + return 1; + if (stat == KAHO_STATE_LOADED) + return 1; + if (stat == KAHO_STATE_DEACTIVE) + return 1; + return 0; +} + +static int chk_stat_load(int stat) +{ + if (stat == KAHO_STATE_MAPPED) + return 1; + if (stat == KAHO_STATE_UNMAPPED) + return 1; + if (stat == KAHO_STATE_LOADED) + return 1; + return 0; +} + +static int chk_stat_quiesce_main_thr(int stat) +{ + if (stat == KAHO_STATE_MAPPING) + return 1; + if (stat == KAHO_STATE_UNMAPPING) + return 1; + return 0; +} + +static int chk_stat_activate(int stat) +{ + if (stat == KAHO_STATE_LOADED) + return 1; + if (stat == KAHO_STATE_DEACTIVE) + return 1; + return 0; +} + +static int chk_stat_deactivate(int stat) +{ + if (stat == KAHO_STATE_ACTIVE) + return 1; + return 0; +} + +static int chk_stat_del_patch(int stat) +{ + if (stat == KAHO_STATE_ENTRY) + return 1; + if (stat == KAHO_STATE_MAPPED) + return 1; + if (stat == KAHO_STATE_UNMAPPED) + return 1; + if (stat == KAHO_STATE_LOADED) + return 1; + if (stat == KAHO_STATE_DEACTIVE) + return 1; + if (stat == KAHO_STATE_ERROR) + return 1; + return 0; +} + +/* ------------------------------------------------------------------ + * Utility functions + * ---------------------------------------------------------------- */ +static struct kaho_patch_info* get_kpi(int handle) +{ + struct kaho_patch_info* kpi; + + rcu_read_lock(); + list_for_each_entry_rcu(kpi, &kaho_patch_list_head, list) { + if (kpi->handle == handle) { + atomic_inc(&kpi->used); + rcu_read_unlock(); + down(&kpi->sem); + return kpi; + } + } + rcu_read_unlock(); + + return NULL; +} + +static void put_kpi(struct kaho_patch_info* kpi) +{ + up(&kpi->sem); + atomic_dec(&kpi->used); +} + +#ifdef CONFIG_KAHO_RT +static void free_ktl(struct rcu_head* head) +{ + struct kaho_thr_list* ktl; + ktl = container_of(head, struct kaho_thr_list, rcu_head); + kfree(ktl); +} +#endif + +static void kaho_timer(unsigned long arg) +{ + struct semaphore* sem = (struct semaphore*)arg; + up(sem); +} + +static void kaho_sleep(unsigned int ms) +{ + struct semaphore sem; + struct timer_list timer; + init_MUTEX_LOCKED(&sem); + setup_timer(&timer, kaho_timer, (unsigned long)&sem); + timer.expires = jiffies+(ms*HZ/1000); + add_timer(&timer); + down(&sem); + +} + +static void free_kpi(struct kaho_patch_info* kpi, int thr, int skip_detach) +{ + int i; + DPRINT("free_kpi (entry)\n"); + /* + * kpi->used does not increase, because this function must be called + * after the kpi was deleted from the list. + */ + synchronize_rcu(); + while (atomic_read(&kpi->used) > thr) { + + /* up semaphore if necessary */ + if (down_trylock(&kpi->mem_ope_sem) != 0) { + DPRINT("cancel: Mem allocation\n"); + kpi->canceled = CANCELED; + up(&kpi->mem_ope_sem); + } +#ifdef CONFIG_KAHO_RT + else if (down_trylock(&kpi->rt_sem) != 0) { + DPRINT("cancel: RT activation\n"); + kpi->canceled = CANCELED; + up(&kpi->rt_sem); + } +#endif + /* waiting for a while */ + kaho_sleep(10); + } + + if (!skip_detach) { + if (utrace_detach(kpi->target_task, kpi->engine) < 0) + printk(KERN_ERR MSG_HEAD "Failed to utrace_detach()\n"); + } + put_handle(kpi->handle); + atomic_dec(&nr_patch); + + /* + * Free buffer for var. patch + */ + for (i = 0; i < kpi->nr_sub_patch; i++ ) { + if (kpi->items[i].patch_type == KAHO_PTYPE_VAR){ + void* addr = (void*)kpi->items[i].patch_addr; + if (addr != NULL){ + DPRINT("free buf for var. patch: %p\n", addr); + kfree(addr); + } + } + } + + /* + * Free buffer for descriptions of sub patches. + */ + kfree(kpi->items); + +#ifdef CONFIG_KAHO_RT + if (!list_empty(&kpi->chk_list)) + panic("list_empty(&kpi->chk_list) is not empty"); + if (!list_empty(&kpi->thr_list)) + panic("list_empty(&kpi->thr_list) is not empty"); +#endif + kfree(kpi); + DPRINT("free_kpi (exit)\n"); +} + +static int valid_patch_type(int patch_type) +{ + if (patch_type == KAHO_PTYPE_FUNC) + return 0; + if (patch_type == KAHO_PTYPE_VAR) + return 0; + if (patch_type == KAHO_PTYPE_POINTER) + return 0; + if (patch_type == KAHO_PTYPE_NONE) + return 0; + return -1; +} + +/* ------------------------------------------------------------------ + * Handlers of UTRACE for target process + * ---------------------------------------------------------------- */ +static void kaho_prepare_exit(struct utrace_attached_engine *engine, + struct task_struct *tsk, int skip_detach) +{ + struct kaho_patch_info* kpi; + struct list_head *pos, *next; + + DPRINT("kaho_prepare_exit (entry)\n"); + spin_lock(&kaho_patch_list_lock); + list_for_each_safe_rcu(pos, next, &kaho_patch_list_head) { + kpi = list_entry(pos, struct kaho_patch_info, list); + if (engine == kpi->engine) { + list_del_rcu(&kpi->list); + spin_unlock(&kaho_patch_list_lock); + free_kpi(kpi, 0, skip_detach); + DPRINT("kaho_prepare_exit (exit) w/ free_kpi\n"); + return; + } + } + spin_unlock(&kaho_patch_list_lock); + + panic("kaho_prepare_exit: kpi was not found, task: %p, engine: %p\n", + tsk, engine); +} + +static void kaho_report_reap(struct utrace_attached_engine *engine, + struct task_struct *tsk) +{ + DPRINT("kaho_report_reap (entry)\n"); + kaho_prepare_exit(engine, tsk, 1); + DPRINT("kaho_report_reap (exit)\n"); +} + +static u32 kaho_report_exec(struct utrace_attached_engine *engine, + struct task_struct *tsk, + const struct linux_binprm *bprm, + struct pt_regs *regs) +{ + + DPRINT("kaho_report_exec (entry)\n"); + kaho_prepare_exit(engine, tsk, 0); + DPRINT("kaho_report_exec (exit)\n"); + return 0; +} + +static void kaho_mem_timer(unsigned long arg) +{ + struct kaho_patch_info* kpi = (struct kaho_patch_info*)arg; + if (kpi->canceled == CANCELED) { + atomic_dec(&kpi->used); + return; + } + + if ((kpi->nvcsw != kpi->target_task->nvcsw) || + (kpi->nivcsw != kpi->target_task->nivcsw) ){ + up(&kpi->mem_ope_sem); + atomic_dec(&kpi->used); + } + else { + setup_timer(&kpi->timer, kaho_mem_timer, (unsigned long)kpi); + kpi->timer.expires = jiffies+(50*HZ/1000); + add_timer(&kpi->timer); + } +} + +static void kaho_report_quiesce_mem_ope(struct kaho_patch_info* kpi) +{ + struct kaho_map_struct* kms; + + /* memory allocation or free */ + kms = kpi->kms; + down_write(¤t->mm->mmap_sem); + if (atomic_read(&kpi->state) == KAHO_STATE_MAPPING) { + kms->act_addr = do_mmap(kms->filp, kms->addr, kms->length, + kms->prot, kms->flag, kms->offset); + } else { + kms->act_addr = do_munmap(current->mm, kms->addr, kms->length); + } + up_write(¤t->mm->mmap_sem); + + /* wake up the mem alloc ioctl after switing of the current task*/ + atomic_inc(&kpi->used); + kpi->nvcsw = current->nvcsw; + kpi->nivcsw = current->nivcsw; + setup_timer(&kpi->timer, kaho_mem_timer, (unsigned long)kpi); + kpi->timer.expires = jiffies+(10*HZ/1000); + add_timer(&kpi->timer); +} + +static u32 kaho_report_quiesce(struct utrace_attached_engine *engine, + struct task_struct *tsk) +{ + int stat; + unsigned long flags = 0; + struct kaho_patch_info* kpi; + DPRINT("kaho_report_quiesce (entry)\n"); + + /* engine and kpi should be exist because this routine must be + called with kpi->used > 0 */ + kpi = (struct kaho_patch_info*)engine->data; + stat = atomic_read(&kpi->state); + if (!chk_stat_quiesce_main_thr(stat)) + panic("kaho_report_quiesce: unintened call: %d\n", stat); + + kaho_report_quiesce_mem_ope(kpi); + + flags = engine->flags; + flags &= ~UTRACE_ACTION_QUIESCE; + flags &= ~UTRACE_EVENT(QUIESCE); + if (utrace_set_flags(kpi->target_task, kpi->engine, flags) < 0) { + printk(KERN_ERR MSG_HEAD + "Failed to set utrace flags at kaho_report_quiesce\n"); + } + DPRINT("kaho_report_quiesce (exit)\n"); + return 0; +} + +static const struct utrace_engine_ops kaho_utrace_ops = +{ + .report_exec = kaho_report_exec, + .report_quiesce = kaho_report_quiesce, + .report_reap = kaho_report_reap, +}; + +/* ------------------------------------------------------------------ + * I/O control functions + * ---------------------------------------------------------------- */ +static int ioctl_make_patch(void __user *uaddr) +{ + int i, nr_used; + int handle = 0; + unsigned long flags = 0; + struct kaho_make_patch mp; + struct kaho_patch_info* kpi; + int ret = 0; + + DPRINT("ioctl_make_patch (entry)\n"); + /* Get the request data */ + if (copy_from_user(&mp, uaddr, sizeof(mp)) != 0) { + printk(KERN_ERR MSG_HEAD "Failed to copy mp\n"); + ret = -EFAULT; + goto out; + } + + /* Parameter check */ + if (mp.nr_sub_patch <= 0 || mp.nr_sub_patch > KAHO_MAX_NR_SUB) { + printk(KERN_INFO MSG_HEAD + "sub patch id is illegal: %d\n", mp.nr_sub_patch); + ret = -EINVAL; + goto out; + } + + /* Check the number of available handle */ + spin_lock(&kaho_patch_list_lock); + nr_used = atomic_read(&nr_patch); + if (nr_used >= KAHO_MAX_NR_PATCH) { + ret = -ENOSPC; + spin_unlock(&kaho_patch_list_lock); + printk(KERN_INFO MSG_HEAD + "The number of patches reaches maximum: %d\n", nr_used); + goto out; + } + + /* Find the available handle + (Note: The number of handle beings from 1) */ + handle = get_handle(); + if (handle <= 0) { + ret = -EFAULT; + spin_unlock(&kaho_patch_list_lock); + printk(KERN_ERR MSG_HEAD "Finding handle was failed\n"); + goto out; + } + atomic_inc(&nr_patch); + spin_unlock(&kaho_patch_list_lock); + + /* Allocate memory */ + kpi = kmalloc(sizeof(struct kaho_patch_info), GFP_KERNEL); + if (kpi == NULL) { + printk(KERN_ERR MSG_HEAD + "Failed to kmalloc() for kaho_patch_info \n"); + ret = -EFAULT; + goto err0; + } + + /* Allocate memory for sub patches */ + kpi->items = kmalloc(mp.nr_sub_patch*sizeof(struct kaho_patch_item), + GFP_KERNEL); + if (kpi->items == NULL) { + printk(KERN_ERR MSG_HEAD + "Failed to kmalloc() for kaho_patch_item\n"); + ret = -EFAULT; + goto err1; + } + + /* Initialize members */ + atomic_set(&kpi->state, KAHO_STATE_ENTRY); + atomic_set(&kpi->used, 0); + init_MUTEX(&kpi->sem); + kpi->handle = handle; + kpi->nr_sub_patch = mp.nr_sub_patch; + kpi->kms = NULL; + init_MUTEX(&kpi->mem_ope_sem); + + for (i = 0; i < kpi->nr_sub_patch; i++) { + kpi->items[i].patch_type = KAHO_PTYPE_UNKNOWN; + kpi->items[i].patch_addr = (unsigned long)NULL; + kpi->items[i].flags = 0; + } + kpi->canceled = !CANCELED; + +#ifdef CONFIG_KAHO_RT + INIT_LIST_HEAD(&kpi->chk_list); + INIT_LIST_HEAD(&kpi->thr_list); + spin_lock_init(&kpi->list_lock); + kpi->jmp_code_size = -1; + init_MUTEX(&kpi->rt_sem); +#endif + + /* Get task_struct of the target */ + read_lock(&tasklist_lock); + kpi->target_task = find_task_by_pid(mp.target_pid); + if (kpi->target_task == NULL) { + read_unlock(&tasklist_lock); + printk(KERN_INFO MSG_HEAD + "Failed to get the target with pid %d\n", mp.target_pid); + ret = -ENOENT; + goto err1a; + } + get_task_struct(kpi->target_task); + read_unlock(&tasklist_lock); + + /* init */ + kpi->exec_id = kpi->target_task->self_exec_id; + + /* check status of the target */ + if (kpi->target_task->exit_state) { + printk(KERN_INFO MSG_HEAD + "Target task is now exiting: %ld\n", + (long)kpi->target_task->exit_state); + ret = -EINVAL; + goto err1b; + } + + /* add list with confirmation again */ + atomic_inc(&kpi->used); + down(&kpi->sem); + spin_lock(&kaho_patch_list_lock); + list_add_rcu(&kpi->list, &kaho_patch_list_head); + spin_unlock(&kaho_patch_list_lock); + + /* Attach to the target */ + kpi->engine = utrace_attach(kpi->target_task, UTRACE_ATTACH_CREATE, + &kaho_utrace_ops, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) + (unsigned long) +#endif + kpi); + + + if (IS_ERR(kpi->engine) || kpi->engine == NULL){ + printk(KERN_ERR MSG_HEAD "Failed to utrace_attach\n"); + /* delete kpi from the list */ + spin_lock(&kaho_patch_list_lock); + list_del_rcu(&kpi->list); + spin_unlock(&kaho_patch_list_lock); + ret = -EFAULT; + goto err2; + } + + flags |= UTRACE_EVENT(EXEC); + flags |= UTRACE_EVENT(REAP); + if (utrace_set_flags(kpi->target_task, kpi->engine, flags) < 0) { + printk(KERN_ERR MSG_HEAD "Failed to set utrace flags\n"); + if (utrace_detach(kpi->target_task, kpi->engine) < 0) + printk(KERN_ERR MSG_HEAD "Failed to utrace_detach()\n"); + + /* delete kpi from the list */ + spin_lock(&kaho_patch_list_lock); + list_del_rcu(&kpi->list); + spin_unlock(&kaho_patch_list_lock); + ret = -EFAULT; + goto err2; + } + + put_kpi(kpi); + put_task_struct(kpi->target_task); + + /* return the handle to user application */ + mp.handle = handle; + if (copy_to_user(uaddr, &mp, sizeof(mp)) != 0) { + printk(KERN_ERR MSG_HEAD "Failed to copy mp\n"); + ret = -EFAULT; + goto err1a; + } + + DPRINT("ioctl_make_patch (exit)\n"); + return 0; + +err2: + put_kpi(kpi); +err1b: + put_task_struct(kpi->target_task); +err1a: + kfree(kpi->items); +err1: + kfree(kpi); +err0: + atomic_dec(&nr_patch); + put_handle(handle); +out: + DPRINT("ioctl_make_patch (exit:out0)\n"); + return ret; +} + +static int ioctl_map_ope(void __user *uaddr, int ope) +{ + unsigned long flags = 0; + struct kaho_map_opt kmo; + struct kaho_patch_info* kpi = NULL; + int ret = 0; + int st; + struct file *filp = NULL; + struct kaho_map_struct* kms; + + DPRINT("ioctl_map_patch (entry)\n"); + /* Get the argument */ + if (copy_from_user(&kmo, uaddr, sizeof(kmo)) != 0) { + printk(KERN_ERR MSG_HEAD "Failed to copy kmo\n"); + ret = -EFAULT; + goto err0; + } + + /* Get memory for memory allocation */ + kms = kmalloc(sizeof(struct kaho_map_struct), GFP_KERNEL); + if (kms == NULL) { + printk(KERN_ERR MSG_HEAD + "Failed to kmolloc() for kaho_map_patch_struct\n"); + ret = -EFAULT; + goto err0; + } + + /* Open the patch file */ + if (kmo.filename != NULL) { + filp = filp_open( kmo.filename, O_RDONLY, 0); + if (IS_ERR(filp)) { + ret = -ENOENT; + goto err1; + } + } + + /* Search the patch descripter */ + kpi = get_kpi(kmo.handle); + if (kpi == NULL) { + ret = -EINVAL; + goto err2; + } + + /* State check and setting new state */ + st = atomic_read(&kpi->state); + if (ope == OPE_MAP) { + if (!chk_stat_map_ope_alloc(st)) { + printk(KERN_INFO MSG_HEAD + "received map request " + "in the state: %d\n", st); + ret = -EPERM; + goto err3; + } + atomic_set(&kpi->state, KAHO_STATE_MAPPING); + } else { + if (!chk_stat_map_ope_free(st)) { + printk(KERN_INFO MSG_HEAD + "Unmap request received in the state: %d\n", st); + ret = -EPERM; + goto err3; + } + atomic_set(&kpi->state, KAHO_STATE_UNMAPPING); + } + + /* Copy parameters */ + kms->filp = filp; + kms->addr = kmo.addr; + kms->length = kmo.length; + kms->offset = kmo.offset; + kms->prot = kmo.prot; + kms->flag = kmo.flag; + init_MUTEX_LOCKED(&kpi->mem_ope_sem); + kpi->kms = kms; + //printk(KERN_ERR "filp: %p, addr: %lx, len: %lx, off: %lx, prot: %lx, + + /* Prepare operation for on the application context */ + flags = kpi->engine->flags; + flags |= UTRACE_ACTION_QUIESCE; + flags |= UTRACE_EVENT(QUIESCE); + if (utrace_set_flags(kpi->target_task, kpi->engine, flags) < 0) { + printk(KERN_ERR MSG_HEAD "Failed to set utrace flags\n"); + ret = -EFAULT; + goto err3; + } + + /* Sleep until the memory be obtained */ + down(&kpi->mem_ope_sem); + if( kpi->canceled == CANCELED ){ + ret = -EINVAL; + goto err3; + } + + /* check the result */ + kmo.act_addr = kms->act_addr; + if (IS_ERR((void*)kmo.act_addr)) { + printk(KERN_ERR MSG_HEAD "Failed to mmap or unmmap\n"); + ret = kmo.act_addr; + goto err3; + } + + /* return the result to the user application */ + if (copy_to_user(uaddr, &kmo, sizeof(kmo)) != 0) { + printk(KERN_ERR MSG_HEAD "Failed to copy kmo\n"); + ret = -EFAULT; + goto err3; + } + + /* release the memory */ + kfree(kpi->kms); + kpi->kms = NULL; + + /* set current patch state */ + if (ope == OPE_MAP) + atomic_set(&kpi->state, KAHO_STATE_MAPPED); + else + atomic_set(&kpi->state, KAHO_STATE_UNMAPPED); + + put_kpi(kpi); + if (filp) + filp_close(filp, current->files); + + DPRINT("ioctl_map_patch (exit)\n"); + return 0; + +err3: + atomic_set(&kpi->state, KAHO_STATE_ERROR); + kpi->kms = NULL; + put_kpi(kpi); + +err2: + if (filp) + filp_close(filp, current->files); +err1: + if (kpi) + kfree(kms); +err0: + DPRINT("ioctl_mem_ope (exit:err0)\n"); + return ret; +} + +static int ioctl_load_patch(void __user *uaddr) +{ + int ret = 0; + struct kaho_load_patch klp; + struct kaho_patch_info* kpi = NULL; + struct kaho_patch_item* pitem; + struct kaho_patch_item pitem_none; + int st; + + DPRINT("ioctl_load_patch (entry)\n"); + /* Get the arguments */ + if (copy_from_user(&klp, uaddr, sizeof(klp)) != 0) { + printk(KERN_ERR MSG_HEAD "Failed to copy klp\n"); + ret = -EFAULT; + goto err0; + } + + /* Search from the patch */ + kpi = get_kpi(klp.handle); + if (kpi == NULL) { + ret = -EINVAL; + goto err0; + } + + /* Check if arguments are valid */ + if (valid_patch_type(klp.patch_type) < 0) { + printk(KERN_ERR MSG_HEAD + "Unknown patch type: %d\n", klp.patch_type); + ret = -EINVAL; + goto err1; + } + + /* Check if parameter is correct */ + if (klp.patch_type != KAHO_PTYPE_NONE) { + if (klp.sub_patch_id <= 0 || + klp.sub_patch_id > kpi->nr_sub_patch) { + printk(KERN_INFO MSG_HEAD + "sub_patch_id id is illegal: %d\n", + klp.sub_patch_id); + ret = -EINVAL; + goto err1; + } + } + + /* State check and setting new state */ + st = atomic_read(&kpi->state); + if (!chk_stat_load(st) && + !(klp.patch_type == KAHO_PTYPE_VAR && st == KAHO_STATE_ENTRY)){ + + printk(KERN_INFO MSG_HEAD + "load patch request received in the state: %d\n", st); + ret = -EPERM; + if (st == KAHO_STATE_ACTIVE) + goto err0a; + goto err1; + } + atomic_set(&kpi->state, KAHO_STATE_LOADING); + + /* Copy data except KAHO_PTYPE_NONE */ + if (klp.patch_type != KAHO_PTYPE_NONE) + pitem = &(kpi->items[klp.sub_patch_id-1]); + else + pitem = &pitem_none; + pitem->patch_type = klp.patch_type; + pitem->target_addr = klp.target_addr; + pitem->patch_size = klp.patch_size; +#ifdef CONFIG_KAHO_RT + atomic_set(&pitem->backup_done, 0); +#endif + if (klp.patch_type == KAHO_PTYPE_VAR) { + /* We allocate buffer region which has twice the size of + patched data to store original data */ + pitem->patch_addr = (unsigned long) + kmalloc(2*klp.patch_size, GFP_KERNEL); + if ((void*)pitem->patch_addr == NULL) { + printk(KERN_ERR MSG_HEAD + "patch_addr is NULL, size: %ld\n", + 2*klp.patch_size); + ret = -EFAULT; + goto err1; + } + if (copy_from_user((void*)pitem->patch_addr, + (void*)klp.src_addr, + pitem->patch_size) != 0) { + printk(KERN_ERR MSG_HEAD "Failed to copy form user\n"); + kfree((void*)pitem->patch_addr); + ret = -EFAULT; + goto err1; + } + } + else + pitem->patch_addr = klp.patch_addr; + + if (klp.patch_type != KAHO_PTYPE_VAR && + pitem->patch_size > 0 && klp.src_addr != 0) + __WRITE_MEM(kpi->target_task, pitem->patch_addr, + (void*)klp.src_addr, pitem->patch_size, + kpi->exec_id, + "(ioctl_load_patch)\n", ret = -EINVAL; goto err1); + + /* return */ + if (klp.patch_type != KAHO_PTYPE_NONE) + atomic_set(&kpi->state, KAHO_STATE_LOADED); + else + atomic_set(&kpi->state, st); + put_kpi(kpi); + DPRINT("ioctl_load_patch (exit)\n"); + return ret; + +err1: + atomic_set(&kpi->state, KAHO_STATE_ERROR); +err0a: + put_kpi(kpi); +err0: + DPRINT("ioctl_load_patch (err.exit)\n"); + return ret; +} + +#ifdef CONFIG_KAHO_RT +static u32 kaho_report_quiesce_thr(struct utrace_attached_engine *engine, + struct task_struct *tsk); + +static void kaho_report_reap_thr(struct utrace_attached_engine *engine, + struct task_struct *tsk); + +static u32 kaho_report_clone_thr(struct utrace_attached_engine *engine, + struct task_struct *parent, + unsigned long clone_flags, + struct task_struct *child); + +static const struct utrace_engine_ops kaho_utrace_ops_chk_thr = +{ + .report_reap = kaho_report_reap_thr, + .report_clone = kaho_report_clone_thr, + .report_quiesce = kaho_report_quiesce_thr, +}; + +static struct kaho_thr_list* alloc_ktl(struct kaho_patch_info* kpi, + struct task_struct* task) +{ + struct kaho_thr_list* ktl; + int flags = 0; + int ret; + + DPRINT("alloc_ktl (entry): kpi: %p\n", kpi); + + ktl = kmalloc(sizeof(struct kaho_thr_list), GFP_KERNEL); + if (ktl == NULL) + return NULL; + + /* Initialize */ + ktl->task = task; + ktl->kpi = kpi; + atomic_set(&ktl->checked, THR_NO_MARK); + atomic_set(&ktl->used, 0); + ktl->sem_upped = -1; + + /* attach the thread */ + ktl->engine = utrace_attach(ktl->task, UTRACE_ATTACH_CREATE, + &kaho_utrace_ops_chk_thr, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) + (unsigned long) +#endif + ktl); + + if (ktl->engine == ERR_PTR(-ESRCH)) { + /* target thread is dead */ + atomic_set(&ktl->checked, THR_CHK_ERR); + goto out; + } + else if (IS_ERR(ktl->engine) || ktl->engine == NULL){ + printk(KERN_ERR MSG_HEAD "Failed to utrace_attach\n"); + goto err; + } + + /* set events */ + flags |= UTRACE_EVENT(CLONE); + flags |= UTRACE_EVENT(REAP); + //DPRINT("ktl: %p, pid: %d\n", ktl, task->pid); + //DPRINT("task: %p, engine: %p, flags: %08x\n", ktl->task, ktl->engine, flags); + ret = utrace_set_flags(ktl->task, ktl->engine, flags); + if (ret == -EALREADY){ + /* target thread is dead */ + atomic_set(&ktl->checked, THR_CHK_ERR); + DPRINT("safety_check_each_thr: EALREADY\n"); + } + else if (ret < 0) { + atomic_set(&ktl->checked, THR_CHK_ERR); + printk(KERN_ERR MSG_HEAD "Failed to set utrace flags " + "(alloc_ktl)\n"); + if (utrace_detach(ktl->task, ktl->engine) < 0) { + printk(KERN_ERR MSG_HEAD + "Failed to utrace_detach (alloc_ktl)\n"); + } + goto err; + } +out: + DPRINT("alloc_ktl (exit)\n"); + return ktl; + +err: + kfree(ktl); + return NULL; +} + +static u32 kaho_report_clone_thr(struct utrace_attached_engine *engine, + struct task_struct *parent, + unsigned long clone_flags, + struct task_struct *child) +{ + int found = 0; + struct kaho_thr_list* ktl = (struct kaho_thr_list*)engine->data; + struct kaho_patch_info* kpi = ktl->kpi; + struct list_head *pos, *next; + + DPRINT("kaho_report_clone_thr (entry)\n"); + /* check if ktl is including in the thread list */ + spin_lock(&kpi->list_lock); + list_for_each_safe_rcu(pos, next, &kpi->thr_list) { + struct kaho_thr_list* _ktl; + _ktl = list_entry(pos, struct kaho_thr_list, thr_list); + if (ktl == _ktl){ + atomic_inc(&ktl->used); + found = 1; + } + } + if (found == 0) { + /* This condition may cause when another CPU runs + delete_thread_list() at the same time */ + spin_unlock(&kpi->list_lock); + return 0; + } + spin_unlock(&kpi->list_lock); + + /* Thread: the new thread is added to thr_list */ + if (clone_flags & CLONE_THREAD) { + struct kaho_thr_list* new_ktl = alloc_ktl(kpi, child); + if (new_ktl != NULL) { + if (atomic_read(&new_ktl->checked) == THR_CHK_ERR) { + kfree(new_ktl); + } + else { + spin_lock(&kpi->list_lock); + list_add(&new_ktl->thr_list, &kpi->thr_list); + spin_unlock(&kpi->list_lock); + } + } + else { + printk(KERN_ERR MSG_HEAD "Failed to alloc_ktl\n"); + } + } + /* Process: check if code at target address has changed */ + else { + int i, ret; + int diff = 0; + unsigned char buf[trap_code_size]; + struct kaho_patch_item* pitem = &(kpi->items[0]); + + /* If backup_done is 0, preparation of Copy-On-Write has + completed before implant of int3. That is, the child + process does not have code that includes int3. */ + if (atomic_read(&pitem->backup_done) == 0) + goto out; + + /* check the code at target address */ + __READ_MEM(child, pitem->target_addr, buf, trap_code_size, + kpi->exec_id, "(kaho_report_clone_thr:read)\n", + goto err); + for (i = 0; i < trap_code_size; i++) { + if (buf[i] != pitem->backup[i]) { + diff = 1; + break; + } + } + /* rewrite if the code has been changed */ + if (diff) { + DPRINT("The code at target address is different\n"); + __WRITE_MEM(child, pitem->target_addr, pitem->backup, + trap_code_size, kpi->exec_id, + "(kaho_report_clone_thr:write)\n", + goto err); + } + else + DPRINT("The code at target address is the same\n"); + } +out: + atomic_dec(&ktl->used); + DPRINT("kaho_report_clone_thr (exit)\n"); + return 0; +err: + atomic_dec(&ktl->used); + return -EFAULT; +} + +static void delete_chk_list(struct kaho_thr_list* ktl) +{ + struct list_head *pos, *next; + struct kaho_thr_list* __ktl; + list_for_each_safe_rcu(pos, next, &ktl->kpi->chk_list) { + __ktl = list_entry(pos, struct kaho_thr_list, chk_list); + if (__ktl == ktl) { + list_del_rcu(&ktl->chk_list); + break; + } + } +} + +static void __kaho_report_reap_thr(struct kaho_thr_list* ktl) +{ + int st = atomic_read(&ktl->kpi->state); + if (st == KAHO_STATE_IN_ACT_RT2 || st == KAHO_STATE_IN_DEACT_RT2) { + if (ktl->sem_upped == 0) { + up(&ktl->kpi->rt_sem); + ktl->sem_upped = 1; + DPRINT("rt_sem was upped in __kaho_report_reap_thr\n"); + } + if (ktl->sem_upped >= 0) + delete_chk_list(ktl); + } + list_del(&ktl->thr_list); + call_rcu(&ktl->rcu_head, free_ktl); +} + +static void kaho_report_reap_thr(struct utrace_attached_engine *engine, + struct task_struct *tsk) +{ + struct list_head *pos, *next; + struct kaho_thr_list* ktl = (struct kaho_thr_list*)engine->data; + struct kaho_patch_info* kpi = ktl->kpi; + + DPRINT("kaho_report_reap_thr (entry)\n"); + + spin_lock(&kpi->list_lock); + list_for_each_safe_rcu(pos, next, &kpi->thr_list) { + struct kaho_thr_list* _ktl; + _ktl = list_entry(pos, struct kaho_thr_list, thr_list); + if (_ktl == ktl) { + __kaho_report_reap_thr(ktl); + spin_unlock(&kpi->list_lock); + return; + } + } + spin_unlock(&kpi->list_lock); + DPRINT("kaho_report_reap_thr (exit)\n"); +} + +static int ktl_contain(struct kaho_patch_info* kpi, struct task_struct* task) +{ + struct kaho_thr_list* ktl; + spin_lock(&kpi->list_lock); + list_for_each_entry(ktl, &kpi->thr_list, thr_list) { + if (ktl->task == task) { + spin_unlock(&kpi->list_lock); + return 1; + } + } + spin_unlock(&kpi->list_lock); + return 0; +} + +static int make_thread_list(struct kaho_patch_info* kpi) +{ + struct task_struct* task; + struct kaho_thr_list *ktl, *n; + DPRINT("make_thread_list (entry)\n"); +loop: + /* loop for all threads in this process */ + read_lock(&tasklist_lock); + task = next_thread(kpi->target_task); + do { + if (!ktl_contain(kpi, task)) { + DPRINT("pid: %d\n", task->pid); + get_task_struct(task); + read_unlock(&tasklist_lock); + /* get allocated ktl that has utrace engine that + is attaching to the thread */ + ktl = alloc_ktl(kpi, task); + put_task_struct(task); + if (ktl == NULL) + return -EFAULT; + /* add ktl to thread list */ + spin_lock(&kpi->list_lock); + list_add(&ktl->thr_list, &kpi->thr_list); + spin_unlock(&kpi->list_lock); + goto loop; + } + /* get next thread */ + task = next_thread(task); + } while (task != next_thread(kpi->target_task)); + read_unlock(&tasklist_lock); + + /* delete dead tasks */ + spin_lock(&kpi->list_lock); + list_for_each_entry_safe(ktl, n, &kpi->thr_list, thr_list) { + if (atomic_read(&ktl->checked) == THR_CHK_ERR) { + list_del(&ktl->thr_list); + kfree(ktl); + } + } + spin_unlock(&kpi->list_lock); + + DPRINT("make_thread_list (exit)\n"); + return 0; +} + +static void delete_thread_list(struct kaho_patch_info* kpi) +{ + struct kaho_thr_list* ktl; + struct list_head *pos, *next; + int used; +top: + used = 0; + spin_lock(&kpi->list_lock); + list_for_each_safe_rcu(pos, next, &kpi->thr_list) { + ktl = list_entry(pos, struct kaho_thr_list, thr_list); + DPRINT("[del] ktl: %p, target: %p, utrace: %p\n", ktl, ktl->task, ktl->task->utrace); + + /* If the ktl is using by kaho_report_clone_thr(), + kaho_report_quiesce_thr(), or, kaho_report_reap_thr(), + deletion of the ktl is put off */ + if (atomic_read(&ktl->used) > 0){ + used++; + continue; + } + + /* detach, delete from list, and, free it */ + if (utrace_detach(ktl->task, ktl->engine) < 0) { + printk(KERN_ERR MSG_HEAD + "Failed to utrace_detach (delete_thread_list)\n"); + } + delete_chk_list(ktl); + list_del_rcu(&ktl->thr_list); + call_rcu(&ktl->rcu_head, free_ktl); + } + spin_unlock(&kpi->list_lock); + + if (used > 0) { + /* waiting for a while */ + kaho_sleep(10); + goto top; + } +} + +static int safety_check(struct kaho_patch_info* kpi); + +static int pre_proc_act_rt(struct kaho_patch_info* kpi, + struct kaho_std_opt* kso, int op) +{ + int ret, bkup; + struct kaho_patch_item* pi; + + DPRINT("pre_proc_act_rt (entry)\n"); + + /* Check parameters */ + pi = &(kpi->items[0]); + if (op == REQ_ACT_RT) { + pi->flags |= KAHO_RT_PATCH; + if (kpi->nr_sub_patch != 1) { + printk(KERN_INFO MSG_HEAD + "# of sub patches must be one in RT mode.\n"); + ret = -EPERM; + goto err1; + } + + if (pi->patch_type != KAHO_PTYPE_FUNC) { + printk(KERN_INFO MSG_HEAD + "Only FUNC patch is supported in RT mode.\n"); + ret = -EPERM; + goto err1; + } + + /* Set jump code size for backup */ + kpi->jmp_code_size = arch_get_jump_code_size(kpi, 0); + } + + /* make check sheet */ + ret = make_thread_list(kpi); + if (ret < 0) { + printk(KERN_ERR MSG_HEAD "Failed to make_thread_list\n"); + ret = -EFAULT; + goto err1; + } + + /* add to list */ + spin_lock(&kaho_trap_list_lock); + list_add_rcu(&kpi->trap_list, &kaho_trap_list_head); + spin_unlock(&kaho_trap_list_lock); + + if (op == REQ_ACT_RT){ + bkup = CODE_BKUP; + atomic_set(&kpi->state, KAHO_STATE_IN_ACT_RT1); + } + else { + bkup = NOT_CODE_BKUP; + atomic_set(&kpi->state, KAHO_STATE_IN_DEACT_RT1); + } + + /* Implant trap instruction */ + ret = arch_implant_trap(kpi, 0, bkup); + if (ret < 0) { + printk(KERN_ERR MSG_HEAD "Failed to implant trap code\n"); + ret = -EFAULT; + goto err2; + } + + /* Safety check */ + ret = safety_check(kpi); + if (ret < 0) { + printk(KERN_ERR MSG_HEAD "Failed to safety check\n"); + ret = -EFAULT; + goto err3; + } + + DPRINT("pre_proc_act_rt (exit)\n"); + return 0; + +err3: + /* Restore original instruction instead of trap */ + if (arch_implant_trap(kpi, 0, CODE_RESTORE) < 0) + printk(KERN_ERR MSG_HEAD "Failed to restore original code\n"); +err2: + /* delete trap list */ + spin_lock(&kaho_trap_list_lock); + list_del_rcu(&kpi->trap_list); + spin_unlock(&kaho_trap_list_lock); +err1: + DPRINT("pre_proc_act_rt (exit:err0)\n"); + return ret; +} + +static int post_proc_act_rt(struct kaho_patch_info* kpi) +{ + /* delete this kpi from trap list */ + spin_lock(&kaho_trap_list_lock); + list_del_rcu(&kpi->trap_list); + spin_unlock(&kaho_trap_list_lock); + return 0; +} +#endif + +static int __ioctl_activate(void __user *uaddr, int op) +{ + int i, ret, st; + struct kaho_std_opt kso; + struct kaho_patch_info* kpi; + struct kaho_patch_item* pitem; + + DPRINT("__ioctl_activate (entry)\n"); + /* Get the arguments */ + if (copy_from_user(&kso, uaddr, sizeof(kso)) != 0) { + ret = -EFAULT; + printk(KERN_ERR MSG_HEAD "Failed to copy kso\n"); + goto err0; + } + + /* Search kpi */ + kpi = get_kpi(kso.handle); + if (kpi == NULL) { + ret = -EINVAL; + goto err0; + } +#ifdef CONFIG_KAHO_RT + if (op == REQ_DEACT && (kpi->items[0].flags & KAHO_RT_PATCH)) + op = REQ_DEACT_RT; +#endif + /* State check and setting new state */ + st = atomic_read(&kpi->state); + do { + if (op == REQ_ACT) { + if (chk_stat_activate(st)) { + atomic_set(&kpi->state, KAHO_STATE_IN_ACT); + break; + } + } +#ifdef CONFIG_KAHO_RT + else if (op == REQ_ACT_RT) { + if (chk_stat_activate(st)) { + atomic_set(&kpi->state, KAHO_STATE_IN_ACT_RT0); + break; + } + } + else if (op == REQ_DEACT_RT) { + if (chk_stat_deactivate(st)) { + atomic_set(&kpi->state, + KAHO_STATE_IN_DEACT_RT0); + break; + } + } +#endif + else if (op == REQ_DEACT) { + if (chk_stat_deactivate(st)) { + atomic_set(&kpi->state, KAHO_STATE_IN_DEACT); + break; + } + } + else + panic("ioctl_activate: unintended op: %d\n", op); + + printk(KERN_INFO MSG_HEAD "invalid state: op: %d, st:%d\n", + op, st); + ret = -EPERM; + if (st == KAHO_STATE_ACTIVE) + goto err0a; + goto err1; + } while (0); + + /* check parameters */ + for (i = 0; i < kpi->nr_sub_patch; i++) { + pitem = &(kpi->items[i]); + pitem->flags |= kso.flags; + if (valid_patch_type(pitem->patch_type) < 0) { + printk(KERN_ERR MSG_HEAD + "Unknown patch type: %d\n", pitem->patch_type); + ret = -EFAULT; + goto err1; + } + } + +#ifdef CONFIG_KAHO_RT + if (op == REQ_ACT_RT || op == REQ_DEACT_RT) { + ret = pre_proc_act_rt(kpi, &kso, op); + if (ret < 0) + goto err1; + } +#endif + /* Write code and data */ + for (i = 0; i < kpi->nr_sub_patch; i++) { + pitem = &(kpi->items[i]); + if (op == REQ_ACT || op == REQ_ACT_RT) { + ret = arch_activate(kpi, i); + } else { + int backup_len = 0; + unsigned char* src = NULL; + unsigned long dst = pitem->target_addr; + + if (pitem->patch_type == KAHO_PTYPE_FUNC) { + backup_len = arch_get_jump_code_size(kpi, i); + src = pitem->backup; + } + else if (pitem->patch_type == KAHO_PTYPE_VAR) { + backup_len = pitem->patch_size; + src = &((char*)pitem->patch_addr)[backup_len]; + } + else if (pitem->patch_type == KAHO_PTYPE_POINTER) { + backup_len = sizeof(void*); + src = pitem->backup; + } + ret = arch_deactivate(kpi, dst, src, backup_len, i); + } + if (ret < 0) + goto err1; + } + +#ifdef CONFIG_KAHO_RT + if (op == REQ_ACT_RT || op == REQ_DEACT_RT) + post_proc_act_rt(kpi); +#endif + /* set status and return */ + if (op == REQ_ACT) + atomic_set(&kpi->state, KAHO_STATE_ACTIVE); +#ifdef CONFIG_KAHO_RT + else if (op == REQ_ACT_RT) + atomic_set(&kpi->state, KAHO_STATE_ACTIVE); +#endif + else + atomic_set(&kpi->state, KAHO_STATE_DEACTIVE); + +#ifdef CONFIG_KAHO_RT + delete_thread_list(kpi); +#endif + + put_kpi(kpi); + DPRINT("__ioctl_activate (exit)\n"); + return 0; + +err1: + atomic_set(&kpi->state, KAHO_STATE_ERROR); +#ifdef CONFIG_KAHO_RT + /* delete thread list */ + delete_thread_list(kpi); +#endif + +err0a: + put_kpi(kpi); +err0: + DPRINT("__ioctl_activate (exit:err)\n"); + return ret; +} + +static int ioctl_del_patch(void __user *uaddr) +{ + int ret, st; + struct kaho_std_opt kso; + struct kaho_patch_info* kpi = NULL; + struct kaho_patch_info* _kpi = NULL; + struct kaho_patch_info* n; + + DPRINT("ioctl_del_patch (entry)\n"); + /* Get the arguments */ + if (copy_from_user(&kso, uaddr, sizeof(kso)) != 0) { + printk(KERN_ERR MSG_HEAD "Failed to copy kso\n"); + ret = -EFAULT; + goto err0; + } + + /* Search kpi */ + kpi = get_kpi(kso.handle); + if (kpi == NULL) { + ret = -EINVAL; + goto err0; + } + + /* State check and setting new state */ + st = atomic_read(&kpi->state); + if (!chk_stat_del_patch(st)) { + printk(KERN_INFO MSG_HEAD + "delete patch request received in the state: %d\n", st); + ret = -EPERM; + if (st == KAHO_STATE_ACTIVE) + goto err0a; + goto err1; + } + atomic_set(&kpi->state, KAHO_STATE_IN_DEL_PATCH); + + /* Delete Data structure */ + spin_lock(&kaho_patch_list_lock); + list_for_each_entry_safe(_kpi, n, &kaho_patch_list_head, list) { + if (kpi == _kpi) { + list_del_rcu(&kpi->list); + break; + } + } + spin_unlock(&kaho_patch_list_lock); + if (kpi != _kpi) + panic("Not found the requested kpi in the list.\n"); + + /* Deletion */ + up(&kpi->sem); + free_kpi(kpi, 1, 0); + + DPRINT("ioctl_del_patch (exit)\n"); + return 0; + +err1: + atomic_set(&kpi->state, KAHO_STATE_ERROR); +err0a: + put_kpi(kpi); +err0: + return ret; +} + +static int ioctl_get_info(void __user* uaddr) +{ + int ret; + struct kaho_get_info kgi; + struct kaho_patch_info* kpi = NULL; + struct kaho_patch_item* pitem; + + DPRINT("ioctl_get_info (entry)\n"); + /* Get the arguments */ + if (copy_from_user(&kgi, uaddr, sizeof(kgi)) != 0) { + printk(KERN_ERR MSG_HEAD "Failed to copy kgi\n"); + ret = -EFAULT; + goto err0; + } + + /* Is request for the first target? */ + if (kgi.handle == 0) { + if (atomic_read(&nr_patch) == 0) { + /* There is no data */ + kgi.pid = -1; + kgi.next_handle = 0; + goto ret; + } + rcu_read_lock(); + kpi = list_entry(kaho_patch_list_head.next, + struct kaho_patch_info, list); + atomic_inc(&kpi->used); + rcu_read_unlock(); + down(&kpi->sem); + kgi.sub_patch_id = 1; + } + else{ + /* Search kpi */ + kpi = get_kpi(kgi.handle); + if (kpi == NULL) { + ret = -EINVAL; + goto err0; + } + } + + /* Check parameter */ + if (kgi.sub_patch_id <= 0 || kgi.sub_patch_id > kpi->nr_sub_patch) { + printk(KERN_INFO MSG_HEAD "sub patch id is illegal: %d\n", + kgi.sub_patch_id); + ret = -EINVAL; + goto err1; + } + + /* copy data */ + kgi.handle = kpi->handle; + kgi.pid = kpi->target_task->pid; + kgi.nr_sub_patch = kpi->nr_sub_patch; + kgi.state = atomic_read(&kpi->state); + pitem = &(kpi->items[kgi.sub_patch_id-1]); + kgi.patch_type = pitem->patch_type; + kgi.patch_addr = pitem->patch_addr; + kgi.patch_size = pitem->patch_size; + kgi.target_addr = pitem->target_addr; + if (kpi->list.next == &kaho_patch_list_head) { + /* The last info */ + kgi.next_handle = 0; + } + else{ + struct kaho_patch_info* __k; + __k = list_entry(kpi->list.next, struct kaho_patch_info, list); + kgi.next_handle = __k->handle; + } + + put_kpi(kpi); +ret: + /* return the results to user application */ + if (copy_to_user(uaddr, &kgi, sizeof(kgi)) != 0) { + printk(KERN_ERR MSG_HEAD "Failed to copy kmo\n"); + ret = -EFAULT; + goto err1; + } + + DPRINT("ioctl_get_info (exit)\n"); + return 0; + +err1: + put_kpi(kpi); + +err0: + DPRINT("ioctl_get_info (err0)\n"); + return ret; +} + +#if 0 +static int ioctl_cancel(void __user* uaddr) +{ + int ret; + struct kaho_patch_info* kpi = NULL; + struct kaho_std_opt kso; + + /* Get the arguments */ + if (copy_from_user(&kso, uaddr, sizeof(kso)) != 0) { + printk(KERN_ERR MSG_HEAD "Failed to copy ka\n"); + ret = -EFAULT; + goto err0; + } + + /* Search kpi */ + kpi = get_kpi(kso.handle); + if (kpi == NULL) { + ret = -EINVAL; + goto err0; + } + + /* body */ + /*cancel(kpi);*/ + + put_kpi(kpi); + return 0; + +err0: + return ret; +} +#endif + +/* ------------------------------------------------------------------ + * I/O control entry + * ---------------------------------------------------------------- */ +static int kaho_ioctl( + struct inode *inode, struct file *file, u_int cmd, u_long arg) +{ + int ret = 0; + void __user *uaddr = (void __user*)arg; + + switch(cmd) { + case KAHOCTL_MAKE_PATCH: + ret = ioctl_make_patch(uaddr); + break; + case KAHOCTL_MAP_PATCH: + ret = ioctl_map_ope(uaddr, OPE_MAP); + break; + case KAHOCTL_LOAD_PATCH: + ret = ioctl_load_patch(uaddr); + break; + case KAHOCTL_ACTIVATE: + ret = __ioctl_activate(uaddr, REQ_ACT); + break; +#ifdef CONFIG_KAHO_RT + case KAHOCTL_ACTIVATE_RT: + ret = __ioctl_activate(uaddr, REQ_ACT_RT); + break; +#endif + case KAHOCTL_DEACTIVATE: + ret = __ioctl_activate(uaddr, REQ_DEACT); + break; + case KAHOCTL_UNMAP_PATCH: + ret = ioctl_map_ope(uaddr, OPE_UNMAP); + break; + case KAHOCTL_DEL_PATCH: + ret = ioctl_del_patch(uaddr); + break; + case KAHOCTL_GET_INFO: + ret = ioctl_get_info(uaddr); + break; + /*case KAHOCTL_CANCEL: + ret = ioctl_cancel(uaddr); + break;*/ + default: + printk(KERN_INFO MSG_HEAD "Unknown IOCTL: %x\n", (int)cmd); + ret = -ENOTTY; + } + return ret; +} + +static struct file_operations kaho_fops = { + .owner = THIS_MODULE, + .ioctl = kaho_ioctl, +}; + +#ifdef CONFIG_KAHO_RT +/* ------------------------------------------------------------------ + * Safety check functions + * ---------------------------------------------------------------- */ +static int quiesce_chk_thr_valid_state(int st) +{ + if (st == KAHO_STATE_IN_ACT_RT2) + return 1; + if (st == KAHO_STATE_IN_DEACT_RT2) + return 1; + return 0; +} + +static u32 kaho_report_quiesce_thr(struct utrace_attached_engine *engine, + struct task_struct *tsk) +{ + int st, found; + unsigned long pc, target_addr; + unsigned long flags = 0; + struct kaho_patch_info* kpi; + struct kaho_thr_list* ktl; + struct list_head *pos, *next; + DPRINT("kaho_report_quiesce_chk_thr (entry)\n"); + + /* engine and kpi should be exist because this routine must be + called with kpi->used > 0 */ + ktl = (struct kaho_thr_list*)engine->data; + kpi = ktl->kpi; + + /* We check if ktl is including in the list. Because ktl may have been + deleted by the kaho_report_reap_thr() or delete_thread_list() */ + spin_lock(&kpi->list_lock); + list_for_each_safe_rcu(pos, next, &kpi->thr_list) { + struct kaho_thr_list* _ktl; + _ktl = list_entry(pos, struct kaho_thr_list, thr_list); + if (ktl == _ktl) { + atomic_inc(&ktl->used); + found = 1; + } + } + if (found == 0) { + /* This condition may cause when another CPU runs + delete_thread_list() at the same time */ + spin_unlock(&kpi->list_lock); + return 0; + } + spin_unlock(&kpi->list_lock); + + /* If the thread has already executed trap code, + we do not have to check safety by the following */ + if (atomic_read(&ktl->checked) == THR_MARKED) + goto out; + + st = atomic_read(&kpi->state); + if (!quiesce_chk_thr_valid_state(st)) + panic("Unexpected state: %d (kaho_report_quiesce_chk_thr)\n", + st); + if (kpi->jmp_code_size < 0) + panic("jmp_code_size: %d\n", kpi->jmp_code_size); + + /* Check the value of program counter */ + target_addr = kpi->items[0].target_addr; + pc = arch_get_pc(task_pt_regs(ktl->task)); + DPRINT("pc[%d]: %lx\n", current->pid, pc); + if (pc <= target_addr || pc >= (target_addr + kpi->jmp_code_size)) + atomic_set(&ktl->checked, THR_MARKED); + else + DPRINT("Safety check: failed\n"); +out: + flags = engine->flags; + flags &= ~UTRACE_ACTION_QUIESCE; + flags &= ~UTRACE_EVENT(QUIESCE); + if (utrace_set_flags(ktl->task, engine, flags) < 0) { + printk(KERN_ERR MSG_HEAD + "Failed to set utrace flags at kaho_report_quiesce\n"); + atomic_set(&ktl->checked, THR_CHK_ERR); + } + up(&kpi->rt_sem); + ktl->sem_upped = 1; + atomic_dec(&ktl->used); + DPRINT("kaho_report_quiesce_chk_thr (exit)\n"); + return 0; +} + +static void safety_check_each_thr(struct kaho_patch_info* kpi, + struct kaho_thr_list* ktl) +{ + int ret; + unsigned long flags; + + DPRINT("safety_check_each_thr (entry)\n"); + + /* If the thread has already executed trap code, + we do not have to check by the following */ + if (atomic_read(&ktl->checked) == THR_MARKED) + goto out; + + /* Request to quiescent trace */ + flags = ktl->engine->flags; + flags |= UTRACE_ACTION_QUIESCE; + flags |= UTRACE_EVENT(QUIESCE); + ret = utrace_set_flags(ktl->task, ktl->engine, flags); + if (ret == -EALREADY){ + atomic_set(&ktl->checked, THR_MARKED); + DPRINT("safety_check_each_thr: EALREADY\n"); + goto out; + } + else if (ret < 0) { + printk(KERN_ERR MSG_HEAD + "Failed to set utrace flags at check_thr_safety\n"); + goto err; + } + + //DPRINT("safety_check_each_thr (exit): ret: %d, thread: %d\n", ret, ktl->task->pid); + DPRINT("safety_check_each_thr (exit): ret: %d\n", ret); + return; +err: + atomic_set(&ktl->checked, THR_CHK_ERR); +out: + spin_lock(&kpi->list_lock); + up(&kpi->rt_sem); + ktl->sem_upped = 1; + spin_unlock(&kpi->list_lock); + DPRINT("safety_check_each_thr (exit:out)\n"); +} + +static int safety_check(struct kaho_patch_info* kpi) +{ + struct kaho_thr_list* ktl; + struct list_head *pos, *next; + int nthr, st; + int loop_count = 0; + int err = 0; + + DPRINT("safety_check (entry)\n"); + + /* Copy thread list */ + spin_lock(&kpi->list_lock); + list_for_each_entry(ktl, &kpi->thr_list, thr_list) { + list_add_rcu(&ktl->chk_list, &kpi->chk_list); + ktl->sem_upped = 0; + } + spin_unlock(&kpi->list_lock); + +loop: + /* Set quiescent request */ + nthr = 0; + spin_lock(&kpi->list_lock); + + /* Count the number of threads */ + list_for_each_entry(ktl, &kpi->chk_list, chk_list) + nthr++; + /* Initialize the semaphore */ + sema_init(&kpi->rt_sem, 1-nthr); + + /* Set state */ + if (loop_count == 0) { + st = atomic_read(&kpi->state); + if (st == KAHO_STATE_IN_ACT_RT1) + atomic_set(&kpi->state, KAHO_STATE_IN_ACT_RT2); + else if (st == KAHO_STATE_IN_DEACT_RT1) + atomic_set(&kpi->state, KAHO_STATE_IN_DEACT_RT2); + else + panic("Unexpected state: %d (safety_check)\n", st); + } + + spin_unlock(&kpi->list_lock); + + /* Request quiescent to check address at which the thread runs */ + list_for_each_entry(ktl, &kpi->chk_list, chk_list) + safety_check_each_thr(kpi, ktl); + + /* Wait for finish of the safety check */ + down(&kpi->rt_sem); + + /* Delete elements of noexistent threads */ + nthr = 0; + spin_lock(&kpi->list_lock); + list_for_each_safe_rcu(pos, next, &kpi->chk_list) { + ktl = list_entry(pos, struct kaho_thr_list, chk_list); + if (atomic_read(&ktl->checked) == THR_NO_MARK) { + nthr++; + } + else if (atomic_read(&ktl->checked) == THR_MARKED || + atomic_read(&ktl->checked) == THR_ESRCH ) { + list_del_rcu(&ktl->chk_list); + } + else if (atomic_read(&ktl->checked) == THR_CHK_ERR) { + err = 1; + } + else { + panic("Unexpected ktl->checked: %d\n", + atomic_read(&ktl->checked)); + } + } + spin_unlock(&kpi->list_lock); + + /* Check a canceled flag and an error flag */ + if (err == 1) + goto err; + + /* When threre are non-safety threads */ + if (nthr != 0) { + /* check canceled flag and error flag */ + if (kpi->canceled == CANCELED){ + /* Wait until all the in-flight RCUs are complete. */ + rcu_barrier(); + goto err; + } + + /* Retry check if element is remaining */ + if (loop_count < MAX_RETRY_RT_ACT) { + loop_count++; + goto loop; + } + /* Give up */ + printk(KERN_ERR MSG_HEAD + "Loop count reached MAX_RETRY_RT_ACT\n"); + goto err; + } + + DPRINT("safety_check (exit)\n"); + return 0; + +err: + spin_lock(&kpi->list_lock); + list_for_each_safe_rcu(pos, next, &kpi->chk_list) + ktl = list_entry(pos, struct kaho_thr_list, chk_list); + list_del_rcu(&ktl->chk_list); + spin_unlock(&kpi->list_lock); + DPRINT("safety_check (exit:err)\n"); + return -EFAULT; +} + +/* ------------------------------------------------------------------ + * Trap Handlers and threads + * ---------------------------------------------------------------- */ +static void mark_check_list(struct kaho_patch_info *kpi) +{ + struct kaho_thr_list* ktl; + + DPRINT("mark_check_list (entry)\n"); + list_for_each_entry_rcu(ktl, &kpi->chk_list, chk_list) { + if (ktl->task == current) { + atomic_set(&ktl->checked, THR_MARKED); + break; + } + } + DPRINT("mark_check_list (exit)\n"); +} + +static int kaho_trap_handler( + struct notifier_block *nb, unsigned long val, void *data) +{ + struct kaho_patch_info* kpi; + struct die_args * args = (struct die_args *)data; + unsigned long target_addr; + + DPRINT("kaho_trap_handler (entry)\n"); + if (val != DIE_INT3) + return NOTIFY_DONE; + + /* Serach tregistered trap */ + rcu_read_lock(); + list_for_each_entry_rcu(kpi, &kaho_trap_list_head, trap_list) { + + DPRINT("[trap] current->group_leader: %p, target_task: %p\n", + current->group_leader, kpi->target_task); + if (current->group_leader != kpi->target_task) + continue; + + DPRINT("[trap] st: %d\n", atomic_read(&kpi->state)); + if ((atomic_read(&kpi->state) != KAHO_STATE_IN_ACT_RT1) && + (atomic_read(&kpi->state) != KAHO_STATE_IN_ACT_RT2) && + (atomic_read(&kpi->state) != KAHO_STATE_ACTIVE) && + (atomic_read(&kpi->state) != KAHO_STATE_IN_DEACT_RT1) && + (atomic_read(&kpi->state) != KAHO_STATE_IN_DEACT_RT2) && + (atomic_read(&kpi->state) != KAHO_STATE_DEACTIVE)) + continue; + + target_addr = kpi->items[0].target_addr; + DPRINT("[trap] addr: %lx\n", target_addr); + if (!arch_trap_addr_check(args, target_addr)) + continue; + + /* assert checked flag */ + mark_check_list(kpi); + + /* jumpt to patch */ + arch_set_pc(args->regs, kpi->items[0].patch_addr); + + rcu_read_unlock(); + DPRINT("kaho_trap_handler (exit:NOTFY_STOP)\n"); + return NOTIFY_STOP; + } + rcu_read_unlock(); + DPRINT("kaho_trap_handler (exit:NOTFY_DONE)\n"); + return NOTIFY_DONE; +} + +struct notifier_block kaho_trap_notif = { + .notifier_call = kaho_trap_handler, + .priority = 100 +}; + +extern int register_die_notifier(struct notifier_block *nb); +#endif + + +static int __init init_kaho(void) +{ + int rv; + /* Register as a character device */ + rv = register_chrdev(kaho_major, kaho_dev_name, &kaho_fops); + if (rv < 0) { + class_destroy(kaho_class); + printk(KERN_ERR MSG_HEAD "can't register device class\n"); + return rv; + } + /* Register as a class device */ + kaho_class = class_create(THIS_MODULE, (char*)kaho_dev_name); + if (IS_ERR(kaho_class)) { + printk(KERN_ERR MSG_HEAD "can't register device class\n"); + goto err3; + } + if (kaho_major == 0) + kaho_major = rv; + /* add class device */ + class_device_create(kaho_class, NULL, MKDEV(kaho_major, kaho_minor), + NULL, "kaho"); + + /* init variables */ + atomic_set(&nr_patch, 0); + init_handle_tbl(); + +#ifdef CONFIG_KAHO_RT + /* Trap Handler */ + if (register_die_notifier(&kaho_trap_notif) < 0) { + printk(KERN_ERR MSG_HEAD + "Failed to register the trap handler\n"); + goto err2; + } + +#endif + +#ifndef CONFIG_KAHO_RT + printk(KERN_INFO MSG_HEAD "%s initialized\n", KAHO_VER); +#else + printk(KERN_INFO MSG_HEAD "%s initialized (RT)\n", KAHO_VER); +#endif + + return 0; + +#ifdef CONFIG_KAHO_RT +err2: + class_device_destroy(kaho_class, MKDEV(kaho_major, kaho_minor)); + class_destroy(kaho_class); +#endif + +err3: + unregister_chrdev(kaho_major, kaho_dev_name); + + return -EFAULT; +} + +static void __exit cleanup_kaho(void) +{ + struct kaho_patch_info* kpi; + struct kaho_patch_info* n; + + printk(KERN_INFO MSG_HEAD "start cleanup\n"); + +loop: + spin_lock(&kaho_patch_list_lock); + list_for_each_entry_safe(kpi, n, &kaho_patch_list_head, list) { + list_del_rcu(&kpi->list); + spin_unlock(&kaho_patch_list_lock); + free_kpi(kpi, 0, 0); + goto loop; + } + spin_unlock(&kaho_patch_list_lock); + +#ifdef CONFIG_KAHO_RT + /* Unregister the trap handler */ + if (unregister_die_notifier(&kaho_trap_notif) < 0) + printk(KERN_ERR MSG_HEAD "Failed to unregister int3 handler\n"); +#endif + + class_device_destroy(kaho_class, MKDEV(kaho_major, kaho_minor)); + class_destroy(kaho_class); + unregister_chrdev(kaho_major, kaho_dev_name); + printk(KERN_INFO MSG_HEAD "%s removed\n", KAHO_VER); + DPRINT("\n"); +} + +module_init(init_kaho); +module_exit(cleanup_kaho); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("K. Yamato. "); +MODULE_DESCRIPTION("KAHO (kernel Aided Hexadecimal code Operator"); diff -Nrup linux-2.6.23/kernel/kaho_def.h linux-2.6.23.kaho/kernel/kaho_def.h --- linux-2.6.23/kernel/kaho_def.h 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.6.23.kaho/kernel/kaho_def.h 2008-09-16 15:07:56.000000000 +0900 @@ -0,0 +1,184 @@ +#ifndef _kaho_def_h +#define _kaho_def_h + +/* ------------------------------------------------------------------ + * Definitions for x86_64 & i386 + * ---------------------------------------------------------------- */ +#if defined(__x86_64__) || defined(__i386__) + +#ifdef CONFIG_KAHO_RT +static const unsigned char trap_code = 0xcc; +static const int trap_code_size = 1; +#endif + +#endif + +/* ------------------------------------------------------------------ + * Definitions for x86_64 + * ---------------------------------------------------------------- */ +#if defined(__x86_64__) + +static const unsigned char jmp_code_abs[] = { + /* jmpq *0x0(%rip) */ + 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, + /* absolute address */ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef +}; + +static const unsigned char jmp_code_rel[] = { + /* jmpq $0x12345678 */ + 0xe9, 0x12, 0x34, 0x56, 0x78, +}; + +/* size of jump code */ +static const int jmp_code_size_abs = sizeof(jmp_code_abs); +static const int jmp_code_size_rel = sizeof(jmp_code_rel); +#define ARCH_JMP_CODE_SIZE sizeof(jmp_code_abs) + +inline void arch_set_pc(struct pt_regs* regs, unsigned long pc) +{ + regs->rip = pc; +} + +inline unsigned long arch_get_pc(struct pt_regs* regs) +{ + return regs->rip; +} + +#endif + +/* ------------------------------------------------------------------ + * Definitions for i386 + * ---------------------------------------------------------------- */ +#if defined(__i386__) +static const unsigned char jmp_code_rel[] = { + /* jmpq $0x12345678 */ + 0xe9, 0x12, 0x34, 0x56, 0x78, +}; +static const int jmp_code_size_rel = sizeof(jmp_code_rel); +#define ARCH_JMP_CODE_SIZE sizeof(jmp_code_rel) + +inline void arch_set_pc(struct pt_regs* regs, unsigned long pc) +{ + regs->eip = pc; +} + +inline unsigned long arch_get_pc(struct pt_regs* regs) +{ + return regs->eip; +} +#endif + +/* ---------------------------------------------------------------- + * + * Definictions for all architecutres + * + * ---------------------------------------------------------------- */ +#define HANDLE_NOT_USED 0 +#define HANDLE_USED 1 + +#define NO_PATCHER -1 + +#define OPE_MAP 0 +#define OPE_UNMAP 1 + +#define REQ_ACT 0 +#define REQ_DEACT 1 +#define REQ_ACT_RT 2 +#define REQ_DEACT_RT 3 + +#define NOT_CODE_BKUP 0 +#define CODE_BKUP 1 +#define CODE_RESTORE 2 + +#define CANCELED 1 + +#ifdef CONFIG_KAHO_RT +#define THR_NO_MARK 0 +#define THR_MARKED 1 +#define THR_ESRCH 2 +#define THR_NEWFACE 3 +#define THR_CHK_ERR -1 + +#define MAX_RETRY_RT_ACT 10 +#endif + +struct kaho_patch_item { + int patch_type; + unsigned long patch_addr; /* address to patch in the target */ + unsigned long patch_size; /* size of the patch */ + unsigned long target_addr; /* address to be patched */ + unsigned char backup[ARCH_JMP_CODE_SIZE]; +#ifdef CONFIG_KAHO_RT + atomic_t backup_done; +#endif + int flags; +}; + +struct kaho_map_struct { + struct file* filp; + unsigned long addr; + unsigned long length; + unsigned long offset; + unsigned long prot; + unsigned long flag; + unsigned long act_addr; +}; + +#ifdef CONFIG_KAHO_RT +struct kaho_patch_info; +struct kaho_thr_list { + struct utrace_attached_engine* engine; + struct rcu_head rcu_head; + struct list_head chk_list; /* list of threads to be checked */ + struct list_head thr_list; /* list of all threads in the process */ + struct task_struct* task; + struct kaho_patch_info* kpi; + atomic_t checked; + atomic_t used; + int sem_upped; +}; +#endif + +struct kaho_patch_info { + /* basic variables */ + struct list_head list; + + /* lock for the members of this structure */ + struct semaphore sem; + atomic_t used; /* reference counter */ + + int handle; + atomic_t state; + struct task_struct* target_task; + struct utrace_attached_engine* engine; + + /* patch item array */ + struct kaho_map_struct* kms; + struct semaphore mem_ope_sem; + int nr_sub_patch; + struct kaho_patch_item* items; + int canceled; + u32 exec_id; + + /* for delayed release */ + struct timer_list timer; + unsigned long nvcsw, nivcsw; + +#ifdef CONFIG_KAHO_RT + /* list of kaho_patch_info that is waiting for realtime activation */ + struct list_head trap_list; /* it is linked from ... */ + struct list_head chk_list; /* List head for the */ + struct list_head thr_list; /* List head for the */ + /*spinlock_t chk_list_lock; + spinlock_t thr_list_lock;*/ + spinlock_t list_lock; + int jmp_code_size; + struct semaphore rt_sem; + + /* list of thread information */ +#endif +}; + +#endif +