mirror of
https://github.com/torvalds/linux.git
synced 2024-12-19 01:23:20 +00:00
gpiolib: Fix line event handling in syscall compatible mode
The introduced line event handling ABI in the commit61f922db72
("gpio: userspace ABI for reading GPIO line events") missed the fact that 64-bit kernel may serve for 32-bit applications. In such case the very first check in the lineevent_read() will fail due to alignment differences. To workaround this introduce lineevent_get_size() helper which returns actual size of the structure in user space. Fixes:61f922db72
("gpio: userspace ABI for reading GPIO line events") Suggested-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Tested-by: Kent Gibson <warthog618@gmail.com> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
This commit is contained in:
parent
1b02d9e770
commit
5ad284ab3a
@ -423,6 +423,21 @@ static __poll_t lineevent_poll(struct file *file,
|
||||
return events;
|
||||
}
|
||||
|
||||
static ssize_t lineevent_get_size(void)
|
||||
{
|
||||
#ifdef __x86_64__
|
||||
/* i386 has no padding after 'id' */
|
||||
if (in_ia32_syscall()) {
|
||||
struct compat_gpioeevent_data {
|
||||
compat_u64 timestamp;
|
||||
u32 id;
|
||||
};
|
||||
|
||||
return sizeof(struct compat_gpioeevent_data);
|
||||
}
|
||||
#endif
|
||||
return sizeof(struct gpioevent_data);
|
||||
}
|
||||
|
||||
static ssize_t lineevent_read(struct file *file,
|
||||
char __user *buf,
|
||||
@ -432,9 +447,20 @@ static ssize_t lineevent_read(struct file *file,
|
||||
struct lineevent_state *le = file->private_data;
|
||||
struct gpioevent_data ge;
|
||||
ssize_t bytes_read = 0;
|
||||
ssize_t ge_size;
|
||||
int ret;
|
||||
|
||||
if (count < sizeof(ge))
|
||||
/*
|
||||
* When compatible system call is being used the struct gpioevent_data,
|
||||
* in case of at least ia32, has different size due to the alignment
|
||||
* differences. Because we have first member 64 bits followed by one of
|
||||
* 32 bits there is no gap between them. The only difference is the
|
||||
* padding at the end of the data structure. Hence, we calculate the
|
||||
* actual sizeof() and pass this as an argument to copy_to_user() to
|
||||
* drop unneeded bytes from the output.
|
||||
*/
|
||||
ge_size = lineevent_get_size();
|
||||
if (count < ge_size)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
@ -470,10 +496,10 @@ static ssize_t lineevent_read(struct file *file,
|
||||
break;
|
||||
}
|
||||
|
||||
if (copy_to_user(buf + bytes_read, &ge, sizeof(ge)))
|
||||
if (copy_to_user(buf + bytes_read, &ge, ge_size))
|
||||
return -EFAULT;
|
||||
bytes_read += sizeof(ge);
|
||||
} while (count >= bytes_read + sizeof(ge));
|
||||
bytes_read += ge_size;
|
||||
} while (count >= bytes_read + ge_size);
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user