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

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