Linux字符设备驱动写入设备文件报Invalid argument错误排查
问题现象
开发Linux字符设备驱动时,cat读取设备的功能运行正常,但向设备节点写入数据时直接返回Invalid argument错误,错误截图如下:
当前使用的驱动代码如下,未定位到write逻辑的问题:
#include "device_file.h" #include <linux/fs.h> /* file stuff */ #include <linux/kernel.h> /* printk() */ #include <linux/errno.h> /* error codes */ #include <linux/module.h> /* THIS_MODULE */ #include <linux/cdev.h> /* char device stuff */ #include <linux/uaccess.h> /* copy_to_user() */ static const char g_s_Hello_World_string[] = "Hello from Job & Arief!\n\0"; static const ssize_t g_s_Hello_World_size = sizeof(g_s_Hello_World_string); static char memory_buffer[500]; /*===============================================================================================*/ static ssize_t device_file_read(struct file *file_ptr, char __user *user_buffer, size_t count, loff_t *possition){ printk( KERN_NOTICE "Kurniawan_Meulenbeld-driver: Device file is read at offset = %i, read bytes count = %u\n" , (int)*possition , (unsigned int)count ); if( *possition >= g_s_Hello_World_size ) return 0; if( *possition + count > g_s_Hello_World_size ) count = g_s_Hello_World_size - *possition; if( copy_to_user(user_buffer, g_s_Hello_World_string + *possition, count) != 0 ) return -EFAULT; *possition += count; return count; } /*===============================================================================================*/ static ssize_t memory_write( struct file *filp, const char *buf, size_t count, loff_t *f_pos) { const char *tmp; tmp=buf+count-1; if( copy_from_user(memory_buffer,tmp,1) != 0 ) return -EFAULT; return count; } /*===============================================================================================*/ static struct file_operations Kurniawan_Meulenbeld_driver_fops = { .owner = THIS_MODULE, .read = device_file_read, .write = memory_write, }; static int device_file_major_number = 0; static const char device_name[] = "Kurniawan_Meulenbeld-driver"; /*===============================================================================================*/ int register_device(void) { int result = 0; printk( KERN_NOTICE "Kurniawan_Meulenbeld-driver: register_device() is called.\n" ); result = register_chrdev( 0, device_name, &Kurniawan_Meulenbeld_driver_fops ); if( result < 0 ) { printk( KERN_WARNING "Kurniawan_Meulenbeld-driver: can\'t register character device with errorcode = %i\n", result ); return result; } device_file_major_number = result; printk( KERN_NOTICE "Kurniawan_Meulenbeld-driver: registered character device with major number = %i and minor numbers 0...255\n" , device_file_major_number ); return 0; } /*===============================================================================================*/ void unregister_device(void) { printk( KERN_NOTICE "Kurniawan_Meulenbeld-driver: unregister_device() is called\n" ); if(device_file_major_number != 0) { unregister_chrdev(device_file_major_number, device_name); } }
故障原因
报错的核心原因有两点:
- write回调函数的用户态缓冲区参数
buf缺少__user地址空间修饰符。内核会对传入的用户态指针做合法性校验,没有__user标记的指针会被判定为非法地址,直接返回-EINVAL也就是无效参数错误,这是触发报错的直接诱因。 - 现有写入逻辑本身存在严重缺陷:仅计算了用户缓冲区最后1字节的地址,只尝试拷贝1字节数据到内核缓冲区,既没有校验写入长度防止
memory_buffer溢出,也没有更新文件偏移指针f_pos,就算跳过地址校验也无法实现正常写入全量数据的需求。
修复方法
将原有memory_write函数替换为如下实现即可:
static ssize_t memory_write( struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { size_t free_space; // 计算缓冲区剩余可写空间,防止溢出 free_space = sizeof(memory_buffer) - *f_pos; if (free_space == 0) { return -ENOSPC; } // 写入长度超出剩余空间时自动截断 if (count > free_space) { count = free_space; } // 从用户态拷贝全量待写入数据到内核缓冲区对应偏移位置 if (copy_from_user(memory_buffer + *f_pos, buf, count) != 0) { return -EFAULT; } // 更新文件偏移 *f_pos += count; // 打印写入内容到内核日志,方便查看 printk(KERN_NOTICE "Kurniawan_Meulenbeld-driver: write %zu bytes, content: %.*s\n", count, (int)count, memory_buffer + (*f_pos - count)); return count; }
替换后重新编译加载驱动,即可正常通过echo等命令向设备节点写入数据,写入内容会直接打印在内核日志中,可通过dmesg查看。
内容的提问来源于stack exchange,提问作者J.Meulenbeld
相关产品推荐
相关产品推荐

