1
votes

EDIT: I don't have a good answer yet as to why I'm getting a failure here... So let me rephrase this a little. Do I even need the verify_area() check? What is the point of that? I have tested out the fact that my structure gets passed successfully to this ioctl, I'm thinking of just removing the failing check, but I'm not 100% what it's in there to do. Thoughts? END EDIT

I'm working to update some older linux kernel drivers and while testing one out I'm getting a failure which seems odd to me. Here we go:

I have a simple ioctl call in user space:

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

The Config_par_t is defined in can4linux.h file (this is the CAN driver that comes with uCLinux):

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 ;

In the kernel side of things, the ioctl function calls verify_area, which is the failing procedure:

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); 
        }

Now I know that verify_area() isn't used anymore so I updated it in a header file with this macro to access_ok:

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

I'm on a x86 platform so I'm pretty sure the actual access_ok() macro being called is the one in /usr/src/linux/arch/x86/include/asm/uaccess.h as defined here:

#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;                                \
})

I guess to me this looks like it should be working. Any ideas why I'm getting a 1 return from this verify_area if check? Or any ideas on how I can go about narrowing down the problem?

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

1 Answers

6
votes

The macro access_ok returns 0 if the block is invalid and nonzero if it may be valid. So in your test, if the block is valid you immediately return -EIO. The way things look, you might want to negate the result of access_ok, something like:

if (!access_ok(...))