情景假设:
在堆内存中申请了一块内存,然后释放掉该内存,然后再去访问这块内存。也就是所说的野指针访问。
情景分析:
当Cpu使用页面目录和页表把一个线性地址转化成一个物理地址失败时,就会产生一个缺页中断。经过一系列的调用,最后转换到缺页中断的处理程序/arch/i386/mm/fault.c的do_page_fault()函数中。
下面分析下 do_page_fault函数:
asmlinkage do_page_fault(pt_regs regs, unsigned long errorcode )
{
//根据i386的架构,当缺页中断产生时,会把映射错误的地址放到Cr2寄存器中。
//先取得地址
unsigned long address;
__asm__("movl %%cr2,%0":"=r" (address));
//下面判断映射错误造成的原因
task_struct *tsk;
mm_struct * mm;
tsk = current;
mm = tsk.mm;
if(!mm || in_interupt()) goto no_context;
vma_area vma = find_vma(mm, address);
//如果没有找到,必属于越界访问。因为堆栈是从3G高地址向下扩展,如果在堆 //栈中都不存在一个地址空间的结束地址高于address,那么该地址必定在于系统空 //间空间,从用户空间访问系统空间必定属于越界访问。
If(!vma) goto bad_area;
//如果找到了一块虚拟地址空间并且起始地址小于address,说明这块地址被映射 //了,那么继续调查映射失败的原因。
If(vma->startaddress <= address ) goto good_area;
//如果找到了一块虚拟地址空间,那么判断这块地址上方的空洞是否是堆栈区, //判断方法是堆栈区的虚拟地址空间都存在一个VM_GROWDOWN,如果不是说 //明空洞是由于地址映射撤销导致的,所以属于越界访问,也就是我们讨论的情 //景。
If(!(vma->vm_flags & VM_GROWDOWN)) goto bad_area;
//如果上方空洞地址是堆栈区,那么说明堆栈需要扩展。
if (expand_stack(vma, address))
goto bad_area;
bad_area:
// 设置task_struct的一些变量。然后发送一个SIGSEGV信号
up(&mm->mmap_sem);
bad_area_nosemaphore:
/* User mode accesses just cause a SIGSEGV */
if (error_code & 4) {
tsk->thread.cr2 = address;
tsk->thread.error_code = error_code;
tsk->thread.trap_no = 14;
info.si_signo = SIGSEGV;
info.si_errno = 0;
/* info.si_code has been set above */
info.si_addr = (void *)address;
force_sig_info(SIGSEGV, &info, tsk);
return;
}
}
{
//根据i386的架构,当缺页中断产生时,会把映射错误的地址放到Cr2寄存器中。
//先取得地址
unsigned long address;
__asm__("movl %%cr2,%0":"=r" (address));
//下面判断映射错误造成的原因
task_struct *tsk;
mm_struct * mm;
tsk = current;
mm = tsk.mm;
if(!mm || in_interupt()) goto no_context;
vma_area vma = find_vma(mm, address);
//如果没有找到,必属于越界访问。因为堆栈是从3G高地址向下扩展,如果在堆 //栈中都不存在一个地址空间的结束地址高于address,那么该地址必定在于系统空 //间空间,从用户空间访问系统空间必定属于越界访问。
If(!vma) goto bad_area;
//如果找到了一块虚拟地址空间并且起始地址小于address,说明这块地址被映射 //了,那么继续调查映射失败的原因。
If(vma->startaddress <= address ) goto good_area;
//如果找到了一块虚拟地址空间,那么判断这块地址上方的空洞是否是堆栈区, //判断方法是堆栈区的虚拟地址空间都存在一个VM_GROWDOWN,如果不是说 //明空洞是由于地址映射撤销导致的,所以属于越界访问,也就是我们讨论的情 //景。
If(!(vma->vm_flags & VM_GROWDOWN)) goto bad_area;
//如果上方空洞地址是堆栈区,那么说明堆栈需要扩展。
if (expand_stack(vma, address))
goto bad_area;
bad_area:
// 设置task_struct的一些变量。然后发送一个SIGSEGV信号
up(&mm->mmap_sem);
bad_area_nosemaphore:
/* User mode accesses just cause a SIGSEGV */
if (error_code & 4) {
tsk->thread.cr2 = address;
tsk->thread.error_code = error_code;
tsk->thread.trap_no = 14;
info.si_signo = SIGSEGV;
info.si_errno = 0;
/* info.si_code has been set above */
info.si_addr = (void *)address;
force_sig_info(SIGSEGV, &info, tsk);
return;
}
}