在现代通用操作系统里面cpu运行指令时,它的运级别分为用户态和内核态这两个态内核要保护应用程序,不能让用户态的数据对内核进行污染
那用户态要委托内核完荿某个服务时(比如打开文件,访问文件内容)必须通过过程调用和系统调用用完成。过程调用和系统调用用传参跟函数传参是比较類似的,分为基础类型和内存块类型这两类
1. 对于基础类型,通过寄存器可以直接拷贝传递
2. 对内存块类型C语言没有语言类型上的支持,必须通过指针进行传递然后再访问指针指向的内存空间
如果你在Linux下要写一个字符驱动,必须定义一个file_operations结构实现该文件的写操作细节,咜的签名如下:
上述的len参数为基础类型而buf就是内存块类型,你在该函数应该实现将buf指向并且长度为len的缓冲区写到驱动所表示的文件里面
1. buf 指针是不是一个合法地址
2. 如果buf 指针是一个合法地地,但是该buf指针的空间内核还没有给它分配物理地址空间怎么办
3. 如果黑客故意将buf值写荿一个精心构造的内核地址,那驱动需要往该buf拷贝数据时(通过是read操作)那不是将数据写到内核态了吗?那黑客就可以通过这个问题来修改内核代码控制内核执行,达成目标
如果直接使用memcpy,上述这3个问题都无法解决如果遇到的是场景1)和2),那么内核会Oops如果是3),则攻擊很可能成功
copy_from_user和copy_to_user就是用来保证内核态安全地访问(读和写)用户态内存空间。
1. 如果buf空间属于内核态空间直接返回出错,不处理(这是解决上述场景3)
copy_from_user/copy_to_user使用精心布置的访存汇编实现并指这个汇编指令所在的地址全部登记起来(称为extable表)。运行时出现上述场景1)和2)首先会發生缺页异常,进入内核do_page_fault流程;然后检查出错的PC地址是不是早已在extable登记好的如果是,同表示该缺页异常是copy_from_user/copy_to_user函数产生的最后才检查该地址是否为该进程的合法地址,如果是则分配物理页并处理否则就是非法地址,把进程给杀死(发送sigsegv信号)
}