分类: LINUX
2012-10-21 23:05:39
2)来个实现函数:
122 int test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, uns igned long arg)
123 {
124 int ret = 0;
125 struct _test_t *dev = filp->private_data;
126 struct ioctl_data val;
127
128 /*既然这么费劲定义了命令,当然要检验命令是否有效*/
129 if(_IOC_TYPE(cmd) != TEST_MAGIC) return - EINVAL;
130 if(_IOC_NR(cmd) > TEST_MAX_NR) return - EINVAL;
131
132 switch(cmd){
133 case TEST_CLEAR:
134 memset(dev->kbuf, 0, DEV_SIZE);
135 dev->cur_size = 0;
136 filp->f_pos = 0;
137 ret = 0;
138 break;
139 case TEST_OFFSET: //根据传入的参数更改偏移量
140 filp->f_pos += (int)arg;
141 P_DEBUG("change offset!\n");
142 ret = 0;
143 break;
144 case TEST_KBUF: //修改kbuf
145 if(copy_from_user(&val, (struct ioctl_data *)arg, sizeof(struct ioctl_data))){
146 ret = - EFAULT;
147 goto RET;
148 }
149 memset(dev->kbuf, 0, DEV_SIZE);
150 memcpy(dev->kbuf, val.buf, val.size);
151 dev->cur_size = val.size;
152 filp->f_pos = 0;
153 ret = 0;
154 break;
155 default: /*命令错误时的处理*/
156 P_DEBUG("error cmd!\n");
157 ret = - EINVAL;
158 break;
159 }
160
161 RET:
162 return ret;
163 }
第145行,因为指针是从用户程序传来,所以必须检查安全性。
3)来个应用程序
9 int main(void)
10 {
11 char buf[20];
12 int fd;
13 int ret;
14
15 struct ioctl_data my_data= {
16 .size = 10,
17 .buf = "123456789"
18 };
19
20 fd = open("/dev/test", O_RDWR);
21 if(fd < 0)
22 {
23 perror("open");
24 return -1;
25 }
26
27 write(fd, "xiao bai", 10);
28
29 ioctl(fd, TEST_KBUF, &my_data);
30
31 ret = read(fd, buf, 10);
32 printf("
33 if(ret < 0)
34 {
35 perror("read");
36 }
37
38 close(fd);
39 return 0;
40 }
4)再来验证一下:
[root: 4th]# ./app
注:类似copy_xx_user的函数含有put_user、get_user等,我就不细说了。
下面说第二种方法:进入ioctl后使用access_ok检测。
声明一下:下面的验证方法是不正确的。如果不想看下去的话,今天的内容已经讲完了。
先说一下access_ok的使用
access_ok(type, addr, size)
使用:检测地址的安全性
参数:
type:用于指定数据传输的方向,VERIFY_READ表示要读取应用层数据,VERIFT_WRITE表示要往应用层写如数据。注意:这里和IOR IOW的方向相反。如果既读取又写入,那就使用VERIFY_WRITE。
addr:用户空间的地址
size:数据的大小
返回值:
成功返回1,失败返回0。
既然知道怎么用,就直接来程序了:
1)定义命令
1 #ifndef _TEST_CMD_H
2 #define _TEST_CMD_H
3
4 struct ioctl_data{
5 unsigned int size;
6 char buf[100];
7 };
8
9 #define DEV_SIZE 100
10
11 #define TEST_MAGIC 'x' //定义幻数
12 #define TEST_MAX_NR 3 //定义命令的最大序数
13
14 #define TEST_CLEAR _IO(TEST_MAGIC, 1)
15 #define TEST_OFFSET _IO(TEST_MAGIC, 2)
16 #define TEST_KBUF _IOW(TEST_MAGIC, 3, struct ioctl_data)
17
18 #endif /*_TEST_CMD_H*/
这里终于要用_IOW了!
2)实现ioctl
122 int test_ioctl (struct inode *node, struct file *filp, unsigned int cmd, uns igned long arg)
123 {
124 int ret = 0;
125 struct _test_t *dev = filp->private_data;
126
127 /*既然这么费劲定义了命令,当然要检验命令是否有效*/
128 if(_IOC_TYPE(cmd) != TEST_MAGIC) return - EINVAL;
129 if(_IOC_NR(cmd) > TEST_MAX_NR) return - EINVAL;
130 /*根据提取命令指定的方向判断指针的安全性*/
131 if(_IOC_DIR(cmd) & _IOC_READ)
132 ret = access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
133 else if(_IOC_DIR(cmd) & _IOC_WRITE)
134 ret = access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
135 if(!ret) return - EFAULT;
136
137 switch(cmd){
138 case TEST_CLEAR:
139 memset(dev->kbuf, 0, DEV_SIZE);
140 dev->cur_size = 0;
141 filp->f_pos = 0;
142 ret = 0;
143 break;
144 case TEST_OFFSET: //根据传入的参数更改偏移量
145 filp->f_pos += (int)arg;
146 P_DEBUG("change offset!\n");
147 ret = 0;
148 break;
149 case TEST_KBUF: //修改kbuf
150 memset(dev->kbuf, 0, DEV_SIZE);
151 memcpy(dev->kbuf, ((struct ioctl_data *)arg)->buf,
152 ((struct ioctl_data *)arg)->size);
153 dev->cur_size = ((struct ioctl_data *)arg)->size;
154 filp->f_pos = 0;
155 ret = 0;
156 break;
157 default: /*命令错误时的处理*/
158 P_DEBUG("error cmd!\n");
159 ret = - EINVAL;
160 break;
161 }
162
163 return ret;
164 }
上面并没有用copy_to_user,而是通过access_ok来检测。
3)再来个应用程序:
9 int main(void)
10 {
11 char buf[20];
12 int fd;
13 int ret;
14
15 struct ioctl_data my_data= {
16 .size = 10,
17 .buf = "123456789"
18 };
19
20 fd = open("/dev/test", O_RDWR);
21 if(fd < 0)
22 {
23 perror("open");
24 return -1;
25 }
26
27 write(fd, "xiao bai", 10);
28
29 ret = ioctl(fd, TEST_KBUF, &my_data);
30 if(ret < 0)
31 {
32 perror("ioctl");
33 }
34
35 ret = read(fd, buf, 10);
36 printf("
37 if(ret < 0)
38 {
39 perror("read");
40 }
41
42 close(fd);
43 return 0;
44 }
4)验证一下:效果和上一个一样
[root: 5th]# ./app
下面就要如正题了,这个驱动是有问题的,那就是验证安全性完全不起作用!当我传入非法指针时,驱动同样会输出,不信可以自己传个邪恶地址(void *)0进去试一下。
修改应用程序一样代码:
29 ret = ioctl(fd, TEST_KBUF, &my_data);
上面是我做的错误实现,我本来想验证,只要经过access_ok检验,数据就会安全,没想到经过access_ok检验之后照样会出错。
但是,copy_to_user同样是先调用access_ok再调用memcpy,它却没出错。这个我事情我现在都没搞明白,如果谁知道了麻烦指点一下。
我查了设备驱动第三版,在144页有这样的说法:
1.access_ok并没有做完的所有的内存检查,
2.大多数的驱动代码都不是用access_ok的,后面的内存管理会讲述。
在这里书本上有这样的约定:(都是我自己的理解)
1.传入指针需要检查安全性。memcpy函数尽量不要在内核中使用。
2.copy_to_user.copy_from_user.get_user.put_user函数会再拷贝数据前检测指针的安全性。不需要access_ok。
3.如果在ioctl函数开头使用了accsee_ok检验数据,接下来的代码可以使用__put_user或__get_user这些不需要检测的函数(书上有例子)
虽然还有写东西还没搞懂,但个人觉得,如果使用个access_ok要这么麻烦的话,那我就不用好了,以后我就使用copy_xx_user函数,省力又省心。
七、总结:
这次讲了ioctl的实现:
1)命令是怎么定义。
2)参数怎么传递。
转:http://janine.blog.51cto.com/3502348/781493