autofs-5.1.9 - fix deadlock in master_notify_submount()

From: Ian Kent <raven@themaw.net>

A deadlock between mnts_remove_submount() and master_notify_submount()
can occur because master_notify_submount() holds the state mutex over
a call to mnts_find_submount() which then needs to take mnts_hash_mutex.
But mnts_remove_submount() takes mnts_hash_mutex and then needs to take
the state mutex to clear the ->ap field so deadlock cann occur.

But it isn't necessary for master_notify_submount() to take the state
mutex before calling mnts_find_submount() because if the submount is'
found a reference is taken on the entry so it won't go away while it's
being used. All that's needed is to ensure that the ->ap field doesn't
get set to NULL by mnts_remove_submount() while it's being used to check
if the submount has shutdown.

Fixes: 81ac572466e3 ("autofs-5.1.9 - fix submount shutdown race")
Signed-off-by: Ian Kent <raven@themaw.net>
---
 CHANGELOG       |    1 +
 daemon/master.c |    7 +++----
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index f3363d5a7..23ef2889e 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -28,6 +28,7 @@
 - update per-mount expire timeout on readmap.
 - clear per-mount timeout if not set.
 - handle sss special case getautomntbyname() error.
+- fix deadlock in master_notify_submount().
 
 02/11/2023 autofs-5.1.9
 - fix kernel mount status notification.
diff --git a/daemon/master.c b/daemon/master.c
index bd7b16f90..ece06414b 100644
--- a/daemon/master.c
+++ b/daemon/master.c
@@ -1246,26 +1246,25 @@ int master_notify_submount(struct autofs_point *ap, const char *path, enum state
 		 * ST_SHUTDOWN_FORCE we need to wait until it goes away
 		 * or changes to state ST_SHUTDOWN or ST_READY.
 		 */
-		st_mutex_lock();
 		while ((sbmnt = mnts_find_submount(path))) {
 			struct timespec t = { 0, 300000000 };
 			struct timespec r;
 
+			st_mutex_lock();
 			if (!sbmnt->ap ||
 			   (sbmnt->ap->state != ST_SHUTDOWN_PENDING &&
 			    sbmnt->ap->state != ST_SHUTDOWN_FORCE)) {
 				ret = 0;
+				st_mutex_unlock();
 				mnts_put_mount(sbmnt);
 				break;
 			}
+			st_mutex_unlock();
 			mnts_put_mount(sbmnt);
 
-			st_mutex_unlock();
 			while (nanosleep(&t, &r) == -1 && errno == EINTR)
 				memcpy(&t, &r, sizeof(struct timespec));
-			st_mutex_lock();
 		}
-		st_mutex_unlock();
 done:
 		mnts_put_mount(this);
 	}
