diff options
Diffstat (limited to 'drivers/usb/gadget/function/f_fs.c')
-rw-r--r-- | drivers/usb/gadget/function/f_fs.c | 61 |
1 files changed, 46 insertions, 15 deletions
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 70ed37065bab..bb3d40acae78 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -640,6 +640,44 @@ static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req) } } +static ssize_t ffs_copy_to_iter(void *data, int data_len, struct iov_iter *iter) +{ + ssize_t ret = copy_to_iter(data, data_len, iter); + if (likely(ret == data_len)) + return ret; + + if (unlikely(iov_iter_count(iter))) + return -EFAULT; + + /* + * Dear user space developer! + * + * TL;DR: To stop getting below error message in your kernel log, change + * user space code using functionfs to align read buffers to a max + * packet size. + * + * Some UDCs (e.g. dwc3) require request sizes to be a multiple of a max + * packet size. When unaligned buffer is passed to functionfs, it + * internally uses a larger, aligned buffer so that such UDCs are happy. + * + * Unfortunately, this means that host may send more data than was + * requested in read(2) system call. f_fs doesn’t know what to do with + * that excess data so it simply drops it. + * + * Was the buffer aligned in the first place, no such problem would + * happen. + * + * This only affects OUT endpoints, i.e. reading data with a read(2), + * aio_read(2) etc. system calls. Writing data to an IN endpoint is not + * affected. + */ + pr_err("functionfs read size %d > requested size %zd, dropping excess data. " + "Align read buffer size to max packet size to avoid the problem.\n", + data_len, ret); + + return ret; +} + static void ffs_user_copy_worker(struct work_struct *work) { struct ffs_io_data *io_data = container_of(work, struct ffs_io_data, @@ -650,9 +688,7 @@ static void ffs_user_copy_worker(struct work_struct *work) if (io_data->read && ret > 0) { use_mm(io_data->mm); - ret = copy_to_iter(io_data->buf, ret, &io_data->data); - if (ret != io_data->req->actual && iov_iter_count(&io_data->data)) - ret = -EFAULT; + ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data); unuse_mm(io_data->mm); } @@ -803,18 +839,13 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) interrupted = ep->status < 0; } - /* - * XXX We may end up silently droping data here. Since data_len - * (i.e. req->length) may be bigger than len (after being - * rounded up to maxpacketsize), we may end up with more data - * then user space has space for. - */ - ret = interrupted ? -EINTR : ep->status; - if (io_data->read && ret > 0) { - ret = copy_to_iter(data, ret, &io_data->data); - if (!ret) - ret = -EFAULT; - } + if (interrupted) + ret = -EINTR; + else if (io_data->read && ep->status > 0) + ret = ffs_copy_to_iter(data, ep->status, + &io_data->data); + else + ret = ep->status; goto error_mutex; } else if (!(req = usb_ep_alloc_request(ep->ep, GFP_KERNEL))) { ret = -ENOMEM; |