情景假设:
在堆内存中申请了一块内存,然后释放掉该内存,然后再去访问这块内存。也就是所说的野指针访问。
当cpu产生页面错误时,会把失败的线性地址放在cr2寄存器.线性地址缺页异常的4种情况
1.如果cpu访问的行现地址在内核态,那么很可能访问的是非连续区,需要vmalloc_fault处理.
2.缺页异常发生在中断或者内核线程时,直接失败,因为不可修改页表
3.地址在一个区间内,那就可能是已经物理地址映射了但权限问题(错误处理)或者其物理地址没有分配(分配物理内存)
4.如果找到一个在线性地址其后面的vma(线性地址在空洞).那么可能是空洞上面的区间是
堆栈区,他表示动态分配而没有分配出去的空间,有一种特殊情况,可以缺页异常使得获取物理页框
5.如果找到一个线性地址气候的vma(线性地址在空洞),那么可能是空洞上面的区间不是堆栈区,说明
这个空洞是由于一个映射区被撤销而留下的,那样直接错误处理
==================== arch/i386/mm/fault.c 106 152 ====================
106 asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
107 {
108 struct task_struct *tsk;
109 struct mm_struct *mm;
110 struct vm_area_struct * vma;
111 unsigned long address;
112 unsigned long page;
113 unsigned long fixup;
114 int write;
115 siginfo_t info;
116
117 /* get the address */
118 __asm__("movl %%cr2,%0":"=r" (address));//得到失败的线性地址
119
120 tsk = current;//获取当前描述符
121
122 /*
123 * We fault-in kernel-space virtual memory on-demand. The
124 * 'reference' page table is init_mm.pgd.
125 *
126 * NOTE! We MUST NOT take any locks for this case. We may
127 * be in an interrupt or a critical region, and should
128 * only copy the information from the master page table,
129 * nothing more.
130 */
//如果大于3G,表示缺页异常时,访问的是内核空间,很有可能是访问了非连续的内核空间,转到vmalloc_fault处理
131 if (address >= TASK_SIZE)
132 goto vmalloc_fault;
133
134 mm = tsk->mm;
135 info.si_code = SEGV_MAPERR;
136
137 /*
138 缺页异常发生在中断时,是错误不可以的,表示是内核线程,不可以对其页表进行修改
140 */
141 if (in_interrupt() || !mm)
142 goto no_context;
143
144 down(&mm->mmap_sem);
145
146 vma = find_vma(mm, address);//查到end大于address的地址
147 if (!vma)//是否在行现地址内,不在转为错误处理
148 goto bad_area;
//
在线性区内,跳到正常处理部分,可能是由于权限问题,也有可能是对应的物理地址没有分配2种情况149 if (vma->vm_start <= address)
150 goto good_area;
151 if (!(vma->vm_flags & VM_GROWSDOWN))//如果发生在一空洞上方的区间不是堆栈区,那么此地址是由于撤销映射留下的,进行错误处理
152 goto bad_area;
220 /*
221 * Something tried to access memory that isn't in our memory map..
222 * Fix it, but check if it's kernel or user first..
223 */
224 bad_area:
225 up(&mm->mmap_sem);
226 //用户态的错误处理
227 bad_area_nosemaphore:
228 /* User mode accesses just cause a SIGSEGV */
229 if (error_code & 4) {//判断错误发生在用户态
230 tsk->thread.cr2 = address;
231 tsk->thread.error_code = error_code;
232 tsk->thread.trap_no = 14;
233 info.si_signo = SIGSEGV;//强制发送SIGEGV信号