staging: comedi: allow buffer wraparound in comedi_write()
authorIan Abbott <abbotti@mev.co.uk>
Wed, 18 Nov 2015 17:55:08 +0000 (17:55 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 21 Dec 2015 23:58:54 +0000 (15:58 -0800)
`comedi_write()` copies data from the user buffer to the acquisition
data buffer, which is cyclic, using a single call to `copy_from_user()`.
It currently avoids having to deal with wraparound of the cyclic buffer
by limiting the amount it copies (and the amount returned to the user).
Change it to deal with the wraparound using two calls to
`copy_from_user()` if necessary.

Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
Reviewed-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/comedi/comedi_fops.c

index 8c784c483c3e733bb82cbac124c971529c1db926..4dd42890c6414992c40cc2720a99f0f807713747 100644 (file)
@@ -2346,6 +2346,7 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
        add_wait_queue(&async->wait_head, &wait);
        while (count == 0 && !retval) {
                unsigned runflags;
+               unsigned int wp, n1, n2;
 
                set_current_state(TASK_INTERRUPTIBLE);
 
@@ -2360,9 +2361,6 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
                /* Allocate all free buffer space. */
                comedi_buf_write_alloc(s, async->prealloc_bufsz);
                m = comedi_buf_write_n_allocated(s);
-               /* Avoid buffer wraparound. */
-               if (async->buf_write_ptr + m > async->prealloc_bufsz)
-                       m = async->prealloc_bufsz - async->buf_write_ptr;
                n = min_t(size_t, m, nbytes);
 
                if (n == 0) {
@@ -2388,8 +2386,14 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
                        continue;
                }
 
-               m = copy_from_user(async->prealloc_buf + async->buf_write_ptr,
-                                  buf, n);
+               wp = async->buf_write_ptr;
+               n1 = min(n, async->prealloc_bufsz - wp);
+               n2 = n - n1;
+               m = copy_from_user(async->prealloc_buf + wp, buf, n1);
+               if (m)
+                       m += n2;
+               else if (n2)
+                       m = copy_from_user(async->prealloc_buf, buf + n1, n2);
                if (m) {
                        n -= m;
                        retval = -EFAULT;