- 论坛徽章:
- 13
|
本帖最后由 karma303 于 2016-08-11 10:11 编辑
linux内核的系统调用的入口,也就是sys_xxx()里,通常都会调用getname(),把字符串从用户空间拷贝到内核空间。其实最终调用的是strncpy_from_user()系列的函数。
原先看vfs的sys_open时候,就发现这个问题了。明明内核是可以直接访问用户空间的。今天又在sys_execve里看到这样的代码:filename = getname((char *) regs.ebx),终于忍不住google了一把。
SO上已经有人问了:
stackoverflow.com/questions/12666493/why-do-you-have-to-use-copy-to-user-copy-from-user-to-access-user-space-from
最佳答案说,sys_caller传进来的指针不保险,比如指向不存在页(尚未映射),或者指向内核空间。所以要检查一下。
提问者又接着问了,那我可不可以自己检查呢,我确认它安全之后,再直接访问,总可以了吧?
然后得到的回答是,I'm not sure what you mean。。。
其实有时候提问者是比回答者想的更深的。
最后,终于找到了一篇博客,是从安全角度解释的。
它提到了double fetch的概念。 内核要避免double fetch,即多次直接访问用户空间的同一个数据
举个例子,用户空间有一个结构体:
struct lstr{ int len; char *s; } lstr;
内核通过系统调用得到指向它的指针pstr,并do something:
char *buf = kmalloc( pstr->len); //first fetch
memcpy(buf, pstr->s, pstr->len); //second fetch
这两句代码可能造成内核崩溃。为什么呢,假如kmalloc之后,memcpy之前,用户空间的lstr结构体的内容被修改了(被另一个线程),len字段修改成了更大的数。 那memcpy就会越界。
所以要避免double fetch。换言之,应该先把它拷贝到内核空间,再访问¹。
linux在do_getname()的开头也注释道,"In order to reduce some races 。。"。 这儿的race,说的也是同一件事。
这篇博客基本上能说明问题了。 中途扫了一眼地址栏,发现竟然是微软的技术博客,唏嘘不已。
blogs.technet.microsoft.com/srd/2008/10/14/ms08-061-the-case-of-the-kernel-mode-double-fetch/
可见这儿是一个漏洞,windows和linux都注意到了,并且都采取了相同的应对措施,就是strncpy_from_user()。
另一篇描述这个漏洞的文章:
lwn.net/Articles/245630/
它讲的是"system call wrappers"(系统调用钩子)存在的安全漏洞。说要避免这个漏洞,除了关闭这个功能,别无它法————linux就没有开放自己的system call table。 但这又是一个很有用的功能,BSD上的很多软件都用到了,像比FreeBSD上的CerbNG firewall。
只看懂这么多。不过后面的评论我看懂了。
Please educate a curious cat
Posted Aug 16, 2007 5:27 UTC (Thu) by felixfix (subscriber, #242) [Link]
I understand what is going on; a pointer or some other piece of user data is changed by a user program, in a different thread probably, between validation and use.
I haven't written this kind of code; the last OS work I did passed all syscall parameters in registers. But I am a bit confused. Wouldn't it be very simple to avoid these race conditions by copying the user data to kernel memory before validating? Obviously this wouldn't work with the infamous setuid switcheroo, but for syscall parameters, it would seem to work very well. The only case I can think of to make it difficult would be where the user data in question is too large for easy copying to kernel memory.
【注】
【1】
对于上面的例子,就应该这样写:
struct lstr lstr;
memcpy(&lstr, pstr, sizeof(*pstr));
char *buf = kmalloc( str.len);
memcpy(buf, lstr.s str.len);
这个例子跟最开始说的sys_execve()那个例子相差了十万八千里,大家能领会我的意思就好了。 |
评分
-
查看全部评分
|