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

Linux内核模块中如何修改struct msghdr插入自定义UDP头部?

解决Linux 6.5.0内核下RUDP_sendmsg插入Header时的页错误问题

核心问题定位

你的页错误(权限违规)和copy_to_iter/copy_to_user返回0的根源在于:

  • 用户态传入的msg->msg_iter是READ方向的迭代器(用于内核从用户空间读取数据),直接用copy_to_iter往里面写会因为方向不匹配失败,返回0。
  • 内核态直接通过memcpy访问用户空间内存,会触发页表权限检查,导致权限违规的页错误。

正确实现思路

内核态不能直接修改用户空间的iov_iter,正确的做法是:先把RUDP Header和用户数据合并到内核缓冲区,再构造内核态的iov_iter传给UDP的sendmsg函数。

具体代码实现步骤

#include <linux/socket.h>
#include <linux/net.h>
#include <linux/udp.h>
#include <linux/mm.h>
#include <linux/uio.h>

int rudp_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
{
    struct iov_iter kernel_iter;
    void *kernel_buf;
    size_t header_len = sizeof(struct rudp_header);
    size_t total_len = header_len + len;
    struct rudp_header header;
    int ret;

    // 1. 分配内核缓冲区(进程上下文用GFP_KERNEL,软中断/中断上下文改用GFP_ATOMIC)
    kernel_buf = kmalloc(total_len, GFP_KERNEL);
    if (!kernel_buf)
        return -ENOMEM;

    // 2. 填充RUDP Header到内核缓冲区开头(根据协议定义修改字段)
    header.seq = htonl(rudp_get_seq(sock));
    header.ack = htonl(rudp_get_ack(sock));
    header.flags = RUDP_FLAG_DATA;
    memcpy(kernel_buf, &header, header_len);

    // 3. 从用户态迭代器拷贝数据到内核缓冲区的Header之后
    ret = copy_from_iter(kernel_buf + header_len, len, &msg->msg_iter);
    if (ret != len) {
        kfree(kernel_buf);
        return -EFAULT;
    }

    // 4. 初始化内核态READ方向的迭代器(供UDP读取数据发送)
    struct iovec kernel_iov = {.iov_base = kernel_buf, .iov_len = total_len};
    iov_iter_init(&kernel_iter, READ, &kernel_iov, 1, total_len);

    // 5. 替换迭代器并调用UDP的sendmsg
    struct iov_iter orig_iter = msg->msg_iter;
    msg->msg_iter = kernel_iter;
    ret = udp_sendmsg(sock, msg, total_len);

    // 6. 恢复原迭代器、释放内核缓冲区
    msg->msg_iter = orig_iter;
    kfree(kernel_buf);

    return ret;
}

关键细节说明

  • 迭代器方向匹配:用户态传入的迭代器是READ方向(内核读用户数据),我们构造的内核迭代器也用READ方向,确保UDP模块能正确读取内核缓冲区的数据。
  • 内存分配上下文:如果rudp_sendmsg运行在软中断/中断上下文,必须用GFP_ATOMIC,避免内存分配时睡眠导致死锁。
  • 完整性检查:必须校验copy_from_iter的返回值,确保用户数据完整拷贝到内核缓冲区,否则返回-EFAULT。

避坑注意事项

  • 禁止直接操作用户态内存:内核态没有用户空间内存的写入权限,强行修改会触发页错误。
  • 不要混用迭代器方向:copy_to_iter仅适用于WRITE方向的迭代器,用户态sendmsg的迭代器是READ方向,调用必然返回0。
  • 及时释放内核内存:无论UDP发送成功或失败,都要释放分配的内核缓冲区,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 09:32:39