Re: [PATCH 08/32] aio: replace kiocb_set_cancel_fn with a cancel_kiocb file operation
On Tue, May 15, 2018 at 09:48:09PM +0200, Christoph Hellwig wrote: > case -EIOCBQUEUED: > + if (req->ki_filp->f_op->cancel_kiocb) { > + struct aio_kiocb *iocb = > + container_of(req, struct aio_kiocb, rw); > + struct kioctx *ctx = iocb->ki_ctx; > + unsigned long flags; > + > + spin_lock_irqsave(>ctx_lock, flags); > + list_add_tail(>ki_list, >active_reqs); Use after free - that list insertion used to be done by drivers and doing so before any ->ki_complete() calls might've happened used to be their responsibility. Now you've taken that to the point after ->read_iter() (or ->write_iter()) return, so there's no way in hell to guarantee it's not been completed (and freed) by that point. Incidentally, none of the callers gives a damn about the difference between 0 and -EIOCBQUEUED now, so aio_rw_ret() might as well had been made void...
Re: [PATCH 08/32] aio: replace kiocb_set_cancel_fn with a cancel_kiocb file operation
On Tue, May 15, 2018 at 09:48:09PM +0200, Christoph Hellwig wrote: > case -EIOCBQUEUED: > + if (req->ki_filp->f_op->cancel_kiocb) { > + struct aio_kiocb *iocb = > + container_of(req, struct aio_kiocb, rw); > + struct kioctx *ctx = iocb->ki_ctx; > + unsigned long flags; > + > + spin_lock_irqsave(>ctx_lock, flags); > + list_add_tail(>ki_list, >active_reqs); Use after free - that list insertion used to be done by drivers and doing so before any ->ki_complete() calls might've happened used to be their responsibility. Now you've taken that to the point after ->read_iter() (or ->write_iter()) return, so there's no way in hell to guarantee it's not been completed (and freed) by that point. Incidentally, none of the callers gives a damn about the difference between 0 and -EIOCBQUEUED now, so aio_rw_ret() might as well had been made void...
[PATCH 08/32] aio: replace kiocb_set_cancel_fn with a cancel_kiocb file operation
The current kiocb_set_cancel_fn implementation assumes the kiocb is embedded into an aio_kiocb, which is fundamentally unsafe as it might have been submitted by non-aio callers. Instead add a cancel_kiocb file operation that replaced the ki_cancel function pointer set by kiocb_set_cancel_fn, and only adds iocbs to the active list when the read/write_iter methods return -EIOCBQUEUED and the file has a cancel_kiocb method. Signed-off-by: Christoph Hellwig--- drivers/usb/gadget/function/f_fs.c | 10 ++ drivers/usb/gadget/legacy/inode.c | 5 ++--- fs/aio.c | 31 -- fs/orangefs/orangefs-kernel.h | 1 - include/linux/aio.h| 7 --- include/linux/fs.h | 1 + 6 files changed, 17 insertions(+), 38 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 0294e4f18873..531a7206407b 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -1072,7 +1071,7 @@ ffs_epfile_open(struct inode *inode, struct file *file) return 0; } -static int ffs_aio_cancel(struct kiocb *kiocb) +static int ffs_epfile_cancel_kiocb(struct kiocb *kiocb) { struct ffs_io_data *io_data = kiocb->private; struct ffs_epfile *epfile = kiocb->ki_filp->private_data; @@ -1115,9 +1114,6 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from) kiocb->private = p; - if (p->aio) - kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); - res = ffs_epfile_io(kiocb->ki_filp, p); if (res == -EIOCBQUEUED) return res; @@ -1160,9 +1156,6 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to) kiocb->private = p; - if (p->aio) - kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); - res = ffs_epfile_io(kiocb->ki_filp, p); if (res == -EIOCBQUEUED) return res; @@ -1274,6 +1267,7 @@ static const struct file_operations ffs_epfile_operations = { .read_iter =ffs_epfile_read_iter, .release = ffs_epfile_release, .unlocked_ioctl = ffs_epfile_ioctl, + .cancel_kiocb = ffs_epfile_cancel_kiocb, }; diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 37ca0e669bd8..02ea83c8861a 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -435,7 +434,7 @@ struct kiocb_priv { unsignedactual; }; -static int ep_aio_cancel(struct kiocb *iocb) +static int ep_cancel_kiocb(struct kiocb *iocb) { struct kiocb_priv *priv = iocb->private; struct ep_data *epdata; @@ -528,7 +527,6 @@ static ssize_t ep_aio(struct kiocb *iocb, iocb->private = priv; priv->iocb = iocb; - kiocb_set_cancel_fn(iocb, ep_aio_cancel); get_ep(epdata); priv->epdata = epdata; priv->actual = 0; @@ -700,6 +698,7 @@ static const struct file_operations ep_io_operations = { .unlocked_ioctl = ep_ioctl, .read_iter =ep_read_iter, .write_iter = ep_write_iter, + .cancel_kiocb = ep_cancel_kiocb, }; /* ENDPOINT INITIALIZATION diff --git a/fs/aio.c b/fs/aio.c index 8991baa38d5d..be10dde20c8e 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -171,7 +171,6 @@ struct aio_kiocb { }; struct kioctx *ki_ctx; - kiocb_cancel_fn *ki_cancel; struct iocb __user *ki_user_iocb; /* user's aiocb */ __u64 ki_user_data; /* user's data for completion */ @@ -545,22 +544,6 @@ static int aio_setup_ring(struct kioctx *ctx, unsigned int nr_events) #define AIO_EVENTS_FIRST_PAGE ((PAGE_SIZE - sizeof(struct aio_ring)) / sizeof(struct io_event)) #define AIO_EVENTS_OFFSET (AIO_EVENTS_PER_PAGE - AIO_EVENTS_FIRST_PAGE) -void kiocb_set_cancel_fn(struct kiocb *iocb, kiocb_cancel_fn *cancel) -{ - struct aio_kiocb *req = container_of(iocb, struct aio_kiocb, rw); - struct kioctx *ctx = req->ki_ctx; - unsigned long flags; - - if (WARN_ON_ONCE(!list_empty(>ki_list))) - return; - - spin_lock_irqsave(>ctx_lock, flags); - list_add_tail(>ki_list, >active_reqs); - req->ki_cancel = cancel; - spin_unlock_irqrestore(>ctx_lock, flags); -} -EXPORT_SYMBOL(kiocb_set_cancel_fn); - /* * free_ioctx() should be RCU delayed to synchronize against the RCU * protected lookup_ioctx() and also needs process context to call @@ -608,7 +591,7 @@ static void free_ioctx_users(struct percpu_ref *ref) req = list_first_entry(>active_reqs, struct aio_kiocb, ki_list);
[PATCH 08/32] aio: replace kiocb_set_cancel_fn with a cancel_kiocb file operation
The current kiocb_set_cancel_fn implementation assumes the kiocb is embedded into an aio_kiocb, which is fundamentally unsafe as it might have been submitted by non-aio callers. Instead add a cancel_kiocb file operation that replaced the ki_cancel function pointer set by kiocb_set_cancel_fn, and only adds iocbs to the active list when the read/write_iter methods return -EIOCBQUEUED and the file has a cancel_kiocb method. Signed-off-by: Christoph Hellwig --- drivers/usb/gadget/function/f_fs.c | 10 ++ drivers/usb/gadget/legacy/inode.c | 5 ++--- fs/aio.c | 31 -- fs/orangefs/orangefs-kernel.h | 1 - include/linux/aio.h| 7 --- include/linux/fs.h | 1 + 6 files changed, 17 insertions(+), 38 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 0294e4f18873..531a7206407b 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -1072,7 +1071,7 @@ ffs_epfile_open(struct inode *inode, struct file *file) return 0; } -static int ffs_aio_cancel(struct kiocb *kiocb) +static int ffs_epfile_cancel_kiocb(struct kiocb *kiocb) { struct ffs_io_data *io_data = kiocb->private; struct ffs_epfile *epfile = kiocb->ki_filp->private_data; @@ -1115,9 +1114,6 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from) kiocb->private = p; - if (p->aio) - kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); - res = ffs_epfile_io(kiocb->ki_filp, p); if (res == -EIOCBQUEUED) return res; @@ -1160,9 +1156,6 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to) kiocb->private = p; - if (p->aio) - kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); - res = ffs_epfile_io(kiocb->ki_filp, p); if (res == -EIOCBQUEUED) return res; @@ -1274,6 +1267,7 @@ static const struct file_operations ffs_epfile_operations = { .read_iter =ffs_epfile_read_iter, .release = ffs_epfile_release, .unlocked_ioctl = ffs_epfile_ioctl, + .cancel_kiocb = ffs_epfile_cancel_kiocb, }; diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 37ca0e669bd8..02ea83c8861a 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -435,7 +434,7 @@ struct kiocb_priv { unsignedactual; }; -static int ep_aio_cancel(struct kiocb *iocb) +static int ep_cancel_kiocb(struct kiocb *iocb) { struct kiocb_priv *priv = iocb->private; struct ep_data *epdata; @@ -528,7 +527,6 @@ static ssize_t ep_aio(struct kiocb *iocb, iocb->private = priv; priv->iocb = iocb; - kiocb_set_cancel_fn(iocb, ep_aio_cancel); get_ep(epdata); priv->epdata = epdata; priv->actual = 0; @@ -700,6 +698,7 @@ static const struct file_operations ep_io_operations = { .unlocked_ioctl = ep_ioctl, .read_iter =ep_read_iter, .write_iter = ep_write_iter, + .cancel_kiocb = ep_cancel_kiocb, }; /* ENDPOINT INITIALIZATION diff --git a/fs/aio.c b/fs/aio.c index 8991baa38d5d..be10dde20c8e 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -171,7 +171,6 @@ struct aio_kiocb { }; struct kioctx *ki_ctx; - kiocb_cancel_fn *ki_cancel; struct iocb __user *ki_user_iocb; /* user's aiocb */ __u64 ki_user_data; /* user's data for completion */ @@ -545,22 +544,6 @@ static int aio_setup_ring(struct kioctx *ctx, unsigned int nr_events) #define AIO_EVENTS_FIRST_PAGE ((PAGE_SIZE - sizeof(struct aio_ring)) / sizeof(struct io_event)) #define AIO_EVENTS_OFFSET (AIO_EVENTS_PER_PAGE - AIO_EVENTS_FIRST_PAGE) -void kiocb_set_cancel_fn(struct kiocb *iocb, kiocb_cancel_fn *cancel) -{ - struct aio_kiocb *req = container_of(iocb, struct aio_kiocb, rw); - struct kioctx *ctx = req->ki_ctx; - unsigned long flags; - - if (WARN_ON_ONCE(!list_empty(>ki_list))) - return; - - spin_lock_irqsave(>ctx_lock, flags); - list_add_tail(>ki_list, >active_reqs); - req->ki_cancel = cancel; - spin_unlock_irqrestore(>ctx_lock, flags); -} -EXPORT_SYMBOL(kiocb_set_cancel_fn); - /* * free_ioctx() should be RCU delayed to synchronize against the RCU * protected lookup_ioctx() and also needs process context to call @@ -608,7 +591,7 @@ static void free_ioctx_users(struct percpu_ref *ref) req = list_first_entry(>active_reqs, struct aio_kiocb, ki_list);
[PATCH 08/32] aio: replace kiocb_set_cancel_fn with a cancel_kiocb file operation
The current kiocb_set_cancel_fn implementation assumes the kiocb is embedded into an aio_kiocb, which is fundamentally unsafe as it might have been submitted by non-aio callers. Instead add a cancel_kiocb file operation that replaced the ki_cancel function pointer set by kiocb_set_cancel_fn, and only adds iocbs to the active list when the read/write_iter methods return -EIOCBQUEUED and the file has a cancel_kiocb method. Signed-off-by: Christoph Hellwig--- drivers/usb/gadget/function/f_fs.c | 10 ++ drivers/usb/gadget/legacy/inode.c | 5 ++--- fs/aio.c | 31 -- fs/orangefs/orangefs-kernel.h | 1 - include/linux/aio.h| 7 --- include/linux/fs.h | 1 + 6 files changed, 17 insertions(+), 38 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 0294e4f18873..531a7206407b 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -1072,7 +1071,7 @@ ffs_epfile_open(struct inode *inode, struct file *file) return 0; } -static int ffs_aio_cancel(struct kiocb *kiocb) +static int ffs_epfile_cancel_kiocb(struct kiocb *kiocb) { struct ffs_io_data *io_data = kiocb->private; struct ffs_epfile *epfile = kiocb->ki_filp->private_data; @@ -1115,9 +1114,6 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from) kiocb->private = p; - if (p->aio) - kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); - res = ffs_epfile_io(kiocb->ki_filp, p); if (res == -EIOCBQUEUED) return res; @@ -1160,9 +1156,6 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to) kiocb->private = p; - if (p->aio) - kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); - res = ffs_epfile_io(kiocb->ki_filp, p); if (res == -EIOCBQUEUED) return res; @@ -1274,6 +1267,7 @@ static const struct file_operations ffs_epfile_operations = { .read_iter =ffs_epfile_read_iter, .release = ffs_epfile_release, .unlocked_ioctl = ffs_epfile_ioctl, + .cancel_kiocb = ffs_epfile_cancel_kiocb, }; diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 37ca0e669bd8..02ea83c8861a 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -435,7 +434,7 @@ struct kiocb_priv { unsignedactual; }; -static int ep_aio_cancel(struct kiocb *iocb) +static int ep_cancel_kiocb(struct kiocb *iocb) { struct kiocb_priv *priv = iocb->private; struct ep_data *epdata; @@ -528,7 +527,6 @@ static ssize_t ep_aio(struct kiocb *iocb, iocb->private = priv; priv->iocb = iocb; - kiocb_set_cancel_fn(iocb, ep_aio_cancel); get_ep(epdata); priv->epdata = epdata; priv->actual = 0; @@ -700,6 +698,7 @@ static const struct file_operations ep_io_operations = { .unlocked_ioctl = ep_ioctl, .read_iter =ep_read_iter, .write_iter = ep_write_iter, + .cancel_kiocb = ep_cancel_kiocb, }; /* ENDPOINT INITIALIZATION diff --git a/fs/aio.c b/fs/aio.c index 8991baa38d5d..be10dde20c8e 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -171,7 +171,6 @@ struct aio_kiocb { }; struct kioctx *ki_ctx; - kiocb_cancel_fn *ki_cancel; struct iocb __user *ki_user_iocb; /* user's aiocb */ __u64 ki_user_data; /* user's data for completion */ @@ -545,22 +544,6 @@ static int aio_setup_ring(struct kioctx *ctx, unsigned int nr_events) #define AIO_EVENTS_FIRST_PAGE ((PAGE_SIZE - sizeof(struct aio_ring)) / sizeof(struct io_event)) #define AIO_EVENTS_OFFSET (AIO_EVENTS_PER_PAGE - AIO_EVENTS_FIRST_PAGE) -void kiocb_set_cancel_fn(struct kiocb *iocb, kiocb_cancel_fn *cancel) -{ - struct aio_kiocb *req = container_of(iocb, struct aio_kiocb, rw); - struct kioctx *ctx = req->ki_ctx; - unsigned long flags; - - if (WARN_ON_ONCE(!list_empty(>ki_list))) - return; - - spin_lock_irqsave(>ctx_lock, flags); - list_add_tail(>ki_list, >active_reqs); - req->ki_cancel = cancel; - spin_unlock_irqrestore(>ctx_lock, flags); -} -EXPORT_SYMBOL(kiocb_set_cancel_fn); - /* * free_ioctx() should be RCU delayed to synchronize against the RCU * protected lookup_ioctx() and also needs process context to call @@ -608,7 +591,7 @@ static void free_ioctx_users(struct percpu_ref *ref) req = list_first_entry(>active_reqs, struct aio_kiocb, ki_list);
[PATCH 08/32] aio: replace kiocb_set_cancel_fn with a cancel_kiocb file operation
The current kiocb_set_cancel_fn implementation assumes the kiocb is embedded into an aio_kiocb, which is fundamentally unsafe as it might have been submitted by non-aio callers. Instead add a cancel_kiocb file operation that replaced the ki_cancel function pointer set by kiocb_set_cancel_fn, and only adds iocbs to the active list when the read/write_iter methods return -EIOCBQUEUED and the file has a cancel_kiocb method. Signed-off-by: Christoph Hellwig --- drivers/usb/gadget/function/f_fs.c | 10 ++ drivers/usb/gadget/legacy/inode.c | 5 ++--- fs/aio.c | 31 -- fs/orangefs/orangefs-kernel.h | 1 - include/linux/aio.h| 7 --- include/linux/fs.h | 1 + 6 files changed, 17 insertions(+), 38 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 0294e4f18873..531a7206407b 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -1072,7 +1071,7 @@ ffs_epfile_open(struct inode *inode, struct file *file) return 0; } -static int ffs_aio_cancel(struct kiocb *kiocb) +static int ffs_epfile_cancel_kiocb(struct kiocb *kiocb) { struct ffs_io_data *io_data = kiocb->private; struct ffs_epfile *epfile = kiocb->ki_filp->private_data; @@ -1115,9 +1114,6 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from) kiocb->private = p; - if (p->aio) - kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); - res = ffs_epfile_io(kiocb->ki_filp, p); if (res == -EIOCBQUEUED) return res; @@ -1160,9 +1156,6 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to) kiocb->private = p; - if (p->aio) - kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); - res = ffs_epfile_io(kiocb->ki_filp, p); if (res == -EIOCBQUEUED) return res; @@ -1274,6 +1267,7 @@ static const struct file_operations ffs_epfile_operations = { .read_iter =ffs_epfile_read_iter, .release = ffs_epfile_release, .unlocked_ioctl = ffs_epfile_ioctl, + .cancel_kiocb = ffs_epfile_cancel_kiocb, }; diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 37ca0e669bd8..02ea83c8861a 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -435,7 +434,7 @@ struct kiocb_priv { unsignedactual; }; -static int ep_aio_cancel(struct kiocb *iocb) +static int ep_cancel_kiocb(struct kiocb *iocb) { struct kiocb_priv *priv = iocb->private; struct ep_data *epdata; @@ -528,7 +527,6 @@ static ssize_t ep_aio(struct kiocb *iocb, iocb->private = priv; priv->iocb = iocb; - kiocb_set_cancel_fn(iocb, ep_aio_cancel); get_ep(epdata); priv->epdata = epdata; priv->actual = 0; @@ -700,6 +698,7 @@ static const struct file_operations ep_io_operations = { .unlocked_ioctl = ep_ioctl, .read_iter =ep_read_iter, .write_iter = ep_write_iter, + .cancel_kiocb = ep_cancel_kiocb, }; /* ENDPOINT INITIALIZATION diff --git a/fs/aio.c b/fs/aio.c index 8991baa38d5d..be10dde20c8e 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -171,7 +171,6 @@ struct aio_kiocb { }; struct kioctx *ki_ctx; - kiocb_cancel_fn *ki_cancel; struct iocb __user *ki_user_iocb; /* user's aiocb */ __u64 ki_user_data; /* user's data for completion */ @@ -545,22 +544,6 @@ static int aio_setup_ring(struct kioctx *ctx, unsigned int nr_events) #define AIO_EVENTS_FIRST_PAGE ((PAGE_SIZE - sizeof(struct aio_ring)) / sizeof(struct io_event)) #define AIO_EVENTS_OFFSET (AIO_EVENTS_PER_PAGE - AIO_EVENTS_FIRST_PAGE) -void kiocb_set_cancel_fn(struct kiocb *iocb, kiocb_cancel_fn *cancel) -{ - struct aio_kiocb *req = container_of(iocb, struct aio_kiocb, rw); - struct kioctx *ctx = req->ki_ctx; - unsigned long flags; - - if (WARN_ON_ONCE(!list_empty(>ki_list))) - return; - - spin_lock_irqsave(>ctx_lock, flags); - list_add_tail(>ki_list, >active_reqs); - req->ki_cancel = cancel; - spin_unlock_irqrestore(>ctx_lock, flags); -} -EXPORT_SYMBOL(kiocb_set_cancel_fn); - /* * free_ioctx() should be RCU delayed to synchronize against the RCU * protected lookup_ioctx() and also needs process context to call @@ -608,7 +591,7 @@ static void free_ioctx_users(struct percpu_ref *ref) req = list_first_entry(>active_reqs, struct aio_kiocb, ki_list);
Re: [PATCH 08/32] aio: replace kiocb_set_cancel_fn with a cancel_kiocb file operation
On Fri, Mar 30, 2018 at 05:07:45PM +0200, Christoph Hellwig wrote: > The current kiocb_set_cancel_fn implementation assumes the kiocb is > embedded into an aio_kiocb, which is fundamentally unsafe as it might > have been submitted by non-aio callers. Instead add a cancel_kiocb > file operation that replaced the ki_cancel function pointer set by > kiocb_set_cancel_fn, and only adds iocbs to the active list when > the read/write_iter methods return -EIOCBQUEUED and the file has > a cancel_kiocb method. > @@ -1440,6 +1423,16 @@ static inline ssize_t aio_rw_ret(struct kiocb *req, > ssize_t ret) > { > switch (ret) { > case -EIOCBQUEUED: > + if (req->ki_filp->f_op->cancel_kiocb) { ... and by that point req might've been already freed by IO completion on another CPU. > + struct aio_kiocb *iocb = > + container_of(req, struct aio_kiocb, rw); > + struct kioctx *ctx = iocb->ki_ctx; > + unsigned long flags; > + > + spin_lock_irqsave(>ctx_lock, flags); > + list_add_tail(>ki_list, >active_reqs);
Re: [PATCH 08/32] aio: replace kiocb_set_cancel_fn with a cancel_kiocb file operation
On Fri, Mar 30, 2018 at 05:07:45PM +0200, Christoph Hellwig wrote: > The current kiocb_set_cancel_fn implementation assumes the kiocb is > embedded into an aio_kiocb, which is fundamentally unsafe as it might > have been submitted by non-aio callers. Instead add a cancel_kiocb > file operation that replaced the ki_cancel function pointer set by > kiocb_set_cancel_fn, and only adds iocbs to the active list when > the read/write_iter methods return -EIOCBQUEUED and the file has > a cancel_kiocb method. > @@ -1440,6 +1423,16 @@ static inline ssize_t aio_rw_ret(struct kiocb *req, > ssize_t ret) > { > switch (ret) { > case -EIOCBQUEUED: > + if (req->ki_filp->f_op->cancel_kiocb) { ... and by that point req might've been already freed by IO completion on another CPU. > + struct aio_kiocb *iocb = > + container_of(req, struct aio_kiocb, rw); > + struct kioctx *ctx = iocb->ki_ctx; > + unsigned long flags; > + > + spin_lock_irqsave(>ctx_lock, flags); > + list_add_tail(>ki_list, >active_reqs);
[PATCH 08/32] aio: replace kiocb_set_cancel_fn with a cancel_kiocb file operation
The current kiocb_set_cancel_fn implementation assumes the kiocb is embedded into an aio_kiocb, which is fundamentally unsafe as it might have been submitted by non-aio callers. Instead add a cancel_kiocb file operation that replaced the ki_cancel function pointer set by kiocb_set_cancel_fn, and only adds iocbs to the active list when the read/write_iter methods return -EIOCBQUEUED and the file has a cancel_kiocb method. Signed-off-by: Christoph Hellwig--- drivers/usb/gadget/function/f_fs.c | 10 ++ drivers/usb/gadget/legacy/inode.c | 5 ++--- fs/aio.c | 31 --- fs/orangefs/orangefs-kernel.h | 1 - include/linux/aio.h| 7 --- include/linux/fs.h | 1 + 6 files changed, 17 insertions(+), 38 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index c2592d883f67..4dcf21ae39f8 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -1068,7 +1067,7 @@ ffs_epfile_open(struct inode *inode, struct file *file) return 0; } -static int ffs_aio_cancel(struct kiocb *kiocb) +static int ffs_epfile_cancel_kiocb(struct kiocb *kiocb) { struct ffs_io_data *io_data = kiocb->private; struct ffs_epfile *epfile = kiocb->ki_filp->private_data; @@ -,9 +1110,6 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from) kiocb->private = p; - if (p->aio) - kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); - res = ffs_epfile_io(kiocb->ki_filp, p); if (res == -EIOCBQUEUED) return res; @@ -1156,9 +1152,6 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to) kiocb->private = p; - if (p->aio) - kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); - res = ffs_epfile_io(kiocb->ki_filp, p); if (res == -EIOCBQUEUED) return res; @@ -1270,6 +1263,7 @@ static const struct file_operations ffs_epfile_operations = { .read_iter =ffs_epfile_read_iter, .release = ffs_epfile_release, .unlocked_ioctl = ffs_epfile_ioctl, + .cancel_kiocb = ffs_epfile_cancel_kiocb, }; diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 37ca0e669bd8..02ea83c8861a 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -435,7 +434,7 @@ struct kiocb_priv { unsignedactual; }; -static int ep_aio_cancel(struct kiocb *iocb) +static int ep_cancel_kiocb(struct kiocb *iocb) { struct kiocb_priv *priv = iocb->private; struct ep_data *epdata; @@ -528,7 +527,6 @@ static ssize_t ep_aio(struct kiocb *iocb, iocb->private = priv; priv->iocb = iocb; - kiocb_set_cancel_fn(iocb, ep_aio_cancel); get_ep(epdata); priv->epdata = epdata; priv->actual = 0; @@ -700,6 +698,7 @@ static const struct file_operations ep_io_operations = { .unlocked_ioctl = ep_ioctl, .read_iter =ep_read_iter, .write_iter = ep_write_iter, + .cancel_kiocb = ep_cancel_kiocb, }; /* ENDPOINT INITIALIZATION diff --git a/fs/aio.c b/fs/aio.c index 5a5d537cec2e..7e4b517c60c3 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -171,7 +171,6 @@ struct aio_kiocb { }; struct kioctx *ki_ctx; - kiocb_cancel_fn *ki_cancel; struct iocb __user *ki_user_iocb; /* user's aiocb */ __u64 ki_user_data; /* user's data for completion */ @@ -545,22 +544,6 @@ static int aio_setup_ring(struct kioctx *ctx, unsigned int nr_events) #define AIO_EVENTS_FIRST_PAGE ((PAGE_SIZE - sizeof(struct aio_ring)) / sizeof(struct io_event)) #define AIO_EVENTS_OFFSET (AIO_EVENTS_PER_PAGE - AIO_EVENTS_FIRST_PAGE) -void kiocb_set_cancel_fn(struct kiocb *iocb, kiocb_cancel_fn *cancel) -{ - struct aio_kiocb *req = container_of(iocb, struct aio_kiocb, rw); - struct kioctx *ctx = req->ki_ctx; - unsigned long flags; - - if (WARN_ON_ONCE(!list_empty(>ki_list))) - return; - - spin_lock_irqsave(>ctx_lock, flags); - list_add_tail(>ki_list, >active_reqs); - req->ki_cancel = cancel; - spin_unlock_irqrestore(>ctx_lock, flags); -} -EXPORT_SYMBOL(kiocb_set_cancel_fn); - static void free_ioctx(struct work_struct *work) { struct kioctx *ctx = container_of(work, struct kioctx, free_work); @@ -602,7 +585,7 @@ static void free_ioctx_users(struct percpu_ref *ref) req = list_first_entry(>active_reqs, struct aio_kiocb, ki_list);
[PATCH 08/32] aio: replace kiocb_set_cancel_fn with a cancel_kiocb file operation
The current kiocb_set_cancel_fn implementation assumes the kiocb is embedded into an aio_kiocb, which is fundamentally unsafe as it might have been submitted by non-aio callers. Instead add a cancel_kiocb file operation that replaced the ki_cancel function pointer set by kiocb_set_cancel_fn, and only adds iocbs to the active list when the read/write_iter methods return -EIOCBQUEUED and the file has a cancel_kiocb method. Signed-off-by: Christoph Hellwig --- drivers/usb/gadget/function/f_fs.c | 10 ++ drivers/usb/gadget/legacy/inode.c | 5 ++--- fs/aio.c | 31 --- fs/orangefs/orangefs-kernel.h | 1 - include/linux/aio.h| 7 --- include/linux/fs.h | 1 + 6 files changed, 17 insertions(+), 38 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index c2592d883f67..4dcf21ae39f8 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -1068,7 +1067,7 @@ ffs_epfile_open(struct inode *inode, struct file *file) return 0; } -static int ffs_aio_cancel(struct kiocb *kiocb) +static int ffs_epfile_cancel_kiocb(struct kiocb *kiocb) { struct ffs_io_data *io_data = kiocb->private; struct ffs_epfile *epfile = kiocb->ki_filp->private_data; @@ -,9 +1110,6 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from) kiocb->private = p; - if (p->aio) - kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); - res = ffs_epfile_io(kiocb->ki_filp, p); if (res == -EIOCBQUEUED) return res; @@ -1156,9 +1152,6 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to) kiocb->private = p; - if (p->aio) - kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); - res = ffs_epfile_io(kiocb->ki_filp, p); if (res == -EIOCBQUEUED) return res; @@ -1270,6 +1263,7 @@ static const struct file_operations ffs_epfile_operations = { .read_iter =ffs_epfile_read_iter, .release = ffs_epfile_release, .unlocked_ioctl = ffs_epfile_ioctl, + .cancel_kiocb = ffs_epfile_cancel_kiocb, }; diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 37ca0e669bd8..02ea83c8861a 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -435,7 +434,7 @@ struct kiocb_priv { unsignedactual; }; -static int ep_aio_cancel(struct kiocb *iocb) +static int ep_cancel_kiocb(struct kiocb *iocb) { struct kiocb_priv *priv = iocb->private; struct ep_data *epdata; @@ -528,7 +527,6 @@ static ssize_t ep_aio(struct kiocb *iocb, iocb->private = priv; priv->iocb = iocb; - kiocb_set_cancel_fn(iocb, ep_aio_cancel); get_ep(epdata); priv->epdata = epdata; priv->actual = 0; @@ -700,6 +698,7 @@ static const struct file_operations ep_io_operations = { .unlocked_ioctl = ep_ioctl, .read_iter =ep_read_iter, .write_iter = ep_write_iter, + .cancel_kiocb = ep_cancel_kiocb, }; /* ENDPOINT INITIALIZATION diff --git a/fs/aio.c b/fs/aio.c index 5a5d537cec2e..7e4b517c60c3 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -171,7 +171,6 @@ struct aio_kiocb { }; struct kioctx *ki_ctx; - kiocb_cancel_fn *ki_cancel; struct iocb __user *ki_user_iocb; /* user's aiocb */ __u64 ki_user_data; /* user's data for completion */ @@ -545,22 +544,6 @@ static int aio_setup_ring(struct kioctx *ctx, unsigned int nr_events) #define AIO_EVENTS_FIRST_PAGE ((PAGE_SIZE - sizeof(struct aio_ring)) / sizeof(struct io_event)) #define AIO_EVENTS_OFFSET (AIO_EVENTS_PER_PAGE - AIO_EVENTS_FIRST_PAGE) -void kiocb_set_cancel_fn(struct kiocb *iocb, kiocb_cancel_fn *cancel) -{ - struct aio_kiocb *req = container_of(iocb, struct aio_kiocb, rw); - struct kioctx *ctx = req->ki_ctx; - unsigned long flags; - - if (WARN_ON_ONCE(!list_empty(>ki_list))) - return; - - spin_lock_irqsave(>ctx_lock, flags); - list_add_tail(>ki_list, >active_reqs); - req->ki_cancel = cancel; - spin_unlock_irqrestore(>ctx_lock, flags); -} -EXPORT_SYMBOL(kiocb_set_cancel_fn); - static void free_ioctx(struct work_struct *work) { struct kioctx *ctx = container_of(work, struct kioctx, free_work); @@ -602,7 +585,7 @@ static void free_ioctx_users(struct percpu_ref *ref) req = list_first_entry(>active_reqs, struct aio_kiocb, ki_list);