Blame SOURCES/0106-sss_client-Fix-race-condition-in-memory-cache.patch

905b4d
From 15240b29d55cfd775221cc6482407c1172e2a5a1 Mon Sep 17 00:00:00 2001
905b4d
From: Lukas Slebodnik <lslebodn@redhat.com>
905b4d
Date: Fri, 21 Nov 2014 11:28:36 +0100
905b4d
Subject: [PATCH 106/112] sss_client: Fix race condition in memory cache
905b4d
MIME-Version: 1.0
905b4d
Content-Type: text/plain; charset=UTF-8
905b4d
Content-Transfer-Encoding: 8bit
905b4d
905b4d
Thread safe initialisation was fixed in ticket #2380, but there is
905b4d
still race condition in reinitialisation.
905b4d
905b4d
If caches is invalidated with command sss_cache -U (-G or -E) then
905b4d
client code will need to reinitialize fast memory cache.
905b4d
Let say we have two threads. The 1st thread find out that memory cache
905b4d
should be reinitialized; therefore the fast memory cached is unmapped
905b4d
and context destroyed. In the same time, 2nd thread tried to check
905b4d
header of memory cache whether it is initialized and valid. As a result
905b4d
of previously unmapped memory the 2nd thread access
905b4d
out of bound memory (SEGFAULT).
905b4d
905b4d
The destroying of fast memory cache cannot be done any time. We need
905b4d
to be sure that there isn't any other thread which uses mmaped memory.
905b4d
The new counter of active threads was added for this purpose. The state
905b4d
of fast memory cache was converted from boolean to three value state
905b4d
(UNINITIALIZED, INITIALIZED, RECYCLED)
905b4d
UNINITIALIZED
905b4d
    - the fast memory cache need to be initialized.
905b4d
    - if there is a problem with initialisation the state will not change
905b4d
    - after successful initialisation, the state will change to INITIALIZED
905b4d
INITIALIZED
905b4d
    - if the cahe was invalidated or there is any other problem was
905b4d
      detected in memory cache header the state will change to RECYCLED
905b4d
      and memory cache IS NOT destroyed.
905b4d
RECYCLED
905b4d
    - nothing will be done is there are any active threads which may use
905b4d
      the data from mmaped memory
905b4d
    - if there aren't active threads the fast memory cahe is destroyed and
905b4d
      state is changed to UNINITIALIZED.
905b4d
905b4d
https://fedorahosted.org/sssd/ticket/2445
905b4d
905b4d
Reviewed-by: Michal Židek <mzidek@redhat.com>
905b4d
---
905b4d
 src/sss_client/nss_mc.h        | 10 ++++++++-
905b4d
 src/sss_client/nss_mc_common.c | 46 ++++++++++++++++++++++++++++++++++--------
905b4d
 src/sss_client/nss_mc_group.c  |  8 ++++++--
905b4d
 src/sss_client/nss_mc_passwd.c |  8 ++++++--
905b4d
 4 files changed, 59 insertions(+), 13 deletions(-)
905b4d
905b4d
diff --git a/src/sss_client/nss_mc.h b/src/sss_client/nss_mc.h
905b4d
index 685cc41c0530750d890050f0917dc88be14d96ea..050bd4100dec091cb096a7d97bfe6615b12654da 100644
905b4d
--- a/src/sss_client/nss_mc.h
905b4d
+++ b/src/sss_client/nss_mc.h
905b4d
@@ -33,9 +33,15 @@
905b4d
 typedef int errno_t;
905b4d
 #endif
905b4d
 
905b4d
+enum sss_mc_state {
905b4d
+    UNINITIALIZED = 0,
905b4d
+    INITIALIZED,
905b4d
+    RECYCLED,
905b4d
+};
905b4d
+
905b4d
 /* common stuff */
905b4d
 struct sss_cli_mc_ctx {
905b4d
-    bool initialized;
905b4d
+    enum sss_mc_state initialized;
905b4d
     int fd;
905b4d
 
905b4d
     uint32_t seed;          /* seed from the tables header */
905b4d
@@ -48,6 +54,8 @@ struct sss_cli_mc_ctx {
905b4d
 
905b4d
     uint32_t *hash_table;   /* hash table address (in mmap) */
905b4d
     uint32_t ht_size;       /* size of hash table */
905b4d
+
905b4d
+    uint32_t active_threads; /* count of threads which use memory cache */
905b4d
 };
905b4d
 
905b4d
 errno_t sss_nss_mc_get_ctx(const char *name, struct sss_cli_mc_ctx *ctx);
905b4d
diff --git a/src/sss_client/nss_mc_common.c b/src/sss_client/nss_mc_common.c
905b4d
index 9c6e1af1642275fc7738b51d7ca80d712d49b2ac..89ff6b46e2abee03039cfd632ef50231eab92eec 100644
905b4d
--- a/src/sss_client/nss_mc_common.c
905b4d
+++ b/src/sss_client/nss_mc_common.c
905b4d
@@ -123,7 +123,7 @@ static errno_t sss_nss_mc_init_ctx(const char *name,
905b4d
 
905b4d
     sss_nss_lock();
905b4d
     /* check if ctx is initialised by previous thread. */
905b4d
-    if (ctx->initialized) {
905b4d
+    if (ctx->initialized != UNINITIALIZED) {
905b4d
         ret = sss_nss_check_header(ctx);
905b4d
         goto done;
905b4d
     }
905b4d
@@ -163,7 +163,7 @@ static errno_t sss_nss_mc_init_ctx(const char *name,
905b4d
         goto done;
905b4d
     }
905b4d
 
905b4d
-    ctx->initialized = true;
905b4d
+    ctx->initialized = INITIALIZED;
905b4d
 
905b4d
     ret = 0;
905b4d
 
905b4d
@@ -181,22 +181,52 @@ errno_t sss_nss_mc_get_ctx(const char *name, struct sss_cli_mc_ctx *ctx)
905b4d
 {
905b4d
     char *envval;
905b4d
     int ret;
905b4d
+    bool need_decrement = false;
905b4d
 
905b4d
     envval = getenv("SSS_NSS_USE_MEMCACHE");
905b4d
     if (envval && strcasecmp(envval, "NO") == 0) {
905b4d
         return EPERM;
905b4d
     }
905b4d
 
905b4d
-    if (ctx->initialized) {
905b4d
+    switch (ctx->initialized) {
905b4d
+    case UNINITIALIZED:
905b4d
+        __sync_add_and_fetch(&ctx->active_threads, 1);
905b4d
+        ret = sss_nss_mc_init_ctx(name, ctx);
905b4d
+        if (ret) {
905b4d
+            need_decrement = true;
905b4d
+        }
905b4d
+        break;
905b4d
+    case INITIALIZED:
905b4d
+        __sync_add_and_fetch(&ctx->active_threads, 1);
905b4d
         ret = sss_nss_check_header(ctx);
905b4d
-        goto done;
905b4d
+        if (ret) {
905b4d
+            need_decrement = true;
905b4d
+        }
905b4d
+        break;
905b4d
+    case RECYCLED:
905b4d
+        /* we need to safely destroy memory cache */
905b4d
+        ret = EAGAIN;
905b4d
+        break;
905b4d
+    default:
905b4d
+        ret = EFAULT;
905b4d
     }
905b4d
 
905b4d
-    ret = sss_nss_mc_init_ctx(name, ctx);
905b4d
-
905b4d
-done:
905b4d
     if (ret) {
905b4d
-        sss_nss_mc_destroy_ctx(ctx);
905b4d
+        if (ctx->initialized == INITIALIZED) {
905b4d
+            ctx->initialized = RECYCLED;
905b4d
+        }
905b4d
+        if (ctx->initialized == RECYCLED && ctx->active_threads == 0) {
905b4d
+            /* just one thread should call munmap */
905b4d
+            sss_nss_lock();
905b4d
+            if (ctx->initialized == RECYCLED) {
905b4d
+                sss_nss_mc_destroy_ctx(ctx);
905b4d
+            }
905b4d
+            sss_nss_unlock();
905b4d
+        }
905b4d
+        if (need_decrement) {
905b4d
+            /* In case of error, we will not touch mmapped area => decrement */
905b4d
+            __sync_sub_and_fetch(&ctx->active_threads, 1);
905b4d
+        }
905b4d
     }
905b4d
     return ret;
905b4d
 }
905b4d
diff --git a/src/sss_client/nss_mc_group.c b/src/sss_client/nss_mc_group.c
905b4d
index 268b40ef02f2a621c4f61755ce4dfe2c3786bfa6..e0fdb97f628ac19741409be29566e4af5a391f74 100644
905b4d
--- a/src/sss_client/nss_mc_group.c
905b4d
+++ b/src/sss_client/nss_mc_group.c
905b4d
@@ -29,7 +29,8 @@
905b4d
 #include "nss_mc.h"
905b4d
 #include "util/util_safealign.h"
905b4d
 
905b4d
-struct sss_cli_mc_ctx gr_mc_ctx = { false, -1, 0, NULL, 0, NULL, 0, NULL, 0 };
905b4d
+struct sss_cli_mc_ctx gr_mc_ctx = { UNINITIALIZED, -1, 0, NULL, 0, NULL, 0,
905b4d
+                                    NULL, 0, 0 };
905b4d
 
905b4d
 static errno_t sss_nss_mc_parse_result(struct sss_mc_rec *rec,
905b4d
                                        struct group *result,
905b4d
@@ -176,6 +177,7 @@ errno_t sss_nss_mc_getgrnam(const char *name, size_t name_len,
905b4d
 
905b4d
 done:
905b4d
     free(rec);
905b4d
+    __sync_sub_and_fetch(&gr_mc_ctx.active_threads, 1);
905b4d
     return ret;
905b4d
 }
905b4d
 
905b4d
@@ -198,7 +200,8 @@ errno_t sss_nss_mc_getgrgid(gid_t gid,
905b4d
 
905b4d
     len = snprintf(gidstr, 11, "%ld", (long)gid);
905b4d
     if (len > 10) {
905b4d
-        return EINVAL;
905b4d
+        ret = EINVAL;
905b4d
+        goto done;
905b4d
     }
905b4d
 
905b4d
     /* hashes are calculated including the NULL terminator */
905b4d
@@ -242,6 +245,7 @@ errno_t sss_nss_mc_getgrgid(gid_t gid,
905b4d
 
905b4d
 done:
905b4d
     free(rec);
905b4d
+    __sync_sub_and_fetch(&gr_mc_ctx.active_threads, 1);
905b4d
     return ret;
905b4d
 }
905b4d
 
905b4d
diff --git a/src/sss_client/nss_mc_passwd.c b/src/sss_client/nss_mc_passwd.c
905b4d
index fa19afc3c0e468430183ed3f13b80e086251ee01..10e43e2af43c5e7f1738e281b3ed260d89f3a004 100644
905b4d
--- a/src/sss_client/nss_mc_passwd.c
905b4d
+++ b/src/sss_client/nss_mc_passwd.c
905b4d
@@ -28,7 +28,8 @@
905b4d
 #include <time.h>
905b4d
 #include "nss_mc.h"
905b4d
 
905b4d
-struct sss_cli_mc_ctx pw_mc_ctx = { false, -1, 0, NULL, 0, NULL, 0, NULL, 0 };
905b4d
+struct sss_cli_mc_ctx pw_mc_ctx = { UNINITIALIZED, -1, 0, NULL, 0, NULL, 0,
905b4d
+                                    NULL, 0, 0 };
905b4d
 
905b4d
 static errno_t sss_nss_mc_parse_result(struct sss_mc_rec *rec,
905b4d
                                        struct passwd *result,
905b4d
@@ -170,6 +171,7 @@ errno_t sss_nss_mc_getpwnam(const char *name, size_t name_len,
905b4d
 
905b4d
 done:
905b4d
     free(rec);
905b4d
+    __sync_sub_and_fetch(&pw_mc_ctx.active_threads, 1);
905b4d
     return ret;
905b4d
 }
905b4d
 
905b4d
@@ -192,7 +194,8 @@ errno_t sss_nss_mc_getpwuid(uid_t uid,
905b4d
 
905b4d
     len = snprintf(uidstr, 11, "%ld", (long)uid);
905b4d
     if (len > 10) {
905b4d
-        return EINVAL;
905b4d
+        ret = EINVAL;
905b4d
+        goto done;
905b4d
     }
905b4d
 
905b4d
     /* hashes are calculated including the NULL terminator */
905b4d
@@ -236,6 +239,7 @@ errno_t sss_nss_mc_getpwuid(uid_t uid,
905b4d
 
905b4d
 done:
905b4d
     free(rec);
905b4d
+    __sync_sub_and_fetch(&pw_mc_ctx.active_threads, 1);
905b4d
     return ret;
905b4d
 }
905b4d
 
905b4d
-- 
905b4d
1.9.3
905b4d