fork 和内核中映射的用户空间内存的交互

Gil*_*il' 5 fork shared-memory virtual-memory linux-kernel

考虑使用get_user_pages(或get_page)映射来自调用进程的页面的 Linux 驱动程序。然后将页面的物理地址传递给硬件设备。进程和设备都可以读取和写入页面,直到双方决定结束通信。特别地,在调用get_user_pages返回的系统调用之后,通信可以继续使用页面。系统调用实际上是在进程和硬件设备之间建立一个共享内存区域

我担心如果进程调用会发生什么fork(它可能来自另一个线程,并且可能在调用的系统调用get_user_pages正在进行中或稍后发生)。特别是,如果父级在fork后写入共享内存区域,我对底层物理地址了解多少(可能是因为copy-on-write而改变)?我想明白:

  1. 内核需要做什么来防御潜在的行为不端的进程(我不想创建安全漏洞!);
  2. 进程需要遵守哪些限制,以便我们的驱动程序的功能正常工作(即物理内存保持映射到父进程中的相同地址)。

    • 理想情况下,我希望子进程根本不使用我们的驱动程序(它可能exec几乎立即调用)来工作的常见情况。
    • 理想情况下,父进程在分配内存时不应采取任何特殊步骤,因为我们已有将堆栈分配的缓冲区传递给驱动程序的现有代码。
    • 我知道madvisewith MADV_DONTFORK,并且可以让内存从子进程的空间中消失,但它不适用于堆栈分配的缓冲区。
    • “当您与我们的驱动程序有活动连接时不要使用叉”会很烦人,但如果满足第 1 点,则可以作为最后的手段。

我愿意被指出文档或源代码。我特别查看了Linux Device Drivers,但没有发现这个问题得到解决。即使只是应用于内核源代码的相关部分的 RTFS 也有点让人不知所措。

内核版本不是完全固定的,而是最近的版本(比如 ??2.6.26)。如果重要的话,我们只针对 Arm 平台(目前是单处理器,但多核即将到来)。

Nic*_*not 4

Afork()不会干涉get_user_pages()get_user_pages()会给你一个struct page

您需要kmap()它才能访问它,并且此映射是在内核空间而不是用户空间中完成的。

编辑:get_user_pages()触摸页表,但您不应该担心这一点(它只是确保页面映射在用户空间中),如果这样做有任何问题,则返回 -EFAULT 。

如果您 fork(),直到执行写入时复制,子进程将能够看到该页面。一旦写时复制完成(因为子进程/驱动程序/父进程通过用户空间映射写入页面——而不是驱动程序具有的内核 kmap()),该页面将不再被共享。如果您仍然在页面上(在驱动程序代码中)持有 kmap(),您将无法知道您持有的是父页面还是子页面。

1)这不是一个安全漏洞,因为一旦你执行了 execve(),所有这些都消失了。

2)当您 fork() 时,您希望两个进程相同(这是一个分叉!!)。我认为你的设计应该允许父母和孩子都访问驱动程序。Execve() 将刷新所有内容。

在用户空间中添加一些功能怎么样:

 f = open("/dev/your_thing")
 mapping = mmap(f, ...)
Run Code Online (Sandbox Code Playgroud)

当在您的设备上调用 mmap() 时,您将安装带有特殊标志的内存映射: http://os1a.cs.columbia.edu/lxr/source/include/linux/mm.h#071

你有一些有趣的事情,例如:

#define VM_SHARED       0x00000008
#define VM_LOCKED       0x00002000
#define VM_DONTCOPY     0x00020000      /* Do not copy this vma on fork */
Run Code Online (Sandbox Code Playgroud)

VM_SHARED 将禁用写入时复制 VM_LOCKED 将禁用该页面上的交换 VM_DONTCOPY 将告诉内核不要在 fork 上复制 vma 区域,尽管我认为这不是一个好主意