Linux RAID subsystem development
 help / color / mirror / Atom feed
* [PATCH] mdadm: fix dangling file descriptor when calling Manage_ro
@ 2026-10-27 14:04 Abd-Alrhman Masalkhi
  0 siblings, 0 replies; only message in thread
From: Abd-Alrhman Masalkhi @ 2026-10-27 14:04 UTC (permalink / raw)
  To: blazej.kucman, mtkaczyk, ncroxon, xni, song, yukuai
  Cc: linux-raid, Abd-Alrhman Masalkhi

When managing an externally managed array, Manage_ro() closes the file
descriptor and writes the read-only state to the array's array_state
sysfs file. Since the file descriptor is passed by value, the caller
retains a descriptor that refers to a closed file. Any subsequent
attempt to use it will fail.

In such cases, when specifying --stop alongside --readonly, mdadm fails
to stop the array and reports "Cannot get exclusive access"

Fixes: 1b4b73fd535a ("mdadm: Manage.c fix coverity issues")
Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
---
 Manage.c | 32 +++++++++++++++++++++++++-------
 mdadm.c  | 18 +++++++++++++-----
 mdadm.h  |  3 ++-
 3 files changed, 40 insertions(+), 13 deletions(-)

diff --git a/Manage.c b/Manage.c
index 40d52cfc913b..b0ba1e31bcb0 100644
--- a/Manage.c
+++ b/Manage.c
@@ -30,7 +30,15 @@
 
 #include <ctype.h>
 
-int Manage_ro(char *devname, int fd, int readonly)
+/*
+ * Switch the array to read-only (readonly > 0) or read-write (readonly < 0).
+ *
+ * Switching to read-only may close *fd: writing "readonly" to array_state
+ * and the control node both need zero openers. *fd is then set to -1.
+ * Switching to read-write never closes *fd, it is better to use Manage_rw()
+ * for it.
+ */
+int Manage_ro(char *devname, int *fd, int readonly)
 {
 	/* switch to readonly or rw
 	 *
@@ -45,7 +53,7 @@ int Manage_ro(char *devname, int fd, int readonly)
 	/* If this is an externally-managed array, we need to modify the
 	 * metadata_version so that mdmon doesn't undo our change.
 	 */
-	mdi = sysfs_read(fd, NULL, GET_LEVEL|GET_VERSION);
+	mdi = sysfs_read(*fd, NULL, GET_LEVEL|GET_VERSION);
 	if (mdi &&
 	    mdi->array.major_version == -1 &&
 	    is_subarray(mdi->text_version)) {
@@ -58,7 +66,7 @@ int Manage_ro(char *devname, int fd, int readonly)
 			vers[MD_VER_BLOCKED_IDX] = '-';
 			sysfs_set_str(mdi, NULL, "metadata_version", vers);
 
-			close_fd(&fd);
+			close_fd(fd);
 			rv = sysfs_set_str(mdi, NULL, "array_state", "readonly");
 
 			if (rv < 0) {
@@ -86,21 +94,21 @@ int Manage_ro(char *devname, int fd, int readonly)
 		goto out;
 	}
 
-	if (!md_array_active(fd)) {
+	if (!md_array_active(*fd)) {
 		pr_err("%s does not appear to be active.\n", devname);
 		rv = 1;
 		goto out;
 	}
 
 	if (readonly > 0) {
-		if (ioctl(fd, STOP_ARRAY_RO, NULL)) {
+		if (ioctl(*fd, STOP_ARRAY_RO, NULL)) {
 			pr_err("failed to set readonly for %s: %s\n",
 				devname, strerror(errno));
 			rv = 1;
 			goto out;
 		}
 	} else if (readonly < 0) {
-		if (ioctl(fd, RESTART_ARRAY_RW, NULL)) {
+		if (ioctl(*fd, RESTART_ARRAY_RW, NULL)) {
 			pr_err("failed to set writable for %s: %s\n",
 				devname, strerror(errno));
 			rv = 1;
@@ -112,6 +120,16 @@ out:
 	return rv;
 }
 
+int Manage_rw(char *devname, int fd)
+{
+	int tmp = fd;
+	int rv = Manage_ro(devname, &tmp, -1);
+
+	/* read-write must not close the caller's fd */
+	assert(tmp == fd);
+	return rv;
+}
+
 static void remove_devices(char *devnm, char *path)
 {
 	/*
@@ -1072,7 +1090,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv,
 		}
 		if (dv->disposition == 'j') {
 			pr_err("Journal added successfully, making %s read-write\n", devname);
-			if (Manage_ro(devname, fd, -1))
+			if (Manage_rw(devname, fd))
 				pr_err("Failed to make %s read-write\n", devname);
 		}
 
diff --git a/mdadm.c b/mdadm.c
index 2517b037c1bc..98f352afb66d 100644
--- a/mdadm.c
+++ b/mdadm.c
@@ -1453,13 +1453,21 @@ int main(int argc, char *argv[])
 	case MANAGE:
 		/* readonly, add/remove, readwrite, runstop */
 		if (c.readonly > 0)
-			rv = Manage_ro(ident.devname, mdfd, c.readonly);
+			rv = Manage_ro(ident.devname, &mdfd, c.readonly);
+
+		/* Manage_ro() might have closed the md device if needed. */
+		if (!rv && mdfd < 0 && (devs_found > 1 || c.runstop)) {
+			mdfd = open_mddev(ident.devname, 1);
+			if (mdfd < 0)
+				rv = 1;
+		}
+
 		if (!rv && devs_found > 1)
 			rv = Manage_subdevs(ident.devname, mdfd,
 					    devlist->next, c.verbose,
 					    c.test, c.update, c.force);
 		if (!rv && c.readonly < 0)
-			rv = Manage_ro(ident.devname, mdfd, c.readonly);
+			rv = Manage_rw(ident.devname, mdfd);
 		if (!rv && c.runstop > 0)
 			rv = Manage_run(ident.devname, mdfd, &c);
 		if (!rv && c.runstop < 0)
@@ -2013,13 +2021,13 @@ static int misc_list(struct mddev_dev *devlist,
 				rv |= Manage_stop(dv->devname, mdfd, c->verbose, 0);
 				break;
 			case 'o':
-				rv |= Manage_ro(dv->devname, mdfd, 1);
+				rv |= Manage_ro(dv->devname, &mdfd, 1);
 				break;
 			case 'w':
-				rv |= Manage_ro(dv->devname, mdfd, -1);
+				rv |= Manage_rw(dv->devname, mdfd);
 				break;
 			}
-			close(mdfd);
+			close_fd(&mdfd);
 		} else
 			rv |= 1;
 	}
diff --git a/mdadm.h b/mdadm.h
index deac7a56f6f8..e2f725e01f9b 100644
--- a/mdadm.h
+++ b/mdadm.h
@@ -1427,7 +1427,8 @@ struct stat64;
 
 extern int add_dev(const char *name, const struct stat *stb, int flag, struct FTW *s);
 
-extern int Manage_ro(char *devname, int fd, int readonly);
+extern int Manage_ro(char *devname, int *fd, int readonly);
+extern int Manage_rw(char *devname, int fd);
 extern int Manage_run(char *devname, int fd, struct context *c);
 extern int Manage_stop(char *devname, int fd, int quiet,
 		       int will_retry);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-10-27 14:04 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-27 14:04 [PATCH] mdadm: fix dangling file descriptor when calling Manage_ro Abd-Alrhman Masalkhi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox