【问题标题】:copy_from_user throws unable to handle kernel paging requestcopy_from_user 抛出无法处理内核分页请求
【发布时间】:2016-09-01 08:17:41
【问题描述】:

我正在尝试使用模块读取/写入内核内存,到目前为止,读取部分正在工作。 我有一个来自用户空间的程序,它打开我的模块创建的设备,并且可以从 x addr(内核内存地址)读取 n 个字节,这可以工作(使用 copy_to_user)。 我还通过使用 gdb 读取相同的地址来确认这一点:

gdb /bin/ls /proc/kcore x/2b [addr]

当我尝试写入相同的内存地址时,问题就出现了。 一旦我尝试这个,我得到:

BUG: unable to handle kernel paging request at [addr] (我正在阅读相同的地址) 我必须说我从 /proc/kallsyms 中获取了这个地址。

令我不安的是,虽然我们将物理内存映射到内核内存,这意味着即使我们没有足够的内存来映射我们所有的物理内存,至少映射到内核的范围内存应该存在,并且如果我能够从该地址读取,这意味着实际上该地址现在就存在。

代码如下:

            case IOCTL_WRITE_KERNEL_MEMORY:

            pr_info ("%s: IOCTL_WRITE_KERNEL_MEMORY\n", r2_devname);

            if (data->addr < PAGE_OFFSET) {
                    pr_info ("%s: error - 0x%lx belongs to USERSPACE\n", r2_devname, (unsigned long)data->addr);
                    ret = -EINVAL;
                    return ret;
            }

            pr_info ("%s: addr: 0x%lx\n", r2_devname, (unsigned long)data->addr);

            ret = copy_from_user ((void *)data->addr, data->buff, len);
            if (ret) {
                    pr_info ("error: copy_from_user failed\n");
                    ret = -EINVAL;
                    return ret;
            }
            break;

data->addr 包含地址,data->buff 包含要读取的内容。这是通过来自用户空间的指针传递的:

            case WRITE_KERNEL_MEMORY:

            if (argc < 4) {
                    printf ("specify bytes to write\n");
                    break;
            }

            unsigned char c = 0xd;
            data.buff = &c;
            data.addr = 0xf8350000;
            data.len = n_bytes;

            printf ("ioctl: going to write: 0x%x\n", c);
            printf ("ioctl: going to write: 0x%x\n", *data.buff);

            ioctl_n = _IOR (R2_TYPE, 0x2, sizeof (struct r2k_data));
            ret = ioctl (fd, ioctl_n, &data);
            break;

和结构:

struct r2k_data {
        unsigned long *addr;
        unsigned long len;
        unsigned char *buff;
};

我认为如果我可以从那个地址读取,我应该也可以写,即使我因为写保护而不能写,我想我应该得到另一个错误消息。

有人有想法吗?

非常感谢

【问题讨论】:

  • I think that if I could read from that addr, I should be able to write too, and even if i'm not able because of write protection, I think I should get another error message. - 不,可读取的内存不需要可用于写入。写入 readonly 内存区域 within kernel 会导致 common 错误消息 unable to handle kernel paging request at;没有针对这种情况的具体消息。
  • 嗨@Tsyvarev,你知道如何检查地址是否被写保护吗?也许从 include/linux/page-flags.h 检查一些 PG_flags ?谢谢
  • 在检查标志之前,您应该找到所需地址的页面描述符。您希望在给定地址找到什么类型的信息?例如,内核代码通常是写保护的,代码中标记为const的全局数据也是如此。
  • 我找到了带有 virt_to_page 的页面描述符。其实这只是一个测试。在这个地址(我正在阅读的地址)处放置了函数 native_read_cr4。我不希望找到任何特别的东西,只是我可以从那个地址中读取,我很好奇我是否也可以写到那个地址。无论如何,我想在尝试之前测试地址是否可写。我正在查看 PG_* 标志,但从名称中我找不到正确的标志。谢谢。
  • 我发现“native_read_cr4”函数的 addr 和模块内部创建的变量的 addre 之间的唯一区别是 native_read_cr4 的 addr 持有 PG_reserved,而另一个没有。所以我想我可以通过检查这个位来检查一个 addr 是否可写。

标签: c linux memory linux-kernel


【解决方案1】:

您不能只读取或写入任意地址!无论是读还是写,所有地址都必须被“映射”,即被内核标记为可用并具有足够的权限让您执行操作。

对于您的特定场景: a) read() 有效,因为目标地址“data.buff”已映射并分配给您的用户模式进程上下文(正在运行的应用程序)。 b) write() 失败,因为: src是

data.addr = 0xf8350000;

这是一个任意的内核虚拟地址;你不能“只是”访问它! 为此,您为什么不分配一个内核缓冲区(使用 kmalloc 或 vmalloc)并将 它的 地址视为目标地址。它现在是一个有效的映射内核内存位置。伪代码:

 to_addr = kmalloc(512, GFP_KERNEL);
  <out of memory check>
 ret = copy_from_user ((void *)to_addr, data->buff, len);
 ...
 kfree(to_addr);

【讨论】:

  • 不是任意地址。顺便说一句,读操作是这样执行的:`ret = copy_to_user (data->buff, (void *)data->addr, len); if (ret) { pr_info ("error: copy_to_user failed\n"); ret = -EINVAL;返回 ret; ` 和 data->addr 指向我在写操作中使用的同一个内核地址。我认为正如@Tsyvarev 提到的,必须对烫发做更多的事情。无论如何,谢谢你;)
猜你喜欢
  • 2019-02-17
  • 2016-11-06
  • 1970-01-01
  • 2018-03-17
  • 2014-08-14
  • 2012-01-04
  • 1970-01-01
  • 2015-01-11
  • 1970-01-01
相关资源
最近更新 更多