Message ID | 20170515203306.26336-3-hch@lst.de (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Mon, 2017-05-15 at 22:33 +0200, Christoph Hellwig wrote: > Instead write a proper compat syscall that calls common helpers. > > Signed-off-by: Christoph Hellwig <hch@lst.de> > --- > fs/fcntl.c | 118 +++++++++++++++++++++++++++++++++++-------------------------- > fs/locks.c | 2 +- > 2 files changed, 68 insertions(+), 52 deletions(-) > > diff --git a/fs/fcntl.c b/fs/fcntl.c > index 671ac43c65df..9d909b029063 100644 > --- a/fs/fcntl.c > +++ b/fs/fcntl.c > @@ -505,76 +505,92 @@ convert_fcntl_cmd(unsigned int cmd) > return cmd; > } > > +/* > + * GETLK was successful and we need to return the data, but it needs to fit in > + * the compat structure. > + * l_start shouldn't be too big, unless the original start + end is greater than > + * COMPAT_OFF_T_MAX, in which case the app was asking for trouble, so we return > + * -EOVERFLOW in that case. l_len could be too big, in which case we just > + * truncate it, and only allow the app to see that part of the conflicting lock > + * that might make sense to it anyway > + */ > +static int fixup_compat_flock(struct flock *flock) > +{ > + if (flock.l_start > COMPAT_OFF_T_MAX) > + return -EOVERFLOW; > + if (flock.l_len > COMPAT_OFF_T_MAX) > + flock.l_len = COMPAT_OFF_T_MAX; > + return 0; > +} > + Erm...build break above though -- flock is a pointer there, so those should be "flock->...". I'll just squash that fix into the patch unless you need to send a respin for other reasons. > COMPAT_SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd, > compat_ulong_t, arg) > { > - mm_segment_t old_fs; > - struct flock f; > - long ret; > - unsigned int conv_cmd; > + struct fd f = fdget_raw(fd); > + struct flock flock; > + long err = -EBADF; > + > + if (!f.file) > + return err; > + > + if (unlikely(f.file->f_mode & FMODE_PATH)) { > + if (!check_fcntl_cmd(cmd)) > + goto out_put; > + } > + > + err = security_file_fcntl(f.file, cmd, arg); > + if (err) > + goto out_put; > > switch (cmd) { > case F_GETLK: > + err = get_compat_flock(&flock, compat_ptr(arg)); > + if (err) > + break; > + err = fcntl_getlk(f.file, convert_fcntl_cmd(cmd), &flock); > + if (err) > + break; > + err = fixup_compat_flock(&flock); > + if (err) > + return err; > + err = put_compat_flock(&flock, compat_ptr(arg)); > + break; > + case F_GETLK64: > + case F_OFD_GETLK: > + err = get_compat_flock64(&flock, compat_ptr(arg)); > + if (err) > + break; > + err = fcntl_getlk(f.file, convert_fcntl_cmd(cmd), &flock); > + if (err) > + break; > + err = fixup_compat_flock(&flock); > + if (err) > + return err; > + err = put_compat_flock64(&flock, compat_ptr(arg)); > + break; > case F_SETLK: > case F_SETLKW: > - ret = get_compat_flock(&f, compat_ptr(arg)); > - if (ret != 0) > + err = get_compat_flock(&flock, compat_ptr(arg)); > + if (err) > break; > - old_fs = get_fs(); > - set_fs(KERNEL_DS); > - ret = sys_fcntl(fd, cmd, (unsigned long)&f); > - set_fs(old_fs); > - if (cmd == F_GETLK && ret == 0) { > - /* GETLK was successful and we need to return the data... > - * but it needs to fit in the compat structure. > - * l_start shouldn't be too big, unless the original > - * start + end is greater than COMPAT_OFF_T_MAX, in which > - * case the app was asking for trouble, so we return > - * -EOVERFLOW in that case. > - * l_len could be too big, in which case we just truncate it, > - * and only allow the app to see that part of the conflicting > - * lock that might make sense to it anyway > - */ > - > - if (f.l_start > COMPAT_OFF_T_MAX) > - ret = -EOVERFLOW; > - if (f.l_len > COMPAT_OFF_T_MAX) > - f.l_len = COMPAT_OFF_T_MAX; > - if (ret == 0) > - ret = put_compat_flock(&f, compat_ptr(arg)); > - } > + err = fcntl_setlk(fd, f.file, convert_fcntl_cmd(cmd), &flock); > break; > - > - case F_GETLK64: > case F_SETLK64: > case F_SETLKW64: > - case F_OFD_GETLK: > case F_OFD_SETLK: > case F_OFD_SETLKW: > - ret = get_compat_flock64(&f, compat_ptr(arg)); > - if (ret != 0) > + err = get_compat_flock64(&flock, compat_ptr(arg)); > + if (err) > break; > - old_fs = get_fs(); > - set_fs(KERNEL_DS); > - conv_cmd = convert_fcntl_cmd(cmd); > - ret = sys_fcntl(fd, conv_cmd, (unsigned long)&f); > - set_fs(old_fs); > - if ((conv_cmd == F_GETLK || conv_cmd == F_OFD_GETLK) && ret == 0) { > - /* need to return lock information - see above for commentary */ > - if (f.l_start > COMPAT_LOFF_T_MAX) > - ret = -EOVERFLOW; > - if (f.l_len > COMPAT_LOFF_T_MAX) > - f.l_len = COMPAT_LOFF_T_MAX; > - if (ret == 0) > - ret = put_compat_flock64(&f, compat_ptr(arg)); > - } > + err = fcntl_setlk(fd, f.file, convert_fcntl_cmd(cmd), &flock); > break; > - > default: > - ret = sys_fcntl(fd, cmd, arg); > + err = do_fcntl(fd, cmd, arg, f.file); > break; > } > - return ret; > +out_put: > + fdput(f); > + return err; > } > > COMPAT_SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, > diff --git a/fs/locks.c b/fs/locks.c > index 054f6fb7641e..8b4e78e4c054 100644 > --- a/fs/locks.c > +++ b/fs/locks.c > @@ -2325,7 +2325,7 @@ int fcntl_getlk64(struct file *filp, unsigned int cmd, struct flock64 *flock) > if (error) > goto out; > > - flock.l_type = file_lock.fl_type; > + flock->l_type = file_lock.fl_type; > if (file_lock.fl_type != F_UNLCK) > posix_lock_to_flock64(flock, &file_lock); >
On Tue, May 16, 2017 at 06:46:10AM -0400, Jeff Layton wrote: > Erm...build break above though -- flock is a pointer there, so those > should be "flock->...". I'll just squash that fix into the patch unless > you need to send a respin for other reasons. Thanks. I did a last minute fixup to factor this into a helper and messed up..
diff --git a/fs/fcntl.c b/fs/fcntl.c index 671ac43c65df..9d909b029063 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -505,76 +505,92 @@ convert_fcntl_cmd(unsigned int cmd) return cmd; } +/* + * GETLK was successful and we need to return the data, but it needs to fit in + * the compat structure. + * l_start shouldn't be too big, unless the original start + end is greater than + * COMPAT_OFF_T_MAX, in which case the app was asking for trouble, so we return + * -EOVERFLOW in that case. l_len could be too big, in which case we just + * truncate it, and only allow the app to see that part of the conflicting lock + * that might make sense to it anyway + */ +static int fixup_compat_flock(struct flock *flock) +{ + if (flock.l_start > COMPAT_OFF_T_MAX) + return -EOVERFLOW; + if (flock.l_len > COMPAT_OFF_T_MAX) + flock.l_len = COMPAT_OFF_T_MAX; + return 0; +} + COMPAT_SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd, compat_ulong_t, arg) { - mm_segment_t old_fs; - struct flock f; - long ret; - unsigned int conv_cmd; + struct fd f = fdget_raw(fd); + struct flock flock; + long err = -EBADF; + + if (!f.file) + return err; + + if (unlikely(f.file->f_mode & FMODE_PATH)) { + if (!check_fcntl_cmd(cmd)) + goto out_put; + } + + err = security_file_fcntl(f.file, cmd, arg); + if (err) + goto out_put; switch (cmd) { case F_GETLK: + err = get_compat_flock(&flock, compat_ptr(arg)); + if (err) + break; + err = fcntl_getlk(f.file, convert_fcntl_cmd(cmd), &flock); + if (err) + break; + err = fixup_compat_flock(&flock); + if (err) + return err; + err = put_compat_flock(&flock, compat_ptr(arg)); + break; + case F_GETLK64: + case F_OFD_GETLK: + err = get_compat_flock64(&flock, compat_ptr(arg)); + if (err) + break; + err = fcntl_getlk(f.file, convert_fcntl_cmd(cmd), &flock); + if (err) + break; + err = fixup_compat_flock(&flock); + if (err) + return err; + err = put_compat_flock64(&flock, compat_ptr(arg)); + break; case F_SETLK: case F_SETLKW: - ret = get_compat_flock(&f, compat_ptr(arg)); - if (ret != 0) + err = get_compat_flock(&flock, compat_ptr(arg)); + if (err) break; - old_fs = get_fs(); - set_fs(KERNEL_DS); - ret = sys_fcntl(fd, cmd, (unsigned long)&f); - set_fs(old_fs); - if (cmd == F_GETLK && ret == 0) { - /* GETLK was successful and we need to return the data... - * but it needs to fit in the compat structure. - * l_start shouldn't be too big, unless the original - * start + end is greater than COMPAT_OFF_T_MAX, in which - * case the app was asking for trouble, so we return - * -EOVERFLOW in that case. - * l_len could be too big, in which case we just truncate it, - * and only allow the app to see that part of the conflicting - * lock that might make sense to it anyway - */ - - if (f.l_start > COMPAT_OFF_T_MAX) - ret = -EOVERFLOW; - if (f.l_len > COMPAT_OFF_T_MAX) - f.l_len = COMPAT_OFF_T_MAX; - if (ret == 0) - ret = put_compat_flock(&f, compat_ptr(arg)); - } + err = fcntl_setlk(fd, f.file, convert_fcntl_cmd(cmd), &flock); break; - - case F_GETLK64: case F_SETLK64: case F_SETLKW64: - case F_OFD_GETLK: case F_OFD_SETLK: case F_OFD_SETLKW: - ret = get_compat_flock64(&f, compat_ptr(arg)); - if (ret != 0) + err = get_compat_flock64(&flock, compat_ptr(arg)); + if (err) break; - old_fs = get_fs(); - set_fs(KERNEL_DS); - conv_cmd = convert_fcntl_cmd(cmd); - ret = sys_fcntl(fd, conv_cmd, (unsigned long)&f); - set_fs(old_fs); - if ((conv_cmd == F_GETLK || conv_cmd == F_OFD_GETLK) && ret == 0) { - /* need to return lock information - see above for commentary */ - if (f.l_start > COMPAT_LOFF_T_MAX) - ret = -EOVERFLOW; - if (f.l_len > COMPAT_LOFF_T_MAX) - f.l_len = COMPAT_LOFF_T_MAX; - if (ret == 0) - ret = put_compat_flock64(&f, compat_ptr(arg)); - } + err = fcntl_setlk(fd, f.file, convert_fcntl_cmd(cmd), &flock); break; - default: - ret = sys_fcntl(fd, cmd, arg); + err = do_fcntl(fd, cmd, arg, f.file); break; } - return ret; +out_put: + fdput(f); + return err; } COMPAT_SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, diff --git a/fs/locks.c b/fs/locks.c index 054f6fb7641e..8b4e78e4c054 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -2325,7 +2325,7 @@ int fcntl_getlk64(struct file *filp, unsigned int cmd, struct flock64 *flock) if (error) goto out; - flock.l_type = file_lock.fl_type; + flock->l_type = file_lock.fl_type; if (file_lock.fl_type != F_UNLCK) posix_lock_to_flock64(flock, &file_lock);
Instead write a proper compat syscall that calls common helpers. Signed-off-by: Christoph Hellwig <hch@lst.de> --- fs/fcntl.c | 118 +++++++++++++++++++++++++++++++++++-------------------------- fs/locks.c | 2 +- 2 files changed, 68 insertions(+), 52 deletions(-)