《Linux内核编程》的源代码示例

9
我正在阅读罗伯特·洛夫的书,第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'是一个类型为'unsigned long'的对象,并且定义在内核堆栈上,即其初始值可能是垃圾。无论如何,在堆栈中复制'len'个字节到buf所在的位置是否有效?也就是说,它可能会覆盖一些有用的东西吗?也许只有对于这个特定的例子才可以这样做。


只要len == sizeof(unsigned long),看起来就没问题。所有的操作都是将src中的len个字节写入到buf中。只是不要让len太大,否则返回地址和其他重要结构可能会被覆盖。 - flumpb
1
@kisplit: 无论何时,你绝对不能依赖调用者遵守规则,尤其是在内核中。如果你想确保拷贝仅限于一个ulong,你应该在拷贝操作中使用sizeof(unsigned long)而不是len。我在我的回答中提供了更安全的版本。 - paxdiablo
@paxdiablo:我完全同意。当然,在这个特定的示例中,除非len == sizeof(unsigned long),否则它是危险的。 - flumpb
2个回答

17

这段内容极其值得商榷,事实上,它非常危险。考虑到作者只是试图展示copy_from_usercopy_to_user 的工作原理,因此我会给予作者信任,但他们确实应该提供更安全的示例。

特别是,因为书中对如何必须格外小心进行了赞美:

系统调用必须仔细验证所有参数,以确保它们是有效和合法的。系统调用在内核空间中运行,如果用户可以毫无限制地将无效输入传递到内核,那么系统的安全性和稳定性就会受到影响。

然后提供了一种让用户完全摧毁内核的方法 :-)


我手头的副本中写着:

让我们考虑一个使用copy_from_user()copy_to_user()的系统调用的示例。这个系统调用叫做silly_copy(),它毫无价值;它将数据从第一个参数复制到第二个参数中。这不够优化,因为它涉及一次中间和无用的拷贝到内核空间,没有任何好处。但这有助于说明这个调用的点。

/*
* silly_copy - pointless syscall that copies the len bytes from
* ‘src’ to ‘dst’ using the kernel as an intermediary in the copy.
* Intended as an example of copying to and from the kernel.
*/
SYSCALL_DEFINE3(silly_copy,
                unsigned long *, src,
                unsigned long *, dst,
                unsigned long len)
{
    unsigned long buf;

    /* copy src, which is in the user’s address space, into buf */
    if (copy_from_user(&buf, src, len))
        return -EFAULT;

    /* copy buf into dst, which is in the user’s address space */
    if (copy_to_user(dst, &buf, len))
        return -EFAULT;

    /* return amount of data copied */
    return len;
}
除了没有检查参数的灾难性故障之外,我非常确定SYSCALL_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;
}

任何试图实现该系统调用的人都最好避免使用书中的那个示例,虽然我想,至少它会给你一些很好的内核调试经验 :-)


感谢您的评论!在您的示例中,应该是copy_from_user(buf, src, chunklen)而不是copy_from_user(&buf, src, chunklen),即只需提供数组的第一个元素的指针和长度即可。 - Mark
@Mark,是的,你说得对,我已经改成正确的形式了。 - paxdiablo

-1
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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接