为什么access_ok失败了这个ioctl

时间:2012-09-07 19:52:12

标签: c linux macros ioctl uclinux

编辑:我还没有一个好的答案,为什么我在这里失败...所以让我稍微改一下。我甚至需要verify_area()检查吗?那是什么意思?我已经测试了我的结构成功传递给这个ioctl的事实,我正在考虑删除失败的检查,但我不是100%在那里做的事情。思考? 结束编辑

我正在努力更新一些旧的Linux内核驱动程序,在测试一个时,我遇到了一个对我来说很奇怪的失败。我们走了:

我在用户空间中进行了简单的ioctl调用:

Config_par_t    cfg;
int ret;
cfg.target = CONF_TIMING;
cfg.val1   = nBaud;
ret = ioctl(fd, CAN_CONFIG, &cfg);

Config_par_t在can4linux.h文件中定义(这是uCLinux附带的CAN驱动程序):

typedef struct Command_par {
  int cmd;          /**< special driver command */
  int target;           /**< special configuration target */
  unsigned long val1;       /**< 1. parameter for the target */
  unsigned long val2;       /**< 2. parameter for the target */
  int error;            /**< return value */
  unsigned long retval; /**< return value */
} Command_par_t ;

在内核方面,ioctl函数调用verify_area,这是失败的过程:

long can_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    void *argp;
    long retval = -EIO;
    Message_par_t Message;
    Command_par_t Command;
    struct inode *inode = file->f_path.dentry->d_inode;
    argp = &Message;

    Can_errno = 0;

    switch(cmd) {
      case CONFIG:
        if( verify_area(VERIFY_READ, (void *) arg, sizeof(Command_par_t))) {
          return(retval); 
        }

现在我知道不再使用verify_area(),所以我在头文件中使用此宏更新了access_ok:

#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 0)
#define verify_area(type, addr, size) access_ok(type, addr, size)
#endif

我在x86平台上,所以我很确定调用的实际access_ok()宏是/usr/src/linux/arch/x86/include/asm/uaccess.h中的一个,如下所示:

#define access_ok(type, addr, size) (likely(__range_not_ok(addr, size) == 0))

#define __range_not_ok(addr, size)                  \
({                                  \
    unsigned long flag, roksum;                 \
    __chk_user_ptr(addr);                       \
    asm("add %3,%1 ; sbb %0,%0 ; cmp %1,%4 ; sbb $0,%0"     \
      : "=&r" (flag), "=r" (roksum)             \
      : "1" (addr), "g" ((long)(size)),             \
        "rm" (current_thread_info()->addr_limit.seg));      \
   flag;                                \
})

我觉得这看起来应该有效。如果检查我有什么想法从这个verify_area获得1回报?或者关于如何缩小问题的任何想法?

if( verify_area(VERIFY_READ, (void *) arg, sizeof(Command_par_t))) {

1 个答案:

答案 0 :(得分:6)

access_ok如果块无效则返回0 ,如果有效则返回非零值。因此,在测试中,如果块有效,则立即返回-EIO。事情看起来,你可能想要否定access_ok的结果,例如:

if (!access_ok(...))