/* * Author: HIT CS HDMC team. * Create: 2010-3-12 8:50 * Last modified: 2010-6-13 14:06:20 * Description: * Memory fault injection engine running as a kernel module. * This module will create "/proc/memoryEngine/" directory and 9 proc nodes. * Write paramenters and request to these proc nodes and read the output from related proc node. */ #include "memoryEngine.h" /* * proc entries */ struct proc_dir_entry *dir = NULL; /// the directory of the MEMORY INJECT Moudle struct proc_dir_entry *proc_pid = NULL; /// write only struct proc_dir_entry *proc_va = NULL; /// write only struct proc_dir_entry *proc_ctl = NULL; /// write only struct proc_dir_entry *proc_kFuncName = NULL; /// write only struct proc_dir_entry *proc_val = NULL; /// rw struct proc_dir_entry *proc_signal = NULL; /// rw struct proc_dir_entry *proc_pa = NULL; /// read only struct proc_dir_entry *proc_taskInfo = NULL; /// read only /* * proc node values */ int pid; /// pid unsigned long va; /// virtual Address unsigned long pa; /// physical Addreess int ctl; /// ctl int signal; /// signal char kFuncName[MAX_LINE]; /// kFuncName long memVal; /// memVal unsigned long ack_pa; /// physical Address unsigned long ack_va; /// virtual Address int ack_signal; /// signal int ret; /// return value char taskInfo[PAGE_SIZE]; /// taskInfo /////////////////////////////////////////////// unsigned long userspace_phy_mem; /// user space physical memory long orig_pa_data; /// origin data of the physics memory long new_pa_data; /// the new data you want write to physics memory int faultInterval; /* * kprobe */ static struct kprobe kp_kFunc; struct jprobe jprobe1 = { .entry = jforce_sig_info, .kp = { .symbol_name = "force_sig_info", }, }; //时钟中断数计数 static int count = 0; //保存原始代码 static long orig_code = 0; /* * process the request */ void do_request(void) { struct task_struct *task = NULL; unsigned long pa = 0; long kernel_va = 0; int status; int temp = 0; /// get a task's memory map information if(ctl == REQUEST_TASK_INFO) { dbginfo("Rcv request:Get task info\n"); memset(taskInfo,'\0',sizeof(taskInfo)); if(pid <= 0) { ack_signal = ACK_TASK_INFO; return; } task = findTaskByPid(pid); if( task != NULL ) { getTaskInfo(task, taskInfo, sizeof(taskInfo)); } else { dbginfo("No such process\n"); } ack_signal = ACK_TASK_INFO; return; } /// convert a process's linear address to physical address else if(ctl == REQUEST_V2P) { task = findTaskByPid(pid); if( task == NULL ) { dbginfo("No such process\n"); ack_pa = -1; ack_signal = ACK_V2P; return; } if( task->mm == NULL ) { ack_pa = -1; ack_signal = ACK_V2P; return; } ack_pa = v2p(task->mm,va,&status); if(ack_pa == FAIL) { dbginfo("No physical address\n"); } ack_signal = ACK_V2P; return; } /// convert kernel virtual address to physical address else if(ctl == REQUEST_KV2P) { ack_pa = kv2p(va,&status); if(pa == FAIL) { dbginfo("No physical address\n"); } ack_signal = ACK_KV2P; return; } /// get kernel function's addr(kernel virtual address) else if(ctl == REQUEST_KFUNC_VA) { ack_va = kFunc2v(kFuncName); ack_signal = ACK_KFUNC_VA; return; } /// 请求读取内核函数起始地址内容 else if(ctl == REQUEST_READ_KFUNC) { kernel_va = kFunc2v(kFuncName); memVal = *((long *)kernel_va); ack_signal = ACK_READ_KFUNC; } /// 请求改写内核函数起始地址内容 else if(ctl == REQUEST_WRITE_KFUNC) { //利用kprobe,在第一次调用do_timer()时,注入故障 int ret; count = 0; if(strlen(kFuncName) > 0) { faultInterval = 1; //故障仅持续一个时钟周期 kp_kFunc.addr = 0; kp_kFunc.symbol_name = kFuncName; kp_kFunc.pre_handler = handler_pre_kFunc; ret = register_kprobe(&kp_kFunc); if(ret < 0) { dbginfo("Fained to register kprobe\n"); ack_signal = ACK_WRITE_KFUNC; return; } // 等待故障注入结束 dbginfo("start count\n"); temp = 0; while(1) { if(count == -1) { unregister_kprobe(&kp_kFunc); dbginfo("recovery\n"); break; } if(temp == -1) { break; } temp++; //dbginfo("count:%d\n",count); } } ack_signal = ACK_WRITE_KFUNC; dbginfo("Success to inject MTTR fault\n"); return; } } struct dentry* file_entry(struct file *pfile) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) return pfile->f_path.dentry; #else return pfile->f_dentry; #endif } /* * get a task's memory map information */ int getTaskInfo(struct task_struct *pTask, char *pData, int length) { struct mm_struct *pMM; // struct mm_struct is defined in `include/linux/mm_types.h` struct vm_area_struct *pVMA; struct vm_area_struct *p; struct dentry * pPath = NULL; char *info = pData; char file[MAX_LINE]; char *end, *start; long phy_addr; unsigned long start_va, end_va; int status; if(pTask == NULL) { return FAIL; } if((pMM = pTask->mm) == NULL) { return FAIL; } memset(pData, '\0', length); // 前19个字段是关于进程内存信息的总体信息 safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->total_vm, DELIMITER); safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->locked_vm, DELIMITER); safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->shared_vm, DELIMITER); safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->exec_vm, DELIMITER); safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->stack_vm, DELIMITER); /// modify by gatieme for system porting NeoKylin-linux-3.14/16 #if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 7, 0) // error: ‘struct mm_struct’ has no member named ‘reserved_vm’ // // 从linux 3.7.0开始内核不再支持RESERVED_VM // struct mm_struct 也没有了reserved_mm字段 // struct vm_area_struct结构体中flag标志使用值 VM_RESERVED -=> (VM_DONTEXPAND | VM_DONTDUMP) // safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->reserved_vm, DELIMITER); #endif safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->def_flags, DELIMITER); safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->nr_ptes, DELIMITER); safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->start_code, DELIMITER); safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->end_code, DELIMITER); safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->start_data, DELIMITER); safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->end_data, DELIMITER); safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->start_brk, DELIMITER); safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->brk, DELIMITER); safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->start_stack, DELIMITER); safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->arg_start, DELIMITER); safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->arg_end, DELIMITER); safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->env_start, DELIMITER); safe_sprintf(pData, length, info+strlen(info), "%lx%c", pMM->env_end, DELIMITER); pVMA = pMM->mmap; if(pVMA == NULL) { return OK; } for(p = pVMA; p != NULL; p = p->vm_next) { // 起始地址 safe_sprintf(pData, length, info + strlen(info), "%lx %lx ", p->vm_start, p->vm_end); // 属性 if(p->vm_flags & VM_READ) { safe_sprintf(pData, length, info+strlen(info), "r"); } else { safe_sprintf(pData, length, info+strlen(info), "-"); } if(p->vm_flags & VM_WRITE) { safe_sprintf(pData, length, info+strlen(info), "w"); } else { safe_sprintf(pData, length, info+strlen(info), "-"); } if(p->vm_flags & VM_EXEC) { safe_sprintf(pData, length, info+strlen(info), "x"); } else { safe_sprintf(pData, length, info+strlen(info), "-"); } if(p->vm_flags & VM_SHARED) { safe_sprintf(pData, length, info+strlen(info), "s"); } else { safe_sprintf(pData, length, info+strlen(info), "p"); } // 对应文件名 if(p->vm_file != NULL) { // i find in linux-kernel-3.16 // http://lxr.free-electrons.com/source/include/linux/fs.h?v=3.16#L827 // struct path f_path; // #define f_dentry f_path.dentry struct dentry *den = file_entry(p->vm_file); //if(p->vm_file->f_path.dentry != NULL) if(den != NULL) { safe_sprintf(pData, length, info+strlen(info), " "); memset(file,'\0',sizeof(file)); //for(pPath = p->vm_file->f_path.dentry; for(pPath = den; pPath != NULL; pPath = pPath->d_parent) { if(strcmp(pPath->d_name.name, "/") != 0) { strcpy(file + strlen(file), pPath->d_name.name); strcpy(file + strlen(file), "/"); continue; } break; } do { end = file + strlen(file) - 1; for(start = end - 1; *start != '/' && start > file; start--); if(*start == '/') {start++;} *end = '\0'; safe_sprintf(pData, length, info+strlen(info), "/%s", start); *start = '\0'; } while(start > file); } } safe_sprintf(pData, length, info+strlen(info), "%c", DELIMITER); //对应物理地址页 start_va = p->vm_start; end_va = p->vm_end; while(end_va > start_va) { safe_sprintf(pData, length, info+strlen(info), "%lx-%lx\t", start_va, start_va + PAGE_SIZE); phy_addr = v2p(pMM, start_va, &status); if(phy_addr != FAIL) { safe_sprintf(pData, length, info+strlen(info), "va:0x%lx <--> pa:0x%lx", start_va, phy_addr); } start_va += PAGE_SIZE; safe_sprintf(pData, length, info+strlen(info), "%c", DELIMITER); } safe_sprintf(pData, length, info+strlen(info), "%c", DELIMITER); } dbginfo("get task info success...\n"); return OK; } /* * */ static int handler_pre_kFunc(struct kprobe *p, struct pt_regs *regs) { unsigned long va; va = (unsigned long)p->addr; if(va <= 0) { return OK; } //第一次触发 if(count == 0) { //读取前64字节,注入故障 orig_code = *((long *)va); //_inject_fault(va,memVal); *((long *)va) = memVal; //故障 return OK; } count ++; //到达结束时间 if(count == faultInterval + 1) { //恢复code *((long *)va) = orig_code; count = -1; } return OK; } /* * find_task_by_pid maybe not supported * O(n) is fine :) */ struct task_struct * findTaskByPid(pid_t pid) { struct task_struct *task = NULL; // Traversing the process in the system to find the PID // add by gatieme @2016-03-20 for_each_process(task) { if(task->pid == pid) { dbginfo("find task by pid = %d\n", pid); return task; } } return NULL; } /** add by gatieme */ /* * */ long readPhysicsAddress(unsigned long pa, int *pStatus) { long res = FAIL; //long *point = (long *)pa; res = *(long *)pa; dbgprint("physics address : 0x%lx, data : 0x%lx", pa, res); return res; } long writePhysicsAddress(unsigned long pa, int *pStatus) { } /* * convert a process's linear address to physical address */ long v2p(struct mm_struct *pMM,unsigned long va,int *pStatus) { pte_t *pte = NULL; unsigned long phyaddress = FAIL; pte = getPte(pMM, va); if(pte != NULL) { phyaddress = (pte_val(*pte) & PAGE_MASK) | (va & ~PAGE_MASK); } return phyaddress; } /* * convert kernel virtual address to physical address */ long kv2p(unsigned long va,int *pStatus) { if(va < 0) return FAIL; if(__pa(va) >= 0) return __pa(va); return FAIL; } /* * get kernel function's addr(kernel virtual address) * the kernel function should be looked up in the System.map */ static struct kprobe kp; long kFunc2v(char *funcName) { int ret; unsigned long va; kp.addr = 0; kp.symbol_name = funcName; ret = register_kprobe(&kp); if(ret < 0) { dbginfo("Fained to register kprobe\n"); return FAIL; } va = (unsigned long)kp.addr; unregister_kprobe(&kp); if(va == 0) return FAIL; return va; } /* * */ struct vm_area_struct * getVMA(struct mm_struct *pMM,unsigned long va) { struct vm_area_struct *p; if(pMM == NULL) return NULL; p = pMM->mmap; if(p == NULL) return NULL; for(; p != NULL; p = p->vm_next) { if( va >= p->vm_start && va < p->vm_end ) { return p; } } return NULL; } /* * */ pte_t * getPte(struct mm_struct *pMM, unsigned long va) { pgd_t *pgd = NULL; pmd_t *pmd = NULL; pud_t *pud = NULL; pte_t *pte = NULL; ///get the pdg entry pointer pgd = pgd_offset(pMM, va); if(pgd_none(*pgd)) { return NULL; } pud = pud_offset(pgd,va); if(pud_none(*pud)) { return NULL; } pmd = pmd_offset(pud,va); if(pmd_none(*pmd)) { return NULL; } pte = pte_offset_kernel(pmd,va); if(pte_none(*pte)) { return NULL; } if(!pte_present(*pte)) { return NULL; } return pte; } /* * */ int setVMAFlags(struct mm_struct *pMM,unsigned long va,int *pStatus,int flags) { struct vm_area_struct *p; p = getVMA(pMM,va); if(p == NULL) return FAIL; if(flags > 0) { p->vm_flags |= VM_WRITE; p->vm_flags |= VM_SHARED; } if(flags == 0) { p->vm_flags &= ~VM_WRITE; p->vm_flags &= ~VM_SHARED; } else { return FAIL; } return OK; } /* * */ int setPageFlags(struct mm_struct *pMM,unsigned long va,int *pStatus,int flags) { pte_t *pte = NULL; pte_t ret; pte = getPte(pMM, va); if( pte == NULL ) { return FAIL; } if(flags > 0) { ret = pte_mkwrite(*pte); } else if(flags == 0) { ret = pte_wrprotect(*pte); } else { return FAIL; } return OK; } /* * */ int proc_write_pid( struct file *file, const char __user *buffer, unsigned long count, void * data) { int iRet; char sPid[MAX_LINE]; if(count <= 0) { return FAIL; } memset(sPid, '\0', sizeof(sPid)); ///////////////////////////////////////////////////////////////////// // // copy_from_user函数的目的是从用户空间拷贝数据到内核空间, // 失败返回没有被拷贝的字节数,成功返回0. // 这么简单的一个函数却含盖了许多关于内核方面的知识, // 比如内核关于异常出错的处理. // 从用户空间拷贝数据到内核中时必须很小心, // 假如用户空间的数据地址是个非法的地址,或是超出用户空间的范围, // 或是那些地址还没有被映射到,都可能对内核产生很大的影响, // 如oops,或被造成系统安全的影响. // 所以copy_from_user函数的功能就不只是从用户空间拷贝数据那样简单了, // 他还要做一些指针检查连同处理这些问题的方法. // // 函数原型在[arch/i386/lib/usercopy.c]中 // unsigned long // copy_from_user( void *to, // const void __user *from, // unsigned long n) // ///////////////////////////////////////////////////////////////////// // // 将用户空间中, 地址buffr指向的count个数据拷贝到内核空间地址sPid中 iRet = copy_from_user(sPid, buffer, count); if(iRet != 0) { dbginfo("Error when copy_from_user...\n"); return FAIL; } iRet = sscanf(sPid, "%d", &pid); // 将读出来的数据sPid赋值给模块的全局变量pid if(iRet != 1) { return FAIL; } dbginfo("Rcv pid:%d\n",pid); return count; } /* * */ int proc_read_virtualAddr( char * page, char **start, off_t off, int count, int * eof, void * data) { int iLen; iLen = sprintf(page, "%lx", ack_va); return iLen; } /* * */ int proc_write_virtualAddr( struct file *file, const char *buffer, unsigned long count, void * data) { int iRet; char sVa[MAX_LINE]; if(count <= 0) { return FAIL; } memset(sVa, '\0', sizeof(sVa)); iRet = copy_from_user(sVa, buffer, count); if(iRet) { return FAIL; } iRet = sscanf(sVa,"%lx",&va); if(iRet != 1) { return FAIL; } dbginfo("Rcv virtual addr:0x%lx\n",va); return count; } /* * */ int proc_write_ctl( struct file *file, const char *buffer, unsigned long count, void * data) { int iRet; char sCtl[MAX_LINE]; if(count <= 0) { return FAIL; } memset(sCtl, '\0', sizeof(sCtl)); iRet = copy_from_user(sCtl, buffer, count); if(iRet) { return FAIL; } iRet = sscanf(sCtl,"%d",&ctl); if(iRet != 1) { return FAIL; } do_request(); return count; } /* * */ int proc_read_signal( char * page, char **start, off_t off, int count, int * eof, void * data) { int iLen; iLen = sprintf(page, "%d", ack_signal); return iLen; } /* * */ int proc_write_signal( struct file *file, const char *buffer, unsigned long count, void * data) { int iRet; char sSignal[MAX_LINE]; if(count <= 0) { return FAIL; } memset(sSignal, '\0', sizeof(sSignal)); iRet = copy_from_user(sSignal, buffer, count); if(iRet) { return FAIL; } iRet = sscanf(sSignal,"%d",&signal); if(iRet != 1) { return FAIL; } dbginfo("Rcv signal:%d\n",signal); return count; } /* * */ int proc_read_pa(char * page,char **start, off_t off, int count, int * eof,void * data) { int iLen; iLen = sprintf(page, "%lx", ack_pa); return iLen; } /* * */ int proc_write_pa(struct file *file,const char *buffer,unsigned long count,void * data) { int iRet; char sPa[MAX_LINE]; if(count <= 0) { return FAIL; } memset(sPa, '\0', sizeof(sPa)); iRet = copy_from_user(sPa, buffer, count); if(iRet) { return FAIL; } iRet = sscanf(sPa,"%lx",&pa); if(iRet != 1) { return FAIL; } dbginfo("Rcv pa:0x%lx\n",pa); return count; } /* * */ int proc_write_kFuncName(struct file *file,const char *buffer,unsigned long count,void * data) { int iRet; if(count <= 0) { return FAIL; } memset(kFuncName, '\0', sizeof(kFuncName)); iRet = copy_from_user(kFuncName, buffer, count); if(iRet) { return FAIL; } //remove '\n' if(kFuncName[strlen(kFuncName) - 1] == '\n') { kFuncName[strlen(kFuncName) - 1] = '\0'; } dbginfo("Rcv kernel func name:%s\n",kFuncName); return count; } /* * */ int proc_read_taskInfo(char * page,char **start, off_t off, int count, int * eof,void * data) { int iLen; iLen = sprintf(page, "%s", taskInfo); return iLen; } /* * */ int proc_write_memVal(struct file *file,const char *buffer,unsigned long count,void * data) { int iRet; char sMemVal[MAX_LINE]; if(count <= 0) { return FAIL; } memset(sMemVal, '\0', sizeof(sMemVal)); iRet = copy_from_user(sMemVal, buffer, count); if(iRet) { return FAIL; } iRet = sscanf(sMemVal,"%lx",&memVal); if(iRet != 1) { return FAIL; } dbginfo("Rcv memVal:0x%lx\n",memVal); return count; } /* * */ int proc_read_memVal( char * page, char **start, off_t off, int count, int * eof, void * data) { int iLen; iLen = sprintf(page, "%lx", memVal); return iLen; } /* * init memory fault injection module * 初始化内存注入故障模块 * * 使用proc_mkdir()创建一个dir = /proc/memoryEngine * 再利用create_proc_read_entry()函数创建一个processinfo文件。 * 我们从模块里面获取的信息都将写入到这些文件中。 * */ static int __init initME(void) { /* * create a direntory named "memoryEngine" in /proc for the moudles * as the interface between the kernel and the user program. * */ dir = proc_mkdir("memoryEngine", NULL); if(dir == NULL) { dbginfo("Can't create /proc/memoryEngine/\n"); return FAIL; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) /// modify by gatieme for system porting NeoKylin-linux-3.14/16 /// error: dereferencing pointer to incomplete type dir->owner = THIS_MODULE; #endif /// modify by gatieme for system porting NeoKylin-linux-3.14/16 @ 2016--03-28 20:08 /* * == * write in STACK_OVER_FLOW http://stackoverflow.com/questions/26808325/implicit-declaration-of-function-create-proc-entry * == * * proc filesystem has been refactored in 3.10, * the function `create_proc_entry` has been removed, * you should use the full featured `proc_create function` family. * * Note that the signatures are different, you can find them in LXR * 3.10 version: http://lxr.free-electrons.com/source/include/linux/proc_fs.h?v=3.10 * 3.9 version: http://lxr.free-electrons.com/source/include/linux/proc_fs.h?v=3.9 * * You can find greater explanation of using full featured /proc functions in the book Linux Device Drivers 4, * or, if you want shorter solution, check this link (https://github.com/jesstess/ldd4/blob/master/scull/main.c) * where you can see how the struct file_operations has been used. You do not have to setup to all fields of the struct. * * but the function remove_proc_remove, you can do nothing for it, becase there are tow function for it * static inline void proc_remove(struct proc_dir_entry *de) {} * #define remove_proc_entry(name, parent) do {} while (0) */ /// create a file named "pid" in direntory #if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 0) proc_pid = create_proc_entry("pid", PERMISSION, dir); if(proc_pid == NULL) { dbginfo("Can't create /proc/memoryEngine/pid\n"); goto create_pid_failed; } proc_pid->write_proc = proc_write_pid; /// write only #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) proc_pid->owner = THIS_MODULE; #endif #else static const struct file_operations pid_fops = { .owner = THIS_MODULE, // .read = proc_write_read, .write = proc_write_pid, /* write only */ }; proc_pid = proc_create("pid", PERMISSION, dir, &pid_fops); if(proc_pid == NULL) { dbginfo("Can't create /proc/memoryEngine/pid\n"); goto create_pid_failed; } #endif /// create a file named "virtualAddr" in direntory #if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 0) proc_va = create_proc_entry("virtualAddr", PERMISSION, dir); if(proc_va == NULL) { dbginfo("Can't create /proc/memoryEngine/virtualAddr\n"); goto create_va_failed; } proc_va->read_proc = proc_read_virtualAddr; // can read proc_va->write_proc = proc_write_virtualAddr; // can write #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) proc_va->owner = THIS_MODULE; #endif #else static const struct file_operations va_fops = { .owner = THIS_MODULE, .read = proc_read_virtualAddr, // can read .write = proc_write_virtualAddr, // can write }; proc_va = proc_create("virtualAddr", PERMISSION, dir, &va_fops); if(proc_va == NULL) { dbginfo("Can't create /proc/memoryEngine/virtualAddr\n"); goto create_va_failed; } #endif /// create a file named "ctl" in direntory #if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 0) proc_ctl = create_proc_entry("ctl", PERMISSION, dir); if(proc_ctl == NULL) { dbginfo("Can't create /proc/memoryEngine/ctl\n"); goto create_ctl_failed; return FAIL; } proc_ctl->write_proc = proc_write_ctl; // write only #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) proc_ctl->owner = THIS_MODULE; #endif #else static const struct file_operations ctl_fops = { .owner = THIS_MODULE, //.read = proc_read_ctl, // can read .write = proc_write_ctl, // write only }; proc_ctl = proc_create("ctl", PERMISSION, dir, &ctl_fops); if(proc_ctl == NULL) { dbginfo("Can't create /proc/memoryEngine/ctl\n"); goto create_ctl_failed; } else { dbginfo("Create /proc/memoryEngine/ctl success...\n"); } #endif /// create a file named "signal" in direntory #if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 0) proc_signal = create_proc_entry("signal", PERMISSION, dir); if(proc_signal == NULL) { dbginfo("Can't create /proc/memoryEngine/signal\n"); goto create_signal_failed; } proc_signal->read_proc = proc_read_signal; // can read proc_signal->write_proc = proc_write_signal; // can write #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) proc_signal->owner = THIS_MODULE; #endif #else static const struct file_operations signal_fops = { .owner = THIS_MODULE, .read = proc_read_signal, // can read .write = proc_write_signal, // can write }; proc_signal = proc_create("signal", PERMISSION, dir, &signal_fops); if(proc_signal == NULL) { dbginfo("Can't create /proc/memoryEngine/signal\n"); goto create_signal_failed; } else { dbginfo("Create /proc/memoryEngine/signal success\n"); } #endif /// create a file named "physicalAddr" in direntory #if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 0) proc_pa = create_proc_entry("physicalAddr", PERMISSION, dir); if(proc_pa == NULL) { dbginfo("Can't create /proc/memoryEngine/physicalAddr\n"); goto create_pa_failed; } proc_pa->read_proc = proc_read_pa; // can read proc_pa->write_proc = proc_write_pa; // can write #else static const struct file_operations pa_fops = { .owner = THIS_MODULE, .read = proc_read_pa, // read only .write = proc_write_pa, // write only }; proc_pa = proc_create("physicalAddr", PERMISSION, dir, &pa_fops); if(proc_pa == NULL) { dbginfo("Can't create /proc/memoryEngine/physicalAddr\n"); goto create_pa_failed; } #endif /// create a file named "kFuncName" in direntory #if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 0) proc_kFuncName = create_proc_entry("kFuncName", PERMISSION, dir); if(proc_kFuncName == NULL) { dbginfo("Can't create /proc/memoryEngine/kFuncName\n"); goto create_kFuncName_failed; } proc_kFuncName->write_proc = proc_write_kFuncName; // write only #else static const struct file_operations kFuncName_fops = { .owner = THIS_MODULE, //.read = proc_read_kFuncName, // read only .write = proc_write_kFuncName, // write only }; proc_kFuncName = proc_create("kFuncName", PERMISSION, dir, &kFuncName_fops); if(proc_kFuncName == NULL) { dbginfo("Can't create /proc/memoryEngine/kFuncName\n"); goto create_kFuncName_failed; } #endif /// create a file named "taskInfo" in direntory #if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 0) proc_taskInfo = create_proc_entry("taskInfo", PERMISSION, dir); if(proc_taskInfo == NULL) { dbginfo("Can't create /proc/memoryEngine/taskInfo\n"); goto create_taskInfo_failed; } proc_taskInfo->read_proc = proc_read_taskInfo; // read only #else static const struct file_operations taskInfo_fops = { .owner = THIS_MODULE, .read = proc_read_taskInfo, // read only //.write = proc_write_taskInfo, // write only }; proc_taskInfo = proc_create("taskInfo", PERMISSION, dir, &taskInfo_fops); if(proc_taskInfo == NULL) { dbginfo("Can't create /proc/memoryEngine/taskIndfo\n"); goto create_taskInfo_failed; } #endif /// create a file named "memVal" in direntory #if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 0) proc_val = create_proc_entry("memVal", PERMISSION, dir); if(proc_val == NULL) { dbginfo("Can't create /proc/memoryEngine/memVal\n"); goto create_val_failed; } proc_val->write_proc = proc_write_memVal; // can write proc_val->read_proc = proc_read_memVal; // can read #else static const struct file_operations val_fops = { .owner = THIS_MODULE, .read = proc_read_memVal, // can read .write = proc_write_memVal, // can write }; proc_val = proc_create("memVal", PERMISSION, dir, &val_fops); if(proc_val == NULL) { dbginfo("Can't create /proc/memoryEngine/memVal\n"); goto create_val_failed; } #endif ret = register_jprobe(&jprobe1); if (ret < 0) { printk("register_jprobe jprobe1 failed, returned %d\n", ret); return ret; } printk("Planted jprobe at force_sig_info: %p\n", jprobe1.kp.addr); dbginfo("Memory engine module init\n"); return OK; // remove the proc files create_val_failed : remove_proc_entry("kFuncName", dir); create_kFuncName_failed : remove_proc_entry("taskInfo", dir); create_taskInfo_failed : remove_proc_entry("physicalAddr", dir); create_pa_failed : remove_proc_entry("signal", dir); create_signal_failed : remove_proc_entry("ctl", dir); create_ctl_failed : remove_proc_entry("virtualAddr", dir); create_va_failed : remove_proc_entry("pid", dir); create_pid_failed : remove_proc_entry("memoryEngine", NULL); return FAIL; } /// modify by gatieme for system porting NeoKylin-linux-3.14/16 @ 2016--03-28 20:08 // invalid storage class for function ‘jforce_sig_info’ static int jforce_sig_info(int sig, struct siginfo *info, struct task_struct *t) { printk("MemSysFI: kernel is sending signal %d to process pid: %d, comm: %s\n",sig,t->pid,t->comm); /* if (f_inject == 'N') { jprobe_return(); return 0; } down_interruptible(&sem); if ( addone(addone(inj_info.rear))==inj_info.front ) { */ /*error:队列满*/ /* sprintf(inj_info.inj_log[inj_info.rear].msg,"caution : buf is full, messages have been dropped\n"); } else { inj_info.rear = addone(inj_info.rear); sprintf(inj_info.inj_log[inj_info.rear].msg,"warning : kernel is sending signal %d to process pid: %d, comm: %s\n",sig,current->pid,current->comm); //inj_info->inj_log[inj_info->rear ] = x ; } up(&sem); */ /* if(message!=NULL) { for(i=0;i<256;i++) message[i]='\0'; sprintf(message,"warning : kernel is sending signal %d to process pid: %d, comm: %s\n\0",sig,current->pid,current->comm); } else return -1; struct bufferList *buflist; buflist = (struct bufferList *)kmalloc(sizeof(struct bufferList),GFP_KERNEL); buflist->pNext = NULL; buflist->buffer = message; down_interruptible(&sem); if(Head==NULL) { Head = buflist; Tail = Head; } else { Tail->pNext = buflist; Tail = buflist; } flag = 1; up(&sem); */ // wake_up_interruptible(&wq2); jprobe_return(); return 0; } /* * uninit memory fault injection module */ static void __exit exitME(void) { remove_proc_entry("pid", dir); remove_proc_entry("virtualAddr", dir); remove_proc_entry("ctl", dir); remove_proc_entry("signal", dir); remove_proc_entry("physicalAddr", dir); remove_proc_entry("kFuncName", dir); remove_proc_entry("taskInfo", dir); remove_proc_entry("memVal", dir); remove_proc_entry("memoryEngine", NULL); unregister_jprobe(&jprobe1); printk("jprobe at %p unregistered.\n", jprobe1.kp.addr); dbginfo("Memory engine module exit\n"); } module_init(initME); module_exit(exitME); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Gatieme @ HIT CS HDMC team"); MODULE_DESCRIPTION("Memory Engine Module.");