From 2bc010600d0a8add4470eb37e1ccca8aaa3d0070 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 20 May 2020 17:58:10 +0200 Subject: [PATCH 01/17] fs: simplify do_splice_to No need for a local function pointer when we can trivial branch on the ->splice_read presence. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/splice.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/fs/splice.c b/fs/splice.c index 4735defc46ee..77b10f45a3da 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -870,8 +870,6 @@ static long do_splice_to(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, unsigned int flags) { - ssize_t (*splice_read)(struct file *, loff_t *, - struct pipe_inode_info *, size_t, unsigned int); int ret; if (unlikely(!(in->f_mode & FMODE_READ))) @@ -885,11 +883,8 @@ static long do_splice_to(struct file *in, loff_t *ppos, len = MAX_RW_COUNT; if (in->f_op->splice_read) - splice_read = in->f_op->splice_read; - else - splice_read = default_file_splice_read; - - return splice_read(in, ppos, pipe, len, flags); + return in->f_op->splice_read(in, ppos, pipe, len, flags); + return default_file_splice_read(in, ppos, pipe, len, flags); } /** From 00c285d0d0fe4606d20fe88f1c824962475ba880 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 20 May 2020 17:58:11 +0200 Subject: [PATCH 02/17] fs: simplify do_splice_from No need for a local function pointer when we can trivial branch on the ->splice_write presence. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/splice.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/fs/splice.c b/fs/splice.c index 77b10f45a3da..88942bf177d1 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -852,15 +852,9 @@ EXPORT_SYMBOL(generic_splice_sendpage); static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, loff_t *ppos, size_t len, unsigned int flags) { - ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, - loff_t *, size_t, unsigned int); - if (out->f_op->splice_write) - splice_write = out->f_op->splice_write; - else - splice_write = default_file_splice_write; - - return splice_write(pipe, out, ppos, len, flags); + return out->f_op->splice_write(pipe, out, ppos, len, flags); + return default_file_splice_write(pipe, out, ppos, len, flags); } /* From f6dd975583bd8ce088400648fd9819e4691c8958 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 20 May 2020 17:58:12 +0200 Subject: [PATCH 03/17] pipe: merge anon_pipe_buf*_ops All the op vectors are exactly the same, they are just used to encode packet or nomerge behavior. There already is a flag for the packet behavior, so just add a new one to allow for merging. Inverting it vs the previous nomerge special casing actually allows for much nicer code. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/pipe.c | 45 +++++---------------------------------- fs/splice.c | 12 +++++------ include/linux/pipe_fs_i.h | 2 +- 3 files changed, 11 insertions(+), 48 deletions(-) diff --git a/fs/pipe.c b/fs/pipe.c index 16fb72e9abf7..8e52b78b4042 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -231,7 +231,6 @@ void generic_pipe_buf_release(struct pipe_inode_info *pipe, } EXPORT_SYMBOL(generic_pipe_buf_release); -/* New data written to a pipe may be appended to a buffer with this type. */ static const struct pipe_buf_operations anon_pipe_buf_ops = { .confirm = generic_pipe_buf_confirm, .release = anon_pipe_buf_release, @@ -239,40 +238,6 @@ static const struct pipe_buf_operations anon_pipe_buf_ops = { .get = generic_pipe_buf_get, }; -static const struct pipe_buf_operations anon_pipe_buf_nomerge_ops = { - .confirm = generic_pipe_buf_confirm, - .release = anon_pipe_buf_release, - .steal = anon_pipe_buf_steal, - .get = generic_pipe_buf_get, -}; - -static const struct pipe_buf_operations packet_pipe_buf_ops = { - .confirm = generic_pipe_buf_confirm, - .release = anon_pipe_buf_release, - .steal = anon_pipe_buf_steal, - .get = generic_pipe_buf_get, -}; - -/** - * pipe_buf_mark_unmergeable - mark a &struct pipe_buffer as unmergeable - * @buf: the buffer to mark - * - * Description: - * This function ensures that no future writes will be merged into the - * given &struct pipe_buffer. This is necessary when multiple pipe buffers - * share the same backing page. - */ -void pipe_buf_mark_unmergeable(struct pipe_buffer *buf) -{ - if (buf->ops == &anon_pipe_buf_ops) - buf->ops = &anon_pipe_buf_nomerge_ops; -} - -static bool pipe_buf_can_merge(struct pipe_buffer *buf) -{ - return buf->ops == &anon_pipe_buf_ops; -} - /* Done while waiting without holding the pipe lock - thus the READ_ONCE() */ static inline bool pipe_readable(const struct pipe_inode_info *pipe) { @@ -478,7 +443,8 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from) struct pipe_buffer *buf = &pipe->bufs[(head - 1) & mask]; int offset = buf->offset + buf->len; - if (pipe_buf_can_merge(buf) && offset + chars <= PAGE_SIZE) { + if ((buf->flags & PIPE_BUF_FLAG_CAN_MERGE) && + offset + chars <= PAGE_SIZE) { ret = pipe_buf_confirm(pipe, buf); if (ret) goto out; @@ -541,11 +507,10 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from) buf->ops = &anon_pipe_buf_ops; buf->offset = 0; buf->len = 0; - buf->flags = 0; - if (is_packetized(filp)) { - buf->ops = &packet_pipe_buf_ops; + if (is_packetized(filp)) buf->flags = PIPE_BUF_FLAG_PACKET; - } + else + buf->flags = PIPE_BUF_FLAG_CAN_MERGE; pipe->tmp_page = NULL; copied = copy_page_from_iter(page, 0, PAGE_SIZE, from); diff --git a/fs/splice.c b/fs/splice.c index 88942bf177d1..fb9670e7fc1f 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -1624,12 +1624,11 @@ retry: *obuf = *ibuf; /* - * Don't inherit the gift flag, we need to + * Don't inherit the gift and merge flags, we need to * prevent multiple steals of this page. */ obuf->flags &= ~PIPE_BUF_FLAG_GIFT; - - pipe_buf_mark_unmergeable(obuf); + obuf->flags &= ~PIPE_BUF_FLAG_CAN_MERGE; obuf->len = len; ibuf->offset += len; @@ -1717,12 +1716,11 @@ static int link_pipe(struct pipe_inode_info *ipipe, *obuf = *ibuf; /* - * Don't inherit the gift flag, we need to - * prevent multiple steals of this page. + * Don't inherit the gift and merge flag, we need to prevent + * multiple steals of this page. */ obuf->flags &= ~PIPE_BUF_FLAG_GIFT; - - pipe_buf_mark_unmergeable(obuf); + obuf->flags &= ~PIPE_BUF_FLAG_CAN_MERGE; if (obuf->len > len) obuf->len = len; diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index ae58fad7f1e0..3f7b07b38824 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -8,6 +8,7 @@ #define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */ #define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */ #define PIPE_BUF_FLAG_PACKET 0x08 /* read() as a packet */ +#define PIPE_BUF_FLAG_CAN_MERGE 0x10 /* can merge buffers */ /** * struct pipe_buffer - a linux kernel pipe buffer @@ -233,7 +234,6 @@ int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *); int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *); int generic_pipe_buf_nosteal(struct pipe_inode_info *, struct pipe_buffer *); void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *); -void pipe_buf_mark_unmergeable(struct pipe_buffer *buf); extern const struct pipe_buf_operations nosteal_pipe_buf_ops; From 6797d97ab9d1b0ef94bf6063920669409dc2d730 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 20 May 2020 17:58:13 +0200 Subject: [PATCH 04/17] trace: remove tracing_pipe_buf_ops tracing_pipe_buf_ops has identical ops to default_pipe_buf_ops, so use that instead. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- kernel/trace/trace.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 8d2b98812625..bc9783797d27 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -6304,13 +6304,6 @@ static void tracing_spd_release_pipe(struct splice_pipe_desc *spd, __free_page(spd->pages[idx]); } -static const struct pipe_buf_operations tracing_pipe_buf_ops = { - .confirm = generic_pipe_buf_confirm, - .release = generic_pipe_buf_release, - .steal = generic_pipe_buf_steal, - .get = generic_pipe_buf_get, -}; - static size_t tracing_fill_pipe_page(size_t rem, struct trace_iterator *iter) { @@ -6372,7 +6365,7 @@ static ssize_t tracing_splice_read_pipe(struct file *filp, .partial = partial_def, .nr_pages = 0, /* This gets updated below. */ .nr_pages_max = PIPE_DEF_BUFFERS, - .ops = &tracing_pipe_buf_ops, + .ops = &default_pipe_buf_ops, .spd_release = tracing_spd_release_pipe, }; ssize_t ret; From 76887c256744740d6121af9bc4aa787712a1f694 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 20 May 2020 17:58:14 +0200 Subject: [PATCH 05/17] fs: make the pipe_buf_operations ->steal operation optional Just return 1 for failure if it is not present. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/splice.c | 7 ------- include/linux/pipe_fs_i.h | 3 ++- kernel/trace/trace.c | 1 - net/smc/smc_rx.c | 7 ------- 4 files changed, 2 insertions(+), 16 deletions(-) diff --git a/fs/splice.c b/fs/splice.c index fb9670e7fc1f..6c19bda274c8 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -337,17 +337,10 @@ const struct pipe_buf_operations default_pipe_buf_ops = { .get = generic_pipe_buf_get, }; -int generic_pipe_buf_nosteal(struct pipe_inode_info *pipe, - struct pipe_buffer *buf) -{ - return 1; -} - /* Pipe buffer operations for a socket and similar. */ const struct pipe_buf_operations nosteal_pipe_buf_ops = { .confirm = generic_pipe_buf_confirm, .release = generic_pipe_buf_release, - .steal = generic_pipe_buf_nosteal, .get = generic_pipe_buf_get, }; EXPORT_SYMBOL(nosteal_pipe_buf_ops); diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 3f7b07b38824..e022b2459301 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -206,6 +206,8 @@ static inline int pipe_buf_confirm(struct pipe_inode_info *pipe, static inline int pipe_buf_steal(struct pipe_inode_info *pipe, struct pipe_buffer *buf) { + if (!buf->ops->steal) + return 1; return buf->ops->steal(pipe, buf); } @@ -232,7 +234,6 @@ void free_pipe_info(struct pipe_inode_info *); bool generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *); int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *); int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *); -int generic_pipe_buf_nosteal(struct pipe_inode_info *, struct pipe_buffer *); void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *); extern const struct pipe_buf_operations nosteal_pipe_buf_ops; diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index bc9783797d27..29fa25cfb6c2 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -7576,7 +7576,6 @@ static bool buffer_pipe_buf_get(struct pipe_inode_info *pipe, static const struct pipe_buf_operations buffer_pipe_buf_ops = { .confirm = generic_pipe_buf_confirm, .release = buffer_pipe_buf_release, - .steal = generic_pipe_buf_nosteal, .get = buffer_pipe_buf_get, }; diff --git a/net/smc/smc_rx.c b/net/smc/smc_rx.c index 39d7b34d06d2..5fe25279702d 100644 --- a/net/smc/smc_rx.c +++ b/net/smc/smc_rx.c @@ -129,16 +129,9 @@ out: sock_put(sk); } -static int smc_rx_pipe_buf_nosteal(struct pipe_inode_info *pipe, - struct pipe_buffer *buf) -{ - return 1; -} - static const struct pipe_buf_operations smc_pipe_ops = { .confirm = generic_pipe_buf_confirm, .release = smc_rx_pipe_buf_release, - .steal = smc_rx_pipe_buf_nosteal, .get = generic_pipe_buf_get }; From b8d9e7f2411b0744df2ec33e80d7698180fef21a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 20 May 2020 17:58:15 +0200 Subject: [PATCH 06/17] fs: make the pipe_buf_operations ->confirm operation optional Just return 0 for success if it is not present. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/pipe.c | 17 ----------------- fs/splice.c | 3 --- include/linux/pipe_fs_i.h | 5 +++-- kernel/relay.c | 1 - kernel/trace/trace.c | 1 - net/smc/smc_rx.c | 1 - 6 files changed, 3 insertions(+), 25 deletions(-) diff --git a/fs/pipe.c b/fs/pipe.c index 8e52b78b4042..58890897402a 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -200,22 +200,6 @@ bool generic_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf) } EXPORT_SYMBOL(generic_pipe_buf_get); -/** - * generic_pipe_buf_confirm - verify contents of the pipe buffer - * @info: the pipe that the buffer belongs to - * @buf: the buffer to confirm - * - * Description: - * This function does nothing, because the generic pipe code uses - * pages that are always good when inserted into the pipe. - */ -int generic_pipe_buf_confirm(struct pipe_inode_info *info, - struct pipe_buffer *buf) -{ - return 0; -} -EXPORT_SYMBOL(generic_pipe_buf_confirm); - /** * generic_pipe_buf_release - put a reference to a &struct pipe_buffer * @pipe: the pipe that the buffer belongs to @@ -232,7 +216,6 @@ void generic_pipe_buf_release(struct pipe_inode_info *pipe, EXPORT_SYMBOL(generic_pipe_buf_release); static const struct pipe_buf_operations anon_pipe_buf_ops = { - .confirm = generic_pipe_buf_confirm, .release = anon_pipe_buf_release, .steal = anon_pipe_buf_steal, .get = generic_pipe_buf_get, diff --git a/fs/splice.c b/fs/splice.c index 6c19bda274c8..bc834073cf74 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -156,7 +156,6 @@ static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe, } static const struct pipe_buf_operations user_page_pipe_buf_ops = { - .confirm = generic_pipe_buf_confirm, .release = page_cache_pipe_buf_release, .steal = user_page_pipe_buf_steal, .get = generic_pipe_buf_get, @@ -331,7 +330,6 @@ ssize_t generic_file_splice_read(struct file *in, loff_t *ppos, EXPORT_SYMBOL(generic_file_splice_read); const struct pipe_buf_operations default_pipe_buf_ops = { - .confirm = generic_pipe_buf_confirm, .release = generic_pipe_buf_release, .steal = generic_pipe_buf_steal, .get = generic_pipe_buf_get, @@ -339,7 +337,6 @@ const struct pipe_buf_operations default_pipe_buf_ops = { /* Pipe buffer operations for a socket and similar. */ const struct pipe_buf_operations nosteal_pipe_buf_ops = { - .confirm = generic_pipe_buf_confirm, .release = generic_pipe_buf_release, .get = generic_pipe_buf_get, }; diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index e022b2459301..7c057daa0931 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -82,7 +82,7 @@ struct pipe_buf_operations { * and that the contents are good. If the pages in the pipe belong * to a file system, we may need to wait for IO completion in this * hook. Returns 0 for good, or a negative error value in case of - * error. + * error. If not present all pages are considered good. */ int (*confirm)(struct pipe_inode_info *, struct pipe_buffer *); @@ -195,6 +195,8 @@ static inline void pipe_buf_release(struct pipe_inode_info *pipe, static inline int pipe_buf_confirm(struct pipe_inode_info *pipe, struct pipe_buffer *buf) { + if (!buf->ops->confirm) + return 0; return buf->ops->confirm(pipe, buf); } @@ -232,7 +234,6 @@ void free_pipe_info(struct pipe_inode_info *); /* Generic pipe buffer ops functions */ bool generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *); -int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *); int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *); void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *); diff --git a/kernel/relay.c b/kernel/relay.c index ade14fb7ce2e..c5ece4c2b04d 100644 --- a/kernel/relay.c +++ b/kernel/relay.c @@ -1177,7 +1177,6 @@ static void relay_pipe_buf_release(struct pipe_inode_info *pipe, } static const struct pipe_buf_operations relay_pipe_buf_ops = { - .confirm = generic_pipe_buf_confirm, .release = relay_pipe_buf_release, .steal = generic_pipe_buf_steal, .get = generic_pipe_buf_get, diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 29fa25cfb6c2..63d1ab978435 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -7574,7 +7574,6 @@ static bool buffer_pipe_buf_get(struct pipe_inode_info *pipe, /* Pipe buffer operations for a buffer. */ static const struct pipe_buf_operations buffer_pipe_buf_ops = { - .confirm = generic_pipe_buf_confirm, .release = buffer_pipe_buf_release, .get = buffer_pipe_buf_get, }; diff --git a/net/smc/smc_rx.c b/net/smc/smc_rx.c index 5fe25279702d..fcfac59f8b72 100644 --- a/net/smc/smc_rx.c +++ b/net/smc/smc_rx.c @@ -130,7 +130,6 @@ out: } static const struct pipe_buf_operations smc_pipe_ops = { - .confirm = generic_pipe_buf_confirm, .release = smc_rx_pipe_buf_release, .get = generic_pipe_buf_get }; From c928f642c29a5ffb02e16f2430b42b876dde69de Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 20 May 2020 17:58:16 +0200 Subject: [PATCH 07/17] fs: rename pipe_buf ->steal to ->try_steal And replace the arcane return value convention with a simple bool where true means success and false means failure. [AV: braino fix folded in] Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- drivers/char/virtio_console.c | 2 +- fs/fuse/dev.c | 2 +- fs/pipe.c | 34 ++++++++++++++--------------- fs/splice.c | 40 +++++++++++++++++------------------ include/linux/pipe_fs_i.h | 34 ++++++++++++++--------------- kernel/relay.c | 6 +++--- 6 files changed, 58 insertions(+), 60 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 3cbaec925606..00c5e3acee46 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -871,7 +871,7 @@ static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf, return 0; /* Try lock this page */ - if (pipe_buf_steal(pipe, buf) == 0) { + if (pipe_buf_try_steal(pipe, buf)) { /* Get reference and unlock page for moving */ get_page(buf->page); unlock_page(buf->page); diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 97eec7522bf2..7a2b2de87b1f 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -805,7 +805,7 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep) if (cs->len != PAGE_SIZE) goto out_fallback; - if (pipe_buf_steal(cs->pipe, buf) != 0) + if (!pipe_buf_try_steal(cs->pipe, buf)) goto out_fallback; newpage = buf->page; diff --git a/fs/pipe.c b/fs/pipe.c index 58890897402a..c7c4fb5f345f 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -140,21 +140,20 @@ static void anon_pipe_buf_release(struct pipe_inode_info *pipe, put_page(page); } -static int anon_pipe_buf_steal(struct pipe_inode_info *pipe, - struct pipe_buffer *buf) +static bool anon_pipe_buf_try_steal(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) { struct page *page = buf->page; - if (page_count(page) == 1) { - memcg_kmem_uncharge_page(page, 0); - __SetPageLocked(page); - return 0; - } - return 1; + if (page_count(page) != 1) + return false; + memcg_kmem_uncharge_page(page, 0); + __SetPageLocked(page); + return true; } /** - * generic_pipe_buf_steal - attempt to take ownership of a &pipe_buffer + * generic_pipe_buf_try_steal - attempt to take ownership of a &pipe_buffer * @pipe: the pipe that the buffer belongs to * @buf: the buffer to attempt to steal * @@ -165,8 +164,8 @@ static int anon_pipe_buf_steal(struct pipe_inode_info *pipe, * he wishes; the typical use is insertion into a different file * page cache. */ -int generic_pipe_buf_steal(struct pipe_inode_info *pipe, - struct pipe_buffer *buf) +bool generic_pipe_buf_try_steal(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) { struct page *page = buf->page; @@ -177,12 +176,11 @@ int generic_pipe_buf_steal(struct pipe_inode_info *pipe, */ if (page_count(page) == 1) { lock_page(page); - return 0; + return true; } - - return 1; + return false; } -EXPORT_SYMBOL(generic_pipe_buf_steal); +EXPORT_SYMBOL(generic_pipe_buf_try_steal); /** * generic_pipe_buf_get - get a reference to a &struct pipe_buffer @@ -216,9 +214,9 @@ void generic_pipe_buf_release(struct pipe_inode_info *pipe, EXPORT_SYMBOL(generic_pipe_buf_release); static const struct pipe_buf_operations anon_pipe_buf_ops = { - .release = anon_pipe_buf_release, - .steal = anon_pipe_buf_steal, - .get = generic_pipe_buf_get, + .release = anon_pipe_buf_release, + .try_steal = anon_pipe_buf_try_steal, + .get = generic_pipe_buf_get, }; /* Done while waiting without holding the pipe lock - thus the READ_ONCE() */ diff --git a/fs/splice.c b/fs/splice.c index bc834073cf74..2c2f45a9afc0 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -44,8 +44,8 @@ * addition of remove_mapping(). If success is returned, the caller may * attempt to reuse this page for another destination. */ -static int page_cache_pipe_buf_steal(struct pipe_inode_info *pipe, - struct pipe_buffer *buf) +static bool page_cache_pipe_buf_try_steal(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) { struct page *page = buf->page; struct address_space *mapping; @@ -76,7 +76,7 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *pipe, */ if (remove_mapping(mapping, page)) { buf->flags |= PIPE_BUF_FLAG_LRU; - return 0; + return true; } } @@ -86,7 +86,7 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *pipe, */ out_unlock: unlock_page(page); - return 1; + return false; } static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe, @@ -139,26 +139,26 @@ error: } const struct pipe_buf_operations page_cache_pipe_buf_ops = { - .confirm = page_cache_pipe_buf_confirm, - .release = page_cache_pipe_buf_release, - .steal = page_cache_pipe_buf_steal, - .get = generic_pipe_buf_get, + .confirm = page_cache_pipe_buf_confirm, + .release = page_cache_pipe_buf_release, + .try_steal = page_cache_pipe_buf_try_steal, + .get = generic_pipe_buf_get, }; -static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe, - struct pipe_buffer *buf) +static bool user_page_pipe_buf_try_steal(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) { if (!(buf->flags & PIPE_BUF_FLAG_GIFT)) - return 1; + return false; buf->flags |= PIPE_BUF_FLAG_LRU; - return generic_pipe_buf_steal(pipe, buf); + return generic_pipe_buf_try_steal(pipe, buf); } static const struct pipe_buf_operations user_page_pipe_buf_ops = { - .release = page_cache_pipe_buf_release, - .steal = user_page_pipe_buf_steal, - .get = generic_pipe_buf_get, + .release = page_cache_pipe_buf_release, + .try_steal = user_page_pipe_buf_try_steal, + .get = generic_pipe_buf_get, }; static void wakeup_pipe_readers(struct pipe_inode_info *pipe) @@ -330,15 +330,15 @@ ssize_t generic_file_splice_read(struct file *in, loff_t *ppos, EXPORT_SYMBOL(generic_file_splice_read); const struct pipe_buf_operations default_pipe_buf_ops = { - .release = generic_pipe_buf_release, - .steal = generic_pipe_buf_steal, - .get = generic_pipe_buf_get, + .release = generic_pipe_buf_release, + .try_steal = generic_pipe_buf_try_steal, + .get = generic_pipe_buf_get, }; /* Pipe buffer operations for a socket and similar. */ const struct pipe_buf_operations nosteal_pipe_buf_ops = { - .release = generic_pipe_buf_release, - .get = generic_pipe_buf_get, + .release = generic_pipe_buf_release, + .get = generic_pipe_buf_get, }; EXPORT_SYMBOL(nosteal_pipe_buf_ops); diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 7c057daa0931..0c31b9461262 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -70,11 +70,11 @@ struct pipe_inode_info { * Note on the nesting of these functions: * * ->confirm() - * ->steal() + * ->try_steal() * - * That is, ->steal() must be called on a confirmed buffer. - * See below for the meaning of each operation. Also see kerneldoc - * in fs/pipe.c for the pipe and generic variants of these hooks. + * That is, ->try_steal() must be called on a confirmed buffer. See below for + * the meaning of each operation. Also see the kerneldoc in fs/pipe.c for the + * pipe and generic variants of these hooks. */ struct pipe_buf_operations { /* @@ -94,13 +94,13 @@ struct pipe_buf_operations { /* * Attempt to take ownership of the pipe buffer and its contents. - * ->steal() returns 0 for success, in which case the contents - * of the pipe (the buf->page) is locked and now completely owned - * by the caller. The page may then be transferred to a different - * mapping, the most often used case is insertion into different - * file address space cache. + * ->try_steal() returns %true for success, in which case the contents + * of the pipe (the buf->page) is locked and now completely owned by the + * caller. The page may then be transferred to a different mapping, the + * most often used case is insertion into different file address space + * cache. */ - int (*steal)(struct pipe_inode_info *, struct pipe_buffer *); + bool (*try_steal)(struct pipe_inode_info *, struct pipe_buffer *); /* * Get a reference to the pipe buffer. @@ -201,16 +201,16 @@ static inline int pipe_buf_confirm(struct pipe_inode_info *pipe, } /** - * pipe_buf_steal - attempt to take ownership of a pipe_buffer + * pipe_buf_try_steal - attempt to take ownership of a pipe_buffer * @pipe: the pipe that the buffer belongs to * @buf: the buffer to attempt to steal */ -static inline int pipe_buf_steal(struct pipe_inode_info *pipe, - struct pipe_buffer *buf) +static inline bool pipe_buf_try_steal(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) { - if (!buf->ops->steal) - return 1; - return buf->ops->steal(pipe, buf); + if (!buf->ops->try_steal) + return false; + return buf->ops->try_steal(pipe, buf); } /* Differs from PIPE_BUF in that PIPE_SIZE is the length of the actual @@ -234,7 +234,7 @@ void free_pipe_info(struct pipe_inode_info *); /* Generic pipe buffer ops functions */ bool generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *); -int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *); +bool generic_pipe_buf_try_steal(struct pipe_inode_info *, struct pipe_buffer *); void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *); extern const struct pipe_buf_operations nosteal_pipe_buf_ops; diff --git a/kernel/relay.c b/kernel/relay.c index c5ece4c2b04d..5c2263f3f857 100644 --- a/kernel/relay.c +++ b/kernel/relay.c @@ -1177,9 +1177,9 @@ static void relay_pipe_buf_release(struct pipe_inode_info *pipe, } static const struct pipe_buf_operations relay_pipe_buf_ops = { - .release = relay_pipe_buf_release, - .steal = generic_pipe_buf_steal, - .get = generic_pipe_buf_get, + .release = relay_pipe_buf_release, + .try_steal = generic_pipe_buf_try_steal, + .get = generic_pipe_buf_get, }; static void relay_page_release(struct splice_pipe_desc *spd, unsigned int i) From e0d0bf8a28eb04efd997478ef3d82319cdef9455 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 25 Apr 2020 18:19:30 -0400 Subject: [PATCH 08/17] comedi: move compat ioctl handling to native fops mechanical move Signed-off-by: Al Viro --- drivers/staging/comedi/Makefile | 1 - drivers/staging/comedi/comedi_compat32.c | 455 ----------------------- drivers/staging/comedi/comedi_compat32.h | 28 -- drivers/staging/comedi/comedi_fops.c | 451 +++++++++++++++++++++- 4 files changed, 448 insertions(+), 487 deletions(-) delete mode 100644 drivers/staging/comedi/comedi_compat32.c delete mode 100644 drivers/staging/comedi/comedi_compat32.h diff --git a/drivers/staging/comedi/Makefile b/drivers/staging/comedi/Makefile index 6af5da3b4315..072ed83a5a6a 100644 --- a/drivers/staging/comedi/Makefile +++ b/drivers/staging/comedi/Makefile @@ -4,7 +4,6 @@ ccflags-$(CONFIG_COMEDI_DEBUG) := -DDEBUG comedi-y := comedi_fops.o range.o drivers.o \ comedi_buf.o comedi-$(CONFIG_PROC_FS) += proc.o -comedi-$(CONFIG_COMPAT) += comedi_compat32.o obj-$(CONFIG_COMEDI_PCI_DRIVERS) += comedi_pci.o obj-$(CONFIG_COMEDI_PCMCIA_DRIVERS) += comedi_pcmcia.o diff --git a/drivers/staging/comedi/comedi_compat32.c b/drivers/staging/comedi/comedi_compat32.c deleted file mode 100644 index 36a3564ba1fb..000000000000 --- a/drivers/staging/comedi/comedi_compat32.c +++ /dev/null @@ -1,455 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * comedi/comedi_compat32.c - * 32-bit ioctl compatibility for 64-bit comedi kernel module. - * - * Author: Ian Abbott, MEV Ltd. - * Copyright (C) 2007 MEV Ltd. - * - * COMEDI - Linux Control and Measurement Device Interface - * Copyright (C) 1997-2007 David A. Schleef - */ - -#include -#include -#include -#include "comedi.h" -#include "comedi_compat32.h" - -#define COMEDI32_CHANINFO _IOR(CIO, 3, struct comedi32_chaninfo_struct) -#define COMEDI32_RANGEINFO _IOR(CIO, 8, struct comedi32_rangeinfo_struct) -/* - * N.B. COMEDI32_CMD and COMEDI_CMD ought to use _IOWR, not _IOR. - * It's too late to change it now, but it only affects the command number. - */ -#define COMEDI32_CMD _IOR(CIO, 9, struct comedi32_cmd_struct) -/* - * N.B. COMEDI32_CMDTEST and COMEDI_CMDTEST ought to use _IOWR, not _IOR. - * It's too late to change it now, but it only affects the command number. - */ -#define COMEDI32_CMDTEST _IOR(CIO, 10, struct comedi32_cmd_struct) -#define COMEDI32_INSNLIST _IOR(CIO, 11, struct comedi32_insnlist_struct) -#define COMEDI32_INSN _IOR(CIO, 12, struct comedi32_insn_struct) - -struct comedi32_chaninfo_struct { - unsigned int subdev; - compat_uptr_t maxdata_list; /* 32-bit 'unsigned int *' */ - compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */ - compat_uptr_t rangelist; /* 32-bit 'unsigned int *' */ - unsigned int unused[4]; -}; - -struct comedi32_rangeinfo_struct { - unsigned int range_type; - compat_uptr_t range_ptr; /* 32-bit 'void *' */ -}; - -struct comedi32_cmd_struct { - unsigned int subdev; - unsigned int flags; - unsigned int start_src; - unsigned int start_arg; - unsigned int scan_begin_src; - unsigned int scan_begin_arg; - unsigned int convert_src; - unsigned int convert_arg; - unsigned int scan_end_src; - unsigned int scan_end_arg; - unsigned int stop_src; - unsigned int stop_arg; - compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */ - unsigned int chanlist_len; - compat_uptr_t data; /* 32-bit 'short *' */ - unsigned int data_len; -}; - -struct comedi32_insn_struct { - unsigned int insn; - unsigned int n; - compat_uptr_t data; /* 32-bit 'unsigned int *' */ - unsigned int subdev; - unsigned int chanspec; - unsigned int unused[3]; -}; - -struct comedi32_insnlist_struct { - unsigned int n_insns; - compat_uptr_t insns; /* 32-bit 'struct comedi_insn *' */ -}; - -/* Handle translated ioctl. */ -static int translated_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - if (file->f_op->unlocked_ioctl) - return file->f_op->unlocked_ioctl(file, cmd, arg); - - return -ENOTTY; -} - -/* Handle 32-bit COMEDI_CHANINFO ioctl. */ -static int compat_chaninfo(struct file *file, unsigned long arg) -{ - struct comedi_chaninfo __user *chaninfo; - struct comedi32_chaninfo_struct __user *chaninfo32; - int err; - union { - unsigned int uint; - compat_uptr_t uptr; - } temp; - - chaninfo32 = compat_ptr(arg); - chaninfo = compat_alloc_user_space(sizeof(*chaninfo)); - - /* Copy chaninfo structure. Ignore unused members. */ - if (!access_ok(chaninfo32, sizeof(*chaninfo32)) || - !access_ok(chaninfo, sizeof(*chaninfo))) - return -EFAULT; - - err = 0; - err |= __get_user(temp.uint, &chaninfo32->subdev); - err |= __put_user(temp.uint, &chaninfo->subdev); - err |= __get_user(temp.uptr, &chaninfo32->maxdata_list); - err |= __put_user(compat_ptr(temp.uptr), &chaninfo->maxdata_list); - err |= __get_user(temp.uptr, &chaninfo32->flaglist); - err |= __put_user(compat_ptr(temp.uptr), &chaninfo->flaglist); - err |= __get_user(temp.uptr, &chaninfo32->rangelist); - err |= __put_user(compat_ptr(temp.uptr), &chaninfo->rangelist); - if (err) - return -EFAULT; - - return translated_ioctl(file, COMEDI_CHANINFO, (unsigned long)chaninfo); -} - -/* Handle 32-bit COMEDI_RANGEINFO ioctl. */ -static int compat_rangeinfo(struct file *file, unsigned long arg) -{ - struct comedi_rangeinfo __user *rangeinfo; - struct comedi32_rangeinfo_struct __user *rangeinfo32; - int err; - union { - unsigned int uint; - compat_uptr_t uptr; - } temp; - - rangeinfo32 = compat_ptr(arg); - rangeinfo = compat_alloc_user_space(sizeof(*rangeinfo)); - - /* Copy rangeinfo structure. */ - if (!access_ok(rangeinfo32, sizeof(*rangeinfo32)) || - !access_ok(rangeinfo, sizeof(*rangeinfo))) - return -EFAULT; - - err = 0; - err |= __get_user(temp.uint, &rangeinfo32->range_type); - err |= __put_user(temp.uint, &rangeinfo->range_type); - err |= __get_user(temp.uptr, &rangeinfo32->range_ptr); - err |= __put_user(compat_ptr(temp.uptr), &rangeinfo->range_ptr); - if (err) - return -EFAULT; - - return translated_ioctl(file, COMEDI_RANGEINFO, - (unsigned long)rangeinfo); -} - -/* Copy 32-bit cmd structure to native cmd structure. */ -static int get_compat_cmd(struct comedi_cmd __user *cmd, - struct comedi32_cmd_struct __user *cmd32) -{ - int err; - union { - unsigned int uint; - compat_uptr_t uptr; - } temp; - - /* Copy cmd structure. */ - if (!access_ok(cmd32, sizeof(*cmd32)) || - !access_ok(cmd, sizeof(*cmd))) - return -EFAULT; - - err = 0; - err |= __get_user(temp.uint, &cmd32->subdev); - err |= __put_user(temp.uint, &cmd->subdev); - err |= __get_user(temp.uint, &cmd32->flags); - err |= __put_user(temp.uint, &cmd->flags); - err |= __get_user(temp.uint, &cmd32->start_src); - err |= __put_user(temp.uint, &cmd->start_src); - err |= __get_user(temp.uint, &cmd32->start_arg); - err |= __put_user(temp.uint, &cmd->start_arg); - err |= __get_user(temp.uint, &cmd32->scan_begin_src); - err |= __put_user(temp.uint, &cmd->scan_begin_src); - err |= __get_user(temp.uint, &cmd32->scan_begin_arg); - err |= __put_user(temp.uint, &cmd->scan_begin_arg); - err |= __get_user(temp.uint, &cmd32->convert_src); - err |= __put_user(temp.uint, &cmd->convert_src); - err |= __get_user(temp.uint, &cmd32->convert_arg); - err |= __put_user(temp.uint, &cmd->convert_arg); - err |= __get_user(temp.uint, &cmd32->scan_end_src); - err |= __put_user(temp.uint, &cmd->scan_end_src); - err |= __get_user(temp.uint, &cmd32->scan_end_arg); - err |= __put_user(temp.uint, &cmd->scan_end_arg); - err |= __get_user(temp.uint, &cmd32->stop_src); - err |= __put_user(temp.uint, &cmd->stop_src); - err |= __get_user(temp.uint, &cmd32->stop_arg); - err |= __put_user(temp.uint, &cmd->stop_arg); - err |= __get_user(temp.uptr, &cmd32->chanlist); - err |= __put_user((unsigned int __force *)compat_ptr(temp.uptr), - &cmd->chanlist); - err |= __get_user(temp.uint, &cmd32->chanlist_len); - err |= __put_user(temp.uint, &cmd->chanlist_len); - err |= __get_user(temp.uptr, &cmd32->data); - err |= __put_user(compat_ptr(temp.uptr), &cmd->data); - err |= __get_user(temp.uint, &cmd32->data_len); - err |= __put_user(temp.uint, &cmd->data_len); - return err ? -EFAULT : 0; -} - -/* Copy native cmd structure to 32-bit cmd structure. */ -static int put_compat_cmd(struct comedi32_cmd_struct __user *cmd32, - struct comedi_cmd __user *cmd) -{ - int err; - unsigned int temp; - - /* - * Copy back most of cmd structure. - * - * Assume the pointer values are already valid. - * (Could use ptr_to_compat() to set them.) - */ - if (!access_ok(cmd, sizeof(*cmd)) || - !access_ok(cmd32, sizeof(*cmd32))) - return -EFAULT; - - err = 0; - err |= __get_user(temp, &cmd->subdev); - err |= __put_user(temp, &cmd32->subdev); - err |= __get_user(temp, &cmd->flags); - err |= __put_user(temp, &cmd32->flags); - err |= __get_user(temp, &cmd->start_src); - err |= __put_user(temp, &cmd32->start_src); - err |= __get_user(temp, &cmd->start_arg); - err |= __put_user(temp, &cmd32->start_arg); - err |= __get_user(temp, &cmd->scan_begin_src); - err |= __put_user(temp, &cmd32->scan_begin_src); - err |= __get_user(temp, &cmd->scan_begin_arg); - err |= __put_user(temp, &cmd32->scan_begin_arg); - err |= __get_user(temp, &cmd->convert_src); - err |= __put_user(temp, &cmd32->convert_src); - err |= __get_user(temp, &cmd->convert_arg); - err |= __put_user(temp, &cmd32->convert_arg); - err |= __get_user(temp, &cmd->scan_end_src); - err |= __put_user(temp, &cmd32->scan_end_src); - err |= __get_user(temp, &cmd->scan_end_arg); - err |= __put_user(temp, &cmd32->scan_end_arg); - err |= __get_user(temp, &cmd->stop_src); - err |= __put_user(temp, &cmd32->stop_src); - err |= __get_user(temp, &cmd->stop_arg); - err |= __put_user(temp, &cmd32->stop_arg); - /* Assume chanlist pointer is unchanged. */ - err |= __get_user(temp, &cmd->chanlist_len); - err |= __put_user(temp, &cmd32->chanlist_len); - /* Assume data pointer is unchanged. */ - err |= __get_user(temp, &cmd->data_len); - err |= __put_user(temp, &cmd32->data_len); - return err ? -EFAULT : 0; -} - -/* Handle 32-bit COMEDI_CMD ioctl. */ -static int compat_cmd(struct file *file, unsigned long arg) -{ - struct comedi_cmd __user *cmd; - struct comedi32_cmd_struct __user *cmd32; - int rc, err; - - cmd32 = compat_ptr(arg); - cmd = compat_alloc_user_space(sizeof(*cmd)); - - rc = get_compat_cmd(cmd, cmd32); - if (rc) - return rc; - - rc = translated_ioctl(file, COMEDI_CMD, (unsigned long)cmd); - if (rc == -EAGAIN) { - /* Special case: copy cmd back to user. */ - err = put_compat_cmd(cmd32, cmd); - if (err) - rc = err; - } - - return rc; -} - -/* Handle 32-bit COMEDI_CMDTEST ioctl. */ -static int compat_cmdtest(struct file *file, unsigned long arg) -{ - struct comedi_cmd __user *cmd; - struct comedi32_cmd_struct __user *cmd32; - int rc, err; - - cmd32 = compat_ptr(arg); - cmd = compat_alloc_user_space(sizeof(*cmd)); - - rc = get_compat_cmd(cmd, cmd32); - if (rc) - return rc; - - rc = translated_ioctl(file, COMEDI_CMDTEST, (unsigned long)cmd); - if (rc < 0) - return rc; - - err = put_compat_cmd(cmd32, cmd); - if (err) - rc = err; - - return rc; -} - -/* Copy 32-bit insn structure to native insn structure. */ -static int get_compat_insn(struct comedi_insn __user *insn, - struct comedi32_insn_struct __user *insn32) -{ - int err; - union { - unsigned int uint; - compat_uptr_t uptr; - } temp; - - /* Copy insn structure. Ignore the unused members. */ - err = 0; - if (!access_ok(insn32, sizeof(*insn32)) || - !access_ok(insn, sizeof(*insn))) - return -EFAULT; - - err |= __get_user(temp.uint, &insn32->insn); - err |= __put_user(temp.uint, &insn->insn); - err |= __get_user(temp.uint, &insn32->n); - err |= __put_user(temp.uint, &insn->n); - err |= __get_user(temp.uptr, &insn32->data); - err |= __put_user(compat_ptr(temp.uptr), &insn->data); - err |= __get_user(temp.uint, &insn32->subdev); - err |= __put_user(temp.uint, &insn->subdev); - err |= __get_user(temp.uint, &insn32->chanspec); - err |= __put_user(temp.uint, &insn->chanspec); - return err ? -EFAULT : 0; -} - -/* Handle 32-bit COMEDI_INSNLIST ioctl. */ -static int compat_insnlist(struct file *file, unsigned long arg) -{ - struct combined_insnlist { - struct comedi_insnlist insnlist; - struct comedi_insn insn[1]; - } __user *s; - struct comedi32_insnlist_struct __user *insnlist32; - struct comedi32_insn_struct __user *insn32; - compat_uptr_t uptr; - unsigned int n_insns, n; - int err, rc; - - insnlist32 = compat_ptr(arg); - - /* Get 32-bit insnlist structure. */ - if (!access_ok(insnlist32, sizeof(*insnlist32))) - return -EFAULT; - - err = 0; - err |= __get_user(n_insns, &insnlist32->n_insns); - err |= __get_user(uptr, &insnlist32->insns); - insn32 = compat_ptr(uptr); - if (err) - return -EFAULT; - - /* Allocate user memory to copy insnlist and insns into. */ - s = compat_alloc_user_space(offsetof(struct combined_insnlist, - insn[n_insns])); - - /* Set native insnlist structure. */ - if (!access_ok(&s->insnlist, sizeof(s->insnlist))) - return -EFAULT; - - err |= __put_user(n_insns, &s->insnlist.n_insns); - err |= __put_user(&s->insn[0], &s->insnlist.insns); - if (err) - return -EFAULT; - - /* Copy insn structures. */ - for (n = 0; n < n_insns; n++) { - rc = get_compat_insn(&s->insn[n], &insn32[n]); - if (rc) - return rc; - } - - return translated_ioctl(file, COMEDI_INSNLIST, - (unsigned long)&s->insnlist); -} - -/* Handle 32-bit COMEDI_INSN ioctl. */ -static int compat_insn(struct file *file, unsigned long arg) -{ - struct comedi_insn __user *insn; - struct comedi32_insn_struct __user *insn32; - int rc; - - insn32 = compat_ptr(arg); - insn = compat_alloc_user_space(sizeof(*insn)); - - rc = get_compat_insn(insn, insn32); - if (rc) - return rc; - - return translated_ioctl(file, COMEDI_INSN, (unsigned long)insn); -} - -/* - * compat_ioctl file operation. - * - * Returns -ENOIOCTLCMD for unrecognised ioctl codes. - */ -long comedi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int rc; - - switch (cmd) { - case COMEDI_DEVCONFIG: - case COMEDI_DEVINFO: - case COMEDI_SUBDINFO: - case COMEDI_BUFCONFIG: - case COMEDI_BUFINFO: - /* Just need to translate the pointer argument. */ - arg = (unsigned long)compat_ptr(arg); - rc = translated_ioctl(file, cmd, arg); - break; - case COMEDI_LOCK: - case COMEDI_UNLOCK: - case COMEDI_CANCEL: - case COMEDI_POLL: - case COMEDI_SETRSUBD: - case COMEDI_SETWSUBD: - /* No translation needed. */ - rc = translated_ioctl(file, cmd, arg); - break; - case COMEDI32_CHANINFO: - rc = compat_chaninfo(file, arg); - break; - case COMEDI32_RANGEINFO: - rc = compat_rangeinfo(file, arg); - break; - case COMEDI32_CMD: - rc = compat_cmd(file, arg); - break; - case COMEDI32_CMDTEST: - rc = compat_cmdtest(file, arg); - break; - case COMEDI32_INSNLIST: - rc = compat_insnlist(file, arg); - break; - case COMEDI32_INSN: - rc = compat_insn(file, arg); - break; - default: - rc = -ENOIOCTLCMD; - break; - } - return rc; -} diff --git a/drivers/staging/comedi/comedi_compat32.h b/drivers/staging/comedi/comedi_compat32.h deleted file mode 100644 index dc3e2a9442c7..000000000000 --- a/drivers/staging/comedi/comedi_compat32.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * comedi/comedi_compat32.h - * 32-bit ioctl compatibility for 64-bit comedi kernel module. - * - * Author: Ian Abbott, MEV Ltd. - * Copyright (C) 2007 MEV Ltd. - * - * COMEDI - Linux Control and Measurement Device Interface - * Copyright (C) 1997-2007 David A. Schleef - */ - -#ifndef _COMEDI_COMPAT32_H -#define _COMEDI_COMPAT32_H - -#ifdef CONFIG_COMPAT - -struct file; -long comedi_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg); - -#else /* CONFIG_COMPAT */ - -#define comedi_compat_ioctl NULL - -#endif /* CONFIG_COMPAT */ - -#endif /* _COMEDI_COMPAT32_H */ diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index 08d1bbbebf2d..9dfb81dfe43c 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -4,13 +4,14 @@ * comedi kernel module * * COMEDI - Linux Control and Measurement Device Interface - * Copyright (C) 1997-2000 David A. Schleef + * Copyright (C) 1997-2007 David A. Schleef + * compat ioctls: + * Author: Ian Abbott, MEV Ltd. + * Copyright (C) 2007 MEV Ltd. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include "comedi_compat32.h" - #include #include #include @@ -27,6 +28,7 @@ #include #include +#include #include "comedi_internal.h" @@ -2806,6 +2808,449 @@ static int comedi_close(struct inode *inode, struct file *file) return 0; } +#ifdef CONFIG_COMPAT + +#define COMEDI32_CHANINFO _IOR(CIO, 3, struct comedi32_chaninfo_struct) +#define COMEDI32_RANGEINFO _IOR(CIO, 8, struct comedi32_rangeinfo_struct) +/* + * N.B. COMEDI32_CMD and COMEDI_CMD ought to use _IOWR, not _IOR. + * It's too late to change it now, but it only affects the command number. + */ +#define COMEDI32_CMD _IOR(CIO, 9, struct comedi32_cmd_struct) +/* + * N.B. COMEDI32_CMDTEST and COMEDI_CMDTEST ought to use _IOWR, not _IOR. + * It's too late to change it now, but it only affects the command number. + */ +#define COMEDI32_CMDTEST _IOR(CIO, 10, struct comedi32_cmd_struct) +#define COMEDI32_INSNLIST _IOR(CIO, 11, struct comedi32_insnlist_struct) +#define COMEDI32_INSN _IOR(CIO, 12, struct comedi32_insn_struct) + +struct comedi32_chaninfo_struct { + unsigned int subdev; + compat_uptr_t maxdata_list; /* 32-bit 'unsigned int *' */ + compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */ + compat_uptr_t rangelist; /* 32-bit 'unsigned int *' */ + unsigned int unused[4]; +}; + +struct comedi32_rangeinfo_struct { + unsigned int range_type; + compat_uptr_t range_ptr; /* 32-bit 'void *' */ +}; + +struct comedi32_cmd_struct { + unsigned int subdev; + unsigned int flags; + unsigned int start_src; + unsigned int start_arg; + unsigned int scan_begin_src; + unsigned int scan_begin_arg; + unsigned int convert_src; + unsigned int convert_arg; + unsigned int scan_end_src; + unsigned int scan_end_arg; + unsigned int stop_src; + unsigned int stop_arg; + compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */ + unsigned int chanlist_len; + compat_uptr_t data; /* 32-bit 'short *' */ + unsigned int data_len; +}; + +struct comedi32_insn_struct { + unsigned int insn; + unsigned int n; + compat_uptr_t data; /* 32-bit 'unsigned int *' */ + unsigned int subdev; + unsigned int chanspec; + unsigned int unused[3]; +}; + +struct comedi32_insnlist_struct { + unsigned int n_insns; + compat_uptr_t insns; /* 32-bit 'struct comedi_insn *' */ +}; + +/* Handle translated ioctl. */ +static int translated_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + if (file->f_op->unlocked_ioctl) + return file->f_op->unlocked_ioctl(file, cmd, arg); + + return -ENOTTY; +} + +/* Handle 32-bit COMEDI_CHANINFO ioctl. */ +static int compat_chaninfo(struct file *file, unsigned long arg) +{ + struct comedi_chaninfo __user *chaninfo; + struct comedi32_chaninfo_struct __user *chaninfo32; + int err; + union { + unsigned int uint; + compat_uptr_t uptr; + } temp; + + chaninfo32 = compat_ptr(arg); + chaninfo = compat_alloc_user_space(sizeof(*chaninfo)); + + /* Copy chaninfo structure. Ignore unused members. */ + if (!access_ok(chaninfo32, sizeof(*chaninfo32)) || + !access_ok(chaninfo, sizeof(*chaninfo))) + return -EFAULT; + + err = 0; + err |= __get_user(temp.uint, &chaninfo32->subdev); + err |= __put_user(temp.uint, &chaninfo->subdev); + err |= __get_user(temp.uptr, &chaninfo32->maxdata_list); + err |= __put_user(compat_ptr(temp.uptr), &chaninfo->maxdata_list); + err |= __get_user(temp.uptr, &chaninfo32->flaglist); + err |= __put_user(compat_ptr(temp.uptr), &chaninfo->flaglist); + err |= __get_user(temp.uptr, &chaninfo32->rangelist); + err |= __put_user(compat_ptr(temp.uptr), &chaninfo->rangelist); + if (err) + return -EFAULT; + + return translated_ioctl(file, COMEDI_CHANINFO, (unsigned long)chaninfo); +} + +/* Handle 32-bit COMEDI_RANGEINFO ioctl. */ +static int compat_rangeinfo(struct file *file, unsigned long arg) +{ + struct comedi_rangeinfo __user *rangeinfo; + struct comedi32_rangeinfo_struct __user *rangeinfo32; + int err; + union { + unsigned int uint; + compat_uptr_t uptr; + } temp; + + rangeinfo32 = compat_ptr(arg); + rangeinfo = compat_alloc_user_space(sizeof(*rangeinfo)); + + /* Copy rangeinfo structure. */ + if (!access_ok(rangeinfo32, sizeof(*rangeinfo32)) || + !access_ok(rangeinfo, sizeof(*rangeinfo))) + return -EFAULT; + + err = 0; + err |= __get_user(temp.uint, &rangeinfo32->range_type); + err |= __put_user(temp.uint, &rangeinfo->range_type); + err |= __get_user(temp.uptr, &rangeinfo32->range_ptr); + err |= __put_user(compat_ptr(temp.uptr), &rangeinfo->range_ptr); + if (err) + return -EFAULT; + + return translated_ioctl(file, COMEDI_RANGEINFO, + (unsigned long)rangeinfo); +} + +/* Copy 32-bit cmd structure to native cmd structure. */ +static int get_compat_cmd(struct comedi_cmd __user *cmd, + struct comedi32_cmd_struct __user *cmd32) +{ + int err; + union { + unsigned int uint; + compat_uptr_t uptr; + } temp; + + /* Copy cmd structure. */ + if (!access_ok(cmd32, sizeof(*cmd32)) || + !access_ok(cmd, sizeof(*cmd))) + return -EFAULT; + + err = 0; + err |= __get_user(temp.uint, &cmd32->subdev); + err |= __put_user(temp.uint, &cmd->subdev); + err |= __get_user(temp.uint, &cmd32->flags); + err |= __put_user(temp.uint, &cmd->flags); + err |= __get_user(temp.uint, &cmd32->start_src); + err |= __put_user(temp.uint, &cmd->start_src); + err |= __get_user(temp.uint, &cmd32->start_arg); + err |= __put_user(temp.uint, &cmd->start_arg); + err |= __get_user(temp.uint, &cmd32->scan_begin_src); + err |= __put_user(temp.uint, &cmd->scan_begin_src); + err |= __get_user(temp.uint, &cmd32->scan_begin_arg); + err |= __put_user(temp.uint, &cmd->scan_begin_arg); + err |= __get_user(temp.uint, &cmd32->convert_src); + err |= __put_user(temp.uint, &cmd->convert_src); + err |= __get_user(temp.uint, &cmd32->convert_arg); + err |= __put_user(temp.uint, &cmd->convert_arg); + err |= __get_user(temp.uint, &cmd32->scan_end_src); + err |= __put_user(temp.uint, &cmd->scan_end_src); + err |= __get_user(temp.uint, &cmd32->scan_end_arg); + err |= __put_user(temp.uint, &cmd->scan_end_arg); + err |= __get_user(temp.uint, &cmd32->stop_src); + err |= __put_user(temp.uint, &cmd->stop_src); + err |= __get_user(temp.uint, &cmd32->stop_arg); + err |= __put_user(temp.uint, &cmd->stop_arg); + err |= __get_user(temp.uptr, &cmd32->chanlist); + err |= __put_user((unsigned int __force *)compat_ptr(temp.uptr), + &cmd->chanlist); + err |= __get_user(temp.uint, &cmd32->chanlist_len); + err |= __put_user(temp.uint, &cmd->chanlist_len); + err |= __get_user(temp.uptr, &cmd32->data); + err |= __put_user(compat_ptr(temp.uptr), &cmd->data); + err |= __get_user(temp.uint, &cmd32->data_len); + err |= __put_user(temp.uint, &cmd->data_len); + return err ? -EFAULT : 0; +} + +/* Copy native cmd structure to 32-bit cmd structure. */ +static int put_compat_cmd(struct comedi32_cmd_struct __user *cmd32, + struct comedi_cmd __user *cmd) +{ + int err; + unsigned int temp; + + /* + * Copy back most of cmd structure. + * + * Assume the pointer values are already valid. + * (Could use ptr_to_compat() to set them.) + */ + if (!access_ok(cmd, sizeof(*cmd)) || + !access_ok(cmd32, sizeof(*cmd32))) + return -EFAULT; + + err = 0; + err |= __get_user(temp, &cmd->subdev); + err |= __put_user(temp, &cmd32->subdev); + err |= __get_user(temp, &cmd->flags); + err |= __put_user(temp, &cmd32->flags); + err |= __get_user(temp, &cmd->start_src); + err |= __put_user(temp, &cmd32->start_src); + err |= __get_user(temp, &cmd->start_arg); + err |= __put_user(temp, &cmd32->start_arg); + err |= __get_user(temp, &cmd->scan_begin_src); + err |= __put_user(temp, &cmd32->scan_begin_src); + err |= __get_user(temp, &cmd->scan_begin_arg); + err |= __put_user(temp, &cmd32->scan_begin_arg); + err |= __get_user(temp, &cmd->convert_src); + err |= __put_user(temp, &cmd32->convert_src); + err |= __get_user(temp, &cmd->convert_arg); + err |= __put_user(temp, &cmd32->convert_arg); + err |= __get_user(temp, &cmd->scan_end_src); + err |= __put_user(temp, &cmd32->scan_end_src); + err |= __get_user(temp, &cmd->scan_end_arg); + err |= __put_user(temp, &cmd32->scan_end_arg); + err |= __get_user(temp, &cmd->stop_src); + err |= __put_user(temp, &cmd32->stop_src); + err |= __get_user(temp, &cmd->stop_arg); + err |= __put_user(temp, &cmd32->stop_arg); + /* Assume chanlist pointer is unchanged. */ + err |= __get_user(temp, &cmd->chanlist_len); + err |= __put_user(temp, &cmd32->chanlist_len); + /* Assume data pointer is unchanged. */ + err |= __get_user(temp, &cmd->data_len); + err |= __put_user(temp, &cmd32->data_len); + return err ? -EFAULT : 0; +} + +/* Handle 32-bit COMEDI_CMD ioctl. */ +static int compat_cmd(struct file *file, unsigned long arg) +{ + struct comedi_cmd __user *cmd; + struct comedi32_cmd_struct __user *cmd32; + int rc, err; + + cmd32 = compat_ptr(arg); + cmd = compat_alloc_user_space(sizeof(*cmd)); + + rc = get_compat_cmd(cmd, cmd32); + if (rc) + return rc; + + rc = translated_ioctl(file, COMEDI_CMD, (unsigned long)cmd); + if (rc == -EAGAIN) { + /* Special case: copy cmd back to user. */ + err = put_compat_cmd(cmd32, cmd); + if (err) + rc = err; + } + + return rc; +} + +/* Handle 32-bit COMEDI_CMDTEST ioctl. */ +static int compat_cmdtest(struct file *file, unsigned long arg) +{ + struct comedi_cmd __user *cmd; + struct comedi32_cmd_struct __user *cmd32; + int rc, err; + + cmd32 = compat_ptr(arg); + cmd = compat_alloc_user_space(sizeof(*cmd)); + + rc = get_compat_cmd(cmd, cmd32); + if (rc) + return rc; + + rc = translated_ioctl(file, COMEDI_CMDTEST, (unsigned long)cmd); + if (rc < 0) + return rc; + + err = put_compat_cmd(cmd32, cmd); + if (err) + rc = err; + + return rc; +} + +/* Copy 32-bit insn structure to native insn structure. */ +static int get_compat_insn(struct comedi_insn __user *insn, + struct comedi32_insn_struct __user *insn32) +{ + int err; + union { + unsigned int uint; + compat_uptr_t uptr; + } temp; + + /* Copy insn structure. Ignore the unused members. */ + err = 0; + if (!access_ok(insn32, sizeof(*insn32)) || + !access_ok(insn, sizeof(*insn))) + return -EFAULT; + + err |= __get_user(temp.uint, &insn32->insn); + err |= __put_user(temp.uint, &insn->insn); + err |= __get_user(temp.uint, &insn32->n); + err |= __put_user(temp.uint, &insn->n); + err |= __get_user(temp.uptr, &insn32->data); + err |= __put_user(compat_ptr(temp.uptr), &insn->data); + err |= __get_user(temp.uint, &insn32->subdev); + err |= __put_user(temp.uint, &insn->subdev); + err |= __get_user(temp.uint, &insn32->chanspec); + err |= __put_user(temp.uint, &insn->chanspec); + return err ? -EFAULT : 0; +} + +/* Handle 32-bit COMEDI_INSNLIST ioctl. */ +static int compat_insnlist(struct file *file, unsigned long arg) +{ + struct combined_insnlist { + struct comedi_insnlist insnlist; + struct comedi_insn insn[1]; + } __user *s; + struct comedi32_insnlist_struct __user *insnlist32; + struct comedi32_insn_struct __user *insn32; + compat_uptr_t uptr; + unsigned int n_insns, n; + int err, rc; + + insnlist32 = compat_ptr(arg); + + /* Get 32-bit insnlist structure. */ + if (!access_ok(insnlist32, sizeof(*insnlist32))) + return -EFAULT; + + err = 0; + err |= __get_user(n_insns, &insnlist32->n_insns); + err |= __get_user(uptr, &insnlist32->insns); + insn32 = compat_ptr(uptr); + if (err) + return -EFAULT; + + /* Allocate user memory to copy insnlist and insns into. */ + s = compat_alloc_user_space(offsetof(struct combined_insnlist, + insn[n_insns])); + + /* Set native insnlist structure. */ + if (!access_ok(&s->insnlist, sizeof(s->insnlist))) + return -EFAULT; + + err |= __put_user(n_insns, &s->insnlist.n_insns); + err |= __put_user(&s->insn[0], &s->insnlist.insns); + if (err) + return -EFAULT; + + /* Copy insn structures. */ + for (n = 0; n < n_insns; n++) { + rc = get_compat_insn(&s->insn[n], &insn32[n]); + if (rc) + return rc; + } + + return translated_ioctl(file, COMEDI_INSNLIST, + (unsigned long)&s->insnlist); +} + +/* Handle 32-bit COMEDI_INSN ioctl. */ +static int compat_insn(struct file *file, unsigned long arg) +{ + struct comedi_insn __user *insn; + struct comedi32_insn_struct __user *insn32; + int rc; + + insn32 = compat_ptr(arg); + insn = compat_alloc_user_space(sizeof(*insn)); + + rc = get_compat_insn(insn, insn32); + if (rc) + return rc; + + return translated_ioctl(file, COMEDI_INSN, (unsigned long)insn); +} + +/* + * compat_ioctl file operation. + * + * Returns -ENOIOCTLCMD for unrecognised ioctl codes. + */ +static long comedi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int rc; + + switch (cmd) { + case COMEDI_DEVCONFIG: + case COMEDI_DEVINFO: + case COMEDI_SUBDINFO: + case COMEDI_BUFCONFIG: + case COMEDI_BUFINFO: + /* Just need to translate the pointer argument. */ + arg = (unsigned long)compat_ptr(arg); + rc = translated_ioctl(file, cmd, arg); + break; + case COMEDI_LOCK: + case COMEDI_UNLOCK: + case COMEDI_CANCEL: + case COMEDI_POLL: + case COMEDI_SETRSUBD: + case COMEDI_SETWSUBD: + /* No translation needed. */ + rc = translated_ioctl(file, cmd, arg); + break; + case COMEDI32_CHANINFO: + rc = compat_chaninfo(file, arg); + break; + case COMEDI32_RANGEINFO: + rc = compat_rangeinfo(file, arg); + break; + case COMEDI32_CMD: + rc = compat_cmd(file, arg); + break; + case COMEDI32_CMDTEST: + rc = compat_cmdtest(file, arg); + break; + case COMEDI32_INSNLIST: + rc = compat_insnlist(file, arg); + break; + case COMEDI32_INSN: + rc = compat_insn(file, arg); + break; + default: + rc = -ENOIOCTLCMD; + break; + } + return rc; +} +#else +#define comedi_compat_ioctl NULL +#endif + static const struct file_operations comedi_fops = { .owner = THIS_MODULE, .unlocked_ioctl = comedi_unlocked_ioctl, From 5c6a8747e0cff47071d6ad3fcfe6f86713cf543a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 25 Apr 2020 18:23:17 -0400 Subject: [PATCH 09/17] comedi: get rid of indirection via translated_ioctl() Signed-off-by: Al Viro --- drivers/staging/comedi/comedi_fops.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index 9dfb81dfe43c..ecd29f28673c 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -2871,16 +2871,6 @@ struct comedi32_insnlist_struct { compat_uptr_t insns; /* 32-bit 'struct comedi_insn *' */ }; -/* Handle translated ioctl. */ -static int translated_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - if (file->f_op->unlocked_ioctl) - return file->f_op->unlocked_ioctl(file, cmd, arg); - - return -ENOTTY; -} - /* Handle 32-bit COMEDI_CHANINFO ioctl. */ static int compat_chaninfo(struct file *file, unsigned long arg) { @@ -2912,7 +2902,7 @@ static int compat_chaninfo(struct file *file, unsigned long arg) if (err) return -EFAULT; - return translated_ioctl(file, COMEDI_CHANINFO, (unsigned long)chaninfo); + return comedi_unlocked_ioctl(file, COMEDI_CHANINFO, (unsigned long)chaninfo); } /* Handle 32-bit COMEDI_RANGEINFO ioctl. */ @@ -2942,7 +2932,7 @@ static int compat_rangeinfo(struct file *file, unsigned long arg) if (err) return -EFAULT; - return translated_ioctl(file, COMEDI_RANGEINFO, + return comedi_unlocked_ioctl(file, COMEDI_RANGEINFO, (unsigned long)rangeinfo); } @@ -3063,7 +3053,7 @@ static int compat_cmd(struct file *file, unsigned long arg) if (rc) return rc; - rc = translated_ioctl(file, COMEDI_CMD, (unsigned long)cmd); + rc = comedi_unlocked_ioctl(file, COMEDI_CMD, (unsigned long)cmd); if (rc == -EAGAIN) { /* Special case: copy cmd back to user. */ err = put_compat_cmd(cmd32, cmd); @@ -3088,7 +3078,7 @@ static int compat_cmdtest(struct file *file, unsigned long arg) if (rc) return rc; - rc = translated_ioctl(file, COMEDI_CMDTEST, (unsigned long)cmd); + rc = comedi_unlocked_ioctl(file, COMEDI_CMDTEST, (unsigned long)cmd); if (rc < 0) return rc; @@ -3174,7 +3164,7 @@ static int compat_insnlist(struct file *file, unsigned long arg) return rc; } - return translated_ioctl(file, COMEDI_INSNLIST, + return comedi_unlocked_ioctl(file, COMEDI_INSNLIST, (unsigned long)&s->insnlist); } @@ -3192,7 +3182,7 @@ static int compat_insn(struct file *file, unsigned long arg) if (rc) return rc; - return translated_ioctl(file, COMEDI_INSN, (unsigned long)insn); + return comedi_unlocked_ioctl(file, COMEDI_INSN, (unsigned long)insn); } /* @@ -3212,7 +3202,7 @@ static long comedi_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo case COMEDI_BUFINFO: /* Just need to translate the pointer argument. */ arg = (unsigned long)compat_ptr(arg); - rc = translated_ioctl(file, cmd, arg); + rc = comedi_unlocked_ioctl(file, cmd, arg); break; case COMEDI_LOCK: case COMEDI_UNLOCK: @@ -3221,7 +3211,7 @@ static long comedi_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo case COMEDI_SETRSUBD: case COMEDI_SETWSUBD: /* No translation needed. */ - rc = translated_ioctl(file, cmd, arg); + rc = comedi_unlocked_ioctl(file, cmd, arg); break; case COMEDI32_CHANINFO: rc = compat_chaninfo(file, arg); From 3fbfd2223a271426509830e6340c386a1054cfad Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 25 Apr 2020 18:35:03 -0400 Subject: [PATCH 10/17] comedi: get rid of compat_alloc_user_space() mess in COMEDI_CHANINFO compat Just take copy_from_user() out of do_chaninfo_ioctl() into the caller and have compat_chaninfo() build a native version and pass it to do_chaninfo_ioctl() directly. Signed-off-by: Al Viro --- drivers/staging/comedi/comedi_fops.c | 68 ++++++++++++---------------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index ecd29f28673c..ab811735cd1b 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -1049,31 +1049,28 @@ static int do_subdinfo_ioctl(struct comedi_device *dev, * array of range table lengths to chaninfo->range_table_list if requested */ static int do_chaninfo_ioctl(struct comedi_device *dev, - struct comedi_chaninfo __user *arg) + struct comedi_chaninfo *it) { struct comedi_subdevice *s; - struct comedi_chaninfo it; lockdep_assert_held(&dev->mutex); - if (copy_from_user(&it, arg, sizeof(it))) - return -EFAULT; - if (it.subdev >= dev->n_subdevices) + if (it->subdev >= dev->n_subdevices) return -EINVAL; - s = &dev->subdevices[it.subdev]; + s = &dev->subdevices[it->subdev]; - if (it.maxdata_list) { + if (it->maxdata_list) { if (s->maxdata || !s->maxdata_list) return -EINVAL; - if (copy_to_user(it.maxdata_list, s->maxdata_list, + if (copy_to_user(it->maxdata_list, s->maxdata_list, s->n_chan * sizeof(unsigned int))) return -EFAULT; } - if (it.flaglist) + if (it->flaglist) return -EINVAL; /* flaglist not supported */ - if (it.rangelist) { + if (it->rangelist) { int i; if (!s->range_table_list) @@ -1081,9 +1078,9 @@ static int do_chaninfo_ioctl(struct comedi_device *dev, for (i = 0; i < s->n_chan; i++) { int x; - x = (dev->minor << 28) | (it.subdev << 24) | (i << 16) | + x = (dev->minor << 28) | (it->subdev << 24) | (i << 16) | (s->range_table_list[i]->length); - if (put_user(x, it.rangelist + i)) + if (put_user(x, it->rangelist + i)) return -EFAULT; } } @@ -2205,9 +2202,14 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd, (struct comedi_subdinfo __user *)arg, file); break; - case COMEDI_CHANINFO: - rc = do_chaninfo_ioctl(dev, (void __user *)arg); + case COMEDI_CHANINFO: { + struct comedi_chaninfo it; + if (copy_from_user(&it, (void __user *)arg, sizeof(it))) + rc = -EFAULT; + else + rc = do_chaninfo_ioctl(dev, &it); break; + } case COMEDI_RANGEINFO: rc = do_rangeinfo_ioctl(dev, (void __user *)arg); break; @@ -2874,35 +2876,25 @@ struct comedi32_insnlist_struct { /* Handle 32-bit COMEDI_CHANINFO ioctl. */ static int compat_chaninfo(struct file *file, unsigned long arg) { - struct comedi_chaninfo __user *chaninfo; - struct comedi32_chaninfo_struct __user *chaninfo32; + struct comedi_file *cfp = file->private_data; + struct comedi_device *dev = cfp->dev; + struct comedi32_chaninfo_struct chaninfo32; + struct comedi_chaninfo chaninfo; int err; - union { - unsigned int uint; - compat_uptr_t uptr; - } temp; - chaninfo32 = compat_ptr(arg); - chaninfo = compat_alloc_user_space(sizeof(*chaninfo)); - - /* Copy chaninfo structure. Ignore unused members. */ - if (!access_ok(chaninfo32, sizeof(*chaninfo32)) || - !access_ok(chaninfo, sizeof(*chaninfo))) + if (copy_from_user(&chaninfo32, compat_ptr(arg), sizeof(chaninfo32))) return -EFAULT; - err = 0; - err |= __get_user(temp.uint, &chaninfo32->subdev); - err |= __put_user(temp.uint, &chaninfo->subdev); - err |= __get_user(temp.uptr, &chaninfo32->maxdata_list); - err |= __put_user(compat_ptr(temp.uptr), &chaninfo->maxdata_list); - err |= __get_user(temp.uptr, &chaninfo32->flaglist); - err |= __put_user(compat_ptr(temp.uptr), &chaninfo->flaglist); - err |= __get_user(temp.uptr, &chaninfo32->rangelist); - err |= __put_user(compat_ptr(temp.uptr), &chaninfo->rangelist); - if (err) - return -EFAULT; + memset(&chaninfo, 0, sizeof(chaninfo)); + chaninfo.subdev = chaninfo32.subdev; + chaninfo.maxdata_list = compat_ptr(chaninfo32.maxdata_list); + chaninfo.flaglist = compat_ptr(chaninfo32.flaglist); + chaninfo.rangelist = compat_ptr(chaninfo32.rangelist); - return comedi_unlocked_ioctl(file, COMEDI_CHANINFO, (unsigned long)chaninfo); + mutex_lock(&dev->mutex); + err = do_chaninfo_ioctl(dev, &chaninfo); + mutex_unlock(&dev->mutex); + return err; } /* Handle 32-bit COMEDI_RANGEINFO ioctl. */ From 388138764e2549520afd0b3b4e15de8deb592ff6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 25 Apr 2020 18:44:30 -0400 Subject: [PATCH 11/17] comedi: get rid of compat_alloc_user_space() mess in COMEDI_RANGEINFO compat Just take copy_from_user() out of do_rangeing_ioctl() into the caller and have compat_rangeinfo() build a native version and pass it to do_rangeinfo_ioctl() directly. Signed-off-by: Al Viro --- drivers/staging/comedi/comedi_fops.c | 43 +++++++++++------------- drivers/staging/comedi/comedi_internal.h | 2 +- drivers/staging/comedi/range.c | 17 ++++------ 3 files changed, 27 insertions(+), 35 deletions(-) diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index ab811735cd1b..d96dc85d8a98 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -2210,9 +2210,14 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd, rc = do_chaninfo_ioctl(dev, &it); break; } - case COMEDI_RANGEINFO: - rc = do_rangeinfo_ioctl(dev, (void __user *)arg); + case COMEDI_RANGEINFO: { + struct comedi_rangeinfo it; + if (copy_from_user(&it, (void __user *)arg, sizeof(it))) + rc = -EFAULT; + else + rc = do_rangeinfo_ioctl(dev, &it); break; + } case COMEDI_BUFINFO: rc = do_bufinfo_ioctl(dev, (struct comedi_bufinfo __user *)arg, @@ -2900,32 +2905,22 @@ static int compat_chaninfo(struct file *file, unsigned long arg) /* Handle 32-bit COMEDI_RANGEINFO ioctl. */ static int compat_rangeinfo(struct file *file, unsigned long arg) { - struct comedi_rangeinfo __user *rangeinfo; - struct comedi32_rangeinfo_struct __user *rangeinfo32; + struct comedi_file *cfp = file->private_data; + struct comedi_device *dev = cfp->dev; + struct comedi32_rangeinfo_struct rangeinfo32; + struct comedi_rangeinfo rangeinfo; int err; - union { - unsigned int uint; - compat_uptr_t uptr; - } temp; - rangeinfo32 = compat_ptr(arg); - rangeinfo = compat_alloc_user_space(sizeof(*rangeinfo)); - - /* Copy rangeinfo structure. */ - if (!access_ok(rangeinfo32, sizeof(*rangeinfo32)) || - !access_ok(rangeinfo, sizeof(*rangeinfo))) + if (copy_from_user(&rangeinfo32, compat_ptr(arg), sizeof(rangeinfo32))) return -EFAULT; + memset(&rangeinfo, 0, sizeof(rangeinfo)); + rangeinfo.range_type = rangeinfo32.range_type; + rangeinfo.range_ptr = compat_ptr(rangeinfo32.range_ptr); - err = 0; - err |= __get_user(temp.uint, &rangeinfo32->range_type); - err |= __put_user(temp.uint, &rangeinfo->range_type); - err |= __get_user(temp.uptr, &rangeinfo32->range_ptr); - err |= __put_user(compat_ptr(temp.uptr), &rangeinfo->range_ptr); - if (err) - return -EFAULT; - - return comedi_unlocked_ioctl(file, COMEDI_RANGEINFO, - (unsigned long)rangeinfo); + mutex_lock(&dev->mutex); + err = do_rangeinfo_ioctl(dev, &rangeinfo); + mutex_unlock(&dev->mutex); + return err; } /* Copy 32-bit cmd structure to native cmd structure. */ diff --git a/drivers/staging/comedi/comedi_internal.h b/drivers/staging/comedi/comedi_internal.h index 515f293a5d26..7c8f18f55122 100644 --- a/drivers/staging/comedi/comedi_internal.h +++ b/drivers/staging/comedi/comedi_internal.h @@ -18,7 +18,7 @@ struct comedi_subdevice; struct device; int do_rangeinfo_ioctl(struct comedi_device *dev, - struct comedi_rangeinfo __user *arg); + struct comedi_rangeinfo *it); struct comedi_device *comedi_alloc_board_minor(struct device *hardware_device); void comedi_release_hardware_device(struct device *hardware_device); int comedi_alloc_subdevice_minor(struct comedi_subdevice *s); diff --git a/drivers/staging/comedi/range.c b/drivers/staging/comedi/range.c index 89d599877445..a4e6fe0fb729 100644 --- a/drivers/staging/comedi/range.c +++ b/drivers/staging/comedi/range.c @@ -46,17 +46,14 @@ EXPORT_SYMBOL_GPL(range_unknown); * array of comedi_krange structures to rangeinfo->range_ptr pointer */ int do_rangeinfo_ioctl(struct comedi_device *dev, - struct comedi_rangeinfo __user *arg) + struct comedi_rangeinfo *it) { - struct comedi_rangeinfo it; int subd, chan; const struct comedi_lrange *lr; struct comedi_subdevice *s; - if (copy_from_user(&it, arg, sizeof(struct comedi_rangeinfo))) - return -EFAULT; - subd = (it.range_type >> 24) & 0xf; - chan = (it.range_type >> 16) & 0xff; + subd = (it->range_type >> 24) & 0xf; + chan = (it->range_type >> 16) & 0xff; if (!dev->attached) return -EINVAL; @@ -73,15 +70,15 @@ int do_rangeinfo_ioctl(struct comedi_device *dev, return -EINVAL; } - if (RANGE_LENGTH(it.range_type) != lr->length) { + if (RANGE_LENGTH(it->range_type) != lr->length) { dev_dbg(dev->class_dev, "wrong length %d should be %d (0x%08x)\n", - RANGE_LENGTH(it.range_type), - lr->length, it.range_type); + RANGE_LENGTH(it->range_type), + lr->length, it->range_type); return -EINVAL; } - if (copy_to_user(it.range_ptr, lr->range, + if (copy_to_user(it->range_ptr, lr->range, sizeof(struct comedi_krange) * lr->length)) return -EFAULT; From aa332e6759fac21b454e4959703777c771a9cd93 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 25 Apr 2020 20:11:57 -0400 Subject: [PATCH 12/17] comedi: get rid of compat_alloc_user_space() mess in COMEDI_INSN compat Just take copy_from_user() out of do_insn_ioctl() into the caller and have compat_insn() build a native version and pass it to do_insn_ioctl() directly. One difference from the previous commits is that the helper used to convert 32bit variant to native has two users - compat_insn() and compat_insnlist(). The latter will be converted in next commit; for now we simply split the helper in two variants - "userland 32bit to kernel native" and "userland 32bit to userland native". The latter is renamed old get_compat_insn(); it will be gone in the next commit. Signed-off-by: Al Viro --- drivers/staging/comedi/comedi_fops.c | 74 ++++++++++++++++++---------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index d96dc85d8a98..0e7ba0d3fa03 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -1615,22 +1615,19 @@ error: * data (for reads) to insn->data pointer */ static int do_insn_ioctl(struct comedi_device *dev, - struct comedi_insn __user *arg, void *file) + struct comedi_insn *insn, void *file) { - struct comedi_insn insn; unsigned int *data = NULL; unsigned int n_data = MIN_SAMPLES; int ret = 0; lockdep_assert_held(&dev->mutex); - if (copy_from_user(&insn, arg, sizeof(insn))) - return -EFAULT; - n_data = max(n_data, insn.n); + n_data = max(n_data, insn->n); /* This is where the behavior of insn and insnlist deviate. */ - if (insn.n > MAX_SAMPLES) { - insn.n = MAX_SAMPLES; + if (insn->n > MAX_SAMPLES) { + insn->n = MAX_SAMPLES; n_data = MAX_SAMPLES; } @@ -1640,26 +1637,26 @@ static int do_insn_ioctl(struct comedi_device *dev, goto error; } - if (insn.insn & INSN_MASK_WRITE) { + if (insn->insn & INSN_MASK_WRITE) { if (copy_from_user(data, - insn.data, - insn.n * sizeof(unsigned int))) { + insn->data, + insn->n * sizeof(unsigned int))) { ret = -EFAULT; goto error; } } - ret = parse_insn(dev, &insn, data, file); + ret = parse_insn(dev, insn, data, file); if (ret < 0) goto error; - if (insn.insn & INSN_MASK_READ) { - if (copy_to_user(insn.data, + if (insn->insn & INSN_MASK_READ) { + if (copy_to_user(insn->data, data, - insn.n * sizeof(unsigned int))) { + insn->n * sizeof(unsigned int))) { ret = -EFAULT; goto error; } } - ret = insn.n; + ret = insn->n; error: kfree(data); @@ -2244,10 +2241,14 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd, (struct comedi_insnlist __user *)arg, file); break; - case COMEDI_INSN: - rc = do_insn_ioctl(dev, (struct comedi_insn __user *)arg, - file); + case COMEDI_INSN: { + struct comedi_insn insn; + if (copy_from_user(&insn, (void __user *)arg, sizeof(insn))) + rc = -EFAULT; + else + rc = do_insn_ioctl(dev, &insn, file); break; + } case COMEDI_POLL: rc = do_poll_ioctl(dev, arg, file); break; @@ -3077,7 +3078,25 @@ static int compat_cmdtest(struct file *file, unsigned long arg) } /* Copy 32-bit insn structure to native insn structure. */ -static int get_compat_insn(struct comedi_insn __user *insn, +static int get_compat_insn(struct comedi_insn *insn, + struct comedi32_insn_struct __user *insn32) +{ + struct comedi32_insn_struct v32; + + /* Copy insn structure. Ignore the unused members. */ + if (copy_from_user(&v32, insn32, sizeof(v32))) + return -EFAULT; + memset(insn, 0, sizeof(*insn)); + insn->insn = v32.insn; + insn->n = v32.n; + insn->data = compat_ptr(v32.data); + insn->subdev = v32.subdev; + insn->chanspec = v32.chanspec; + return 0; +} + +/* Copy 32-bit insn structure to native insn structure. */ +static int __get_compat_insn(struct comedi_insn __user *insn, struct comedi32_insn_struct __user *insn32) { int err; @@ -3146,7 +3165,7 @@ static int compat_insnlist(struct file *file, unsigned long arg) /* Copy insn structures. */ for (n = 0; n < n_insns; n++) { - rc = get_compat_insn(&s->insn[n], &insn32[n]); + rc = __get_compat_insn(&s->insn[n], &insn32[n]); if (rc) return rc; } @@ -3158,18 +3177,19 @@ static int compat_insnlist(struct file *file, unsigned long arg) /* Handle 32-bit COMEDI_INSN ioctl. */ static int compat_insn(struct file *file, unsigned long arg) { - struct comedi_insn __user *insn; - struct comedi32_insn_struct __user *insn32; + struct comedi_file *cfp = file->private_data; + struct comedi_device *dev = cfp->dev; + struct comedi_insn insn; int rc; - insn32 = compat_ptr(arg); - insn = compat_alloc_user_space(sizeof(*insn)); - - rc = get_compat_insn(insn, insn32); + rc = get_compat_insn(&insn, (void __user *)arg); if (rc) return rc; - return comedi_unlocked_ioctl(file, COMEDI_INSN, (unsigned long)insn); + mutex_lock(&dev->mutex); + rc = do_insn_ioctl(dev, &insn, file); + mutex_unlock(&dev->mutex); + return rc; } /* From b8d47d8813055ce38c0d2ad913d5462017e52692 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 25 Apr 2020 20:33:04 -0400 Subject: [PATCH 13/17] comedi: get rid of compat_alloc_user_space() mess in COMEDI_INSNLIST compat Signed-off-by: Al Viro --- drivers/staging/comedi/comedi_fops.c | 138 ++++++++++----------------- 1 file changed, 48 insertions(+), 90 deletions(-) diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index 0e7ba0d3fa03..10ab24019fa5 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -1520,34 +1520,19 @@ out: #define MIN_SAMPLES 16 #define MAX_SAMPLES 65536 static int do_insnlist_ioctl(struct comedi_device *dev, - struct comedi_insnlist __user *arg, void *file) + struct comedi_insn *insns, + unsigned int n_insns, + void *file) { - struct comedi_insnlist insnlist; - struct comedi_insn *insns = NULL; unsigned int *data = NULL; unsigned int max_n_data_required = MIN_SAMPLES; int i = 0; int ret = 0; lockdep_assert_held(&dev->mutex); - if (copy_from_user(&insnlist, arg, sizeof(insnlist))) - return -EFAULT; - - insns = kcalloc(insnlist.n_insns, sizeof(*insns), GFP_KERNEL); - if (!insns) { - ret = -ENOMEM; - goto error; - } - - if (copy_from_user(insns, insnlist.insns, - sizeof(*insns) * insnlist.n_insns)) { - dev_dbg(dev->class_dev, "copy_from_user failed\n"); - ret = -EFAULT; - goto error; - } /* Determine maximum memory needed for all instructions. */ - for (i = 0; i < insnlist.n_insns; ++i) { + for (i = 0; i < n_insns; ++i) { if (insns[i].n > MAX_SAMPLES) { dev_dbg(dev->class_dev, "number of samples too large\n"); @@ -1565,7 +1550,7 @@ static int do_insnlist_ioctl(struct comedi_device *dev, goto error; } - for (i = 0; i < insnlist.n_insns; ++i) { + for (i = 0; i < n_insns; ++i) { if (insns[i].insn & INSN_MASK_WRITE) { if (copy_from_user(data, insns[i].data, insns[i].n * sizeof(unsigned int))) { @@ -1592,7 +1577,6 @@ static int do_insnlist_ioctl(struct comedi_device *dev, } error: - kfree(insns); kfree(data); if (ret < 0) @@ -2236,11 +2220,30 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd, rc = do_cmdtest_ioctl(dev, (struct comedi_cmd __user *)arg, file); break; - case COMEDI_INSNLIST: - rc = do_insnlist_ioctl(dev, - (struct comedi_insnlist __user *)arg, - file); + case COMEDI_INSNLIST: { + struct comedi_insnlist insnlist; + struct comedi_insn *insns = NULL; + + if (copy_from_user(&insnlist, (void __user *)arg, + sizeof(insnlist))) { + rc = -EFAULT; + break; + } + insns = kcalloc(insnlist.n_insns, sizeof(*insns), GFP_KERNEL); + if (!insns) { + rc = -ENOMEM; + break; + } + if (copy_from_user(insns, insnlist.insns, + sizeof(*insns) * insnlist.n_insns)) { + rc = -EFAULT; + kfree(insns); + break; + } + rc = do_insnlist_ioctl(dev, insns, insnlist.n_insns, file); + kfree(insns); break; + } case COMEDI_INSN: { struct comedi_insn insn; if (copy_from_user(&insn, (void __user *)arg, sizeof(insn))) @@ -3095,83 +3098,38 @@ static int get_compat_insn(struct comedi_insn *insn, return 0; } -/* Copy 32-bit insn structure to native insn structure. */ -static int __get_compat_insn(struct comedi_insn __user *insn, - struct comedi32_insn_struct __user *insn32) -{ - int err; - union { - unsigned int uint; - compat_uptr_t uptr; - } temp; - - /* Copy insn structure. Ignore the unused members. */ - err = 0; - if (!access_ok(insn32, sizeof(*insn32)) || - !access_ok(insn, sizeof(*insn))) - return -EFAULT; - - err |= __get_user(temp.uint, &insn32->insn); - err |= __put_user(temp.uint, &insn->insn); - err |= __get_user(temp.uint, &insn32->n); - err |= __put_user(temp.uint, &insn->n); - err |= __get_user(temp.uptr, &insn32->data); - err |= __put_user(compat_ptr(temp.uptr), &insn->data); - err |= __get_user(temp.uint, &insn32->subdev); - err |= __put_user(temp.uint, &insn->subdev); - err |= __get_user(temp.uint, &insn32->chanspec); - err |= __put_user(temp.uint, &insn->chanspec); - return err ? -EFAULT : 0; -} - /* Handle 32-bit COMEDI_INSNLIST ioctl. */ static int compat_insnlist(struct file *file, unsigned long arg) { - struct combined_insnlist { - struct comedi_insnlist insnlist; - struct comedi_insn insn[1]; - } __user *s; - struct comedi32_insnlist_struct __user *insnlist32; + struct comedi_file *cfp = file->private_data; + struct comedi_device *dev = cfp->dev; + struct comedi32_insnlist_struct insnlist32; struct comedi32_insn_struct __user *insn32; - compat_uptr_t uptr; - unsigned int n_insns, n; - int err, rc; + struct comedi_insn *insns; + unsigned int n; + int rc; - insnlist32 = compat_ptr(arg); - - /* Get 32-bit insnlist structure. */ - if (!access_ok(insnlist32, sizeof(*insnlist32))) + if (copy_from_user(&insnlist32, compat_ptr(arg), sizeof(insnlist32))) return -EFAULT; - err = 0; - err |= __get_user(n_insns, &insnlist32->n_insns); - err |= __get_user(uptr, &insnlist32->insns); - insn32 = compat_ptr(uptr); - if (err) - return -EFAULT; - - /* Allocate user memory to copy insnlist and insns into. */ - s = compat_alloc_user_space(offsetof(struct combined_insnlist, - insn[n_insns])); - - /* Set native insnlist structure. */ - if (!access_ok(&s->insnlist, sizeof(s->insnlist))) - return -EFAULT; - - err |= __put_user(n_insns, &s->insnlist.n_insns); - err |= __put_user(&s->insn[0], &s->insnlist.insns); - if (err) - return -EFAULT; + insns = kcalloc(insnlist32.n_insns, sizeof(*insns), GFP_KERNEL); + if (!insns) + return -ENOMEM; /* Copy insn structures. */ - for (n = 0; n < n_insns; n++) { - rc = __get_compat_insn(&s->insn[n], &insn32[n]); - if (rc) + insn32 = compat_ptr(insnlist32.insns); + for (n = 0; n < insnlist32.n_insns; n++) { + rc = get_compat_insn(insns + n, insn32 + n); + if (rc) { + kfree(insns); return rc; + } } - return comedi_unlocked_ioctl(file, COMEDI_INSNLIST, - (unsigned long)&s->insnlist); + mutex_lock(&dev->mutex); + rc = do_insnlist_ioctl(dev, insns, insnlist32.n_insns, file); + mutex_unlock(&dev->mutex); + return rc; } /* Handle 32-bit COMEDI_INSN ioctl. */ From 00035beeec2c067a8094d3d86b34cc797c3d6b21 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 26 Apr 2020 08:46:04 -0400 Subject: [PATCH 14/17] comedi: lift copy_from_user() into callers of __comedi_get_user_cmd() Signed-off-by: Al Viro --- drivers/staging/comedi/comedi_fops.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index 10ab24019fa5..c136cb2f676a 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -1649,17 +1649,11 @@ error: } static int __comedi_get_user_cmd(struct comedi_device *dev, - struct comedi_cmd __user *arg, struct comedi_cmd *cmd) { struct comedi_subdevice *s; lockdep_assert_held(&dev->mutex); - if (copy_from_user(cmd, arg, sizeof(*cmd))) { - dev_dbg(dev->class_dev, "bad cmd address\n"); - return -EFAULT; - } - if (cmd->subdev >= dev->n_subdevices) { dev_dbg(dev->class_dev, "%d no such subdevice\n", cmd->subdev); return -ENODEV; @@ -1757,8 +1751,13 @@ static int do_cmd_ioctl(struct comedi_device *dev, lockdep_assert_held(&dev->mutex); + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + dev_dbg(dev->class_dev, "bad cmd address\n"); + return -EFAULT; + } + /* get the user's cmd and do some simple validation */ - ret = __comedi_get_user_cmd(dev, arg, &cmd); + ret = __comedi_get_user_cmd(dev, &cmd); if (ret) return ret; @@ -1866,8 +1865,13 @@ static int do_cmdtest_ioctl(struct comedi_device *dev, lockdep_assert_held(&dev->mutex); + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + dev_dbg(dev->class_dev, "bad cmd address\n"); + return -EFAULT; + } + /* get the user's cmd and do some simple validation */ - ret = __comedi_get_user_cmd(dev, arg, &cmd); + ret = __comedi_get_user_cmd(dev, &cmd); if (ret) return ret; From f0e4de5cd0bb99a82445c5a3a876f162052689f8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 26 Apr 2020 08:56:55 -0400 Subject: [PATCH 15/17] comedi: do_cmdtest_ioctl(): lift copyin/copyout into the caller Signed-off-by: Al Viro --- drivers/staging/comedi/comedi_fops.c | 45 ++++++++++++++-------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index c136cb2f676a..74020fee0ae9 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -1856,49 +1856,39 @@ cleanup: * possibly modified comedi_cmd structure */ static int do_cmdtest_ioctl(struct comedi_device *dev, - struct comedi_cmd __user *arg, void *file) + struct comedi_cmd *cmd, bool *copy, void *file) { - struct comedi_cmd cmd; struct comedi_subdevice *s; unsigned int __user *user_chanlist; int ret; lockdep_assert_held(&dev->mutex); - if (copy_from_user(&cmd, arg, sizeof(cmd))) { - dev_dbg(dev->class_dev, "bad cmd address\n"); - return -EFAULT; - } - - /* get the user's cmd and do some simple validation */ - ret = __comedi_get_user_cmd(dev, &cmd); + /* do some simple cmd validation */ + ret = __comedi_get_user_cmd(dev, cmd); if (ret) return ret; /* save user's chanlist pointer so it can be restored later */ - user_chanlist = (unsigned int __user *)cmd.chanlist; + user_chanlist = (unsigned int __user *)cmd->chanlist; - s = &dev->subdevices[cmd.subdev]; + s = &dev->subdevices[cmd->subdev]; /* user_chanlist can be NULL for COMEDI_CMDTEST ioctl */ if (user_chanlist) { /* load channel/gain list */ - ret = __comedi_get_user_chanlist(dev, s, user_chanlist, &cmd); + ret = __comedi_get_user_chanlist(dev, s, user_chanlist, cmd); if (ret) return ret; } - ret = s->do_cmdtest(dev, s, &cmd); + ret = s->do_cmdtest(dev, s, cmd); - kfree(cmd.chanlist); /* free kernel copy of user chanlist */ + kfree(cmd->chanlist); /* free kernel copy of user chanlist */ /* restore chanlist pointer before copying back */ - cmd.chanlist = (unsigned int __force *)user_chanlist; - - if (copy_to_user(arg, &cmd, sizeof(cmd))) { - dev_dbg(dev->class_dev, "bad cmd address\n"); - ret = -EFAULT; - } + cmd->chanlist = (unsigned int __force *)user_chanlist; + *copy = true; return ret; } @@ -2220,10 +2210,19 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd, case COMEDI_CMD: rc = do_cmd_ioctl(dev, (struct comedi_cmd __user *)arg, file); break; - case COMEDI_CMDTEST: - rc = do_cmdtest_ioctl(dev, (struct comedi_cmd __user *)arg, - file); + case COMEDI_CMDTEST: { + struct comedi_cmd cmd; + bool copy = false; + + if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd))) { + rc = -EFAULT; + break; + } + rc = do_cmdtest_ioctl(dev, &cmd, ©, file); + if (copy && copy_to_user((void __user *)arg, &cmd, sizeof(cmd))) + rc = -EFAULT; break; + } case COMEDI_INSNLIST: { struct comedi_insnlist insnlist; struct comedi_insn *insns = NULL; From 0a3ccc75a95ff682de9315dee7e55efde1d5e30c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 26 Apr 2020 09:01:49 -0400 Subject: [PATCH 16/17] comedi: do_cmd_ioctl(): lift copyin/copyout into the caller Signed-off-by: Al Viro --- drivers/staging/comedi/comedi_fops.c | 48 ++++++++++++++-------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index 74020fee0ae9..c40df64e3ec7 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -1741,9 +1741,8 @@ static int __comedi_get_user_chanlist(struct comedi_device *dev, * possibly modified comedi_cmd structure (when -EAGAIN returned) */ static int do_cmd_ioctl(struct comedi_device *dev, - struct comedi_cmd __user *arg, void *file) + struct comedi_cmd *cmd, bool *copy, void *file) { - struct comedi_cmd cmd; struct comedi_subdevice *s; struct comedi_async *async; unsigned int __user *user_chanlist; @@ -1751,20 +1750,15 @@ static int do_cmd_ioctl(struct comedi_device *dev, lockdep_assert_held(&dev->mutex); - if (copy_from_user(&cmd, arg, sizeof(cmd))) { - dev_dbg(dev->class_dev, "bad cmd address\n"); - return -EFAULT; - } - - /* get the user's cmd and do some simple validation */ - ret = __comedi_get_user_cmd(dev, &cmd); + /* do some simple cmd validation */ + ret = __comedi_get_user_cmd(dev, cmd); if (ret) return ret; /* save user's chanlist pointer so it can be restored later */ - user_chanlist = (unsigned int __user *)cmd.chanlist; + user_chanlist = (unsigned int __user *)cmd->chanlist; - s = &dev->subdevices[cmd.subdev]; + s = &dev->subdevices[cmd->subdev]; async = s->async; /* are we locked? (ioctl lock) */ @@ -1780,13 +1774,13 @@ static int do_cmd_ioctl(struct comedi_device *dev, } /* make sure channel/gain list isn't too short */ - if (cmd.chanlist_len < 1) { + if (cmd->chanlist_len < 1) { dev_dbg(dev->class_dev, "channel/gain list too short %u < 1\n", - cmd.chanlist_len); + cmd->chanlist_len); return -EINVAL; } - async->cmd = cmd; + async->cmd = *cmd; async->cmd.data = NULL; /* load channel/gain list */ @@ -1798,15 +1792,11 @@ static int do_cmd_ioctl(struct comedi_device *dev, if (async->cmd.flags & CMDF_BOGUS || ret) { dev_dbg(dev->class_dev, "test returned %d\n", ret); - cmd = async->cmd; + *cmd = async->cmd; /* restore chanlist pointer before copying back */ - cmd.chanlist = (unsigned int __force *)user_chanlist; - cmd.data = NULL; - if (copy_to_user(arg, &cmd, sizeof(cmd))) { - dev_dbg(dev->class_dev, "fault writing cmd\n"); - ret = -EFAULT; - goto cleanup; - } + cmd->chanlist = (unsigned int __force *)user_chanlist; + cmd->data = NULL; + *copy = true; ret = -EAGAIN; goto cleanup; } @@ -2207,9 +2197,19 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd, case COMEDI_CANCEL: rc = do_cancel_ioctl(dev, arg, file); break; - case COMEDI_CMD: - rc = do_cmd_ioctl(dev, (struct comedi_cmd __user *)arg, file); + case COMEDI_CMD: { + struct comedi_cmd cmd; + bool copy = false; + + if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd))) { + rc = -EFAULT; + break; + } + rc = do_cmd_ioctl(dev, &cmd, ©, file); + if (copy && copy_to_user((void __user *)arg, &cmd, sizeof(cmd))) + rc = -EFAULT; break; + } case COMEDI_CMDTEST: { struct comedi_cmd cmd; bool copy = false; From bac42fb21259783cb748ae54227a5e755340a396 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 26 Apr 2020 09:27:23 -0400 Subject: [PATCH 17/17] comedi: get rid of compat_alloc_user_space() mess in COMEDI_CMD{,TEST} compat Signed-off-by: Al Viro --- drivers/staging/comedi/comedi_fops.c | 175 ++++++++++----------------- 1 file changed, 63 insertions(+), 112 deletions(-) diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index c40df64e3ec7..dd14c2935292 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -2931,155 +2931,106 @@ static int compat_rangeinfo(struct file *file, unsigned long arg) } /* Copy 32-bit cmd structure to native cmd structure. */ -static int get_compat_cmd(struct comedi_cmd __user *cmd, +static int get_compat_cmd(struct comedi_cmd *cmd, struct comedi32_cmd_struct __user *cmd32) { - int err; - union { - unsigned int uint; - compat_uptr_t uptr; - } temp; + struct comedi32_cmd_struct v32; - /* Copy cmd structure. */ - if (!access_ok(cmd32, sizeof(*cmd32)) || - !access_ok(cmd, sizeof(*cmd))) + if (copy_from_user(&v32, cmd32, sizeof(v32))) return -EFAULT; - err = 0; - err |= __get_user(temp.uint, &cmd32->subdev); - err |= __put_user(temp.uint, &cmd->subdev); - err |= __get_user(temp.uint, &cmd32->flags); - err |= __put_user(temp.uint, &cmd->flags); - err |= __get_user(temp.uint, &cmd32->start_src); - err |= __put_user(temp.uint, &cmd->start_src); - err |= __get_user(temp.uint, &cmd32->start_arg); - err |= __put_user(temp.uint, &cmd->start_arg); - err |= __get_user(temp.uint, &cmd32->scan_begin_src); - err |= __put_user(temp.uint, &cmd->scan_begin_src); - err |= __get_user(temp.uint, &cmd32->scan_begin_arg); - err |= __put_user(temp.uint, &cmd->scan_begin_arg); - err |= __get_user(temp.uint, &cmd32->convert_src); - err |= __put_user(temp.uint, &cmd->convert_src); - err |= __get_user(temp.uint, &cmd32->convert_arg); - err |= __put_user(temp.uint, &cmd->convert_arg); - err |= __get_user(temp.uint, &cmd32->scan_end_src); - err |= __put_user(temp.uint, &cmd->scan_end_src); - err |= __get_user(temp.uint, &cmd32->scan_end_arg); - err |= __put_user(temp.uint, &cmd->scan_end_arg); - err |= __get_user(temp.uint, &cmd32->stop_src); - err |= __put_user(temp.uint, &cmd->stop_src); - err |= __get_user(temp.uint, &cmd32->stop_arg); - err |= __put_user(temp.uint, &cmd->stop_arg); - err |= __get_user(temp.uptr, &cmd32->chanlist); - err |= __put_user((unsigned int __force *)compat_ptr(temp.uptr), - &cmd->chanlist); - err |= __get_user(temp.uint, &cmd32->chanlist_len); - err |= __put_user(temp.uint, &cmd->chanlist_len); - err |= __get_user(temp.uptr, &cmd32->data); - err |= __put_user(compat_ptr(temp.uptr), &cmd->data); - err |= __get_user(temp.uint, &cmd32->data_len); - err |= __put_user(temp.uint, &cmd->data_len); - return err ? -EFAULT : 0; + cmd->subdev = v32.subdev; + cmd->flags = v32.flags; + cmd->start_src = v32.start_src; + cmd->start_arg = v32.start_arg; + cmd->scan_begin_src = v32.scan_begin_src; + cmd->scan_begin_arg = v32.scan_begin_arg; + cmd->convert_src = v32.convert_src; + cmd->convert_arg = v32.convert_arg; + cmd->scan_end_src = v32.scan_end_src; + cmd->scan_end_arg = v32.scan_end_arg; + cmd->stop_src = v32.stop_src; + cmd->stop_arg = v32.stop_arg; + cmd->chanlist = compat_ptr(v32.chanlist); + cmd->chanlist_len = v32.chanlist_len; + cmd->data = compat_ptr(v32.data); + cmd->data_len = v32.data_len; + return 0; } /* Copy native cmd structure to 32-bit cmd structure. */ static int put_compat_cmd(struct comedi32_cmd_struct __user *cmd32, - struct comedi_cmd __user *cmd) + struct comedi_cmd *cmd) { - int err; - unsigned int temp; + struct comedi32_cmd_struct v32; - /* - * Copy back most of cmd structure. - * - * Assume the pointer values are already valid. - * (Could use ptr_to_compat() to set them.) - */ - if (!access_ok(cmd, sizeof(*cmd)) || - !access_ok(cmd32, sizeof(*cmd32))) - return -EFAULT; - - err = 0; - err |= __get_user(temp, &cmd->subdev); - err |= __put_user(temp, &cmd32->subdev); - err |= __get_user(temp, &cmd->flags); - err |= __put_user(temp, &cmd32->flags); - err |= __get_user(temp, &cmd->start_src); - err |= __put_user(temp, &cmd32->start_src); - err |= __get_user(temp, &cmd->start_arg); - err |= __put_user(temp, &cmd32->start_arg); - err |= __get_user(temp, &cmd->scan_begin_src); - err |= __put_user(temp, &cmd32->scan_begin_src); - err |= __get_user(temp, &cmd->scan_begin_arg); - err |= __put_user(temp, &cmd32->scan_begin_arg); - err |= __get_user(temp, &cmd->convert_src); - err |= __put_user(temp, &cmd32->convert_src); - err |= __get_user(temp, &cmd->convert_arg); - err |= __put_user(temp, &cmd32->convert_arg); - err |= __get_user(temp, &cmd->scan_end_src); - err |= __put_user(temp, &cmd32->scan_end_src); - err |= __get_user(temp, &cmd->scan_end_arg); - err |= __put_user(temp, &cmd32->scan_end_arg); - err |= __get_user(temp, &cmd->stop_src); - err |= __put_user(temp, &cmd32->stop_src); - err |= __get_user(temp, &cmd->stop_arg); - err |= __put_user(temp, &cmd32->stop_arg); + memset(&v32, 0, sizeof(v32)); + v32.subdev = cmd->subdev; + v32.flags = cmd->flags; + v32.start_src = cmd->start_src; + v32.start_arg = cmd->start_arg; + v32.scan_begin_src = cmd->scan_begin_src; + v32.scan_begin_arg = cmd->scan_begin_arg; + v32.convert_src = cmd->convert_src; + v32.convert_arg = cmd->convert_arg; + v32.scan_end_src = cmd->scan_end_src; + v32.scan_end_arg = cmd->scan_end_arg; + v32.stop_src = cmd->stop_src; + v32.stop_arg = cmd->stop_arg; /* Assume chanlist pointer is unchanged. */ - err |= __get_user(temp, &cmd->chanlist_len); - err |= __put_user(temp, &cmd32->chanlist_len); - /* Assume data pointer is unchanged. */ - err |= __get_user(temp, &cmd->data_len); - err |= __put_user(temp, &cmd32->data_len); - return err ? -EFAULT : 0; + v32.chanlist = ptr_to_compat(cmd->chanlist); + v32.chanlist_len = cmd->chanlist_len; + v32.data = ptr_to_compat(cmd->data); + v32.data_len = cmd->data_len; + return copy_to_user(cmd32, &v32, sizeof(v32)); } /* Handle 32-bit COMEDI_CMD ioctl. */ static int compat_cmd(struct file *file, unsigned long arg) { - struct comedi_cmd __user *cmd; - struct comedi32_cmd_struct __user *cmd32; + struct comedi_file *cfp = file->private_data; + struct comedi_device *dev = cfp->dev; + struct comedi_cmd cmd; + bool copy = false; int rc, err; - cmd32 = compat_ptr(arg); - cmd = compat_alloc_user_space(sizeof(*cmd)); - - rc = get_compat_cmd(cmd, cmd32); + rc = get_compat_cmd(&cmd, compat_ptr(arg)); if (rc) return rc; - rc = comedi_unlocked_ioctl(file, COMEDI_CMD, (unsigned long)cmd); - if (rc == -EAGAIN) { + mutex_lock(&dev->mutex); + rc = do_cmd_ioctl(dev, &cmd, ©, file); + mutex_unlock(&dev->mutex); + if (copy) { /* Special case: copy cmd back to user. */ - err = put_compat_cmd(cmd32, cmd); + err = put_compat_cmd(compat_ptr(arg), &cmd); if (err) rc = err; } - return rc; } /* Handle 32-bit COMEDI_CMDTEST ioctl. */ static int compat_cmdtest(struct file *file, unsigned long arg) { - struct comedi_cmd __user *cmd; - struct comedi32_cmd_struct __user *cmd32; + struct comedi_file *cfp = file->private_data; + struct comedi_device *dev = cfp->dev; + struct comedi_cmd cmd; + bool copy = false; int rc, err; - cmd32 = compat_ptr(arg); - cmd = compat_alloc_user_space(sizeof(*cmd)); - - rc = get_compat_cmd(cmd, cmd32); + rc = get_compat_cmd(&cmd, compat_ptr(arg)); if (rc) return rc; - rc = comedi_unlocked_ioctl(file, COMEDI_CMDTEST, (unsigned long)cmd); - if (rc < 0) - return rc; - - err = put_compat_cmd(cmd32, cmd); - if (err) - rc = err; - + mutex_lock(&dev->mutex); + rc = do_cmdtest_ioctl(dev, &cmd, ©, file); + mutex_unlock(&dev->mutex); + if (copy) { + err = put_compat_cmd(compat_ptr(arg), &cmd); + if (err) + rc = err; + } return rc; }