如何从 Linux 内核访问用户空间内存?

Posted

技术标签:

【中文标题】如何从 Linux 内核访问用户空间内存?【英文标题】:How to access user space memory from the Linux kernel? 【发布时间】:2012-05-17 14:24:03 【问题描述】:

我知道copy_to_user/copy_from_userget_user/put_user 函数就是为此目的。

我的问题是,给定一个用户空间地址/指针,我一般如何从内核访问该地址所指向的数据?

我可以想象,首先我必须确保包含页面应该在物理内存中(而不是在磁盘中)。

下一步是什么?可以用*p,其中p是指向一些用户空间数据的指针,直接引用数据吗?

还是我必须先调用kmap 将包含的物理页框映射到内核虚拟地址空间?为什么?

【问题讨论】:

【参考方案1】:

只有指针是不够的!您需要知道该指针“属于”哪个进程。

当进程被抢占时,指针指向另一个进程的地址空间。地址可能不会再映射了,yadda yadda,

如果该进程是您访问数据时的当前进程,那么您应该使用 copy_to_user/copy_from_user 函数。

如果进程可能被调度,您可以尝试 mlock() RAM 中的页面并找出该页面的物理 RAM 地址。每当您想访问它时,您将该物理页面映射到内核虚拟地址。

注意:

恶意进程可以 munlock() 页面并诱骗您访问错误的 RAM 页面。 我不确定 mlock() 语义是否要求下划线 RAM 页面不得更改。 内核应该能够将页面锁定到 RAM,我不熟悉 mm 子系统。

【讨论】:

【参考方案2】:

不同的用户空间应用有不同的页表。

    您需要获取用户空间程序 pid。 在 pid 的页表中搜索地址。

以下是将用户空间虚拟地址转换为物理地址的示例代码。 它适用于 x86 平台。

taskpid = find_get_pid(curpid);
task = pid_task(taskpid, PIDTYPE_PID );
mm = get_task_mm(task);
down_read(&mm->mmap_sem);

start_vaddr = vaddr;
end_vaddr = 0xC0000000;

while( start_vaddr < end_vaddr)
    u32 end;

    end = (( start_vaddr + PMD_SIZE) & PMD_MASK);

    if( end < start_vaddr || end > end_vaddr)
        end = end_vaddr;

    ret = walk_pgd(start_vaddr, end, mm);
    if(ret != 0)
        printk("ret: %08x \n", ret);
        break;
    

    start_vaddr = end;



up_read(&mm->mmap_sem);

paddr = ret;
kaddr = __va(paddr);
mmput(mm);

【讨论】:

好点,代码逻辑很好。但我想有一些哈希表或类似的数据结构,给定一个虚拟地址,可以帮助您快速定位物理页面。有一个缺陷:kaddr = __va(paddr);此行仅在 paddr 驻留在低内存时才有效,对吧? paddr 表示物理地址,所以,一直存在于内存中。 kaddr 表示内核地址。在 Linux 内核中定义为#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))。内核地址内存映射并不复杂,只是一个PAGE_OFFSET。 (在 x86 模式下应为 0xC0000000)。还有其他方法可以获取地址。用户空间应用程序可以通过 /proc//pagemap 访问内核地址以获取页面信息。如果能得到PFN,也能得到内核地址。【参考方案3】:

您需要follow 一个地址来获得相应的page 结构(参见follow_page 的示例)。接下来,获取page 结构,您需要通过kmapkmap_atomic 将其映射到内核的地址空间。

【讨论】:

【参考方案4】:

您可能会发现这很有用。

让我们重复一遍 read 和 write 方法的 buff 参数是 用户空间指针。因此,它不能被直接取消引用 内核代码。这种限制有几个原因:

取决于您的驱动程序在哪个架构上运行,以及 内核已配置,用户空间指针可能无效,而 完全在内核模式下运行。可能没有映射 地址,或者它可以指向其他一些随机数据。

即使指针在内核空间中的含义相同, 用户空间内存被分页,并且有问题的内存可能不是 进行系统调用时驻留在 RAM 中。试图参考 用户空间内存直接会产生页面错误,即 内核代码不允许做的事情。结果将是 “哎呀”,这将导致进程的死亡 系统调用。

有问题的指针由用户程序提供,该程序 可能是错误的或恶意的。如果您的驱动程序曾经盲目地取消引用 用户提供的指针,它提供了一个开放的门口,允许 用户空间程序访问或覆盖内存中的任何位置 系统。如果您不希望对损害 用户系统的安全性,你永远不能取消引用 直接用户空间指针。

来源:http://www.makelinux.net/ldd3/chp-3-sect-7

也就是说,我自己很想知道如果用户空间地址确实有效,并且上述条件都不适用会发生什么......

【讨论】:

以上是关于如何从 Linux 内核访问用户空间内存?的主要内容,如果未能解决你的问题,请参考以下文章

Linux的内核空间和用户空间是如何划分的(以32位系统为例)?

linux内存地址分配

Linux内存模型和Linux访问用户空间内存API详解

linux申请用户空间内存原则?

Linux x86 上的内核空间是不是映射到用户空间?

Linux - 用户态内存映射 和 内核态内存映射