Re: [PATCH] selinux: fix race between old and new sidtab

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Apr 6, 2021 at 9:15 PM Paul Moore <paul@xxxxxxxxxxxxxx> wrote:
> On Tue, Apr 6, 2021 at 5:02 AM Ondrej Mosnacek <omosnace@xxxxxxxxxx> wrote:
> > On Tue, Apr 6, 2021 at 12:11 AM Paul Moore <paul@xxxxxxxxxxxxxx> wrote:
> > > On Mon, Apr 5, 2021 at 5:11 AM Ondrej Mosnacek <omosnace@xxxxxxxxxx> wrote:
>
> ...
>
> > > > @@ -2534,10 +2577,18 @@ int security_netif_sid(struct selinux_state *state,
> > > >                 if (!c->sid[0] || !c->sid[1]) {
> > > >                         rc = sidtab_context_to_sid(sidtab, &c->context[0],
> > > >                                                    &c->sid[0]);
> > > > +                       if (rc == -ESTALE) {
> > > > +                               rcu_read_unlock();
> > > > +                               goto retry;
> > > > +                       }
> > > >                         if (rc)
> > > >                                 goto out;
> > > >                         rc = sidtab_context_to_sid(sidtab, &c->context[1],
> > > >                                                    &c->sid[1]);
> > > > +                       if (rc == -ESTALE) {
> > > > +                               rcu_read_unlock();
> > > > +                               goto retry;
> > > > +                       }
> > >
> > > If the first sidtab_context_to_sid() ran successfully we are not going
> > > to see -ESTALE here, correct?  Assuming yes, perhaps we can drop this
> > > -ESTALE check with a comment about how a frozen sidtab will be caught
> > > by the call above.
> >
> > No, nothing really prevents the sidtab from becoming frozen between
> > the two calls.
>
> Yes, my mistake, I was focused on the RCU policy copies and not
> looking at the spinlock for the freeze state.
>
> > > > @@ -2676,18 +2732,20 @@ int security_get_user_sids(struct selinux_state *state,
> > > >         struct sidtab *sidtab;
> > > >         struct context *fromcon, usercon;
> > > >         u32 *mysids = NULL, *mysids2, sid;
> > > > -       u32 mynel = 0, maxnel = SIDS_NEL;
> > > > +       u32 i, j, mynel, maxnel = SIDS_NEL;
> > > >         struct user_datum *user;
> > > >         struct role_datum *role;
> > > >         struct ebitmap_node *rnode, *tnode;
> > > > -       int rc = 0, i, j;
> > > > +       int rc;
> > > >
> > > >         *sids = NULL;
> > > >         *nel = 0;
> > > >
> > > >         if (!selinux_initialized(state))
> > > > -               goto out;
> > > > +               return 0;
> > > >
> > > > +retry:
> > > > +       mynel = 0;
> > > >         rcu_read_lock();
> > > >         policy = rcu_dereference(state->policy);
> > > >         policydb = &policy->policydb;
> > > > @@ -2723,6 +2781,10 @@ int security_get_user_sids(struct selinux_state *state,
> > > >                                 continue;
> > > >
> > > >                         rc = sidtab_context_to_sid(sidtab, &usercon, &sid);
> > > > +                       if (rc == -ESTALE) {
> > > > +                               rcu_read_unlock();
> > >
> > > Don't we need to free and reset 'mysids' here?  'mynel' and 'maxnel'
> > > are probably less critical since the -ESTALE condition should happen
> > > before they are ever modified, but a constant assignment is pretty
> > > cheap so it may make sense to reset those as well.
> >
> > No, we can keep "mysids" and "maxnel", since they represent an
> > automatically growing vector, which we can keep and reuse in the retry
> > path rather than starting from scratch. It is more efficient, since
> > the new policy will likely have the same number of SIDs in the result.
> > If it has more, we just grow the vector further as needed; if it has
> > less, we just don't use the full capacity and the array will be freed
> > after a while anyway (see "out_unlock"), so the extra memory shouldn't
> > be held for too long. Not to mention that this is a deprecated
> > interface that will hopefully be removed one day :)
>
> I believe you are ignoring the case where mysids is non-NULL when an
> -ESTALE occurs and the code jumps to 'retry' and that ends up calling
> 'mysids = kcalloc(...)' before the ebitmap loop.  Perhaps I'm
> mistaken, but this looks like a memory leak to me.

Ah, yes, I was blind... somehow I believed it was allocated outside
the retry loop, yet it pretty obviously isn't :) It's a constant-size
allocation though, so it could just be moved outside and even changed
to a GFP_KERNEL allocation. So that'll be an easy fix and even a tiny
improvement.

>
> > (And you're wrong that mynel/maxnel can't be modified - notice that
> > the sidtab_context_to_sid() call is inside a loop ;)
>
> My comments were correct if you work under the (faulty) assumption
> that the sidtab isn't being frozen underneath you :-P

Hah, yeah, didn't realize that :)

--
Ondrej Mosnacek
Software Engineer, Linux Security - SELinux kernel
Red Hat, Inc.




[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux