How was this discovered? Does it address a reported user problem? On Wed, Apr 10, 2019 at 2:38 PM <jglisse@xxxxxxxxxx> wrote: > > From: Jérôme Glisse <jglisse@xxxxxxxxxx> > > CIFS can leak pages reference gotten through GUP (get_user_pages*() > through iov_iter_get_pages()). This happen if cifs_send_async_read() > or cifs_write_from_iter() calls fail from within __cifs_readv() and > __cifs_writev() respectively. This patch move page unreference to > cifs_aio_ctx_release() which will happens on all code paths this is > all simpler to follow for correctness. > > Signed-off-by: Jérôme Glisse <jglisse@xxxxxxxxxx> > Cc: Steve French <sfrench@xxxxxxxxx> > Cc: linux-cifs@xxxxxxxxxxxxxxx > Cc: samba-technical@xxxxxxxxxxxxxxx > Cc: Alexander Viro <viro@xxxxxxxxxxxxxxxxxx> > Cc: linux-fsdevel@xxxxxxxxxxxxxxx > Cc: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx> > Cc: Stable <stable@xxxxxxxxxxxxxxx> > --- > fs/cifs/file.c | 15 +-------------- > fs/cifs/misc.c | 23 ++++++++++++++++++++++- > 2 files changed, 23 insertions(+), 15 deletions(-) > > diff --git a/fs/cifs/file.c b/fs/cifs/file.c > index 89006e044973..a756a4d3f70f 100644 > --- a/fs/cifs/file.c > +++ b/fs/cifs/file.c > @@ -2858,7 +2858,6 @@ static void collect_uncached_write_data(struct cifs_aio_ctx *ctx) > struct cifs_tcon *tcon; > struct cifs_sb_info *cifs_sb; > struct dentry *dentry = ctx->cfile->dentry; > - unsigned int i; > int rc; > > tcon = tlink_tcon(ctx->cfile->tlink); > @@ -2922,10 +2921,6 @@ static void collect_uncached_write_data(struct cifs_aio_ctx *ctx) > kref_put(&wdata->refcount, cifs_uncached_writedata_release); > } > > - if (!ctx->direct_io) > - for (i = 0; i < ctx->npages; i++) > - put_page(ctx->bv[i].bv_page); > - > cifs_stats_bytes_written(tcon, ctx->total_len); > set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(dentry->d_inode)->flags); > > @@ -3563,7 +3558,6 @@ collect_uncached_read_data(struct cifs_aio_ctx *ctx) > struct iov_iter *to = &ctx->iter; > struct cifs_sb_info *cifs_sb; > struct cifs_tcon *tcon; > - unsigned int i; > int rc; > > tcon = tlink_tcon(ctx->cfile->tlink); > @@ -3647,15 +3641,8 @@ collect_uncached_read_data(struct cifs_aio_ctx *ctx) > kref_put(&rdata->refcount, cifs_uncached_readdata_release); > } > > - if (!ctx->direct_io) { > - for (i = 0; i < ctx->npages; i++) { > - if (ctx->should_dirty) > - set_page_dirty(ctx->bv[i].bv_page); > - put_page(ctx->bv[i].bv_page); > - } > - > + if (!ctx->direct_io) > ctx->total_len = ctx->len - iov_iter_count(to); > - } > > /* mask nodata case */ > if (rc == -ENODATA) > diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c > index bee203055b30..9bc0d17a9d77 100644 > --- a/fs/cifs/misc.c > +++ b/fs/cifs/misc.c > @@ -768,6 +768,11 @@ cifs_aio_ctx_alloc(void) > { > struct cifs_aio_ctx *ctx; > > + /* > + * Must use kzalloc to initialize ctx->bv to NULL and ctx->direct_io > + * to false so that we know when we have to unreference pages within > + * cifs_aio_ctx_release() > + */ > ctx = kzalloc(sizeof(struct cifs_aio_ctx), GFP_KERNEL); > if (!ctx) > return NULL; > @@ -786,7 +791,23 @@ cifs_aio_ctx_release(struct kref *refcount) > struct cifs_aio_ctx, refcount); > > cifsFileInfo_put(ctx->cfile); > - kvfree(ctx->bv); > + > + /* > + * ctx->bv is only set if setup_aio_ctx_iter() was call successfuly > + * which means that iov_iter_get_pages() was a success and thus that > + * we have taken reference on pages. > + */ > + if (ctx->bv) { > + unsigned i; > + > + for (i = 0; i < ctx->npages; i++) { > + if (ctx->should_dirty) > + set_page_dirty(ctx->bv[i].bv_page); > + put_page(ctx->bv[i].bv_page); > + } > + kvfree(ctx->bv); > + } > + > kfree(ctx); > } > > -- > 2.20.1 > -- Thanks, Steve