Blame SOURCES/autofs-5.1.8-fix-deadlock-with-hosts-map-reload.patch

5a83b1
autofs-5.1.8 - fix deadlock with hosts map reload
5a83b1
5a83b1
From: Ian Kent <raven@themaw.net>
5a83b1
5a83b1
When reloading maps the hosts map calls lookup method ->parse_mount()
5a83b1
for each multi-mount root entry in the map (each host) while holding
5a83b1
the cache read lock which leads to a cache lock deadlock.
5a83b1
5a83b1
Remove the need to hold the cache read lock by creating an independent
5a83b1
list of entries for the update so the lock doesn't need to be taken.
5a83b1
5a83b1
Signed-off-by: Ian Kent <raven@themaw.net>
5a83b1
---
5a83b1
 CHANGELOG              |    1 
5a83b1
 modules/lookup_hosts.c |  100 ++++++++++++++++++++++++++++++++++++++++---------
5a83b1
 2 files changed, 83 insertions(+), 18 deletions(-)
5a83b1
5a83b1
--- autofs-5.1.7.orig/CHANGELOG
5a83b1
+++ autofs-5.1.7/CHANGELOG
5a83b1
@@ -108,6 +108,7 @@
5a83b1
 - serialise lookup module open and reinit.
5a83b1
 - coverity fix for invalid access.
5a83b1
 - fix hosts map deadlock on restart.
5a83b1
+- fix deadlock with hosts map reload.
5a83b1
 
5a83b1
 25/01/2021 autofs-5.1.7
5a83b1
 - make bind mounts propagation slave by default.
5a83b1
--- autofs-5.1.7.orig/modules/lookup_hosts.c
5a83b1
+++ autofs-5.1.7/modules/lookup_hosts.c
5a83b1
@@ -201,10 +201,72 @@ static int do_parse_mount(struct autofs_
5a83b1
 	return NSS_STATUS_SUCCESS;
5a83b1
 }
5a83b1
 
5a83b1
+struct update_context {
5a83b1
+	char *key;
5a83b1
+	int key_len;
5a83b1
+	char *entry;
5a83b1
+	struct update_context *next;
5a83b1
+};
5a83b1
+
5a83b1
+static int add_update_entry(struct update_context **entries, struct mapent *me)
5a83b1
+{
5a83b1
+	struct update_context *upd;
5a83b1
+	char *key, *ent;
5a83b1
+
5a83b1
+	key = strdup(me->key);
5a83b1
+	if (!key)
5a83b1
+		return 0;
5a83b1
+
5a83b1
+	ent = strdup(me->mapent);
5a83b1
+	if (!ent) {
5a83b1
+		free(key);
5a83b1
+		return 0;
5a83b1
+	}
5a83b1
+
5a83b1
+	upd = malloc(sizeof(struct update_context));
5a83b1
+	if (!upd) {
5a83b1
+		free(ent);
5a83b1
+		free(key);
5a83b1
+		return 0;
5a83b1
+	}
5a83b1
+
5a83b1
+	upd->key = key;
5a83b1
+	upd->key_len = me->len;
5a83b1
+	upd->entry = ent;
5a83b1
+	upd->next = NULL;
5a83b1
+	if (*entries)
5a83b1
+		(*entries)->next = upd;
5a83b1
+	*entries = upd;
5a83b1
+
5a83b1
+	return 1;
5a83b1
+}
5a83b1
+
5a83b1
+static void free_update_entries(struct update_context *entries)
5a83b1
+{
5a83b1
+	struct update_context *this = entries;
5a83b1
+
5a83b1
+	while (this) {
5a83b1
+		struct update_context *next = this->next;
5a83b1
+		free(this->key);
5a83b1
+		free(this->entry);
5a83b1
+		free(this);
5a83b1
+		this = next;
5a83b1
+	}
5a83b1
+}
5a83b1
+
5a83b1
+void entries_cleanup(void *arg)
5a83b1
+{
5a83b1
+	struct update_context *entries = arg;
5a83b1
+
5a83b1
+	free_update_entries(entries);
5a83b1
+}
5a83b1
+
5a83b1
 static void update_hosts_mounts(struct autofs_point *ap,
5a83b1
 				struct map_source *source, time_t age,
5a83b1
 				struct lookup_context *ctxt)
5a83b1
 {
5a83b1
+	struct update_context *head = NULL;
5a83b1
+	struct update_context *entries = NULL;
5a83b1
 	struct mapent_cache *mc;
5a83b1
 	struct mapent *me;
5a83b1
 	char *mapent;
5a83b1
@@ -212,6 +274,8 @@ static void update_hosts_mounts(struct a
5a83b1
 
5a83b1
 	mc = source->mc;
5a83b1
 
5a83b1
+	pthread_cleanup_push(entries_cleanup, head);
5a83b1
+
5a83b1
 	pthread_cleanup_push(cache_lock_cleanup, mc);
5a83b1
 	cache_writelock(mc);
5a83b1
 	me = cache_lookup_first(mc);
5a83b1
@@ -224,39 +288,39 @@ static void update_hosts_mounts(struct a
5a83b1
 
5a83b1
 		mapent = get_exports(ap, me->key);
5a83b1
 		if (mapent) {
5a83b1
-			cache_update(mc, source, me->key, mapent, age);
5a83b1
+			int ret;
5a83b1
+
5a83b1
+			ret = cache_update(mc, source, me->key, mapent, age);
5a83b1
 			free(mapent);
5a83b1
+			if (!IS_MM_ROOT(me))
5a83b1
+				goto next;
5a83b1
+			if (ret != CHE_FAIL) {
5a83b1
+				if (!add_update_entry(&entries, me))
5a83b1
+					warn(ap->logopt, MODPREFIX
5a83b1
+					     "failed to add update entry for %s", me->key);
5a83b1
+				else if (!head)
5a83b1
+					head = entries;
5a83b1
+			}
5a83b1
 		}
5a83b1
 next:
5a83b1
 		me = cache_lookup_next(mc, me);
5a83b1
 	}
5a83b1
 	pthread_cleanup_pop(1);
5a83b1
 
5a83b1
-	pthread_cleanup_push(cache_lock_cleanup, mc);
5a83b1
-	cache_readlock(mc);
5a83b1
-	me = cache_lookup_first(mc);
5a83b1
-	while (me) {
5a83b1
-		/*
5a83b1
-		 * Hosts map entry not yet expanded, already expired
5a83b1
-		 * or not the base of the tree
5a83b1
-		 */
5a83b1
-		if (!IS_MM(me) || !IS_MM_ROOT(me))
5a83b1
-			goto cont;
5a83b1
-
5a83b1
+	while (head) {
5a83b1
 		debug(ap->logopt, MODPREFIX
5a83b1
-		      "attempt to update exports for %s", me->key);
5a83b1
+		      "attempt to update exports for %s", head->key);
5a83b1
 
5a83b1
 		master_source_current_wait(ap->entry);
5a83b1
 		ap->entry->current = source;
5a83b1
 		ap->flags |= MOUNT_FLAG_REMOUNT;
5a83b1
-		ret = ctxt->parse->parse_mount(ap, me->key, strlen(me->key),
5a83b1
-					       me->mapent, ctxt->parse->context);
5a83b1
+		ret = ctxt->parse->parse_mount(ap, head->key, strlen(head->key),
5a83b1
+					       head->entry, ctxt->parse->context);
5a83b1
 		if (ret)
5a83b1
 			warn(ap->logopt, MODPREFIX
5a83b1
-			     "failed to parse mount %s", me->mapent);
5a83b1
+			     "failed to parse mount %s", head->entry);
5a83b1
 		ap->flags &= ~MOUNT_FLAG_REMOUNT;
5a83b1
-cont:
5a83b1
-		me = cache_lookup_next(mc, me);
5a83b1
+		head = head->next;
5a83b1
 	}
5a83b1
 	pthread_cleanup_pop(1);
5a83b1
 }