On Thu, 2012-08-16 at 21:28 +0100, David Howells wrote:
> Linus Torvalds <[email protected]> wrote:
> 
> > David? Why is there a BUG_ON() there? Killing the machine helps us exactly
> > how?
> >
> > There seems to be some missing locking wrt idmap->idmap_key_cons
> > accesses.
> 
> I was thinking that it shouldn't be possible to get there with a non-NULL
> value in idmap->idmap_key_cons, but I left an assertion in there just to be
> sure - after all, we have a leak or other problems if we just overwrite the
> value.
> 
> The reason for I thought this is that there's a mutex (idmap->idmap_mutex)
> guarding access to the legacy upcall-to-running-daemon method.  This should be
> held over the entire life of the upcall since request_key_with_auxdata() waits
> for the request to be completed (it calls wait_for_key_construction()).
> 
> > Instead, it uses the insane
> > 
> > +       cons = ACCESS_ONCE(idmap->idmap_key_cons);
> > +       idmap->idmap_key_cons = NULL;
> > 
> > sequence that makes no sense. What's so special about it that it needs
> > ACCESS_ONCE()? If that access is unlocked, then setting the field to
> > NULL directly afterwards seems singularly unsafe. What's the logic
> > behind such an access pattern?
> 
> I did try and explain that in the preceding comment.  However, you're right,
> ACCESS_ONCE() isn't necessary as we write to the value immediately after.  I
> wonder if I needed some sort of memory barrier instead.  At one point during
> the development of the patch, I was clearing idmap_key_cons later in the
> function and I added the ACCESS_ONCE() at that point.
> 
> The problem I tried to fix is that once nfs_idmap_read_message() is called,
> the request_key_with_auxdata() may wake up immediately and then the mutex can
> get released - which means someone else can get in *before* we call
> complete_request_key().
> 
> Previously, complete_request_key() was being called with the pointer in
> idmap->idmap_key_cons - but that may not be touched from the point the key is
> instantiated, so I was trying to ensure idmap->idmap_key_cons was only read
> once.
> 
> The call sequence looks something like this:
> 
>       {REQUESTER}
>       -->nfs_map_name_to_uid()
>        -->nfs_idmap_lookup_id()
>         -->nfs_idmap_get_key()
>          -->nfs_idmap_request_key(&key_type_id_resolver)
>          <--[fails]
>          mutex_lock(&idmap->idmap_mutex)
>          -->nfs_idmap_request_key(&key_type_id_resolver_legacy, ...)
>           -->request_key_with_auxdata()
>            -->request_key_and_link()
>             -->construct_key_and_link()
>              -->construct_key()
>               -->key->type->request_key: nfs_idmap_legacy_upcall()
>                BUG_ON(idmap->idmap_key_cons != NULL);
>                idmap->idmap_key_cons = cons;
>                -->rpc_queue_upcall()
>                 [WAKE DAEMON]
>                <--[ok] rpc_queue_upcall()
>               <--[ok] nfs_idmap_legacy_upcall()
>              <--[ok] construct_key()
>             <--[ok] construct_key_and_link()
>            <--[ok] request_key_and_link()
>            -->wait_for_key_construction()
>            [SLEEP]
> 
>                       {DAEMON}
>                       -->sys_write()
>                        -->idmap_pipe_downcall()
>                         cons = ACCESS_ONCE(idmap->idmap_key_cons);
>                         idmap->idmap_key_cons = NULL;
>                         -->nfs_idmap_read_message()
>                          -->nfs_idmap_instantiate()
>                           -->key_instantiate_and_link()
>                            -->__key_instantiate_and_link()
>                             [WAKE UP REQUESTER]
> 
>            <--[ok] wait_for_key_construction()
>           <--[ok] request_key_with_auxdata()
>           [USE KEY PAYLOAD]
>           key_put()
>          <--[ok] nfs_idmap_request_key()
>          mutex_unlock(&idmap->idmap_mutex)
> 
>               {NEXT REQUESTER}
>               -->nfs_map_name_to_uid()
>                -->nfs_idmap_lookup_id()
>                 -->nfs_idmap_get_key()
>                  -->nfs_idmap_request_key(&key_type_id_resolver)
>                  <--[fails]
>                  mutex_lock(&idmap->idmap_mutex)
>                  -->nfs_idmap_request_key(&key_type_id_resolver_legacy, ...)
>                   -->request_key_with_auxdata()
>                    -->request_key_and_link()
>                     -->construct_key_and_link()
>                      -->construct_key()
>                       -->key->type->request_key: nfs_idmap_legacy_upcall()
>                        BUG_ON(idmap->idmap_key_cons != NULL);
> 
> So it looks like it oughtn't to be possible to get here with idmap_key_cons
> being non-NULL.

Sure it is. Look at what happens if rpc_queue_upcall() fails, then look
at the 2 patches at

http://git.linux-nfs.org/?p=trondmy/linux-nfs.git;a=commitdiff;h=7f26d74498ca000ccfa28515b86998f0a0c2298a

and

http://git.linux-nfs.org/?p=trondmy/linux-nfs.git;a=commitdiff;h=d0f6eca7eb175373a61173d86f3cf4ec4a9166fa


-- 
Trond Myklebust
Linux NFS client maintainer

NetApp
[email protected]
www.netapp.com

N�����r��y����b�X��ǧv�^�)޺{.n�+������z)����w*jg��������ݢj/���z�ޖ��2�ޙ����&�)ߡ�a�����G���h��j:+v���w��٥

Reply via email to