Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F143922817
D29372.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D29372.diff
View Options
diff --git a/lib/libc/net/nsdispatch.c b/lib/libc/net/nsdispatch.c
--- a/lib/libc/net/nsdispatch.c
+++ b/lib/libc/net/nsdispatch.c
@@ -69,6 +69,7 @@
#include <sys/param.h>
#include <sys/stat.h>
+#include <assert.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
@@ -76,6 +77,7 @@
#include <nsswitch.h>
#include <pthread.h>
#include <pthread_np.h>
+#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -97,7 +99,52 @@
* Global NSS data structures are mostly read-only, but we update
* them when we read or re-read the nsswitch.conf.
*/
-static pthread_rwlock_t nss_lock = PTHREAD_RWLOCK_INITIALIZER;
+static pthread_rwlock_t nss_lock = PTHREAD_RWLOCK_INITIALIZER;
+#ifndef NDEBUG
+static void *nss_wlock_owner __guarded_by(nss_lock);
+#endif
+
+static inline /* __lock_annotate(locks_excluded(nss_lock)) */
+__locks_exclusive(nss_lock) int
+nss_wlock(void)
+{
+ int err;
+
+ err = _pthread_rwlock_wrlock(&nss_lock);
+#ifndef NDEBUG
+ assert(nss_wlock_owner == NULL);
+ nss_wlock_owner = _pthread_self();
+#endif
+ assert(err == 0 && "Locking should not have failed!");
+ return (err);
+}
+
+/*
+ * XXX: The manpage says already held lock may result in EDEADLK, but
+ * actually libthr returns always EBUSY, so we need the extra owner
+ * variable for assertions.
+ */
+#define ASSERT_NSS_WLOCK_HELD() \
+ do { \
+ if (__isthreaded) { \
+ assert(_pthread_rwlock_trywrlock(&nss_lock) == EBUSY); \
+ assert(nss_wlock_owner == _pthread_self()); \
+ } \
+ } while (0)
+
+static inline __requires_exclusive(nss_lock) __unlocks(nss_lock) int
+nss_wunlock(void)
+{
+ int err;
+
+ ASSERT_NSS_WLOCK_HELD();
+#ifndef NDEBUG
+ nss_wlock_owner = NULL;
+#endif
+ err = _pthread_rwlock_unlock(&nss_lock);
+ assert(err == 0 && "Unlocking should not have failed!");
+ return (err);
+}
/*
* Runtime determination of whether we are dynamically linked or not.
@@ -114,12 +161,12 @@
};
/* Database, source mappings. */
-static unsigned int _nsmapsize;
-static ns_dbt *_nsmap = NULL;
+static unsigned int _nsmapsize __guarded_by(nss_lock);
+static ns_dbt *_nsmap __guarded_by(nss_lock);
/* NSS modules. */
-static unsigned int _nsmodsize;
-static ns_mod *_nsmod;
+static unsigned int _nsmodsize __guarded_by(nss_lock);
+static ns_mod *_nsmod __guarded_by(nss_lock);
/* Placeholder for builtin modules' dlopen `handle'. */
static int __nss_builtin_handle;
@@ -166,8 +213,7 @@
typedef void (*vector_free_elem)(void *);
static void vector_sort(void *, unsigned int, size_t,
vector_comparison);
-static void vector_free(void *, unsigned int *, size_t,
- vector_free_elem);
+static void _vector_free(void *, unsigned int, size_t, vector_free_elem);
static void *vector_ref(unsigned int, void *, unsigned int, size_t);
static void *vector_search(const void *, void *, unsigned int, size_t,
vector_comparison);
@@ -238,22 +284,24 @@
}
-#define VECTOR_FREE(v, c, s, f) \
- do { vector_free(v, c, s, f); v = NULL; } while (0)
+#define VECTOR_FREE(vec, count, es, fe) do { \
+ _vector_free(vec, count, es, fe); \
+ vec = NULL; \
+ count = 0; \
+} while (0)
static void
-vector_free(void *vec, unsigned int *count, size_t esize,
+_vector_free(void *vec, unsigned int count, size_t esize,
vector_free_elem free_elem)
{
unsigned int i;
void *elem;
- for (i = 0; i < *count; i++) {
- elem = vector_ref(i, vec, *count, esize);
+ for (i = 0; i < count; i++) {
+ elem = vector_ref(i, vec, count, esize);
if (elem != NULL)
free_elem(elem);
}
free(vec);
- *count = 0;
}
/*
@@ -282,13 +330,14 @@
/*
* NSS nsmap management.
*/
-void
+__requires_exclusive(nss_lock) void
_nsdbtaddsrc(ns_dbt *dbt, const ns_src *src)
{
const ns_mod *modp;
- dbt->srclist = vector_append(src, dbt->srclist, &dbt->srclistsize,
- sizeof(*src));
+ ASSERT_NSS_WLOCK_HELD();
+ dbt->srclist = vector_append(src, dbt->srclist,
+ (unsigned int *)&dbt->srclistsize, sizeof(*src));
modp = vector_search(&src->name, _nsmod, _nsmodsize, sizeof(*_nsmod),
string_compare);
if (modp == NULL)
@@ -325,26 +374,28 @@
}
#endif
+#ifndef NS_REREAD_CONF
+static int __guarded_by(nss_lock) already_initialized = 0;
+#endif
/*
* The first time nsdispatch is called (during a process's lifetime,
* or after nsswitch.conf has been updated), nss_configure will
* prepare global data needed by NSS.
*/
-static int
-nss_configure(void)
+static __requires_shared(nss_lock) int
+__lock_annotate(no_thread_safety_analysis) /* RWLock upgrade not supported. */
+do_nss_configure(void)
{
- static time_t confmod;
- static int already_initialized = 0;
+ static time_t __guarded_by(nss_lock) confmod = 0;
struct stat statbuf;
- int result, isthreaded;
+ int result;
const char *path;
#ifdef NS_CACHING
void *handle;
#endif
result = 0;
- isthreaded = __isthreaded;
#if defined(_NSS_DEBUG) && defined(_NSS_SHOOT_FOOT)
/* NOTE WELL: THIS IS A SECURITY HOLE. This must only be built
* for debugging purposes and MUST NEVER be used in production.
@@ -353,36 +404,33 @@
if (path == NULL)
#endif
path = _PATH_NS_CONF;
+
+ result = _pthread_rwlock_unlock(&nss_lock);
+ if (result != 0)
+ return (result);
+ result = nss_wlock();
+ if (result != 0)
+ return (result);
#ifndef NS_REREAD_CONF
/*
- * Define NS_REREAD_CONF to have nsswitch notice changes
- * to nsswitch.conf(5) during runtime. This involves calling
- * stat(2) every time, which can result in performance hit.
+ * Another thread could have already run the initialization
+ * logic while we were waiting for the write lock. Check
+ * already_initialized to avoid re-initializing.
*/
if (already_initialized)
- return (0);
- already_initialized = 1;
+ goto fin;
#endif /* NS_REREAD_CONF */
+ ASSERT_NSS_WLOCK_HELD();
if (stat(path, &statbuf) != 0)
- return (0);
+ goto fin;
if (statbuf.st_mtime <= confmod)
- return (0);
- if (isthreaded) {
- (void)_pthread_rwlock_unlock(&nss_lock);
- result = _pthread_rwlock_wrlock(&nss_lock);
- if (result != 0)
- return (result);
- if (stat(path, &statbuf) != 0)
- goto fin;
- if (statbuf.st_mtime <= confmod)
- goto fin;
- }
+ goto fin;
_nsyyin = fopen(path, "re");
if (_nsyyin == NULL)
goto fin;
- VECTOR_FREE(_nsmap, &_nsmapsize, sizeof(*_nsmap),
+ VECTOR_FREE(_nsmap, _nsmapsize, sizeof(*_nsmap),
(vector_free_elem)ns_dbt_free);
- VECTOR_FREE(_nsmod, &_nsmodsize, sizeof(*_nsmod),
+ VECTOR_FREE(_nsmod, _nsmodsize, sizeof(*_nsmod),
(vector_free_elem)ns_mod_free);
if (confmod == 0)
(void)atexit(nss_atexit);
@@ -400,22 +448,42 @@
dlclose(handle);
}
#endif
+#ifndef NS_REREAD_CONF
+ already_initialized = 1;
+#endif
fin:
- if (isthreaded) {
- (void)_pthread_rwlock_unlock(&nss_lock);
- if (result == 0)
- result = _pthread_rwlock_rdlock(&nss_lock);
- }
+ result = nss_wunlock();
+ if (result == 0)
+ result = _pthread_rwlock_rdlock(&nss_lock);
+ return (result);
+}
+
+static __requires_shared(nss_lock) int
+nss_configure(void)
+{
+ int result;
+#ifndef NS_REREAD_CONF
+ /*
+ * Define NS_REREAD_CONF to have nsswitch notice changes
+ * to nsswitch.conf(5) during runtime. This involves calling
+ * stat(2) every time, which can result in performance hit.
+ */
+ if (already_initialized)
+ return (0);
+#endif /* NS_REREAD_CONF */
+ result = do_nss_configure();
return (result);
}
-void
+__requires_exclusive(nss_lock) void
_nsdbtput(const ns_dbt *dbt)
{
unsigned int i;
ns_dbt *p;
+ ASSERT_NSS_WLOCK_HELD();
+
for (i = 0; i < _nsmapsize; i++) {
p = vector_ref(i, _nsmap, _nsmapsize, sizeof(*_nsmap));
if (string_compare(&dbt->name, &p->name) == 0) {
@@ -478,13 +546,15 @@
* argument is non-NULL, assume a built-in module and use reg_fn to
* register it. Otherwise, search for a dynamic NSS module.
*/
-static void
+static __requires_exclusive(nss_lock) void
nss_load_module(const char *source, nss_module_register_fn reg_fn)
{
char buf[PATH_MAX];
ns_mod mod;
nss_module_register_fn fn;
+ ASSERT_NSS_WLOCK_HELD();
+
memset(&mod, 0, sizeof(mod));
mod.name = strdup(source);
if (mod.name == NULL) {
@@ -548,9 +618,9 @@
vector_sort(_nsmod, _nsmodsize, sizeof(*_nsmod), string_compare);
}
-static int exiting = 0;
+static int exiting __guarded_by(nss_lock) = 0;
-static void
+static __requires_exclusive(nss_lock) void
ns_mod_free(ns_mod *mod)
{
@@ -569,24 +639,19 @@
static void
nss_atexit(void)
{
- int isthreaded;
-
+ (void)nss_wlock();
exiting = 1;
- isthreaded = __isthreaded;
- if (isthreaded)
- (void)_pthread_rwlock_wrlock(&nss_lock);
- VECTOR_FREE(_nsmap, &_nsmapsize, sizeof(*_nsmap),
+ VECTOR_FREE(_nsmap, _nsmapsize, sizeof(*_nsmap),
(vector_free_elem)ns_dbt_free);
- VECTOR_FREE(_nsmod, &_nsmodsize, sizeof(*_nsmod),
+ VECTOR_FREE(_nsmod, _nsmapsize, sizeof(*_nsmod),
(vector_free_elem)ns_mod_free);
- if (isthreaded)
- (void)_pthread_rwlock_unlock(&nss_lock);
+ (void)nss_wunlock();
}
/*
* Finally, the actual implementation.
*/
-static nss_method
+static __requires_shared(nss_lock) nss_method
nss_method_lookup(const char *source, const char *database,
const char *method, const ns_dtab disp_tab[], void **mdata)
{
@@ -625,7 +690,7 @@
__weak_reference(_nsdispatch, nsdispatch);
-int
+__requires_unlocked(nss_lock) int
_nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database,
const char *method_name, const ns_src defaults[], ...)
{
@@ -634,7 +699,7 @@
const ns_src *srclist;
nss_method method, fb_method;
void *mdata;
- int isthreaded, serrno, i, result, srclistsize;
+ int serrno, i, result, srclistsize;
struct fb_state *st;
int saved_depth;
@@ -647,15 +712,8 @@
dbt = NULL;
fb_method = NULL;
- isthreaded = __isthreaded;
serrno = errno;
- if (isthreaded) {
- result = _pthread_rwlock_rdlock(&nss_lock);
- if (result != 0) {
- result = NS_UNAVAIL;
- goto fin;
- }
- }
+ (void)_pthread_rwlock_rdlock(&nss_lock);
result = fb_getstate(&st);
if (result != 0) {
@@ -774,10 +832,9 @@
}
#endif /* NS_CACHING */
- if (isthreaded)
- (void)_pthread_rwlock_unlock(&nss_lock);
--st->dispatch_depth;
fin:
+ (void)_pthread_rwlock_unlock(&nss_lock);
errno = serrno;
return (result);
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Feb 3, 1:07 AM (16 h, 7 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28419546
Default Alt Text
D29372.diff (10 KB)
Attached To
Mode
D29372: lib/libc/net/nsdispatch.c: Fix missing unlock and add locking annotations
Attached
Detach File
Event Timeline
Log In to Comment