copy_from_user 抛出无法处理内核分页请求

标签 c linux memory linux-kernel

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

gdb/bin/ls/proc/kcore x/2b [地址]

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

BUG:无法处理 [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 包含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;
};

我认为如果我可以从该地址读取,我也应该能够写入,即使由于写保护而无法写入,我认为我应该收到另一条错误消息。

有人有想法吗?

非常感谢

最佳答案

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

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

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

关于copy_from_user 抛出无法处理内核分页请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39266239/

相关文章:

c - 什么时候整数的符号真正重要?

c - MicroC rs-485、pic16f887 字符串发送问题

c - 使用自定义堆的类似 malloc 的函数

memory - PCI BAR 内存地址

c++ 创建新对象时 : iterator not incrementable

c - 网络链接,海湾合作委员会。对 rtnl_open 的 undefined reference

linux - Linux中waitpid()的返回值

linux - 在 TI OMAP 3530 上插入适用于旧文件系统的 Linux 模块

linux - 修改 Path 文件后,所有基本命令(ls、cd、...以及 vi、vim)都不起作用

java - 在java中,如果我扩展一个类并且在重新定义每个函数时不使用 super 字段,该字段是否仍然使用内存?