Blame SOURCES/0013-multipathd-fix-ev_remove_path-return-code-handling.patch

68b27c
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
68b27c
From: Benjamin Marzinski <bmarzins@redhat.com>
68b27c
Date: Tue, 11 May 2021 13:58:57 -0500
68b27c
Subject: [PATCH] multipathd: fix ev_remove_path return code handling
68b27c
68b27c
When ev_remove_path() returned success, callers assumed that the path
68b27c
(and possibly the map) had been removed.  When ev_remove_path() returned
68b27c
failure, callers assumed that the path had not been removed. However,
68b27c
the path could be removed on both success or failure. This could cause
68b27c
callers to dereference the path after it was removed.
68b27c
68b27c
To deal with this, make ev_remove_path() return a different symbolic
68b27c
value for each outcome, and make the callers react appropriately for
68b27c
the different values. Found by coverity.
68b27c
68b27c
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
68b27c
---
68b27c
 multipathd/cli_handlers.c | 24 +++++++++++++++++++++--
68b27c
 multipathd/main.c         | 41 ++++++++++++++++++++-------------------
68b27c
 multipathd/main.h         | 14 +++++++++++++
68b27c
 3 files changed, 57 insertions(+), 22 deletions(-)
68b27c
68b27c
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
68b27c
index 1de6ad8e..6765fcf0 100644
68b27c
--- a/multipathd/cli_handlers.c
68b27c
+++ b/multipathd/cli_handlers.c
68b27c
@@ -752,7 +752,8 @@ cli_add_path (void * v, char ** reply, int * len, void * data)
68b27c
 				/* Have the checker reinstate this path asap */
68b27c
 				pp->tick = 1;
68b27c
 				return 0;
68b27c
-			} else if (!ev_remove_path(pp, vecs, true))
68b27c
+			} else if (ev_remove_path(pp, vecs, true) &
68b27c
+				   REMOVE_PATH_SUCCESS)
68b27c
 				/* Path removed in ev_remove_path() */
68b27c
 				pp = NULL;
68b27c
 			else {
68b27c
@@ -813,6 +814,7 @@ cli_del_path (void * v, char ** reply, int * len, void * data)
68b27c
 	struct vectors * vecs = (struct vectors *)data;
68b27c
 	char * param = get_keyparam(v, PATH);
68b27c
 	struct path *pp;
68b27c
+	int ret;
68b27c
 
68b27c
 	param = convert_dev(param, 1);
68b27c
 	condlog(2, "%s: remove path (operator)", param);
68b27c
@@ -821,7 +823,25 @@ cli_del_path (void * v, char ** reply, int * len, void * data)
68b27c
 		condlog(0, "%s: path already removed", param);
68b27c
 		return 1;
68b27c
 	}
68b27c
-	return ev_remove_path(pp, vecs, 1);
68b27c
+	ret = ev_remove_path(pp, vecs, 1);
68b27c
+	if (ret == REMOVE_PATH_DELAY) {
68b27c
+		*reply = strdup("delayed\n");
68b27c
+		if (*reply)
68b27c
+			*len = strlen(*reply) + 1;
68b27c
+		else {
68b27c
+			*len = 0;
68b27c
+			ret = REMOVE_PATH_FAILURE;
68b27c
+		}
68b27c
+	} else if (ret == REMOVE_PATH_MAP_ERROR) {
68b27c
+		*reply = strdup("map reload error. removed\n");
68b27c
+		if (*reply)
68b27c
+			*len = strlen(*reply) + 1;
68b27c
+		else {
68b27c
+			*len = 0;
68b27c
+			ret = REMOVE_PATH_FAILURE;
68b27c
+		}
68b27c
+	}
68b27c
+	return (ret == REMOVE_PATH_FAILURE);
68b27c
 }
68b27c
 
68b27c
 int
68b27c
diff --git a/multipathd/main.c b/multipathd/main.c
68b27c
index 6090434c..8e2beddd 100644
68b27c
--- a/multipathd/main.c
68b27c
+++ b/multipathd/main.c
68b27c
@@ -838,7 +838,7 @@ handle_path_wwid_change(struct path *pp, struct vectors *vecs)
68b27c
 		return;
68b27c
 
68b27c
 	udd = udev_device_ref(pp->udev);
68b27c
-	if (ev_remove_path(pp, vecs, 1) != 0 && pp->mpp) {
68b27c
+	if (!(ev_remove_path(pp, vecs, 1) & REMOVE_PATH_SUCCESS) && pp->mpp) {
68b27c
 		pp->dmstate = PSTATE_FAILED;
68b27c
 		dm_fail_path(pp->mpp->alias, pp->dev_t);
68b27c
 	}
68b27c
@@ -948,8 +948,8 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
68b27c
 				 * Make another attempt to remove the path
68b27c
 				 */
68b27c
 				pp->mpp = prev_mpp;
68b27c
-				ret = ev_remove_path(pp, vecs, true);
68b27c
-				if (ret != 0) {
68b27c
+				if (!(ev_remove_path(pp, vecs, true) &
68b27c
+				      REMOVE_PATH_SUCCESS)) {
68b27c
 					/*
68b27c
 					 * Failure in ev_remove_path will keep
68b27c
 					 * path in pathvec in INIT_REMOVED state
68b27c
@@ -960,6 +960,7 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
68b27c
 					dm_fail_path(pp->mpp->alias, pp->dev_t);
68b27c
 					condlog(1, "%s: failed to re-add path still mapped in %s",
68b27c
 						pp->dev, pp->mpp->alias);
68b27c
+					ret = 1;
68b27c
 				} else if (r == PATHINFO_OK)
68b27c
 					/*
68b27c
 					 * Path successfully freed, move on to
68b27c
@@ -1167,7 +1168,6 @@ static int
68b27c
 uev_remove_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
68b27c
 {
68b27c
 	struct path *pp;
68b27c
-	int ret;
68b27c
 
68b27c
 	condlog(3, "%s: remove path (uevent)", uev->kernel);
68b27c
 	delete_foreign(uev->udev);
68b27c
@@ -1177,21 +1177,18 @@ uev_remove_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
68b27c
 	pthread_testcancel();
68b27c
 	pp = find_path_by_dev(vecs->pathvec, uev->kernel);
68b27c
 	if (pp)
68b27c
-		ret = ev_remove_path(pp, vecs, need_do_map);
68b27c
+		ev_remove_path(pp, vecs, need_do_map);
68b27c
 	lock_cleanup_pop(vecs->lock);
68b27c
-	if (!pp) {
68b27c
-		/* Not an error; path might have been purged earlier */
68b27c
+	if (!pp) /* Not an error; path might have been purged earlier */
68b27c
 		condlog(0, "%s: path already removed", uev->kernel);
68b27c
-		return 0;
68b27c
-	}
68b27c
-	return ret;
68b27c
+	return 0;
68b27c
 }
68b27c
 
68b27c
 int
68b27c
 ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
68b27c
 {
68b27c
 	struct multipath * mpp;
68b27c
-	int i, retval = 0;
68b27c
+	int i, retval = REMOVE_PATH_SUCCESS;
68b27c
 	char params[PARAMS_SIZE] = {0};
68b27c
 
68b27c
 	/*
68b27c
@@ -1245,7 +1242,6 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
68b27c
 				condlog(2, "%s: removed map after"
68b27c
 					" removing all paths",
68b27c
 					alias);
68b27c
-				retval = 0;
68b27c
 				/* flush_map() has freed the path */
68b27c
 				goto out;
68b27c
 			}
68b27c
@@ -1262,11 +1258,14 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
68b27c
 
68b27c
 		if (mpp->wait_for_udev) {
68b27c
 			mpp->wait_for_udev = 2;
68b27c
+			retval = REMOVE_PATH_DELAY;
68b27c
 			goto out;
68b27c
 		}
68b27c
 
68b27c
-		if (!need_do_map)
68b27c
+		if (!need_do_map) {
68b27c
+			retval = REMOVE_PATH_DELAY;
68b27c
 			goto out;
68b27c
+		}
68b27c
 		/*
68b27c
 		 * reload the map
68b27c
 		 */
68b27c
@@ -1275,7 +1274,7 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
68b27c
 			condlog(0, "%s: failed in domap for "
68b27c
 				"removal of path %s",
68b27c
 				mpp->alias, pp->dev);
68b27c
-			retval = 1;
68b27c
+			retval = REMOVE_PATH_FAILURE;
68b27c
 		} else {
68b27c
 			/*
68b27c
 			 * update our state from kernel
68b27c
@@ -1283,12 +1282,12 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
68b27c
 			char devt[BLK_DEV_SIZE];
68b27c
 
68b27c
 			strlcpy(devt, pp->dev_t, sizeof(devt));
68b27c
+
68b27c
+			/* setup_multipath will free the path
68b27c
+			 * regardless of whether it succeeds or
68b27c
+			 * fails */
68b27c
 			if (setup_multipath(vecs, mpp))
68b27c
-				return 1;
68b27c
-			/*
68b27c
-			 * Successful map reload without this path:
68b27c
-			 * sync_map_state() will free it.
68b27c
-			 */
68b27c
+				return REMOVE_PATH_MAP_ERROR;
68b27c
 			sync_map_state(mpp);
68b27c
 
68b27c
 			condlog(2, "%s: path removed from map %s",
68b27c
@@ -1304,8 +1303,10 @@ out:
68b27c
 	return retval;
68b27c
 
68b27c
 fail:
68b27c
+	condlog(0, "%s: error removing path. removing map %s", pp->dev,
68b27c
+		mpp->alias);
68b27c
 	remove_map_and_stop_waiter(mpp, vecs);
68b27c
-	return 1;
68b27c
+	return REMOVE_PATH_MAP_ERROR;
68b27c
 }
68b27c
 
68b27c
 static int
68b27c
diff --git a/multipathd/main.h b/multipathd/main.h
68b27c
index ddd953f9..bc1f938f 100644
68b27c
--- a/multipathd/main.h
68b27c
+++ b/multipathd/main.h
68b27c
@@ -13,6 +13,20 @@ enum daemon_status {
68b27c
 	DAEMON_STATUS_SIZE,
68b27c
 };
68b27c
 
68b27c
+enum remove_path_result {
68b27c
+	REMOVE_PATH_FAILURE = 0x0, /* path could not be removed. It is still
68b27c
+				    * part of the kernel map, but its state
68b27c
+				    * is set to INIT_REMOVED, and it will be
68b27c
+				    * removed at the next possible occassion */
68b27c
+	REMOVE_PATH_SUCCESS = 0x1, /* path was removed */
68b27c
+	REMOVE_PATH_DELAY = 0x2, /* path is set to be removed later. it
68b27c
+			          * currently still exists and is part of the
68b27c
+			          * kernel map */
68b27c
+	REMOVE_PATH_MAP_ERROR = 0x5, /* map was removed because of error. value
68b27c
+				      * includes REMOVE_PATH_SUCCESS bit
68b27c
+				      * because the path was also removed */
68b27c
+};
68b27c
+
68b27c
 struct prout_param_descriptor;
68b27c
 struct prin_resp;
68b27c