当前位置: 首页 > 科技观察

Linux内核如何判断一个地址是否在用户空间?

时间:2023-03-13 21:34:10 科技观察

1。问题描述access_ok函数的原理是什么?问题二、问题分析我们在内核空间和用户空间之间拷贝数据的时候,必须要判断用户空间地址是否合法。主要通过偶数函数access_ok来判断。1、Linux用户空间和内核地址空间Linux操作系统和驱动运行在内核空间,应用程序运行在用户空间。两者不能简单的使用指针来传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,内核空间使用用户空间指针时,对应的数据可能不在内存中。通常32位Linux内核地址空间分为0~3G为用户空间,3~4G为内核空间。注意这里是32位内核地址空间划分,和64位内核地址空间划分不同。进程寻址空间0~4G进程在用户态只能访问0~3G,只有内核态才能访问3G~4G进程通过系统调用进入内核态各自虚拟空间的3G~4G部分进程是同一个进程,从用户进入内核态,不会引起CR3的变化,但会引起栈的变化。2、access_ok详解原型:access_ok(type,addr,size);功能:access_ok——检查用户空间指针是否有效该函数可能只是检查指针是否在用户空间范围内。调用该函数后,内存访问函数仍可能返回-EFAULT参数说明:type访问类型:VERIFY_READ或VERIFY_WRITE。请注意VERIFY_WRITE是VERIFY_READ的超集——如果write如果写入块是安全的,那么从中读取总是安全的。addr用户空间指针,指向要检查的块的大小size要检查的块的大小返回值:该函数检查用户空间中的内存块是否可用。如果可用则返回true(非零值),否则返回false(0)。2.源码分析#defineaccess_ok(type,addr,size)(__range_ok(addr,size)==0)/*Weuse33-bitarithmetichere...*/#define__range_ok(addr,size)({\unsignedlongflag,roksum;\__chk_user_ptr(addr);\__asm__("adds%1,%2,%3;sbcccs%1,%1,%0;movcc%0,#0"\:"=&r"(标志),"=&r"(roksum)\:"r"(addr),"Ir"(size),"0"(current_thread_info()->addr_limit)\:"cc");\flag;})staticinlinevoid__chk_user_ptr(constvolatilevoid*p,size_tsize){assert(p>=__user_addr_min&&p+size<=__user_addr_max);}其中__range_ok详细解释如下:参数对应关系:flag------%0roksum------%1addr--------%2size--------%3汇编指令详解adds%1,%2,%3等同于:rosum=addr+size这个操作会影响状态位(目的是影响进位标志C)。下面两条指令的条件为CC,即C=0时执行;若上述加法指令携带(C=1),则不执行后面的指令,flag为初值current_thread_info()->addr_limit(非0),返回。如果没有进位(C=0),执行如下指令:sbcccs%1,%1,%0这条指令相当于rosum=rosum-flag-1(addr+size)-(current_thread_info()->addr_limit)-1,运算影响符号位。.If(addr+size)>=(current_thread_info()->addr_limit)-1,thenC=1If(addr+size)<(current_thread_info()->addr_limit)-1,thenC=0当C=0执行以下指令,否则跳过(标志非零)。movcc%0,#0相当于flag=0,将0赋值给flag。总结:__range_ok宏等价于:if(addr+size)>=(current_thread_info()->addr_limit)-1,返回一个非零值if(addr+size)<(current_thread_info()->addr_limit),返回零,access_ok是检查要操作的用户空间的地址范围是否在当前进程的用户地址空间限制范围内。这个宏的功能很简单,用C就可以实现,不需要用汇编。由于这两个函数经常使用,所以使用汇编来实现一些功能以提高效率。3.使用示例当我们从内核空间拷贝数据到用户空间或者从用户空间拷贝数据到内核空间时,我们需要判断用户空间地址是否在用户空间。staticinlineunsignedlong__must_checkcopy_from_user(void*to,constvoid__user*from,unsignedlongn){if(access_ok(VERIFY_READ,from,n))n=__copy_from_user(to,from,n);else/*securityhole-plugit*/memset(to,0,n));returnn;}staticinlineunsignedlong__must_checkcopy_to_user(void__user*to,constvoid*from,unsignedlongn){if(access_ok(VERIFY_WRITE,to,n))n=__copy_to_user(to,from,n);returnn;}