内核空间虽然可以访问用户空间,但是在访问之前,一般需要先检查其合法性,通过access_ok(type, addr, size)进行判断,以确定传入的缓冲区的确属于用户缓冲区,例如:
1 static ssize_t read_port(struct file *file, char __user *buf, size_t count, loff_t *ppos) 2 { 3 unsigned long i = *ppos; 4 char __user *tmp = buf; 5 6 if (!access_ok(VERIFY_WRITE, buf, count)) 7 return -EFAULT; 8 while (count-- > 0 && i < 65536) { 9 if (__put_user(inb(i), tmp) < 0) 10 return -EFAULT; 11 i++; 12 tmp++; 13 } 14 *ppos = i; 15 return tmp - buf; 16 }
上述代码中引用__put_user()与前文件讲解的put_user()的区别在于前者不进行类似access_ok()的检查,而后者会进行这一检查。在本例中,不使用put_user()而使用__put_user()的原因是在__put_user()调用之前,已经手动检查了用户空间缓冲区(buf指向的大小为count的内存)的合法性。get_user()和__get_user()的区别也相似。
特别要提醒读者注意的是:在内核空间与用户空间的界面处,内核检查用户空间缓冲区的合法性显得尤为重要,Linux内核的许多安全漏洞都是因为遗忘了这一检查造成的,非法入侵者可以伪造一片内核空间缓冲区地址传入系统调用的接口,让系统对这个evil指针指向的内核空间填充数据。
其实copy_from_user()、copy_to_user()内部也进行了这样的检查。
1 static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, 2 unsigned long n) 3 { 4 if (access_ok(VERIFY_READ, from, n)) 5 n = __copy_from_user(to, from, n); 6 else /* security hole - plug it */ 7 memset(to, 0, n); 8 return n; 9 } 10 11 static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, 12 unsigned long n) 13 { 14 if (access_ok(VERIFY_WRITE, to, n)) 15 n = __copy_to_user(to, from, n); 16 return n; 17 }