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

内核模块NULL指针解引用及copy_to_user数据回传问题

内核模块my_read函数问题修复方案

核心问题分析

  1. 空指针错误:kmalloc存在分配失败返回NULL的可能,直接使用未检查的buffer会触发memcpy() - unable to handle kernel NULL pointer dereference错误。
  2. 数据无法传递/Buffer为空:
    • 需确认print_log是否正确将temp->data写入buffer(比如是否保证buffer空间充足、是否正确添加字符串终止符);
    • 原代码每次copy_to_user都覆盖用户态buff的起始位置,且最终返回0,用户态只能拿到最后一次写入的内容(甚至可能因覆盖丢失全部有效数据)。
  3. 无法分段回传超长数据:未利用loff_t *offp记录读取进度,导致单次read无法分段返回数据,用户态无法分次获取全部内容。

修复后的代码实现

#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/fs.h>

#define BUFFER_SIZE 1024

ssize_t my_read(struct file *filp, char __user *buff, size_t length, loff_t *offp)
{
    struct log_list *temp;
    char *buffer = NULL;
    ssize_t total_written = 0;
    ssize_t curr_len;
    loff_t current_off = *offp;
    int entry_count = 0;

    // 分配内核缓冲区并检查是否成功
    buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
    if (!buffer) {
        return -ENOMEM;
    }

    temp = log_list_head->next;

    // 跳过已读取过的链表节点
    while (temp != NULL && entry_count < current_off) {
        entry_count++;
        temp = temp->next;
    }

    while (temp != NULL && total_written < length) {
        // 重置buffer避免残留数据干扰
        memset(buffer, 0, BUFFER_SIZE);
        // 将链表节点数据转换为字符串
        print_log(temp->data, buffer);
        curr_len = strlen(buffer);
        if (curr_len == 0) {
            entry_count++;
            temp = temp->next;
            continue;
        }

        // 限制单次拷贝长度不超过用户请求的剩余空间
        if (total_written + curr_len > length) {
            curr_len = length - total_written;
        }

        // 拷贝数据到用户态,检查是否成功
        if (copy_to_user(buff + total_written, buffer, curr_len)) {
            kfree(buffer);
            return -EFAULT;
        }

        total_written += curr_len;
        entry_count++;
        temp = temp->next;
    }

    // 更新偏移量,记录已读取的节点数
    *offp = entry_count;
    kfree(buffer);

    // 返回实际写入用户态的字节数,0表示读取完毕
    return total_written;
}

关键修复点说明

  • 空指针防护:添加kmalloc结果检查,分配失败直接返回-ENOMEM错误码。
  • 读取进度跟踪:利用offp记录已读取的链表节点数,下次read时从上次结束的节点继续,实现分段读取超长数据。
  • 避免数据覆盖:每次拷贝到用户态buff的total_written偏移位置,确保数据不会被覆盖。
  • 合法性校验:检查copy_to_user的返回值,拷贝失败返回-EFAULT;限制单次拷贝长度不超过用户请求的length。
  • Buffer重置:每次调用print_log前清空buffer,避免旧数据残留导致计算错误。

额外注意事项

  • 需确保print_log函数正确添加字符串终止符\0,否则strlen会计算错误甚至触发内存越界。
  • 如果单条日志长度超过BUFFER_SIZE,需修改print_log支持分段输出,或增大内核buffer的大小。
  • 若存在多线程修改链表的场景,需添加自旋锁或互斥锁保护链表访问,避免并发问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 12:03:32