lkml.org 
[lkml]   [2020]   [Oct]   [5]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.8 78/85] gpiolib: Fix line event handling in syscall compatible mode
    Date
    From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>

    [ Upstream commit 5ad284ab3a01e2d6a89be2a8663ae76f4e617549 ]

    The introduced line event handling ABI in the commit

    61f922db7221 ("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: 61f922db7221 ("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>
    Signed-off-by: Sasha Levin <sashal@kernel.org>
    ---
    drivers/gpio/gpiolib.c | 34 ++++++++++++++++++++++++++++++----
    1 file changed, 30 insertions(+), 4 deletions(-)

    --- a/drivers/gpio/gpiolib.c
    +++ b/drivers/gpio/gpiolib.c
    @@ -836,6 +836,21 @@ static __poll_t lineevent_poll(struct fi
    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 *filep,
    char __user *buf,
    @@ -845,9 +860,20 @@ static ssize_t lineevent_read(struct fil
    struct lineevent_state *le = filep->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 {
    @@ -883,10 +909,10 @@ static ssize_t lineevent_read(struct fil
    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;
    }

    \
     
     \ /
      Last update: 2020-10-05 17:35    [W:3.046 / U:0.096 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site