C 源代码示例来自;“Linux内核编程”;
正在阅读Robert Love的书,关于系统调用的第5章,发现这个简单的例子有点可疑:C 源代码示例来自;“Linux内核编程”;,c,linux-kernel,C,Linux Kernel,正在阅读Robert Love的书,关于系统调用的第5章,发现这个简单的例子有点可疑: asmlinkage long sys_silly_copy(unsigned long *src, unsigned long *dst, unsigned long len) { unsigned long buf; if (copy_from_user(&buf, src, len)) return -EFAULT; ... } 正如我们看到的,“buf”是类型
asmlinkage long sys_silly_copy(unsigned long *src, unsigned long *dst, unsigned long len)
{
unsigned long buf;
if (copy_from_user(&buf, src, len))
return -EFAULT;
...
}
正如我们看到的,“buf”是类型为“unsigned long”的对象,定义在内核堆栈上,即其初始值可能是垃圾。无论如何,在buf所在的堆栈中复制“len”字节是否有效,即是否可以覆盖有用的内容?也许这只适用于这个特定的例子?这是非常值得怀疑的。事实上,这是非常危险的。我将在这里给作者一个质疑的好处,因为他们只是想展示从用户
复制到用户和复制到用户
是如何工作的,但他们真的应该提供一个不那么危险的例子
尤其是因为这本书抒情地讲述了你必须格外小心:
系统调用必须仔细验证其所有参数,以确保它们有效且可靠
合法。系统调用在内核空间中运行,如果用户可以将无效输入传递到
内核没有约束,系统的安全性和稳定性都会受到影响
然后为用户提供一种完全消除内核的方法:-)
我拥有的副本中的文本说明:
让我们考虑一个示例系统调用,它使用了
除了没有检查参数的灾难性失败之外,我非常确定
SYSCALL\u DEFINE3
的最后一个参数缺少一个逗号(尽管这只是一个输入错误)
一个更好的例子是,在不必分配任意内存的情况下:
SYSCALL_DEFINE3(silly_copy,
unsigned long *, src,
unsigned long *, dst,
unsigned long, len)
{
unsigned long buf[64]; /* Buffer for chunks */
unsigned long lenleft = len; /* Remaining size */
unsigned long chunklen = sizeof(buf); /* Initial chunk length */
/* Loop handling chunk sizes */
while (lenleft > 0) {
/* Change chunk length on last chunk */
if (lenleft < chunklen) chunklen = lenleft;
/* copy src(user) to buf(kernel) then dst(user) */
if (copy_from_user(buf, src, chunklen)) return -EFAULT;
if (copy_to_user(dst, buf, chunklen)) return -EFAULT;
/* Adjust pointers and remaining size */
src += chunklen; dst += chunklen; lenleft -= chunklen;
}
/* return amount of data copied */
return len;
}
SYSCALL\u DEFINE3(愚蠢的副本,
无符号长*,src,
无符号长*,dst,
无符号长(len)
{
块的无符号长buf[64];/*缓冲区*/
无符号长lenleft=len;/*剩余大小*/
无符号长chunklen=sizeof(buf);/*初始块长度*/
/*循环处理块大小*/
而(左>0){
/*更改最后一个区块的区块长度*/
如果(lenleft
任何试图实现该系统调用的人都最好避开书中的特定示例,尽管我认为,至少它会给您一些良好的内核调试体验:-)只要len==sizeof(unsigned long),就可以了。所发生的一切就是src中的len字节被写入buf。只是不要太大,否则ret地址和其他重要结构可能会太大overwritten@kisplit:您从不依赖调用方遵守规则,特别是如果您是内核。如果要确保复制仅用于单个ulong,则在复制操作中使用
sizeof(unsigned long)
,而不是len
。我在回答中提供了一个更安全的版本。@paxdiablo:我完全同意。当然,这是危险的,除非在此特定示例中len==sizeof(unsigned long)。谢谢您的评论!在您的示例中,它不应该是copy_from_user(buf,src,chunklen)而不是copy_from_user(&buf,src,chunklen),也就是说,它足以提供指向数组第一个元素和长度的指针。@Mark,是的,您是对的,我已将其更改为正确的形式。
SYSCALL_DEFINE3(silly_copy,
unsigned long *, src,
unsigned long *, dst,
unsigned long, len)
{
unsigned long buf[64]; /* Buffer for chunks */
unsigned long lenleft = len; /* Remaining size */
unsigned long chunklen = sizeof(buf); /* Initial chunk length */
/* Loop handling chunk sizes */
while (lenleft > 0) {
/* Change chunk length on last chunk */
if (lenleft < chunklen) chunklen = lenleft;
/* copy src(user) to buf(kernel) then dst(user) */
if (copy_from_user(buf, src, chunklen)) return -EFAULT;
if (copy_to_user(dst, buf, chunklen)) return -EFAULT;
/* Adjust pointers and remaining size */
src += chunklen; dst += chunklen; lenleft -= chunklen;
}
/* return amount of data copied */
return len;
}
int init_module(void)
{
mempool_t *mempool;
struct kmem_cache *kmem_cache;
void *p0 , *p1;
kmem_cache = kmem_cache_create("Ashrama" ,100 , 0 ,SLAB_PANIC ,NULL);
mempool = mempool_create(4 , mempool_alloc_slab , mempool_free_slab , kmem_cache);
p0 = mempool_alloc(mempool, SLAB_PANIC);
p1 = mempool_alloc(mempool , SLAB_PANIC);
strcpy(p0 , "Ranjan.B.M");
strcpy(p1 , "Mithun.V");
mempool_free( p0 , mempool);
printk(KERN_ALERT"%s",p0);
printk(KERN_ALERT"%s",p1);
}