2.4 linux存储管理-越界访问

来源:互联网 发布:遗传算法图像分割 编辑:程序博客网 时间:2024/05/23 19:26
当我们通过页式存储管理机制将线性地址转换成物理地址时,如果这个过程由于遇到某种阻碍而使得CPU无法访问到相应的物理单元,映射便失败了。
映射失败时,CPU会触发一种异常,这种异常称之为缺页异常(page fault exception),进而执行页面异常处理程序。
失败的原因:
1)虚拟、物理映射尚未建立
2)物理页面被swap off
3)访问权限不对应(想要写只读权限内存)
可能还有别的原因

CPU在发生页面异常时,会触发page_fault_exception处理,最后执行do_page_fault() 
do_page_fault的部分代码(开头):
  1. 106 asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
  2. 107 {
  3. 108 struct task_struct *tsk;
  4. 109 struct mm_struct *mm;
  5. 110 struct vm_area_struct * vma;
  6. 111 unsigned long address;
  7. 112 unsigned long page;
  8. 113 unsigned long fixup;
  9. 114 int write;
  10. 115 siginfo_t info;
  11. 116
  12. 117 /* get the address */
  13. 118 __asm__("movl %%cr2,%0":"=r" (address));
  14. 119
  15. 120 tsk = current;
  16. 121
  17. 122 /*
  18. 123 * We fault-in kernel-space virtual memory on-demand. The
  19. 124 * 'reference' page table is init_mm.pgd.
  20. 58
  21. 125 *
  22. 126 * NOTE! We MUST NOT take any locks for this case. We may
  23. 127 * be in an interrupt or a critical region, and should
  24. 128 * only copy the information from the master page table,
  25. 129 * nothing more.
  26. 130 */
  27. 131 if (address >= TASK_SIZE)
  28. 132 goto vmalloc_fault;
  29. 133
  30. 134 mm = tsk->mm;
  31. 135 info.si_code = SEGV_MAPERR;
  32. 136
  33. 137 /*
  34. 138 * If we're in an interrupt or have no user
  35. 139 * context, we must not take the fault..
  36. 140 */
  37. 141 if (in_interrupt() || !mm)
  38. 142 goto no_context;
  39. 143
  40. 144 down(&mm->mmap_sem);
  41. 145
  42. 146 vma = find_vma(mm, address);
  43. 147 if (!vma)
  44. 148 goto bad_area;
  45. 149 if (vma->vm_start <= address)
  46. 150 goto good_area;
  47. 151 if (!(vma->vm_flags & VM_GROWSDOWN))
  48. 152 goto bad_area;
(参见  arch/i386/mm/fault.c)
代码说明:
1.获取产生缺页异常的虚拟地址(线性地址)。产生缺页异常的虚拟地址被保存在CR2寄存器中,由于c语言无法读取CR2寄存器,需要使用gcc内嵌
汇编实现。
2. 内核中断/异常机制还会传回两个参数:struct pt_regs *regs, unsigned long error_code,regs指向寄存器的副本(保存“现场”),error_code指明映射失败的原因。
3. 内核中可以使用current(宏)取得当前进程的task结构体,然后我们可以获取当前进程的mm_struct(保存虚存管理和映射相关信息),CPU的映射过程不影响mm_struct结构,但是mm_struct描述了这种映射。
4. 进程出错:find_vma返回寻找到的第一个末尾地址大于address的vma,根据返回的vma结果进一步判断错误原因。
4.1 第一种 goto bad_area:没有找到合适的vma,说明address过大,访问的地址属于系统空间
4.2 goto good_area:说明找到合适vma,映射已经建立,原因需要进一步查找
除了前面两种情况,还有可能落在vma之间的洞里面
4.3 第一种空洞:堆栈区下面的大空洞
堆栈区的vma的vm_flags有VM_GROWSDOWN这个标志位
(这种情况的处理,也是goto good_area,里面会进一步判断)
4.4 第二种空洞:vma之间的小空洞

        5. 出错特殊情况
            中断处理程序出错(程序能够检测):
            若in_interrupt()返回非0,说明中断处理程序出错
            mm_struct为空(程序不能检测出):
因为mm_struct为空,说明进程的映射尚未建立,因而不可能与当前进程有关,而in_interrupt()又返回0,说明中断异常处理中出错,不过该错误in_interrupt()无法检测出来

do_page_fault的部分代码(bad_area处理):
  1. 220 /*
  2. 221 * Something tried to access memory that isn't in our memory map..
  3. 222 * Fix it, but check if it's kernel or user first..
  4. 223 */
  5. 224 bad_area:
  6. 225 up(&mm->mmap_sem);
  7. 226
  8. 227 bad_area_nosemaphore:
  9. 228 /* User mode accesses just cause a SIGSEGV */
  10. 229 if (error_code & 4) {
  11. 230 tsk->thread.cr2 = address;
  12. 231 tsk->thread.error_code = error_code;
  13. 232 tsk->thread.trap_no = 14;
  14. 233 info.si_signo = SIGSEGV;
  15. 234 info.si_errno = 0;
  16. 235 /* info.si_code has been set above */
  17. 236 info.si_addr = (void *)address;
  18. 237 force_sig_info(SIGSEGV, &info, tsk);
  19. 238 return;
  20. 239 }
error_code的取值和含义:
  1. 96 /*
  2. 97 * This routine handles page faults. It determines the address,
  3. 98 * and the problem, and then passes it off to one of the appropriate
  4. 99 * routines.
  5. 100 *
  6. 101 * error_code:
  7. 102 * bit 0 == 0 means no page found, 1 means protection fault
  8. 103 * bit 1 == 0 means read, 1 means write
  9. 104 * bit 2 == 0 means kernel, 1 means user-mode
  10. 105 */