Re: [PATCH] libselinux: add lock callbacks

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

 



On Mon, Jul 12, 2021 at 12:29 AM Nicolas Iooss <nicolas.iooss@xxxxxxx> wrote:
>
> On Fri, Jul 9, 2021 at 9:08 PM Seth Moore <sethmo@xxxxxxxxxx> wrote:
> >
> > The old mechanism to initialize AVC, avc_init(3), is deprected. This
> > leaves libselinux with no way of guarding the AVC cache when accessed
> > from multiple threads. When applications call access check APIs from
> > multiple threads, the AVC cache may become corrupted.
> >
> > This change adds new callback functions to selinux_set_callback(3).
> > These new callbacks all correspond to the functions that used to be
> > passed via avc_init(3). Multi-threaded applications may set these
> > callbacks to guard the AVC cache against simultaneous access by
> > multiple threads.
> >
> > This change adds the following callbacks:
> >   - SELINUX_CB_ALLOC_LOCK
> >       is invoked to allocate new locks
> >   - SELINUX_CB_GET_LOCK
> >       is invoked to acquire a lock
> >   - SELINUX_CB_RELEASE_LOCK
> >       is invoked to release a previously-acquired lock
> >   - SELINUX_CB_FREE_LOCK
> >       is invoked to free a previosly-allocated lock
> >
> > Signed-off-by: Seth Moore <sethmo@xxxxxxxxxx>
>
> Hello,
>
> I am not very familiar with libselinux's callback, but this patch
> looks good to me and it implements the solution which was discussed
> previously (https://lore.kernel.org/selinux/CAEjxPJ4A7KC=+0vTKNU_Z0K-e9Q6hSfTc5WiNms54EN_C5dCLA@xxxxxxxxxxxxxx/).
>
> Nevertheless there is a slight issue with the CHECK_LOCK_CALLBACKS
> macro: calling "CHECK_LOCK_CALLBACKS();" will expand to "if
> (...){...};", with an unnecessary semicolon which is reported by clang
> as -Wextra-semi-stmt (for example in
> https://github.com/fishilico/selinux/runs/3035178820?check_suite_focus=true).
> While I like this check, it seems better if CHECK_LOCK_CALLBACKS was a
> function (which would be static inline). Could you please send a
> version 2 with this macro replaced by a function? (or I may be wrong,
> in which case please explain why using a macro is better than an
> inline function).

Thanks for looking.

I debated making this a function, but in the end liked the idea of the
assert being inside the calling function body. However, the call stack
should have that info anyway, so... in hindsight perhaps I was
overthinking it.

I'll rework and submit a new patch that just calls a function. (Which
I generally prefer to a macro anyhow).

Cheers,
Seth

>
> Thanks,
> Nicolas
>
> > ---
> >  libselinux/include/selinux/selinux.h       | 12 ++++++
> >  libselinux/man/man3/selinux_set_callback.3 | 46 ++++++++++++++++++++++
> >  libselinux/src/avc_internal.h              | 22 +++++++++--
> >  libselinux/src/callbacks.c                 | 13 ++++++
> >  4 files changed, 90 insertions(+), 3 deletions(-)
> >
> > diff --git a/libselinux/include/selinux/selinux.h b/libselinux/include/selinux/selinux.h
> > index ae98a92e..c3c68b3a 100644
> > --- a/libselinux/include/selinux/selinux.h
> > +++ b/libselinux/include/selinux/selinux.h
> > @@ -166,6 +166,14 @@ __attribute__ ((format(printf, 2, 3)))
> >         int (*func_setenforce) (int enforcing);
> >         /* netlink callback for policyload message */
> >         int (*func_policyload) (int seqno);
> > +       /* create a lock and return an opaque pointer to it */
> > +       void *(*func_alloc_lock) (void);
> > +       /* obtain a given lock, blocking if necessary */
> > +       void (*func_get_lock) (void *lock);
> > +       /* release a given lock */
> > +       void (*func_release_lock) (void *lock);
> > +       /* destroy a given lock */
> > +       void (*func_free_lock) (void *lock);
> >  };
> >
> >  #define SELINUX_CB_LOG         0
> > @@ -173,6 +181,10 @@ __attribute__ ((format(printf, 2, 3)))
> >  #define SELINUX_CB_VALIDATE    2
> >  #define SELINUX_CB_SETENFORCE  3
> >  #define SELINUX_CB_POLICYLOAD  4
> > +#define SELINUX_CB_ALLOC_LOCK   5
> > +#define SELINUX_CB_GET_LOCK     6
> > +#define SELINUX_CB_RELEASE_LOCK 7
> > +#define SELINUX_CB_FREE_LOCK    8
> >
> >  extern union selinux_callback selinux_get_callback(int type);
> >  extern void selinux_set_callback(int type, union selinux_callback cb);
> > diff --git a/libselinux/man/man3/selinux_set_callback.3 b/libselinux/man/man3/selinux_set_callback.3
> > index 75f49b06..f7371504 100644
> > --- a/libselinux/man/man3/selinux_set_callback.3
> > +++ b/libselinux/man/man3/selinux_set_callback.3
> > @@ -116,6 +116,52 @@ The
> >  .I seqno
> >  argument is the current sequential number of the policy generation in the system.
> >  .
> > +.TP
> > +.B SELINUX_CB_ALLOC_LOCK
> > +.BI "void *(*" alloc_lock ") ();"
> > +
> > +This callback is used to allocate a fresh lock for protecting critical sections.
> > +Applications that call selinux library functions from multiple threads must either
> > +perform their own locking or set each of the following:
> > +
> > +.B SELINUX_CB_ALLOC_LOCK
> > +
> > +.B SELINUX_CB_GET_LOCK
> > +
> > +.B SELINUX_CB_RELEASE_LOCK
> > +
> > +.B SELINUX_CB_FREE_LOCK
> > +
> > +.TP
> > +.B SELINUX_CB_GET_LOCK
> > +.BI "void (*" get_lock ") (void *" lock ");"
> > +
> > +This callback acquires the
> > +.I lock
> > +that was previously allocated with
> > +.I alloc_lock.
> > +This function must block until the
> > +.I lock
> > +can be acquired.
> > +.
> > +.TP
> > +.B SELINUX_CB_RELEASE_LOCK
> > +.BI "void (*" release_lock ") (void *" lock ");"
> > +
> > +This callback releases the
> > +.I lock
> > +that was previously acquired with
> > +.I get_lock.
> > +.
> > +.TP
> > +.B SELINUX_CB_FREE_LOCK
> > +.BI "void (*" free_lock ") (void *" lock ");"
> > +
> > +This callback frees the
> > +.I lock
> > +that was previously allocated with
> > +.I alloc_lock.
> > +.
> >  .SH "RETURN VALUE"
> >  None.
> >  .
> > diff --git a/libselinux/src/avc_internal.h b/libselinux/src/avc_internal.h
> > index a9a4aa0b..f23690a1 100644
> > --- a/libselinux/src/avc_internal.h
> > +++ b/libselinux/src/avc_internal.h
> > @@ -9,6 +9,7 @@
> >  #ifndef _SELINUX_AVC_INTERNAL_H_
> >  #define _SELINUX_AVC_INTERNAL_H_
> >
> > +#include <assert.h>
> >  #include <stdio.h>
> >  #include <stdlib.h>
> >  #include <string.h>
> > @@ -112,26 +113,41 @@ static inline void avc_stop_thread(void *thread)
> >                 avc_func_stop_thread(thread);
> >  }
> >
> > +#define CHECK_LOCK_CALLBACKS() \
> > +    if (avc_func_alloc_lock \
> > +            || avc_func_get_lock \
> > +            || avc_func_release_lock \
> > +            || avc_func_free_lock) { \
> > +        assert(avc_func_alloc_lock \
> > +                && avc_func_get_lock \
> > +                && avc_func_release_lock \
> > +                && avc_func_free_lock); \
> > +    }
> > +
> >  static inline void *avc_alloc_lock(void)
> >  {
> > +       CHECK_LOCK_CALLBACKS();
> >         return avc_func_alloc_lock ? avc_func_alloc_lock() : NULL;
> >  }
> >
> >  static inline void avc_get_lock(void *lock)
> >  {
> > -       if (avc_func_get_lock)
> > +       CHECK_LOCK_CALLBACKS();
> > +       if (avc_func_get_lock && lock)
> >                 avc_func_get_lock(lock);
> >  }
> >
> >  static inline void avc_release_lock(void *lock)
> >  {
> > -       if (avc_func_release_lock)
> > +       CHECK_LOCK_CALLBACKS();
> > +       if (avc_func_release_lock && lock)
> >                 avc_func_release_lock(lock);
> >  }
> >
> >  static inline void avc_free_lock(void *lock)
> >  {
> > -       if (avc_func_free_lock)
> > +       CHECK_LOCK_CALLBACKS();
> > +       if (avc_func_free_lock && lock)
> >                 avc_func_free_lock(lock);
> >  }
> >
> > diff --git a/libselinux/src/callbacks.c b/libselinux/src/callbacks.c
> > index c18ccc54..b635c8d8 100644
> > --- a/libselinux/src/callbacks.c
> > +++ b/libselinux/src/callbacks.c
> > @@ -9,6 +9,7 @@
> >  #include <errno.h>
> >  #include <selinux/selinux.h>
> >  #include "callbacks.h"
> > +#include "avc_internal.h"
> >
> >  /* default implementations */
> >  static int __attribute__ ((format(printf, 2, 3)))
> > @@ -95,6 +96,18 @@ selinux_set_callback(int type, union selinux_callback cb)
> >         case SELINUX_CB_POLICYLOAD:
> >                 selinux_netlink_policyload = cb.func_policyload;
> >                 break;
> > +       case SELINUX_CB_ALLOC_LOCK:
> > +               avc_func_alloc_lock = cb.func_alloc_lock;
> > +               break;
> > +       case SELINUX_CB_GET_LOCK:
> > +               avc_func_get_lock = cb.func_get_lock;
> > +               break;
> > +       case SELINUX_CB_RELEASE_LOCK:
> > +               avc_func_release_lock = cb.func_release_lock;
> > +               break;
> > +       case SELINUX_CB_FREE_LOCK:
> > +               avc_func_free_lock = cb.func_free_lock;
> > +               break;
> >         }
> >  }
> >
> > --
> > 2.32.0.93.g670b81a890-goog
> >
>



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

  Powered by Linux