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, ¶ms, 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
相关产品推荐
相关产品推荐

