在内核中使用有些系统调用(如打开,写文件等操作)需要使用get_fs,set_fs对他们进行保护。如:
oldfs=get_fs();
set_fs(KERNEL_DS);
filp->f_op->write(filp,buf,size,&filp->f_pos);
set_fs(oldfs);
只有使用上面的方法,才能在内核中使用open,write等的系统调用。其实这样做的主要原因是open,write的参数在用户空间,在这些系统调用 的实现里需要对参数进行检查,就是检查它的参数指针地址是不是用户空间的。系统调用本来是提供给用户空间的程序访问的,所以,对传递给它的参数(比如上面 的buf),它默认会认为来自用户空间,在->write()函数中,为了保护内核空间,一般会用get_fs()得到的值来和USER_DS进行 比较,从而防止用户空间程序“蓄意”破坏内核空间。 为了解决这个问题; set_fs(KERNEL_DS)将其能访问的空间限制扩大到KERNEL_DS,这样就可以在内核顺利使用系统调用了!
我们这里以open系统调用为例子,它最终会调用下面所示的函数:
satic
int do_getname(const char __user *filename, char *page) {
int retval; unsigned long len = PATH_MAX;
if (!segment_eq(get_fs(), KERNEL_DS)) {
if ((unsigned long) filename >= TASK_SIZE)
return -EFAULT;
}
其 中就会对char __user *filename这个用户指针进行判断,如果它不是segment_eq(get_fs(), KERNEL_DS)就需要如上面描述的检查它的指针是不是用户空间指针。内核使用系统调用参数肯定是内核空间,为了不让这些系统调用检查参数所以必须设 置 set_fs(KERNEL_DS)才能使用该系统调用.
file->f_op->write的流程可能会调用access_ok->__range_ok,而__range_ok会判断访问 的buf是否在0~addr_limit之间,如何是就ok,否则invalid,这显然是为用户准备的检查。addr_limit一般设为 __PAGE_OFFSET,在内核空间,buf肯定>__PAGE_OFFSET,必须修改addr_limit,这就是set_fs的由来。
- #define MAKE_MM_SEG(s) ((mm_segment_t) { (s) })
-
-
-
- /* addr_limit is the maximum accessible address for the task. we misuse
-
- * the KERNEL_DS and USER_DS values to both assign and compare the
-
- * addr_limit values through the equally misnamed get/set_fs macros.
-
- * (see above)
-
- */
-
-
-
- #define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFF)
-
- #define USER_DS MAKE_MM_SEG(TASK_SIZE)
-
-
-
- #define get_ds() (KERNEL_DS)
-
- #define get_fs() (current_thread_info()->addr_limit)
-
- #define set_fs(x) (current_thread_info()->addr_limit = (x))
-
-
-
- #define segment_eq(a,b) ((a).seg == (b).seg)
- /* how to get the thread information struct from C */
-
- static inline struct thread_info *current_thread_info(void)
-
- {
-
- struct thread_info *ti;
-
- __asm__("and.d $sp,%0; ":"=r" (ti) : "0" (~8191UL));
-
- return ti;
-
- }
- struct thread_info {
-
- struct task_struct *task; /* main task structure */
-
- struct exec_domain *exec_domain;/* execution domain */
-
- unsigned long flags; /* thread_info flags (see TIF_*) */
-
- mm_segment_t addr_limit; /* user-level address space limit */
-
- __u32 cpu; /* current CPU */
-
- int preempt_count; /* 0=premptable, <0=BUG; will also serve as bh-counter */
-
- struct restart_block restart_block;
-
- };
- From:http://blog.chinaunix.net/uid-24237502-id-35023.html
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/29517383/viewspace-1376508/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/29517383/viewspace-1376508/