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

如何构造copy_to_user()调用失败的用户态测试场景?

为啥你的copy_to_user()总不失败?我来给你掰扯清楚

哥们,我之前踩过这个坑!你现在的问题核心是对copy_to_user()的行为理解有偏差——它压根不会帮你检查用户态缓冲区的实际容量,只认你传的长度参数和地址的合法性。

先解释为啥你当前的代码没触发失败

不管你是用栈上的type to[1]还是malloc分配的内存,只要这个地址是合法的用户空间可写地址,哪怕你只分配了1个元素的空间,copy_to_user()还是会硬着头皮写完你指定的sizeof(type)*2字节。

这时候用户态其实已经发生了越界写入,但copy_to_user()根本不关心这个——它的职责只是把内核数据复制到你给的用户地址,只要地址属于当前进程的可写用户空间,就会返回0(表示全部复制完成)。至于你用户态缓冲区装不下,那是用户态代码的问题,内核不背锅。

比如你用malloc(sizeof(type)),glibc的分配器通常会多分配一些内存来做管理,所以你越界写的那部分刚好在分配器预留的空间里,内核完全能正常写入,自然不会返回错误。

怎么合法触发copy_to_user()失败(不用空指针)

要让它返回非0值,得让内核无法完成全部复制,这里给你两个靠谱的方法:

方法1:传递部分非法的用户地址

用mmap()分配一块刚好等于1个type大小的内存,这样缓冲区后面的地址直接不属于当前进程的地址空间,copy_to_user()写入到非法区域时就会失败,返回未复制的字节数。

用户态示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>

typedef int type; // 假设你的type是int,按需替换
#define SYS_myfoo 1234 // 替换成你的系统调用号

int main() {
    // 分配刚好1个type的内存,后面是非法地址
    type *to = mmap(NULL, sizeof(type), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (to == MAP_FAILED) {
        perror("mmap");
        return 1;
    }

    long res = syscall(SYS_myfoo, to);
    printf("Syscall returned: %ld\n", res); // 应该返回sizeof(type)

    munmap(to, sizeof(type));
    return 0;
}

方法2:把用户态缓冲区设为只读

用mprotect()把用户态缓冲区改成只读权限,这样copy_to_user()尝试写入时会触发权限错误,直接返回全部未复制的字节数。

用户态示例代码片段:

type *to2 = malloc(sizeof(type)*2);
if (!to2) {
    perror("malloc");
    return 1;
}

// 把缓冲区设为只读
if (mprotect(to2, sizeof(type)*2, PROT_READ) == -1) {
    perror("mprotect");
    free(to2);
    return 1;
}

long res = syscall(SYS_myfoo, to2);
printf("Syscall with readonly buf returned: %ld\n", res); // 应该返回sizeof(type)*2

// 恢复权限再释放
mprotect(to2, sizeof(type)*2, PROT_READ | PROT_WRITE);
free(to2);

内核侧的系统调用代码(参考)

确保你的内核代码正确返回copy_to_user()的结果:

#include <linux/syscalls.h>
#include <linux/uaccess.h>

typedef int type;

SYSCALL_DEFINE1(myfoo, type __user *, to) {
    type from[2] = {0x1234, 0x5678};
    int not_copied = copy_to_user(to, from, sizeof(type)*2);
    printk(KERN_INFO "copy_to_user returned: %d\n", not_copied);
    return not_copied;
}

这样操作后,你就能看到copy_to_user()返回非0值,也就是你想要的失败状态了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:12:55