dotraplinkage void __kprobes
do_page_fault(struct pt_regs *regs, unsigned long error_code)
{
struct vm_area_struct *vma;
struct task_struct *tsk;
unsigned long address;
struct mm_struct *mm;
int write;
int fault;
tsk = current; //获取当前进程
mm = tsk->mm; //获取当前进程的地址空间
/* Get the faulting address: */
address = read_cr2(); //读取CR2寄存器获取触发异常的访问地址
...
...
...
...
vma = find_vma(mm, address);//试图寻找到一个离address最近的vma,vma包含address或在address之后
/*没有找到这样的vma则说明address之后没有虚拟内存区域,因此该address肯定是无效的,
通过bad_area()路径来处理,bad_area()的主体就是__bad_area()-->bad_area_nosemaphore()*/
if (unlikely(!vma)) {
bad_area(regs, error_code, address);
return;
}
/*如果该地址包含在vma之中,则跳转到good_area处进行处理*/
if (likely(vma->vm_start <= address))
goto good_area;
/*不是前面两种情况的话,则判断是不是由于用户堆栈所占的页框已经使用完,而一个PUSH指令
引用了一个尚未和页框绑定的虚拟内存区域导致的一个异常,属于堆栈的虚拟内存区,其VM_GROWSDOWN位
被置位*/
if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {
bad_area(regs, error_code, address);//不是堆栈区域,则用bad_area()来处理
return;
}
if (error_code & PF_USER) {//必须处于用户空间
/*
* Accessing the stack below %sp is always a bug.
* The large cushion allows instructions like enter
* and pusha to work. ("enter $65535, $31" pushes
* 32 pointers and then decrements %sp by 65535.)
*/
/*这里检查address,只有该地址足够高(和堆栈指针的差不大于65536+32*sizeof(unsigned long)),
才能允许用户进程扩展它的堆栈地址空间,否则bad_area()处理*/
if (unlikely(address + 65536 + 32 * sizeof(unsigned long) < regs->sp)) {
bad_area(regs, error_code, address);
return;
}
}
if (unlikely(expand_stack(vma, address))) {//堆栈扩展不成功同样由bad_area()处理
bad_area(regs, error_code, address);
return;
}
/*
* Ok, we have a good vm_area for this memory access, so
* we can handle it..
*/
good_area:
write = error_code & PF_WRITE;
/*访问权限不够则通过bad_area_access_error()处理,该函数是对__bad_area()的封装,只不过
发送给用户进程的信号为SEGV_ACCERR*/
if (unlikely(access_error(error_code, write, vma))) {
bad_area_access_error(regs, error_code, address);
return;
}
/*
* If for any reason at all we couldn't handle the fault,
* make sure we exit gracefully rather than endlessly redo
* the fault:
*/
/*分配新的页表和页框*/
fault = handle_mm_fault(mm, vma, address, write ? FAULT_FLAG_WRITE : 0);
if (unlikely(fault & VM_FAULT_ERROR)) {
mm_fault_error(regs, error_code, address, fault);
return;
}
if (fault & VM_FAULT_MAJOR) {
tsk->maj_flt++;
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0,
regs, address);
} else {
tsk->min_flt++;
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0,
regs, address);
}
check_v8086_mode(regs, address, tsk);
up_read(&mm->mmap_sem);
}