I found a weird issue while writing an IOCTL for a character device driver for custom hardware connected to an old PowerPC. Here is an abstraction of my code:
u32 mydev_data;
...
static long mydev_ioctl(struct file * file, unsigned int cmd, unsigned long arg)
{
void __user *user_arg = (void __user *)arg;
long result;
switch(cmd) {
case MYDEV_GETDATA:
result = put_user(mydev_data, user_arg);
break;
...
}
return result;
}
Now, this returns garbage. However, when I replace the line
result = put_user(mydev_data, user_arg);
with
result = put_user(mydev_data, (unsigned long __user *) user_arg);
the problem goes away.
What is going on here? Since user_arg is marked as __user *, the only difference is void vs. unsigned long. But I wouldn’t think the pointer type would matter here. Obviously I’m mistaken but can someone explain why?
If you look at the definition of the
put_usermacro, you’ll see that it copies data based on size, doing asizeof(*ptr)to determine how many bytes to copy.sizeof(void) == 1, butsizeof(unsigned long)is larger.