+ -
当前位置:首页 → 问答吧 → 在kernel中对文件操作为什么需要get_fs()?

在kernel中对文件操作为什么需要get_fs()?

时间:2008-11-02

来源:互联网

看到牛人们在kernel中对文件进行操作,在write和read前,都要进行get_fs()和set_fs(),不知道是什么原因呢?
例如如下代码:
        mm_segment_t old_fs;
        

        if(file == NULL)
                file = filp_open(MY_FILE, O_RDWR | O_APPEND | O_CREAT, 0644);


        sprintf(buf,"%s", "The Messages.");

        old_fs = get_fs();
        set_fs(KERNEL_DS);
        file->f_op->write(file, (char *)buf, sizeof(buf), &file->f_pos);
        set_fs(old_fs);


其中,
typedef struct {
    unsigned long seg;
} mm_segment_t;

#define KERNEL_DS    MAKE_MM_SEG(0xFFFFFFFFUL)

#define MAKE_MM_SEG(s)    ((mm_segment_t) { (s) })


不太了解mm_segment_t这个字段的含义,请指教!

谢谢!

作者: new_learner   发布时间: 2008-11-02

我来简单说下:
系统调用本来是提供给用户空间的程序访问的,所以,对传递给它的参数(比如上面的buf),它默认会认为来自用户空间,在->write()函数中,为了保护内核空间,一般会用get_fs()得到的值来和USER_DS进行比较,从而放置用户空间程序“蓄意”破坏内核空间;

而现在你要在内核空间使用系统调用,此时传递给->write()的参数地址就是内核空间的地址了,在USER_DS之上(USER_DS ~ KERNEL_DS),如果不做任何其它处理,在write()函数中,会认为该地址超过了USER_DS范围,所以会认为是用户空间的“蓄意破坏”,从而不允许进一步的执行; 为了解决这个问题; set_fs(KERNEL_DS);将其能访问的空间限制扩大到KERNEL_DS,这样就可以在内核顺利使用系统调用了!

good luck!

作者: bshawk   发布时间: 2008-11-02

谢谢LS兄弟的解释!

可能是我说得不太清楚,实际上这个例子中并没有用到系统调用。

这个问题出自保留的帖子:http://linux.chinaunix.net/bbs/thread-738197-1-2.html

源码如下:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/syscalls.h>
#include <asm/unistd.h>
#include <asm/uaccess.h>

#define MY_FILE "/root/LogFile"

char buf[128];
struct file *file = NULL;



static int __init init(void)
{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mm_segment_t old_fs;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printk("Hello, I'm the module that intends to write messages to file.\n");


&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(file == NULL)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file = filp_open(MY_FILE, O_RDWR | O_APPEND | O_CREAT, 0644);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (IS_ERR(file)) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printk("error occured while opening file %s, exiting...\n", MY_FILE);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sprintf(buf,"%s", "The Messages.");

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;old_fs = get_fs();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set_fs(KERNEL_DS);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;file->f_op->write(file, (char *)buf, sizeof(buf), &file->f_pos);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set_fs(old_fs);


&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;
}

static void __exit fini(void)
{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(file != NULL)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;filp_close(file, NULL);
}

module_init(init);
module_exit(fini);
MODULE_LICENSE("GPL");


并没有用到系统调用,buf也是属于内核空间的。
所以,我还是不太明白这里为什么要get_fs()和set_fs()
还请指教,谢谢!

作者: new_learner   发布时间: 2008-11-02

二楼已经说的很清楚了

>>并没有用到系统调用,buf也是属于内核空间的。

但是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的由来。

作者: qtdszws   发布时间: 2008-11-02

多谢LS两位!
搞明白了:)

作者: new_learner   发布时间: 2008-11-03



QUOTE:
原帖由 new_learner 于 2008-11-2 19:49 发表
谢谢LS兄弟的解释!

可能是我说得不太清楚,实际上这个例子中并没有用到系统调用。

这个问题出自保留的帖子:http://linux.chinaunix.net/bbs/thread-738197-1-2.html

源码如下:
#include
#includ ...




如qtdszws 所说,此处虽然不是系统调用,但是顺着file->op->write()进入会进行用户空间参数检查access_ok.

假设是ext3文件系统,ext3_file_operations里面的两个操作函数字段:
(fs/ext3/file.c)
const struct file_operations ext3_file_operations = {
...
.write          = do_sync_write,
...
.aio_write      = ext3_file_write,
...
}
顺着这个线下去(2.6.20),
file->fop->write() =>do_sync_write() => ext3_file_write() => generic_file_aio_write() =>__generic_file_aio_write_nolock() ,

会在__generic_file_aio_write_nolock()函数中对刚开始file->f_op->write()传进来的参数进行access_ok()检查

作者: kissGNU   发布时间: 2008-11-03

现在才看到ULK上有讲解^_^



QUOTE:
10.4.1. Verifying the Parameters


The value of the addr_limit.seg field can be dynamically changed by the get_fs and set_fs macros; this allows the kernel to bypass the security checks made by access_ok( ), so that it can invoke system call service routines, directly passing to them addresses in the kernel data segment.

作者: new_learner   发布时间: 2009-04-22

这篇文章有对在内核中进行系统调用的详细说明:
Kernel System Calls

~linux/include/asm-x86_64/uaccess.h 有关get_fd() get_fs() set_fs()的定义
  1. /*
  2. * The fs value determines whether argument validity checking should be
  3. * performed or not.  If get_fs() == USER_DS, checking is performed, with
  4. * get_fs() == KERNEL_DS, checking is bypassed.
  5. *
  6. * For historical reasons, these macros are grossly misnamed.
  7. */

  8. #define MAKE_MM_SEG(s)        ((mm_segment_t) { (s) })

  9. #define KERNEL_DS        MAKE_MM_SEG(0xFFFFFFFFFFFFFFFFUL)
  10. #define USER_DS                MAKE_MM_SEG(PAGE_OFFSET)

  11. #define get_ds()        (KERNEL_DS)
  12. #define get_fs()        (current_thread_info()->addr_limit)
  13. #define set_fs(x)        (current_thread_info()->addr_limit = (x))

  14. #define segment_eq(a,b)        ((a).seg == (b).seg)

  15. #define __addr_ok(addr) (!((unsigned long)(addr) & (current_thread_info()->addr_limit.seg)))
复制代码

作者: zzyhrb   发布时间: 2010-08-02

热门下载

更多