[SRU T][PATCH 3/6] cachefiles: Fix refcounting bug in backing-file read monitoring

Daniel Axtens daniel.axtens at canonical.com
Tue Aug 28 03:27:13 UTC 2018


Hi Kleber,

Thanks for catching this. Apologies for the trouble - I will open a
new bug in future.

Regards,
Daniel
On Sat, Aug 25, 2018 at 1:20 AM Kleber Souza <kleber.souza at canonical.com> wrote:
>
> On 08/02/18 06:18, Daniel Axtens wrote:
> > From: Kiran Kumar Modukuri <kiran.modukuri at gmail.com>
> >
> > BugLink: https://bugs.launchpad.net/bugs/1774336
>
> Hi Daniel,
>
> We didn't catch it before, but this LP bug had already been marked as
> 'Fix Released' when this patch series was sent.
>
> In the future, please do not re-use a LP bug whose fix has already been
> released. It makes it difficult to track the status of the follow-up
> fixes. The best approach is to create a new bug referencing the old one
> so we can track its status accordingly.
>
>
> Thanks,
> Kleber
>
>
>
> >
> > cachefiles_read_waiter() has the right to access a 'monitor' object by
> > virtue of being called under the waitqueue lock for one of the pages in its
> > purview.  However, it has no ref on that monitor object or on the
> > associated operation.
> >
> > What it is allowed to do is to move the monitor object to the operation's
> > to_do list, but once it drops the work_lock, it's actually no longer
> > permitted to access that object.  However, it is trying to enqueue the
> > retrieval operation for processing - but it can only do this via a pointer
> > in the monitor object, something it shouldn't be doing.
> >
> > If it doesn't enqueue the operation, the operation may not get processed.
> > If the order is flipped so that the enqueue is first, then it's possible
> > for the work processor to look at the to_do list before the monitor is
> > enqueued upon it.
> >
> > Fix this by getting a ref on the operation so that we can trust that it
> > will still be there once we've added the monitor to the to_do list and
> > dropped the work_lock.  The op can then be enqueued after the lock is
> > dropped.
> >
> > The bug can manifest in one of a couple of ways.  The first manifestation
> > looks like:
> >
> >  FS-Cache:
> >  FS-Cache: Assertion failed
> >  FS-Cache: 6 == 5 is false
> >  ------------[ cut here ]------------
> >  kernel BUG at fs/fscache/operation.c:494!
> >  RIP: 0010:fscache_put_operation+0x1e3/0x1f0
> >  ...
> >  fscache_op_work_func+0x26/0x50
> >  process_one_work+0x131/0x290
> >  worker_thread+0x45/0x360
> >  kthread+0xf8/0x130
> >  ? create_worker+0x190/0x190
> >  ? kthread_cancel_work_sync+0x10/0x10
> >  ret_from_fork+0x1f/0x30
> >
> > This is due to the operation being in the DEAD state (6) rather than
> > INITIALISED, COMPLETE or CANCELLED (5) because it's already passed through
> > fscache_put_operation().
> >
> > The bug can also manifest like the following:
> >
> >  kernel BUG at fs/fscache/operation.c:69!
> >  ...
> >     [exception RIP: fscache_enqueue_operation+246]
> >  ...
> >  #7 [ffff883fff083c10] fscache_enqueue_operation at ffffffffa0b793c6
> >  #8 [ffff883fff083c28] cachefiles_read_waiter at ffffffffa0b15a48
> >  #9 [ffff883fff083c48] __wake_up_common at ffffffff810af028
> >
> > I'm not entirely certain as to which is line 69 in Lei's kernel, so I'm not
> > entirely clear which assertion failed.
> >
> > Fixes: 9ae326a69004 ("CacheFiles: A cache that backs onto a mounted filesystem")
> > Reported-by: Lei Xue <carmark.dlut at gmail.com>
> > Reported-by: Vegard Nossum <vegard.nossum at gmail.com>
> > Reported-by: Anthony DeRobertis <aderobertis at metrics.net>
> > Reported-by: NeilBrown <neilb at suse.com>
> > Reported-by: Daniel Axtens <dja at axtens.net>
> > Reported-by: Kiran Kumar Modukuri <kiran.modukuri at gmail.com>
> > Signed-off-by: David Howells <dhowells at redhat.com>
> > Reviewed-by: Daniel Axtens <dja at axtens.net>
> > (cherry picked from commit 934140ab028713a61de8bca58c05332416d037d1)
> > Signed-off-by: Daniel Axtens <daniel.axtens at canonical.com>
> > ---
> >  fs/cachefiles/rdwr.c | 17 ++++++++++++-----
> >  1 file changed, 12 insertions(+), 5 deletions(-)
> >
> > diff --git a/fs/cachefiles/rdwr.c b/fs/cachefiles/rdwr.c
> > index a1210b0322e0..725a0e20a913 100644
> > --- a/fs/cachefiles/rdwr.c
> > +++ b/fs/cachefiles/rdwr.c
> > @@ -27,6 +27,7 @@ static int cachefiles_read_waiter(wait_queue_t *wait, unsigned mode,
> >       struct cachefiles_one_read *monitor =
> >               container_of(wait, struct cachefiles_one_read, monitor);
> >       struct cachefiles_object *object;
> > +     struct fscache_retrieval *op = monitor->op;
> >       struct wait_bit_key *key = _key;
> >       struct page *page = wait->private;
> >
> > @@ -51,16 +52,22 @@ static int cachefiles_read_waiter(wait_queue_t *wait, unsigned mode,
> >       list_del(&wait->task_list);
> >
> >       /* move onto the action list and queue for FS-Cache thread pool */
> > -     ASSERT(monitor->op);
> > +     ASSERT(op);
> >
> > -     object = container_of(monitor->op->op.object,
> > -                           struct cachefiles_object, fscache);
> > +     /* We need to temporarily bump the usage count as we don't own a ref
> > +      * here otherwise cachefiles_read_copier() may free the op between the
> > +      * monitor being enqueued on the op->to_do list and the op getting
> > +      * enqueued on the work queue.
> > +      */
> > +     fscache_get_retrieval(op);
> >
> > +     object = container_of(op->op.object, struct cachefiles_object, fscache);
> >       spin_lock(&object->work_lock);
> > -     list_add_tail(&monitor->op_link, &monitor->op->to_do);
> > +     list_add_tail(&monitor->op_link, &op->to_do);
> >       spin_unlock(&object->work_lock);
> >
> > -     fscache_enqueue_retrieval(monitor->op);
> > +     fscache_enqueue_retrieval(op);
> > +     fscache_put_retrieval(op);
> >       return 0;
> >  }
> >
> >
>




More information about the kernel-team mailing list