Re: [PATCH v3] libsemanage: block on store lock by default

Stephen Smalley <[email protected]> Fri, 24 Jul 2026 10:27:15 -0400
Newsgroups org.kernel.vger.selinux
Message-ID <CAEjxPJ5AhCXDA4W=xxPoJY8H9bZ2V370LCCM4cQYy=JNvZUoEg@mail.gmail.com>
On Wed, Jul 22, 2026 at 10:49 AM Stephen Smalley
<[email protected]> wrote:
>
> semanage_get_lock() polls the store lock once per second and gives up
> after sh->timeout seconds, which is hard-coded to 5 with no
> configuration setting to control it. A single semodule transaction on
> a real policy routinely takes more than 5 seconds, so any overlap
> makes the second caller fail. This makes rpm scriptlets, ansible,
> or other scripted usage of semodule fragile.
>
> Change the default timeout to -1 (wait indefinitely) and in that case
> use a blocking flock() instead of polling. Try a non-blocking flock()
> first for the common case where the lock is not held and if it is
> held, inform the user so they know why it may be taking longer than
> normal. Retain the timeout logic in case we decide to re-introduce
> other configurable settings in the future.
>
> Several other minor cleanups are included as well, e.g. there is no
> need to pass O_TRUNC for the lock file, and there is no need to set
> O_CLOEXEC a second time via fcntl() since open() already passed it.
>
> Fixes: https://github.com/SELinuxProject/selinux/issues/181
> Signed-off-by: Stephen Smalley <[email protected]>
> ---
> v3 fixes the description (s/requires/makes/).

Merged.

>
>  libsemanage/src/handle.c         |  2 +-
>  libsemanage/src/semanage_store.c | 81 +++++++++++++++-----------------
>  2 files changed, 39 insertions(+), 44 deletions(-)
>
> diff --git a/libsemanage/src/handle.c b/libsemanage/src/handle.c
> index 77d0ed8a..1ff86e60 100644
> --- a/libsemanage/src/handle.c
> +++ b/libsemanage/src/handle.c
> @@ -37,7 +37,7 @@
>  #include "semanage_conf.h"
>  #include "semanage_store.h"
>
> -#define SEMANAGE_COMMIT_READ_WAIT 5
> +#define SEMANAGE_COMMIT_READ_WAIT -1
>
>  static char *private_semanage_root = NULL;
>
> diff --git a/libsemanage/src/semanage_store.c b/libsemanage/src/semanage_store.c
> index f14ebe97..f6ca1afb 100644
> --- a/libsemanage/src/semanage_store.c
> +++ b/libsemanage/src/semanage_store.c
> @@ -50,6 +50,7 @@ typedef struct dbase_policydb dbase_t;
>  #include <stdio_ext.h>
>  #include <stdlib.h>
>  #include <string.h>
> +#include <time.h>
>  #include <unistd.h>
>  #include <sys/file.h>
>  #include <sys/stat.h>
> @@ -1944,61 +1945,55 @@ static int semanage_get_lock(semanage_handle_t *sh, const char *lock_name,
>                              const char *lock_file)
>  {
>         int fd;
> -       struct timeval origtime, curtime;
> -       int got_lock = 0;
> +       int left;
>
> -       if ((fd = open(lock_file, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC,
> +       if ((fd = open(lock_file, O_RDWR | O_CREAT | O_CLOEXEC,
>                        S_IRUSR | S_IWUSR)) == -1) {
>                 ERR(sh, "Could not open direct %s at %s.", lock_name,
>                     lock_file);
>                 return -1;
>         }
> -       if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
> -               ERR(sh, "Could not set close-on-exec for %s at %s.", lock_name,
> -                   lock_file);
> -               close(fd);
> -               return -1;
> -       }
> +
> +       if (flock(fd, LOCK_EX | LOCK_NB) == 0)
> +               return fd;
> +       if (errno != EAGAIN && errno != EWOULDBLOCK)
> +               goto err;
>
>         if (sh->timeout == 0) {
> -               /* return immediately */
> -               origtime.tv_sec = 0;
> -       } else {
> -               origtime.tv_sec = sh->timeout;
> -       }
> -       origtime.tv_usec = 0;
> -       do {
> -               curtime.tv_sec = 1;
> -               curtime.tv_usec = 0;
> -               if (flock(fd, LOCK_EX | LOCK_NB) == 0) {
> -                       got_lock = 1;
> -                       break;
> -               } else if (errno != EAGAIN) {
> -                       ERR(sh, "Error obtaining direct %s at %s.", lock_name,
> -                           lock_file);
> -                       close(fd);
> -                       return -1;
> -               }
> -               if (origtime.tv_sec > 0 || sh->timeout == -1) {
> -                       if (select(0, NULL, NULL, NULL, &curtime) == -1) {
> -                               if (errno == EINTR) {
> -                                       continue;
> -                               }
> -                               ERR(sh,
> -                                   "Error while waiting to get direct %s at %s.",
> -                                   lock_name, lock_file);
> -                               close(fd);
> -                               return -1;
> -                       }
> -                       origtime.tv_sec--;
> -               }
> -       } while (origtime.tv_sec > 0 || sh->timeout == -1);
> -       if (!got_lock) {
>                 ERR(sh, "Could not get direct %s at %s.", lock_name, lock_file);
>                 close(fd);
>                 return -1;
>         }
> -       return fd;
> +
> +       INFO(sh, "Waiting for semanage %s at %s (held by another process).",
> +            lock_name, lock_file);
> +
> +       if (sh->timeout < 0) {
> +               while (flock(fd, LOCK_EX) != 0) {
> +                       if (errno != EINTR)
> +                               goto err;
> +               }
> +               return fd;
> +       }
> +
> +       for (left = sh->timeout; left > 0; left--) {
> +               const struct timespec ts = { 1, 0 };
> +
> +               nanosleep(&ts, NULL);
> +               if (flock(fd, LOCK_EX | LOCK_NB) == 0)
> +                       return fd;
> +               if (errno != EAGAIN && errno != EWOULDBLOCK)
> +                       goto err;
> +       }
> +
> +       ERR(sh, "Could not get direct %s at %s after %d seconds.", lock_name,
> +           lock_file, sh->timeout);
> +       close(fd);
> +       return -1;
> +err:
> +       ERR(sh, "Error obtaining direct %s at %s.", lock_name, lock_file);
> +       close(fd);
> +       return -1;
>  }
>
>  /* Locking for the module store for transactions.  This is very basic
> --
> 2.55.0
>