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

Linux内核模块ioctl始终返回-1,未触发unlocked_ioctl函数求助

解决64位内核搭配32位用户空间下IOCTL段错误问题

问题核心排查与修复步骤

1. 修正头文件类型不匹配问题

头文件中存在结构体类型名错误,直接导致IOCTL命令的参数大小计算异常,在32/64位混合环境下触发地址访问问题:

#ifndef COMBLOCKDEV_H 
#define COMBLOCKDEV_H 
#define BUFF_LEN 256 

struct user_params{
    int len;
    int data[BUFF_LEN];
    int reg;
};

#define COMBLOCK_IOC_MAGIC      'a' 
#define COMBLOCK_IOC_RESET              _IO(COMBLOCK_IOC_MAGIC, 0)
// 修正:将struct params*改为struct user_params,避免类型不匹配
#define COMBLOCK_IOC_REG_READ           _IOR(COMBLOCK_IOC_MAGIC, 1, struct user_params)
// 新增:定义最大命令编号,用于内核合法性校验
#define COMBLOCK_IOC_MAXNR              1 
#endif

2. 修正测试程序的类型错误与资源泄漏

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>

#include "comblock.h"

int main() {
        // 修正:将struct params改为struct user_params
        struct user_params test; 
        int ret;
        int dev = open("/dev/comblock_dev0", O_RDWR);
        if(dev == -1) {
                perror("Opening device failed");
                return -1;
        }

        ret = ioctl(dev, COMBLOCK_IOC_RESET);
        printf("Trying to reset the CB: %d, error: %s\n", ret, strerror(errno));
        // 新增:关闭设备文件,避免资源泄漏
        close(dev); 
        return 0;
}

3. 修复内核模块的关键错误

核心问题点:

  • 未释放信号量,导致死锁或内存访问异常
  • compat_ioctl未正确处理32位指针转换
  • 缺少命令默认分支处理

修正后的内核IOCTL实现:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <asm/uaccess.h>
#include <linux/compat.h> // 新增:用于compat_ptr()函数

#include "comblock.h"
#define SUCCESS 0

// 假设struct comblock_local定义如下(需确保sem已初始化)
struct comblock_local {
    struct cdev cdev;
    struct semaphore sem;
    struct fasync_struct *async_queue;
    // 其他设备相关成员
};

static int device_open(struct inode *inode, struct file *file) {
    struct comblock_local * lp;
    pr_info("ComBlock_open(%p)\n", file);

    lp = container_of(inode->i_cdev, struct comblock_local, cdev);
    file->private_data = lp;
    
    return SUCCESS; 
} 

static int device_release(struct inode *inode, struct file *file) {
    struct comblock_local * lp = (struct comblock_local*) file->private_data;
    pr_info("ComBlock_release(%p,%p)\n", inode, file);
    if(lp->async_queue) // 修正:原逻辑判断反向
        device_fasync(-1, file, 0);

    return SUCCESS;
}

static long device_ioctl(struct file *file, unsigned int cmd, unsigned long ioctl_param) {
    int ret = SUCCESS;
    struct comblock_local * lp = file->private_data;
    struct user_params params;
    
    pr_info("Comblock: IOCTL command %u\n", cmd);
    pr_info("Comblock: IOCTL magic %c\n", COMBLOCK_IOC_MAGIC);
    pr_info("Comblock: IOCTL max nr %d\n", COMBLOCK_IOC_MAXNR);
    pr_info("Comblock: IOCTL dir %d\n", _IOC_DIR(cmd));

    if (_IOC_TYPE(cmd) != COMBLOCK_IOC_MAGIC)
        return -ENOTTY;
    if (_IOC_NR(cmd) > COMBLOCK_IOC_MAXNR) 
        return -ENOTTY;
    
    if(_IOC_DIR(cmd) & _IOC_READ)
        ret = !access_ok((void __user*)ioctl_param, _IOC_SIZE(cmd));
    else if(_IOC_DIR(cmd) & _IOC_WRITE)
        ret = !access_ok((void __user*)ioctl_param, _IOC_SIZE(cmd));
    if(ret) 
        return -EFAULT;

    if(down_interruptible(&lp->sem)) 
        return -ERESTARTSYS;

    pr_info("Comblock: Handling command %u\n", cmd);
    switch (cmd) {
        case COMBLOCK_IOC_RESET:
            pr_info("Comblock: Executing reset\n");
            // 此处添加硬件重置的实际逻辑
            break;
        case COMBLOCK_IOC_REG_READ:
            // 示例:填充参数并拷贝到用户空间
            params.len = 0;
            params.reg = 0;
            ret = copy_to_user((struct user_params __user*)ioctl_param, &params, sizeof(params));
            break;
        default:
            ret = -ENOTTY;
            break;
    }

    // 新增:释放信号量,原代码遗漏导致死锁/段错误
    up(&lp->sem); 
    return ret;
}

// 32位用户空间兼容处理函数
static long device_compat_ioctl(struct file *file, unsigned int cmd, unsigned long ioctl_param) {
    // 将32位指针转换为64位内核可识别的地址
    return device_ioctl(file, cmd, (unsigned long)compat_ptr(ioctl_param));
}

// 修正后的file_operations结构体
static const struct file_operations comblock_fops = {
    .owner = THIS_MODULE,
    .open = device_open,
    .release = device_release,
    .unlocked_ioctl = device_ioctl,
    .compat_ioctl = device_compat_ioctl, // 注册兼容处理函数
};

4. 关键错误总结

  • 类型不匹配:头文件与测试程序中结构体名称不一致,导致参数大小计算错误
  • 未定义命令编号上限:内核中COMBLOCK_IOC_MAXNR未定义,触发-ENOTTY错误
  • 信号量未释放:内核IOCTL函数中调用down_interruptible后未执行up,引发死锁或内存访问异常
  • compat_ioctl参数未转换:32位用户空间指针在64位内核中需通过compat_ptr()转换,否则会访问非法地址

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 09:35:21