免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 774 | 回复: 0
打印 上一主题 下一主题

[内核入门] 越界访问 [复制链接]

论坛徽章:
13
15-16赛季CBA联赛之八一
日期:2016-07-08 21:00:1415-16赛季CBA联赛之同曦
日期:2017-02-15 14:26:1515-16赛季CBA联赛之佛山
日期:2017-02-20 14:19:2615-16赛季CBA联赛之青岛
日期:2017-05-07 16:49:1115-16赛季CBA联赛之广夏
日期:2017-07-30 09:13:1215-16赛季CBA联赛之广东
日期:2018-07-05 22:34:3615-16赛季CBA联赛之江苏
日期:2018-09-03 12:10:2115-16赛季CBA联赛之上海
日期:2018-09-25 03:49:2215-16赛季CBA联赛之广东
日期:2018-09-25 04:09:12
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2016-09-19 11:50 |只看该作者 |倒序浏览
  • 越界访问实例
    之前提到过,段式/页式映射管理中,映射关系由Linux内核建立,映射过程由硬件完成。
    现假设分配28K内存返回的起始虚拟地址为0x1bf2000,释放后又访问0x1bf2000:
  
    在未释放前,28K虚拟地址,按4K页面为单位,与物理页面一一建立映射,比如以0x1bf2000为起始的虚拟页面,目录、页表中都有关于该虚拟地址映射关系的目录项/表项:
   
    在释放后,内核就会将目录项/表项清零,此时再访问0x1bf2000虚拟地址时,硬件就会顺着页式映射的过程,找到空目录项或空表项,硬件就会产生一次异常,总之,经过一段~!@#$%^&……过程之后(详见《Linux内核源代码情景分析》第三章),会调用到内核中的do_page_fault()函数。
    由于仅通过目前已总结的内核知识,根本无法彻底理解这个函数,所以等学习过中断异常后,再回头详细分析这个函数,暂时只需要了解有这样一个函数,并且这个函数内部会通过一个current宏得到当前用户进程的管理结构,并向其发送SIGSEGV信号,也便是让程序员们如痴如醉的“段错误”!!

  • 因祸得福
    内核像是一位“严教、随和的老人”,上述的例子,领教了他严教的一面,对于有些栈空间的越界情况,他又会尽量的宽恕,原因是每个进程可以使用的栈空间大小是有上限的(比如2M),但内核在创建进程之初,一般不会一下子分配2M物理页面用于该进程的栈,等真的需要这么多时,才进行扩展。
    什么情况表示“需要”?
    就是进程在栈空间出现访问越界的时候,可以让esp寄存器移动最多的一条栈操作指令是pusha,可以让esp向下移动32byte,所以如果越界超过32byte长度,仍然是明显的非法操作,内核还是会发个SIGSEGV信号给用户进程。所以说内核对用户进程对栈空间越界访问的容忍,也不排除是容忍了一次真的越界访问,那样的话就说明用户进程有逻辑错误,这种错误也只会影响到该进程本身,早晚由程序员自己发现(内核总不至于帮app纠正“对话框歪了”等等这种bug吧)。
    了解了“需要”的判断依据,当do_page_fault()正好面对这种情况时,就会调用expand_stack()对该进程的栈区间进行扩展。对于用户进程来说,一次越界访问,不但没有造成段错误,还得到了更多的内存,其实这种越界操作,对于用户进程往往是透明的,除非故意定义一个超大的局部变量,比如int num[10M]。

    所谓“扩展”,就是分配内存,主要包括三个事情:分配虚拟内存、分配物理内存、建立映射(建议大致看一下do_page_fault())。
    do_page_fault()
        |
        |--> expand_stack()  // 扩展了虚拟内存
        |--> handle_mm_fault()
        |          |
        |          |--> pte_alloc()
        |          |--> handle_pte_fault()
        |          |          |
        |          |          |--> do_no_page()
        |          |          |          |
        |          |          |          |--> do_anonymous_page()
        |          |          |          |          |
        |          |          |          |          |--> alloc_page()  // 扩展了物理内存
        |          |          |          |          |--> set_pte()      // 建立映射(Linux内核建立映射,需要设置PGD、PMD、PT,所以详见代码)


  • 各个用户进程的内存布局
    直接贴上书里的图片(简洁粗略):
   

评分

参与人数 1可用积分 +18 收起 理由
王楠w_n + 18 赞一个!

查看全部评分

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP