mirror of
https://github.com/gatieme/LDD-LinuxDeviceDrivers.git
synced 2026-08-21 06:24:16 +08:00
1377 lines
32 KiB
C
1377 lines
32 KiB
C
/*
|
||
* 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.");
|
||
|