You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android MSM内核copy_to_user仅支持128字节以内拷贝问题问询

解决Android内核驱动中copy_to_user大缓冲区拷贝失败的问题

从你的描述和日志来看,核心问题是copy_to_user在拷贝超过128字节时完全失败(返回未拷贝字节数等于请求长度),但access_ok验证用户缓冲区权限是通过的。这种现象在Android设备上通常和几个特定因素有关,下面一步步分析排查:

1. 先明确copy_to_user的真实返回值

你的代码里用sent -= copy_to_user(...),虽然能得到最终成功拷贝的字节数,但看不到copy_to_user本身返回的未拷贝字节数。建议修改日志打印,直接输出这个关键值:

unsigned long not_copied = copy_to_user(buffer, mybuf + tail, sent);
pr_err("MYLOGGER: copy_to_user returned %lu (unable to copy), requested %lu bytes\n", not_copied, sent);
sent -= not_copied;

这能帮你确认是整个缓冲区都无法拷贝,还是部分失败——从当前日志看是全部失败,但明确这一点很重要。

2. 排查用户缓冲区的页面有效性

access_ok只是验证地址在用户空间范围内,不代表对应的物理页面存在或可写。当dd用大bs时,会使用堆缓冲区,而堆内存可能是离散的,甚至存在页面未被分配的情况(比如COW机制下的空白页面)。你可以在驱动里检查每个用户页面的可用性:

#include <linux/mm.h>
#include <linux/sched.h>

// 检查用户缓冲区的每个页面是否可写
struct page *page;
unsigned long user_addr = (unsigned long)buffer;
size_t remaining = sent;
int page_idx = 0;

while (remaining > 0) {
    page = follow_page(current->mm, user_addr, FOLL_WRITE);
    if (!page) {
        pr_err("MYLOGGER: Failed to get user page at offset %d, addr %lx\n", page_idx, user_addr);
        break;
    }
    put_page(page);
    
    size_t step = min(PAGE_SIZE, remaining);
    user_addr += step;
    remaining -= step;
    page_idx++;
}

如果输出某个页面找不到,那说明用户缓冲区对应的物理页面尚未分配,这时候copy_to_user会失败。这种情况下,你可以先让用户空间预填充缓冲区(比如用memset),或者在驱动里调用get_user_pages强制分配页面。

3. 重点排查SELinux权限(Android设备的常见坑)

Android的SELinux强制访问控制会严格限制内核驱动对用户空间内存的访问。当dd使用栈缓冲区(小bs)时,栈内存的SELinux标签可能允许驱动访问;而堆内存的标签不同,被SELinux规则阻止了。

临时验证方法:在adb shell里执行setenforce 0关闭SELinux强制模式,然后重新测试dd if=/dev/mylog bs=4096。如果此时能正常读取,那就是SELinux的问题。

永久解决方法:需要添加对应的SELinux规则。在你的设备源码的device/sepolicy/private/目录下创建或修改规则文件,添加:

allow mylogger_device appdomain:memprotect rw;
allow mylogger_device appdomain:process getattr;

(mylogger_device是你的驱动设备的SELinux类型,需要和你在驱动里设置的struct device的security属性一致)然后重新编译SEPolicy并刷入设备。

4. 检查内核缓冲区的有效性

确保mybuf + tail指向的是有效的内核可访问内存:

  • 如果mybuf是用vmalloc分配的,虽然copy_to_user支持虚拟地址,但某些ARM64内核版本对vmalloc内存的拷贝有特殊限制,建议换成kmalloc或__get_free_pages分配连续物理内存试试。
  • 确认tail + sent没有超出mybuf的分配长度,避免越界访问导致的未定义行为。

5. 其他可能的排查点

  • 检查内核版本:Nexus 5X的内核通常是Android 7.0-8.1的版本,某些旧版本可能存在copy_to_user的小bug,可以尝试升级内核源码到对应分支的最新版本。
  • 验证进程上下文:确保dev_read是在用户进程上下文执行的(不是中断上下文),current->mm指向正确的用户进程地址空间——从你的描述看pid一致,这一点应该没问题,但可以用in_interrupt()验证一下。

按照上面的步骤排查,大概率能定位到问题,尤其是SELinux和用户页面有效性这两个Android特有的因素。

内容的提问来源于stack exchange,提问作者tux_mind

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:47:36