Linux内核态直接写入用户内存失败问题排查与解决
问题原因分析
stdio缓冲区的误导:你在用户态使用的
fwrite是C标准库的带缓冲IO函数。调用fwrite时,数据不会直接传递给内核的proc write回调,而是先写入stdio库维护的用户态临时缓冲区。内核回调中拿到的ubuf是这个临时缓冲区的地址,而非你用posix_memalign分配的buffer。因此你在内核中修改的是临时缓冲区,而非目标内存,用户态的buffer自然不会有变化。内核代码的潜在健壮性问题:你的内核代码未检查
pin_user_pages的返回值,若该调用失败(如用户态地址非法),后续操作会触发崩溃;另外,现代内核中更推荐用kmap_local_page替代kmap(无全局锁,更安全),不过这不是当前问题的核心。
解决方法
1. 用户态绕过stdio缓冲区,直接使用系统调用
将用户态的fwrite替换为write系统调用,这样内核拿到的ubuf就是你自己分配的buffer地址:
char *buffer; const size_t PAGE_SIZE = getpagesize(); posix_memalign(&buffer,PAGE_SIZE,PAGE_SIZE); *buffer = 89; int fd = open("/proc/dt/alloc", O_WRONLY); write(fd, buffer, 100); close(fd);
2. 修复内核代码的健壮性(可选但推荐)
添加错误检查,并使用更安全的kmap_local_page:
static ssize_t write_proc_alloc(struct file *file, const char __user *ubuf, size_t count, loff_t *data) { struct page *pages[1]; int ret; mmap_read_lock(current->mm); ret = pin_user_pages((unsigned long)ubuf, 1, FOLL_WRITE, pages, NULL); if (ret != 1) { mmap_read_unlock(current->mm); return -EFAULT; } char *buf = kmap_local_page(pages[0]); *buf = 123; kunmap_local(buf); set_page_dirty(pages[0]); unpin_user_pages(pages, 1); mmap_read_unlock(current->mm); return count; // 需返回正确的写入字节数 }
关键说明
- 直接修改用户态内存的前提是:内核拿到的
ubuf确实是目标进程的有效用户态地址,write系统调用能保证这一点(直接传递用户缓冲区地址给内核)。 FOLL_WRITE标志确保页面被标记为可写,set_page_dirty确保修改被刷回用户态(多数情况下内核会自动处理脏页回写,但显式标记更稳妥)。
内容的提问来源于stack exchange,提问作者Segmented
相关产品推荐
相关产品推荐

