免费注册 查看新帖 |

Chinaunix

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

在kernel中对文件操作为什么需要get_fs()? [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-11-02 11:58 |只看该作者 |倒序浏览
看到牛人们在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这个字段的含义,请指教!

谢谢!

论坛徽章:
0
2 [报告]
发表于 2008-11-02 12:44 |只看该作者
我来简单说下:
系统调用本来是提供给用户空间的程序访问的,所以,对传递给它的参数(比如上面的buf),它默认会认为来自用户空间,在->write()函数中,为了保护内核空间,一般会用get_fs()得到的值来和USER_DS进行比较,从而放置用户空间程序“蓄意”破坏内核空间;

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

good luck!

论坛徽章:
0
3 [报告]
发表于 2008-11-02 19:49 |只看该作者
谢谢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()
还请指教,谢谢!

论坛徽章:
0
4 [报告]
发表于 2008-11-02 21:14 |只看该作者
二楼已经说的很清楚了

>>并没有用到系统调用,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的由来。

论坛徽章:
0
5 [报告]
发表于 2008-11-03 10:15 |只看该作者
多谢LS两位!
搞明白了:)

论坛徽章:
0
6 [报告]
发表于 2008-11-03 10:18 |只看该作者
原帖由 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()检查

论坛徽章:
0
7 [报告]
发表于 2009-04-22 10:11 |只看该作者
现在才看到ULK上有讲解^_^

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.

论坛徽章:
0
8 [报告]
发表于 2010-08-02 16:40 |只看该作者
这篇文章有对在内核中进行系统调用的详细说明:
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)))
复制代码

论坛徽章:
0
9 [报告]
发表于 2011-04-26 19:46 |只看该作者
我来简单说下:
系统调用本来是提供给用户空间的程序访问的,所以,对传递给它的参数(比如上面的buf),它 ...
bshawk 发表于 2008-11-02 12:44
这位大侠,太感谢你了。找了很多都没有看懂,你的一席话使我醍醐灌顶,听君一席话,省我十本书!太感谢了!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP