您的位置:首页 > 运维架构 > Linux

书写驱动必要安全性检测

2017-10-26 08:39 246 查看

驱动安全性意义

驱动直接操作的对象是硬件,运行在内核空间,如果驱动不可靠一旦出现问题,操作系统会出现异常,有很大几率造成系统崩溃。因此驱动安全性极为重要,在驱动代码中凡是有可能执行失败的函数,都需要对其返回值进行判断,成功后才可以进入下一个环节。最大限度减少出错的可能。

示例代码(没有安全检测)

#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<linux/kdev_t.h>
#include<linux/slab.h>

#define MY_DEVICE_NAME "linux26"
static struct cdev *pcdev;
static dev_t dev_no;   //第一个设备号(包含主次)
static int major=0;

ssize_t my_device_open(struct inode *node, struct file *file)
{
printk("my device is open\n");
return 0;
}
ssize_t my_device_close(struct inode *node, struct file *file)
{
printk("my device is close\n");
return 0;
}

ssize_t my_device_read(struct file *fp, char __user *buf, size_t size, loff_t *loff)
{
printk("my device is read\n");
return 0;
}

ssize_t my_device_write(struct file *file, const char __user *buf, size_t size, loff_t *loff)
{
printk("my device is write\n");
return 0;
}

//文件结构体
struct file_operations my_fops={
.owner=THIS_MODULE,
.open=my_device_open,
.read=my_device_read,
.write=my_device_write,
.release=my_device_close,
};

static int __init linux26_cdev_init(void)
{
//分配cdev空间
pcdev=cdev_alloc();
//动态分配设备号 次设备号0开始 数量为2个 名称为宏
alloc_chrdev_region(&dev_no,0,2,MY_DEVICE_NAME);
//初始化结构体
cdev_init(pcdev,&my_fops);
//注册驱动
cdev_add(pcdev,dev_no,2);
//再去查注册文件很麻烦,这里打印主设备号方便创建节点
major=MAJOR(dev_no);
printk("linux26 major is %d\n",major);
return 0;
}
static void __exit linux26_cdev_exit(void)
{
cdev_del(pcdev);
unregister_chrdev_region(dev_no,2);
kfree(pcdev);
}

module_init(linux26_cdev_init);
module_exit(linux26_cdev_exit);
MODULE_LICENSE("GPL");


修改后的代码(加入安全检测)

#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<linux/kdev_t.h>
#include<linux/slab.h>

#define MY_DEVICE_NAME "linux26"
static struct cdev *pcdev;
static dev_t dev_no;   //第一个设备号(包含主次)
static int major=0;

ssize_t my_device_open(struct inode *node, struct file *file)
{
printk("my device is open\n");
return 0;
}
ssize_t my_device_close(struct inode *node, struct file *file)
{
printk("my device is close\n");
return 0;
}

ssize_t my_device_read(struct file *fp, char __user *buf, size_t size, loff_t *loff)
{
printk("my device is read\n");
return 0;
}

ssize_t my_device_write(struct file *file, const char __user *buf, size_t size, loff_t *loff)
{
printk("my device is write\n");
return 0;
}

//文件结构体
struct file_operations my_fops={
.owner=THIS_MODULE,
.open=my_device_open,
.read=my_device_read,
.write=my_device_write,
.release=my_device_close,
};

static int __init linux26_cdev_init(void)
{
int ret =-1;
//分配cdev空间
pcdev=cdev_alloc();
if(pcdev==NULL)
{
printk("cdev alloc is fail\n");
ret=-ENOMEM;  //分配内存失败  当然写-1也没有关系
return ret;
}

//动态分配设备号 次设备号0开始 数量为2个 名称为宏
ret=alloc_chrdev_region(&dev_no,0,2,MY_DEVICE_NAME);
if(ret<0)
{
//释放前面成功的资源 否则造成内存泄漏
kfree(pcdev);        //释放结构体空间
printk("alloc chrdev is fail\n");
return ret;
}

//初始化结构体
cdev_init(pcdev,&my_fops);
//cdev_init没有返回值不用管

//注册驱动
ret=cdev_add(pcdev,dev_no,2);
if(ret<0)
{
//释放前面申请的资源 否则会造成内存泄漏
unregister_chrdev_region(dev_no,2); //释放设备号
kfree(pcdev);     //释放结构体空间
printk("cdev_add is fail\n");
return ret;
}
//再去查注册文件很麻烦,这里打印主设备号方便创建节点
major=MAJOR(dev_no);
printk("linux26 major is %d\n",major);
return 0;
}

static void __exit linux26_cdev_exit(void)
{
cdev_del(pcdev);
unregister_chrdev_region(dev_no,2);
kfree(pcdev);
}

module_init(linux26_cdev_init);
module_exit(linux26_cdev_exit);
MODULE_LICENSE("GPL");


如果前面注册的太多,每个需要判断的函数里面释放的资源就比较多,代码重复性较高,造成代码长度大大加长,阅读性变差。可以采取一些方法进行改变可以参考下面的例子。

最终版本(相对完善)

#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<linux/kdev_t.h>
#include<linux/slab.h>

#define MY_DEVICE_NAME "linux26"
static struct cdev *pcdev;
static dev_t dev_no;   //第一个设备号(包含主次)
static int major=0;

ssize_t my_device_open(struct inode *node, struct file *file)
{
printk("my device is open\n");
return 0;
}
ssize_t my_device_close(struct inode *node, struct file *file)
{
printk("my device is close\n");
return 0;
}

ssize_t my_device_read(struct file *fp, char __user *buf, size_t size, loff_t *loff)
{
printk("my device is read\n");
return 0;
}

ssize_t my_device_write(struct file *file, const char __user *buf, size_t size, loff_t *loff)
{
printk("my device is write\n");
return 0;
}

//文件结构体
struct file_operations my_fops={
.owner=THIS_MODULE,
.open=my_device_open,
.read=my_device_read,
.write=my_device_write,
.release=my_device_close,
};

static int __init linux26_cdev_init(void)
{
int ret =-1;
//分配cdev空间
pcdev=cdev_alloc();
if(pcdev==NULL)
{
printk("cdev alloc is fail\n");
ret=-ENOMEM;
goto err_cdev_alloc;
}

//动态分配设备号 次设备号0开始 数量为2个 名称为宏
ret=alloc_chrdev_region(&dev_no,0,2,MY_DEVICE_NAME);
if(ret<0)
{
printk("alloc chrdev is fail\n");
ret=-1;
goto err_alloc_chrdev_region;
}

//初始化结构体
//cdev_init没有返回值不用管
cdev_init(pcdev,&my_fops);

//注册驱动
ret=cdev_add(pcdev,dev_no,2);
if(ret<0)
{
printk("cdev_add is fail\n");
ret=-1;
goto err_cdev_add;
}

//再去查注册文件很麻烦,这里打印主设备号方便创建节点
major=MAJOR(dev_no);
printk("linux26 major is %d\n",major);
return 0; //非常重要 如果没有这个返回值,即使前面全部执行成功,也会执行后面的代码造成前功尽弃

err_cdev_add:
//释放前面申请的资源 否则会造成内存泄漏
unregister_chrdev_region(dev_no,2); //释放设备号
err_alloc_chrdev_region:
//释放前面成功的资源 否则造成内存泄漏
kfree(pcdev);        //释放结构体空间
err_cdev_alloc:
return ret;  //分配内存失败  当然写-1也没有关系

}

static void __exit linux26_cdev_exit(void)
{
//释放资源函数一般都能成功,很少有返回值,这里没有返回值检查
cdev_del(pcdev);
unregister_chrdev_region(dev_no,2);
kfree(pcdev);
}

module_init(linux26_cdev_init);
module_exit(linux26_cdev_exit);
MODULE_LICENSE("GPL");
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息