Blame SOURCES/autofs-5.1.7-fix-inconsistent-locking-in-umount_subtree_mounts.patch

29d2b9
autofs-5.1.7 - fix inconsistent locking in umount_subtree_mounts()
29d2b9
29d2b9
From: Ian Kent <raven@themaw.net>
29d2b9
29d2b9
Some map entry cache locking inconsistencies have crept in.
29d2b9
29d2b9
In umount_subtree_mounts() the cache write lock should be held when
29d2b9
deleting multi-mount cache entries.
29d2b9
29d2b9
Signed-off-by: Ian Kent <raven@themaw.net>
29d2b9
---
29d2b9
 CHANGELOG          |    1 +
29d2b9
 daemon/automount.c |   42 ++++++++++++++++++++++++++++++------------
29d2b9
 lib/mounts.c       |    8 --------
29d2b9
 3 files changed, 31 insertions(+), 20 deletions(-)
29d2b9
29d2b9
diff --git a/CHANGELOG b/CHANGELOG
29d2b9
index 1dded118..64e619ec 100644
29d2b9
--- a/CHANGELOG
29d2b9
+++ b/CHANGELOG
29d2b9
@@ -15,6 +15,7 @@
29d2b9
 - eliminate clean_stale_multi_triggers().
29d2b9
 - simplify mount_subtree() mount check.
29d2b9
 - fix mnts_get_expire_list() expire list construction.
29d2b9
+- fix inconsistent locking in umount_subtree_mounts().
29d2b9
 
29d2b9
 25/01/2021 autofs-5.1.7
29d2b9
 - make bind mounts propagation slave by default.
29d2b9
diff --git a/daemon/automount.c b/daemon/automount.c
29d2b9
index 7fa92877..93bd8556 100644
29d2b9
--- a/daemon/automount.c
29d2b9
+++ b/daemon/automount.c
29d2b9
@@ -527,8 +527,11 @@ static int umount_subtree_mounts(struct autofs_point *ap, const char *path, unsi
29d2b9
 	struct mapent_cache *mc;
29d2b9
 	struct mapent *me;
29d2b9
 	unsigned int is_mm_root = 0;
29d2b9
+	int cur_state;
29d2b9
 	int left;
29d2b9
 
29d2b9
+	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
29d2b9
+
29d2b9
 	me = lookup_source_mapent(ap, path, LKP_DISTINCT);
29d2b9
 	if (!me) {
29d2b9
 		char *ind_key;
29d2b9
@@ -548,11 +551,11 @@ static int umount_subtree_mounts(struct autofs_point *ap, const char *path, unsi
29d2b9
 	left = 0;
29d2b9
 
29d2b9
 	if (me && me->multi) {
29d2b9
-		char root[PATH_MAX];
29d2b9
+		char root[PATH_MAX + 1];
29d2b9
+		char key[PATH_MAX + 1];
29d2b9
+		struct mapent *tmp;
29d2b9
+		int status;
29d2b9
 		char *base;
29d2b9
-		int cur_state;
29d2b9
-
29d2b9
-		pthread_cleanup_push(cache_lock_cleanup, mc);
29d2b9
 
29d2b9
 		if (!strchr(me->multi->key, '/'))
29d2b9
 			/* Indirect multi-mount root */
29d2b9
@@ -567,25 +570,40 @@ static int umount_subtree_mounts(struct autofs_point *ap, const char *path, unsi
29d2b9
 		else
29d2b9
 			base = me->key + strlen(root);
29d2b9
 
29d2b9
-		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
29d2b9
-		/* Lock the closest parent nesting point for umount */
29d2b9
-		cache_multi_writelock(me->parent);
29d2b9
-		if (umount_multi_triggers(ap, me, root, base)) {
29d2b9
+		left = umount_multi_triggers(ap, me, root, base);
29d2b9
+		if (left) {
29d2b9
 			warn(ap->logopt,
29d2b9
 			     "some offset mounts still present under %s", path);
29d2b9
+		}
29d2b9
+
29d2b9
+		strcpy(key, me->key);
29d2b9
+
29d2b9
+		cache_unlock(mc);
29d2b9
+		cache_writelock(mc);
29d2b9
+		tmp = cache_lookup_distinct(mc, key);
29d2b9
+		/* mapent went away while we waited? */
29d2b9
+		if (tmp != me) {
29d2b9
+			cache_unlock(mc);
29d2b9
+			pthread_setcancelstate(cur_state, NULL);
29d2b9
+			return 0;
29d2b9
+		}
29d2b9
+
29d2b9
+		if (!left && is_mm_root) {
29d2b9
+			status = cache_delete_offset_list(mc, me->key);
29d2b9
+			if (status != CHE_OK)
29d2b9
+				warn(ap->logopt, "couldn't delete offset list");
29d2b9
 			left++;
29d2b9
 		}
29d2b9
-		cache_multi_unlock(me->parent);
29d2b9
+
29d2b9
 		if (ap->entry->maps &&
29d2b9
 		    (ap->entry->maps->flags & MAP_FLAG_FORMAT_AMD))
29d2b9
 			cache_pop_mapent(me);
29d2b9
-		pthread_setcancelstate(cur_state, NULL);
29d2b9
-		pthread_cleanup_pop(0);
29d2b9
 	}
29d2b9
-
29d2b9
 	if (me)
29d2b9
 		cache_unlock(mc);
29d2b9
 
29d2b9
+	pthread_setcancelstate(cur_state, NULL);
29d2b9
+
29d2b9
 	if (left || is_autofs_fs)
29d2b9
 		return left;
29d2b9
 
29d2b9
diff --git a/lib/mounts.c b/lib/mounts.c
29d2b9
index 87813b16..5ebfe5fd 100644
29d2b9
--- a/lib/mounts.c
29d2b9
+++ b/lib/mounts.c
29d2b9
@@ -2736,9 +2736,6 @@ int umount_multi_triggers(struct autofs_point *ap, struct mapent *me, char *root
29d2b9
 	left = do_umount_multi_triggers(ap, me, root, base);
29d2b9
 
29d2b9
 	if (!left && me->multi == me) {
29d2b9
-		struct mapent_cache *mc = me->mc;
29d2b9
-		int status;
29d2b9
-
29d2b9
 		/*
29d2b9
 		 * Special case.
29d2b9
 		 * If we can't umount the root container then we can't
29d2b9
@@ -2756,11 +2753,6 @@ int umount_multi_triggers(struct autofs_point *ap, struct mapent *me, char *root
29d2b9
 			}
29d2b9
 		}
29d2b9
 
29d2b9
-		/* We're done - clean out the offsets */
29d2b9
-		status = cache_delete_offset_list(mc, me->key);
29d2b9
-		if (status != CHE_OK)
29d2b9
-			warn(ap->logopt, "couldn't delete offset list");
29d2b9
-
29d2b9
 	       /* check for mounted mount entry and remove it if found */
29d2b9
                mnts_remove_mount(root, MNTS_MOUNTED);
29d2b9
 	}