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

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

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

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

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

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

【问题讨论】:

    标签: memory-management linux-kernel


    【解决方案1】:

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

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

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

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

    注意:

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

    【讨论】:

      【解决方案2】:

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

      1. 您需要获取用户空间程序 pid。
      2. 在 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

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

        【讨论】:

          猜你喜欢
          • 2018-06-30
          • 1970-01-01
          • 2018-05-22
          • 2018-03-27
          • 1970-01-01
          • 1970-01-01
          • 2012-12-26
          • 2018-01-01
          • 1970-01-01
          相关资源
          最近更新 更多