public inbox for dm-devel@redhat.com
 help / color / mirror / Atom feed
* [PATCH 00/21] Multipath-tools: various bug fixes
@ 2025-12-17 21:20 Martin Wilck
  2025-12-17 21:20 ` [PATCH 01/21] libmultipath: drop drop_multipath Martin Wilck
                   ` (21 more replies)
  0 siblings, 22 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:20 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

This series contains a number of fixes for various recent issues with
multipath-tools. The starting point was a use-after-free issue reported on
GitHub [1]. The actual fixes for that are 02/21 and 06/21. Because this
Patches 3-12 generally rework the freeing of maps, trying to avoid unexpected
freeing of paths while freeing multipath structures.

Because this changes memory handling in multipathd, I ran a set of tests to
make sure the series doesn't open up new memory leaks. The good news is that I
haven't found any, except some trivial ones (15/21, 16/21). But I did see one
minor issue related to libudev [2]. After I found a warning in the libudev man
page about the library not being thread-safe, I suspected that this might be
causing the leak, and came up with code wrapping all libudev calls with a
mutex (18/21, 19/21).  Unfortunately it didn't fix the observed leak, but I
suppose it's still useful because multipathd is using libudev in a way that
the authors of the library explicitly dismiss as unsupported.

The release of cmocka 2.0 [3] necessitated rather large-ish adaptations in our
unit test code (20/21, 21/21).

Finally 13/21 and 17/21 are bug fixes; in particular the latter is rather nasty.

[1] https://github.com/opensvc/multipath-tools/issues/128
[2] https://github.com/opensvc/multipath-tools/issues/130
[3] https://github.com/opensvc/multipath-tools/issues/129

Martin Wilck (21):
  libmultipath: drop drop_multipath
  libmultipath: don't access path members in free_pgvec()
  multipathd: free paths in checker_finished()
  libmultipath: don't touch mpvec in remove_map()
  libmpathutil: constify find_slot()
  libmultipath: don't free paths in orphan_paths()
  libmultipath: free orphaned paths in check_removed_paths()
  libmultipath: remove free_paths argument from free_pathgroup()
  libmultipath: fix numeric value of free_paths in free_multipaths()
  libmultipath: remove free_paths argument from free_pgvec()
  libmultipath: remove free_paths argument from free_multipathvec()
  libmultipath: free_multipath: fix FREE_PATHS case
  multipath-tools: Fix ISO C23 errors with strchr()
  libmultipath: simplify sysfs_get_target_nodename()
  multipathd: join the init_unwinder dummy thread
  kpartx: fix some memory leaks
  libmpathutil: use union for bitfield
  libmpathutil: add wrapper code for libudev
  multipath-tools: use the libudev wrapper functions
  Makefile: add functionality to determine cmocka version
  multipath-tools tests: adaptations for cmocka 2.0

 Makefile.inc                          |   2 +-
 create-config.mk                      |   5 +
 kpartx/kpartx.c                       |  18 +-
 libdmmp/Makefile                      |   2 +-
 libdmmp/libdmmp.c                     |   2 +-
 libmpathpersist/mpath_persist.c       |   2 +-
 libmpathpersist/mpath_persist_int.c   |   2 +-
 libmpathpersist/mpath_pr_ioctl.c      |   2 +-
 libmpathpersist/mpath_updatepr.c      |   2 +-
 libmpathutil/Makefile                 |   2 +-
 libmpathutil/globals.c                |   2 +-
 libmpathutil/libmpathutil.version     |  62 ++
 libmpathutil/mt-libudev.c             | 776 ++++++++++++++++++++++++++
 libmpathutil/mt-libudev.h             | 120 ++++
 libmpathutil/mt-udev-wrap.h           |  90 +++
 libmpathutil/parser.c                 |   2 +-
 libmpathutil/util.c                   |  12 +-
 libmpathutil/util.h                   |  43 +-
 libmpathutil/vector.c                 |   3 +-
 libmpathutil/vector.h                 |   2 +-
 libmpathvalid/mpath_valid.c           |   2 +-
 libmultipath/blacklist.c              |   2 +-
 libmultipath/blacklist.h              |   2 +-
 libmultipath/config.c                 |   2 +-
 libmultipath/configure.c              |  22 +-
 libmultipath/dict.c                   |   2 +-
 libmultipath/discovery.c              |  36 +-
 libmultipath/dmparser.c               |   6 +-
 libmultipath/foreign.c                |   2 +-
 libmultipath/foreign.h                |   2 +-
 libmultipath/foreign/nvme.c           |   2 +-
 libmultipath/libmultipath.version     |   2 +
 libmultipath/pgpolicies.c             |  14 +-
 libmultipath/print.c                  |   2 +-
 libmultipath/prio.c                   |   2 +-
 libmultipath/prioritizers/alua_rtpg.c |   2 +-
 libmultipath/prioritizers/ana.c       |   2 +-
 libmultipath/prkey.c                  |   4 +-
 libmultipath/prkey.h                  |   2 +-
 libmultipath/propsel.c                |   2 +-
 libmultipath/structs.c                |  94 ++--
 libmultipath/structs.h                |   7 +-
 libmultipath/structs_vec.c            |  58 +-
 libmultipath/structs_vec.h            |   4 +-
 libmultipath/sysfs.c                  |   2 +-
 libmultipath/uevent.c                 |   2 +-
 libmultipath/valid.c                  |   2 +-
 mpathpersist/main.c                   |   2 +-
 multipath/main.c                      |  16 +-
 multipathd/cli_handlers.c             |   2 +-
 multipathd/fpin_handlers.c            |   2 +-
 multipathd/init_unwinder.c            |   4 +-
 multipathd/main.c                     |  20 +-
 tests/Makefile                        |  22 +-
 tests/alias.c                         |  44 +-
 tests/blacklist.c                     |   2 +-
 tests/cli.c                           |   8 +-
 tests/cmocka-compat.h                 |  16 +
 tests/devt.c                          |   6 +-
 tests/directio.c                      |  23 +-
 tests/dmevents.c                      |  74 +--
 tests/features.c                      |   2 +-
 tests/hwtable.c                       |   6 +-
 tests/mapinfo.c                       |  82 +--
 tests/mpathvalid.c                    |  18 +-
 tests/parser.c                        |   2 +-
 tests/pgpolicy.c                      |   4 +-
 tests/strbuf.c                        | 130 ++---
 tests/sysfs.c                         |  76 +--
 tests/test-lib.c                      |  90 +--
 tests/test-log.c                      |  10 +-
 tests/uevent.c                        |   2 +-
 tests/unaligned.c                     |   8 +-
 tests/util.c                          | 116 ++--
 tests/valid.c                         |  30 +-
 tests/vpd.c                           |  12 +-
 76 files changed, 1680 insertions(+), 581 deletions(-)
 create mode 100644 libmpathutil/mt-libudev.c
 create mode 100644 libmpathutil/mt-libudev.h
 create mode 100644 libmpathutil/mt-udev-wrap.h
 create mode 100644 tests/cmocka-compat.h

-- 
2.52.0


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 01/21] libmultipath: drop drop_multipath
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
@ 2025-12-17 21:20 ` Martin Wilck
  2025-12-17 21:20 ` [PATCH 02/21] libmultipath: don't access path members in free_pgvec() Martin Wilck
                   ` (20 subsequent siblings)
  21 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:20 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

This function is unused.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/structs.c | 18 ------------------
 libmultipath/structs.h |  1 -
 2 files changed, 19 deletions(-)

diff --git a/libmultipath/structs.c b/libmultipath/structs.c
index dfa547b..1cbbea8 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -353,24 +353,6 @@ void cleanup_multipath_and_paths(struct multipath **pmpp)
 		free_multipath(*pmpp, FREE_PATHS);
 }
 
-void
-drop_multipath (vector mpvec, char * wwid, enum free_path_mode free_paths)
-{
-	int i;
-	struct multipath * mpp;
-
-	if (!mpvec)
-		return;
-
-	vector_foreach_slot (mpvec, mpp, i) {
-		if (!strncmp(mpp->wwid, wwid, WWID_SIZE)) {
-			free_multipath(mpp, free_paths);
-			vector_del_slot(mpvec, i);
-			return;
-		}
-	}
-}
-
 void
 free_multipathvec (vector mpvec, enum free_path_mode free_paths)
 {
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index 9247e74..81c69b4 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -583,7 +583,6 @@ void free_multipath (struct multipath *, enum free_path_mode free_paths);
 void cleanup_multipath(struct multipath **pmpp);
 void cleanup_multipath_and_paths(struct multipath **pmpp);
 void free_multipath_attributes (struct multipath *);
-void drop_multipath (vector mpvec, char * wwid, enum free_path_mode free_paths);
 void free_multipathvec (vector mpvec, enum free_path_mode free_paths);
 
 struct adapter_group * alloc_adaptergroup(void);
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 02/21] libmultipath: don't access path members in free_pgvec()
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
  2025-12-17 21:20 ` [PATCH 01/21] libmultipath: drop drop_multipath Martin Wilck
@ 2025-12-17 21:20 ` Martin Wilck
  2025-12-17 21:20 ` [PATCH 03/21] multipathd: free paths in checker_finished() Martin Wilck
                   ` (19 subsequent siblings)
  21 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:20 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

It can happen that a path group contains references to paths that are
already freed. If we access pp->pgindex in such a case, a use-after-free
occurs. In particular, this occurs if we call remove_map() from configure()
-> coalesce_paths(), and some paths are freed in orphan_paths() because
they are in INIT_PARTIAL or INIT_REMOVED state. The paths of the removed
map may still be referenced by maps in the "old" mpvec in configure(). The
UAF occurs when configure() calls remove_maps(vecs).

This code has been introduced in cd912cf ("libmultipath: fix handling of
pp->pgindex"). The commit message stated: "The hunk in group_paths is
mostly redundant with the hunk in free_pgvec(), but because we're looping
over pg->paths in the former and over pg->pgp in the latter, I think it's
better too play safe". It turns out that it would have been better not
to "play safe" here. No caller of free_pgvec() requires the pgindex to
be reset. For the functions in pgpolicies.c, the reset of pgindex is
redundant, as remarked in cd912cf. disassemble_map() will set the pgindex
later anyway, and update_multipath_strings() will call disassemble_map().
The other callers remove the struct multipath, in which case the paths
will eventually be orphaned or freed (except for the special case of
coalesce_paths() mentioned above).

Fixes: cd912cf ("libmultipath: fix handling of pp->pgindex")

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/structs.c | 12 +-----------
 1 file changed, 1 insertion(+), 11 deletions(-)

diff --git a/libmultipath/structs.c b/libmultipath/structs.c
index 1cbbea8..e6e673f 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -239,18 +239,8 @@ free_pgvec (vector pgvec, enum free_path_mode free_paths)
 	if (!pgvec)
 		return;
 
-	vector_foreach_slot(pgvec, pgp, i) {
-
-		/* paths are going to be re-grouped, reset pgindex */
-		if (free_paths != FREE_PATHS) {
-			struct path *pp;
-			int j;
-
-			vector_foreach_slot(pgp->paths, pp, j)
-				pp->pgindex = 0;
-		}
+	vector_foreach_slot(pgvec, pgp, i)
 		free_pathgroup(pgp, free_paths);
-	}
 
 	vector_free(pgvec);
 }
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 03/21] multipathd: free paths in checker_finished()
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
  2025-12-17 21:20 ` [PATCH 01/21] libmultipath: drop drop_multipath Martin Wilck
  2025-12-17 21:20 ` [PATCH 02/21] libmultipath: don't access path members in free_pgvec() Martin Wilck
@ 2025-12-17 21:20 ` Martin Wilck
  2025-12-18 23:34   ` Benjamin Marzinski
  2025-12-17 21:20 ` [PATCH 04/21] libmultipath: don't touch mpvec in remove_map() Martin Wilck
                   ` (18 subsequent siblings)
  21 siblings, 1 reply; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:20 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

Modifying the pathvec in a deep call stack is unexpected and
error-prone. Free paths in the checkerloop instead, after
having synced all maps.

Suggested-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/libmultipath.version | 1 +
 libmultipath/structs_vec.c        | 7 +++----
 libmultipath/structs_vec.h        | 1 +
 multipathd/main.c                 | 1 +
 4 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
index 89ae2a3..e5b7b83 100644
--- a/libmultipath/libmultipath.version
+++ b/libmultipath/libmultipath.version
@@ -58,6 +58,7 @@ global:
 	change_foreign;
 	check_alias_settings;
 	check_daemon;
+	check_removed_paths;
 	checker_clear_message;
 	checker_disable;
 	checker_enable;
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index f651b29..3a9ab58 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -573,16 +573,16 @@ static struct path *find_devt_in_pathgroups(const struct multipath *mpp,
 	return NULL;
 }
 
-static void check_removed_paths(const struct multipath *mpp, vector pathvec)
+void check_removed_paths(vector pathvec)
 {
 	struct path *pp;
 	int i;
 
 	vector_foreach_slot(pathvec, pp, i) {
-		if (pp->mpp == mpp &&
+		if (pp->mpp &&
 		    (pp->initialized == INIT_REMOVED ||
 		     pp->initialized == INIT_PARTIAL) &&
-		    !find_devt_in_pathgroups(mpp, pp->dev_t)) {
+		    !find_devt_in_pathgroups(pp->mpp, pp->dev_t)) {
 			condlog(2, "%s: %s: freeing path in %s state",
 				__func__, pp->dev,
 				pp->initialized == INIT_REMOVED ?
@@ -615,7 +615,6 @@ void sync_paths(struct multipath *mpp, vector pathvec)
 			orphan_path(pp, "path removed externally");
 		}
 	}
-	check_removed_paths(mpp, pathvec);
 	update_mpp_paths(mpp, pathvec);
 	vector_foreach_slot (mpp->paths, pp, i)
 		pp->mpp = mpp;
diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h
index 1188ada..cb02d75 100644
--- a/libmultipath/structs_vec.h
+++ b/libmultipath/structs_vec.h
@@ -18,6 +18,7 @@ int adopt_paths (vector pathvec, struct multipath *mpp,
 void orphan_path (struct path * pp, const char *reason);
 void set_path_removed(struct path *pp);
 
+void check_removed_paths(vector pathvec);
 int verify_paths(struct multipath *mpp);
 int update_mpp_paths(struct multipath * mpp, vector pathvec);
 int update_multipath_strings (struct multipath *mpp, vector pathvec);
diff --git a/multipathd/main.c b/multipathd/main.c
index d3bf4d0..49a1f25 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -3127,6 +3127,7 @@ static void checker_finished(struct vectors *vecs, unsigned int ticks)
 	}
 	if (uev_timed_out && !need_to_delay_reconfig(vecs))
 		unblock_reconfigure();
+	check_removed_paths(vecs->pathvec);
 	partial_retrigger_tick(vecs->pathvec);
 }
 
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 04/21] libmultipath: don't touch mpvec in remove_map()
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (2 preceding siblings ...)
  2025-12-17 21:20 ` [PATCH 03/21] multipathd: free paths in checker_finished() Martin Wilck
@ 2025-12-17 21:20 ` Martin Wilck
  2025-12-17 21:20 ` [PATCH 05/21] libmpathutil: constify find_slot() Martin Wilck
                   ` (17 subsequent siblings)
  21 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:20 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

Move the removal of an mpp from the mpvec into a separate function,
remove_map_from_mpvec(). There should be no functional differences
so far.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/configure.c          | 14 +++++++-------
 libmultipath/libmultipath.version |  1 +
 libmultipath/structs_vec.c        | 26 +++++++++++++-------------
 libmultipath/structs_vec.h        |  3 ++-
 multipath/main.c                  |  8 ++++----
 multipathd/main.c                 | 17 ++++++++++-------
 6 files changed, 37 insertions(+), 32 deletions(-)

diff --git a/libmultipath/configure.c b/libmultipath/configure.c
index baa1357..2d324ad 100644
--- a/libmultipath/configure.c
+++ b/libmultipath/configure.c
@@ -1140,7 +1140,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
 
 		if (!mpp->paths) {
 			condlog(0, "%s: skip coalesce (no paths)", mpp->alias);
-			remove_map(mpp, vecs->pathvec, NULL);
+			remove_map(mpp, vecs->pathvec);
 			continue;
 		}
 
@@ -1172,7 +1172,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
 		if (cmd == CMD_DRY_RUN && mpp->action == ACT_UNDEF)
 			mpp->action = ACT_DRY_RUN;
 		if (setup_map(mpp, &params, vecs)) {
-			remove_map(mpp, vecs->pathvec, NULL);
+			remove_map(mpp, vecs->pathvec);
 			continue;
 		}
 
@@ -1192,7 +1192,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
 				condlog(2, "%s: %s map",
 					mpp->alias, (mpp->action == ACT_CREATE)?
 					"ignoring" : "removing");
-				remove_map(mpp, vecs->pathvec, NULL);
+				remove_map(mpp, vecs->pathvec);
 				continue;
 			} else /* if (r == DOMAP_RETRY && !is_daemon) */ {
 				ret = CP_RETRY;
@@ -1201,7 +1201,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
 		}
 		if (r == DOMAP_DRY) {
 			if (!vector_alloc_slot(newmp)) {
-				remove_map(mpp, vecs->pathvec, NULL);
+				remove_map(mpp, vecs->pathvec);
 				goto out;
 			}
 			vector_set_slot(newmp, mpp);
@@ -1224,20 +1224,20 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
 
 		if (mpp->action != ACT_REJECT) {
 			if (!vector_alloc_slot(newmp)) {
-				remove_map(mpp, vecs->pathvec, NULL);
+				remove_map(mpp, vecs->pathvec);
 				goto out;
 			}
 			vector_set_slot(newmp, mpp);
 		}
 		else
-			remove_map(mpp, vecs->pathvec, NULL);
+			remove_map(mpp, vecs->pathvec);
 	}
 	ret = CP_OK;
 out:
 	free(size_mismatch_seen);
 	if (!mpvec) {
 		vector_foreach_slot (newmp, mpp, i)
-			remove_map(mpp, vecs->pathvec, NULL);
+			remove_map(mpp, vecs->pathvec);
 		vector_free(newmp);
 	}
 	return ret;
diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
index e5b7b83..9282ff2 100644
--- a/libmultipath/libmultipath.version
+++ b/libmultipath/libmultipath.version
@@ -160,6 +160,7 @@ global:
 	remove_map;
 	remove_map_by_alias;
 	remove_map_callback;
+	remove_map_from_mpvec;
 	remove_maps;
 	remove_wwid;
 	replace_wwids;
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 3a9ab58..7f5da21 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -420,11 +420,16 @@ void remove_map_callback(struct multipath *mpp __attribute__((unused)))
 {
 }
 
-void
-remove_map(struct multipath *mpp, vector pathvec, vector mpvec)
+void remove_map_from_mpvec(const struct multipath *mpp, vector mpvec)
 {
-	int i;
+	int i = find_slot(mpvec, mpp);
 
+	if (i != -1)
+		vector_del_slot(mpvec, i);
+}
+
+void remove_map(struct multipath *mpp, vector pathvec)
+{
 	remove_map_callback(mpp);
 
 	free_pathvec(mpp->paths, KEEP_PATHS);
@@ -436,13 +441,6 @@ remove_map(struct multipath *mpp, vector pathvec, vector mpvec)
 	 */
 	orphan_paths(pathvec, mpp, "map removed internally");
 
-	if (mpvec &&
-	    (i = find_slot(mpvec, (void *)mpp)) != -1)
-		vector_del_slot(mpvec, i);
-
-	/*
-	 * final free
-	 */
 	free_multipath(mpp, KEEP_PATHS);
 }
 
@@ -452,7 +450,8 @@ remove_map_by_alias(const char *alias, struct vectors * vecs)
 	struct multipath * mpp = find_mp_by_alias(vecs->mpvec, alias);
 	if (mpp) {
 		condlog(2, "%s: removing map by alias", alias);
-		remove_map(mpp, vecs->pathvec, vecs->mpvec);
+		remove_map_from_mpvec(mpp, vecs->mpvec);
+		remove_map(mpp, vecs->pathvec);
 	}
 }
 
@@ -466,7 +465,7 @@ remove_maps(struct vectors * vecs)
 		return;
 
 	vector_foreach_slot (vecs->mpvec, mpp, i)
-		remove_map(mpp, vecs->pathvec, NULL);
+		remove_map(mpp, vecs->pathvec);
 
 	vector_free(vecs->mpvec);
 	vecs->mpvec = NULL;
@@ -831,7 +830,8 @@ struct multipath *add_map_with_path(struct vectors *vecs, struct path *pp,
 	return mpp;
 
 out:
-	remove_map(mpp, vecs->pathvec, vecs->mpvec);
+	remove_map_from_mpvec(mpp, vecs->mpvec);
+	remove_map(mpp, vecs->pathvec);
 	return NULL;
 }
 
diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h
index cb02d75..89e4213 100644
--- a/libmultipath/structs_vec.h
+++ b/libmultipath/structs_vec.h
@@ -24,7 +24,8 @@ int update_mpp_paths(struct multipath * mpp, vector pathvec);
 int update_multipath_strings (struct multipath *mpp, vector pathvec);
 void extract_hwe_from_path(struct multipath * mpp);
 
-void remove_map (struct multipath *mpp, vector pathvec, vector mpvec);
+void remove_map_from_mpvec(const struct multipath *mpp, vector mpvec);
+void remove_map(struct multipath *mpp, vector pathvec);
 void remove_map_by_alias(const char *alias, struct vectors * vecs);
 void remove_maps (struct vectors * vecs);
 
diff --git a/multipath/main.c b/multipath/main.c
index f2adcde..58db288 100644
--- a/multipath/main.c
+++ b/multipath/main.c
@@ -181,15 +181,15 @@ get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid)
 		if (refwwid && strlen(refwwid) &&
 		    strncmp(mpp->wwid, refwwid, WWID_SIZE)) {
 			condlog(3, "skip map %s: out of scope", mpp->alias);
-			remove_map(mpp, pathvec, curmp);
-			i--;
+			vector_del_slot(curmp, i--);
+			remove_map(mpp, pathvec);
 			continue;
 		}
 
 		if (update_multipath_table(mpp, pathvec, flags) != DMP_OK) {
 			condlog(1, "error parsing map %s", mpp->wwid);
-			remove_map(mpp, pathvec, curmp);
-			i--;
+			vector_del_slot(curmp, i--);
+			remove_map(mpp, pathvec);
 			continue;
 		}
 
diff --git a/multipathd/main.c b/multipathd/main.c
index 49a1f25..2043ff3 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -480,7 +480,8 @@ remove_map_and_stop_waiter(struct multipath *mpp, struct vectors *vecs)
 	condlog(3, "%s: removing map from internal tables", mpp->alias);
 	if (!poll_dmevents)
 		stop_waiter_thread(mpp);
-	remove_map(mpp, vecs->pathvec, vecs->mpvec);
+	remove_map_from_mpvec(mpp, vecs->mpvec);
+	remove_map(mpp, vecs->pathvec);
 }
 
 static void
@@ -745,7 +746,8 @@ retry:
 fail:
 	if (new_map && wait_for_events(mpp, vecs)) {
 		condlog(0, "%s: failed to create new map", mpp->alias);
-		remove_map(mpp, vecs->pathvec, vecs->mpvec);
+		remove_map_from_mpvec(mpp, vecs->mpvec);
+		remove_map(mpp, vecs->pathvec);
 		return 1;
 	}
 
@@ -1439,7 +1441,8 @@ rescan:
 		goto fail;
 
 fail_map:
-	remove_map(mpp, vecs->pathvec, vecs->mpvec);
+	remove_map_from_mpvec(mpp, vecs->mpvec);
+	remove_map(mpp, vecs->pathvec);
 fail:
 	orphan_path(pp, "failed to add path");
 	return 1;
@@ -1837,8 +1840,8 @@ map_discovery (struct vectors * vecs)
 
 	vector_foreach_slot (vecs->mpvec, mpp, i)
 		if (update_multipath_table(mpp, vecs->pathvec, DI_DISCOVERY) != DMP_OK) {
-			remove_map(mpp, vecs->pathvec, vecs->mpvec);
-			i--;
+			vector_del_slot(vecs->mpvec, i--);
+			remove_map(mpp, vecs->pathvec);
 		}
 
 	return 0;
@@ -3353,8 +3356,8 @@ configure (struct vectors * vecs, enum force_reload_types reload_type)
 	 */
 	vector_foreach_slot(vecs->mpvec, mpp, i) {
 		if (wait_for_events(mpp, vecs)) {
-			remove_map(mpp, vecs->pathvec, vecs->mpvec);
-			i--;
+			vector_del_slot(vecs->mpvec, i--);
+			remove_map(mpp, vecs->pathvec);
 			continue;
 		}
 		if (setup_multipath(vecs, mpp))
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 05/21] libmpathutil: constify find_slot()
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (3 preceding siblings ...)
  2025-12-17 21:20 ` [PATCH 04/21] libmultipath: don't touch mpvec in remove_map() Martin Wilck
@ 2025-12-17 21:20 ` Martin Wilck
  2025-12-17 21:20 ` [PATCH 06/21] libmultipath: don't free paths in orphan_paths() Martin Wilck
                   ` (16 subsequent siblings)
  21 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:20 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmpathutil/vector.c | 3 +--
 libmpathutil/vector.h | 2 +-
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/libmpathutil/vector.c b/libmpathutil/vector.c
index 3d54ed9..3e9f941 100644
--- a/libmpathutil/vector.c
+++ b/libmpathutil/vector.c
@@ -81,8 +81,7 @@ vector_insert_slot(vector v, int slot, void *value)
 	return v->slot[slot];
 }
 
-int
-find_slot(vector v, void * addr)
+int find_slot(vector v, const void *addr)
 {
 	int i;
 
diff --git a/libmpathutil/vector.h b/libmpathutil/vector.h
index 0701277..7b2c559 100644
--- a/libmpathutil/vector.h
+++ b/libmpathutil/vector.h
@@ -74,7 +74,7 @@ extern void free_strvec(vector strvec);
 extern void vector_set_slot(vector v, void *value);
 extern void vector_del_slot(vector v, int slot);
 extern void *vector_insert_slot(vector v, int slot, void *value);
-int find_slot(vector v, void * addr);
+int find_slot(vector v, const void *addr);
 int vector_find_or_add_slot(vector v, void *value);
 extern void vector_dump(vector v);
 extern void dump_strvec(vector strvec);
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 06/21] libmultipath: don't free paths in orphan_paths()
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (4 preceding siblings ...)
  2025-12-17 21:20 ` [PATCH 05/21] libmpathutil: constify find_slot() Martin Wilck
@ 2025-12-17 21:20 ` Martin Wilck
  2025-12-17 21:20 ` [PATCH 07/21] libmultipath: free orphaned paths in check_removed_paths() Martin Wilck
                   ` (15 subsequent siblings)
  21 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:20 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

remove_map() should just do what the name says, remove the map.
It is unexpected that it also frees the paths of the map, which
can happen if these paths are in INIT_PARTIAL or INIT_REMOVED
state. This is particularly wrong if remove_map is called from a code path
where other data structures are still holding references to the paths in
question, in particular configure() -> coalesce_paths().

Don't free paths in orphan_paths(). A follow-up patch will make sure
that these paths are freed from the checker loop.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/structs.c     |  4 ++++
 libmultipath/structs_vec.c | 15 +++------------
 2 files changed, 7 insertions(+), 12 deletions(-)

diff --git a/libmultipath/structs.c b/libmultipath/structs.c
index e6e673f..7df5562 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -139,6 +139,10 @@ alloc_path (void)
 	return pp;
 }
 
+/*
+ * we don't set pp->initialized here.
+ * check_removed_path() relies on it being preserved.
+ */
 void
 uninitialize_path(struct path *pp)
 {
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 7f5da21..3344005 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -383,18 +383,9 @@ static void orphan_paths(vector pathvec, struct multipath *mpp, const char *reas
 	struct path * pp;
 
 	vector_foreach_slot (pathvec, pp, i) {
-		if (pp->mpp == mpp) {
-			if (pp->initialized == INIT_REMOVED ||
-			    pp->initialized == INIT_PARTIAL) {
-				condlog(3, "%s: freeing path in %s state",
-					pp->dev,
-					pp->initialized == INIT_REMOVED ?
-					"removed" : "partial");
-				vector_del_slot(pathvec, i--);
-				free_path(pp);
-			} else
-				orphan_path(pp, reason);
-		} else if (pp->add_when_online &&
+		if (pp->mpp == mpp)
+			orphan_path(pp, reason);
+		else if (pp->add_when_online &&
 			   strncmp(mpp->wwid, pp->wwid, WWID_SIZE) == 0) {
 			pp->add_when_online = false;
 		}
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 07/21] libmultipath: free orphaned paths in check_removed_paths()
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (5 preceding siblings ...)
  2025-12-17 21:20 ` [PATCH 06/21] libmultipath: don't free paths in orphan_paths() Martin Wilck
@ 2025-12-17 21:20 ` Martin Wilck
  2025-12-17 21:21 ` [PATCH 08/21] libmultipath: remove free_paths argument from free_pathgroup() Martin Wilck
                   ` (14 subsequent siblings)
  21 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:20 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

We free paths in INIT_REMOVED or INIT_PARTIAL states in check_removed_paths
if they have an associated multipath map but are not part of any PG.
We might as well also free such paths if they aren't member of any map
in the first place. This can happen now, because we don't free such paths
in remove_map -> orphan_paths() any more.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/structs_vec.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 3344005..58c785e 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -569,10 +569,10 @@ void check_removed_paths(vector pathvec)
 	int i;
 
 	vector_foreach_slot(pathvec, pp, i) {
-		if (pp->mpp &&
-		    (pp->initialized == INIT_REMOVED ||
+		if ((pp->initialized == INIT_REMOVED ||
 		     pp->initialized == INIT_PARTIAL) &&
-		    !find_devt_in_pathgroups(pp->mpp, pp->dev_t)) {
+		    (!pp->mpp ||
+		     !find_devt_in_pathgroups(pp->mpp, pp->dev_t))) {
 			condlog(2, "%s: %s: freeing path in %s state",
 				__func__, pp->dev,
 				pp->initialized == INIT_REMOVED ?
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 08/21] libmultipath: remove free_paths argument from free_pathgroup()
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (6 preceding siblings ...)
  2025-12-17 21:20 ` [PATCH 07/21] libmultipath: free orphaned paths in check_removed_paths() Martin Wilck
@ 2025-12-17 21:21 ` Martin Wilck
  2025-12-17 21:21 ` [PATCH 09/21] libmultipath: fix numeric value of free_paths in free_multipaths() Martin Wilck
                   ` (13 subsequent siblings)
  21 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:21 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

free_pathgroup() is always called with free_paths=KEEP_PATHS,
except from free_pgvec(). Remove the argument.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/configure.c   | 2 +-
 libmultipath/dmparser.c    | 2 +-
 libmultipath/pgpolicies.c  | 6 +++---
 libmultipath/structs.c     | 8 ++++----
 libmultipath/structs.h     | 2 +-
 libmultipath/structs_vec.c | 2 +-
 6 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/libmultipath/configure.c b/libmultipath/configure.c
index 2d324ad..a8e18a2 100644
--- a/libmultipath/configure.c
+++ b/libmultipath/configure.c
@@ -375,7 +375,7 @@ int setup_map(struct multipath *mpp, char **params, struct vectors *vecs)
 	 */
 	if (mpp->pg) {
 		vector_foreach_slot (mpp->pg, pgp, i)
-			free_pathgroup(pgp, KEEP_PATHS);
+			free_pathgroup(pgp);
 
 		vector_free(mpp->pg);
 		mpp->pg = NULL;
diff --git a/libmultipath/dmparser.c b/libmultipath/dmparser.c
index c8c47e0..e0151ed 100644
--- a/libmultipath/dmparser.c
+++ b/libmultipath/dmparser.c
@@ -261,7 +261,7 @@ int disassemble_map(const struct vector_s *pathvec,
 			goto out;
 
 		if (add_pathgroup(mpp, pgp)) {
-			free_pathgroup(pgp, KEEP_PATHS);
+			free_pathgroup(pgp);
 			goto out;
 		}
 
diff --git a/libmultipath/pgpolicies.c b/libmultipath/pgpolicies.c
index 23ef2bd..d7302a9 100644
--- a/libmultipath/pgpolicies.c
+++ b/libmultipath/pgpolicies.c
@@ -252,7 +252,7 @@ int group_by_match(struct multipath * mp, vector paths,
 	free(bitmap);
 	return 0;
 out2:
-	free_pathgroup(pgp, KEEP_PATHS);
+	free_pathgroup(pgp);
 out1:
 	free(bitmap);
 out:
@@ -314,7 +314,7 @@ int one_path_per_group(struct multipath *mp, vector paths)
 	}
 	return 0;
 out1:
-	free_pathgroup(pgp, KEEP_PATHS);
+	free_pathgroup(pgp);
 out:
 	free_pgvec(mp->pg, KEEP_PATHS);
 	mp->pg = NULL;
@@ -343,7 +343,7 @@ int one_group(struct multipath *mp, vector paths)	/* aka multibus */
 	}
 	return 0;
 out1:
-	free_pathgroup(pgp, KEEP_PATHS);
+	free_pathgroup(pgp);
 out:
 	free_pgvec(mp->pg, KEEP_PATHS);
 	mp->pg = NULL;
diff --git a/libmultipath/structs.c b/libmultipath/structs.c
index 7df5562..aaa7e50 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -225,17 +225,17 @@ alloc_pathgroup (void)
 }
 
 void
-free_pathgroup (struct pathgroup * pgp, enum free_path_mode free_paths)
+free_pathgroup (struct pathgroup *pgp)
 {
 	if (!pgp)
 		return;
 
-	free_pathvec(pgp->paths, free_paths);
+	free_pathvec(pgp->paths, KEEP_PATHS);
 	free(pgp);
 }
 
 void
-free_pgvec (vector pgvec, enum free_path_mode free_paths)
+free_pgvec (vector pgvec, enum free_path_mode free_paths __attribute__((unused)))
 {
 	int i;
 	struct pathgroup * pgp;
@@ -244,7 +244,7 @@ free_pgvec (vector pgvec, enum free_path_mode free_paths)
 		return;
 
 	vector_foreach_slot(pgvec, pgp, i)
-		free_pathgroup(pgp, free_paths);
+		free_pathgroup(pgp);
 
 	vector_free(pgvec);
 }
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index 81c69b4..8fe74c2 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -577,8 +577,8 @@ void *set_mpp_hwe(struct multipath *mpp, const struct path *pp);
 void uninitialize_path(struct path *pp);
 void free_path (struct path *);
 void free_pathvec (vector vec, enum free_path_mode free_paths);
-void free_pathgroup (struct pathgroup * pgp, enum free_path_mode free_paths);
 void free_pgvec (vector pgvec, enum free_path_mode free_paths);
+void free_pathgroup(struct pathgroup *pgp);
 void free_multipath (struct multipath *, enum free_path_mode free_paths);
 void cleanup_multipath(struct multipath **pmpp);
 void cleanup_multipath_and_paths(struct multipath **pmpp);
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 58c785e..f3f47d4 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -254,7 +254,7 @@ static void update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
 	delete_pg:
 		condlog(2, "%s: removing empty pathgroup %d", mpp->alias, i);
 		vector_del_slot(mpp->pg, i--);
-		free_pathgroup(pgp, KEEP_PATHS);
+		free_pathgroup(pgp);
 		must_reload = true;
 		/* Invalidate pgindex for all other pathgroups */
 		pg_deleted = true;
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 09/21] libmultipath: fix numeric value of free_paths in free_multipaths()
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (7 preceding siblings ...)
  2025-12-17 21:21 ` [PATCH 08/21] libmultipath: remove free_paths argument from free_pathgroup() Martin Wilck
@ 2025-12-17 21:21 ` Martin Wilck
  2025-12-19  0:10   ` Benjamin Marzinski
  2025-12-17 21:21 ` [PATCH 10/21] libmultipath: remove free_paths argument from free_pgvec() Martin Wilck
                   ` (12 subsequent siblings)
  21 siblings, 1 reply; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:21 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

free_paths is an enum, use the enum value.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/structs.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libmultipath/structs.c b/libmultipath/structs.c
index aaa7e50..059d454 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -310,7 +310,7 @@ free_multipath (struct multipath * mpp, enum free_path_mode free_paths)
 		mpp->alias = NULL;
 	}
 
-	if (!free_paths && mpp->pg) {
+	if (free_paths == KEEP_PATHS && mpp->pg) {
 		struct pathgroup *pgp;
 		struct path *pp;
 		int i, j;
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 10/21] libmultipath: remove free_paths argument from free_pgvec()
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (8 preceding siblings ...)
  2025-12-17 21:21 ` [PATCH 09/21] libmultipath: fix numeric value of free_paths in free_multipaths() Martin Wilck
@ 2025-12-17 21:21 ` Martin Wilck
  2025-12-17 21:21 ` [PATCH 11/21] libmultipath: remove free_paths argument from free_multipathvec() Martin Wilck
                   ` (11 subsequent siblings)
  21 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:21 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

free_pgvec is always called with free_paths=KEEP_PATHS, except
from free_multipath(), which will be fixed in a follow-up patch.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/dmparser.c    | 4 ++--
 libmultipath/pgpolicies.c  | 6 +++---
 libmultipath/structs.c     | 4 ++--
 libmultipath/structs.h     | 2 +-
 libmultipath/structs_vec.c | 4 ++--
 tests/pgpolicy.c           | 2 +-
 6 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/libmultipath/dmparser.c b/libmultipath/dmparser.c
index e0151ed..3411bd0 100644
--- a/libmultipath/dmparser.c
+++ b/libmultipath/dmparser.c
@@ -205,7 +205,7 @@ int disassemble_map(const struct vector_s *pathvec,
 				return 1;
 		}
 	} else {
-		free_pgvec(mpp->pg, KEEP_PATHS);
+		free_pgvec(mpp->pg);
 		mpp->pg = NULL;
 	}
 
@@ -336,7 +336,7 @@ int disassemble_map(const struct vector_s *pathvec,
 out1:
 	free(word);
 out:
-	free_pgvec(mpp->pg, KEEP_PATHS);
+	free_pgvec(mpp->pg);
 	mpp->pg = NULL;
 	return 1;
 }
diff --git a/libmultipath/pgpolicies.c b/libmultipath/pgpolicies.c
index d7302a9..2e1a543 100644
--- a/libmultipath/pgpolicies.c
+++ b/libmultipath/pgpolicies.c
@@ -256,7 +256,7 @@ out2:
 out1:
 	free(bitmap);
 out:
-	free_pgvec(mp->pg, KEEP_PATHS);
+	free_pgvec(mp->pg);
 	mp->pg = NULL;
 	return 1;
 }
@@ -316,7 +316,7 @@ int one_path_per_group(struct multipath *mp, vector paths)
 out1:
 	free_pathgroup(pgp);
 out:
-	free_pgvec(mp->pg, KEEP_PATHS);
+	free_pgvec(mp->pg);
 	mp->pg = NULL;
 	return 1;
 }
@@ -345,7 +345,7 @@ int one_group(struct multipath *mp, vector paths)	/* aka multibus */
 out1:
 	free_pathgroup(pgp);
 out:
-	free_pgvec(mp->pg, KEEP_PATHS);
+	free_pgvec(mp->pg);
 	mp->pg = NULL;
 	return 1;
 }
diff --git a/libmultipath/structs.c b/libmultipath/structs.c
index 059d454..7b5b483 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -235,7 +235,7 @@ free_pathgroup (struct pathgroup *pgp)
 }
 
 void
-free_pgvec (vector pgvec, enum free_path_mode free_paths __attribute__((unused)))
+free_pgvec (vector pgvec)
 {
 	int i;
 	struct pathgroup * pgp;
@@ -326,7 +326,7 @@ free_multipath (struct multipath * mpp, enum free_path_mode free_paths)
 	}
 
 	free_pathvec(mpp->paths, free_paths);
-	free_pgvec(mpp->pg, free_paths);
+	free_pgvec(mpp->pg);
 	if (mpp->hwe) {
 		vector_free(mpp->hwe);
 		mpp->hwe = NULL;
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index 8fe74c2..3a52c01 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -577,8 +577,8 @@ void *set_mpp_hwe(struct multipath *mpp, const struct path *pp);
 void uninitialize_path(struct path *pp);
 void free_path (struct path *);
 void free_pathvec (vector vec, enum free_path_mode free_paths);
-void free_pgvec (vector pgvec, enum free_path_mode free_paths);
 void free_pathgroup(struct pathgroup *pgp);
+void free_pgvec(vector pgvec);
 void free_multipath (struct multipath *, enum free_path_mode free_paths);
 void cleanup_multipath(struct multipath **pmpp);
 void cleanup_multipath_and_paths(struct multipath **pmpp);
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index f3f47d4..c23e754 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -424,7 +424,7 @@ void remove_map(struct multipath *mpp, vector pathvec)
 	remove_map_callback(mpp);
 
 	free_pathvec(mpp->paths, KEEP_PATHS);
-	free_pgvec(mpp->pg, KEEP_PATHS);
+	free_pgvec(mpp->pg);
 	mpp->paths = mpp->pg = NULL;
 
 	/*
@@ -623,7 +623,7 @@ update_multipath_strings(struct multipath *mpp, vector pathvec)
 	condlog(4, "%s: %s", mpp->alias, __FUNCTION__);
 
 	free_multipath_attributes(mpp);
-	free_pgvec(mpp->pg, KEEP_PATHS);
+	free_pgvec(mpp->pg);
 	mpp->pg = NULL;
 
 	r = update_multipath_table(mpp, pathvec, 0);
diff --git a/tests/pgpolicy.c b/tests/pgpolicy.c
index fb956d1..ed050fa 100644
--- a/tests/pgpolicy.c
+++ b/tests/pgpolicy.c
@@ -131,7 +131,7 @@ static int setup_null(void **state)
 
 static int teardownX(struct multipath *mp)
 {
-	free_pgvec(mp->pg, KEEP_PATHS);
+	free_pgvec(mp->pg);
 	mp->pg = NULL;
 	return 0;
 }
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 11/21] libmultipath: remove free_paths argument from free_multipathvec()
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (9 preceding siblings ...)
  2025-12-17 21:21 ` [PATCH 10/21] libmultipath: remove free_paths argument from free_pgvec() Martin Wilck
@ 2025-12-17 21:21 ` Martin Wilck
  2025-12-17 21:21 ` [PATCH 12/21] libmultipath: free_multipath: fix FREE_PATHS case Martin Wilck
                   ` (10 subsequent siblings)
  21 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:21 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

free_multipathvec() is always called with KEEP_PATHS=free_paths.
Remove the argument.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmpathpersist/mpath_persist.c | 2 +-
 libmultipath/structs.c          | 4 ++--
 libmultipath/structs.h          | 2 +-
 multipath/main.c                | 6 +++---
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index f5267eb..e786e91 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -76,7 +76,7 @@ static vector pathvec;
 
 static void mpath_persistent_reserve_free_vecs__(vector curmp, vector pathvec)
 {
-	free_multipathvec(curmp, KEEP_PATHS);
+	free_multipathvec(curmp);
 	free_pathvec(pathvec, FREE_PATHS);
 }
 
diff --git a/libmultipath/structs.c b/libmultipath/structs.c
index 7b5b483..1ee29f6 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -348,7 +348,7 @@ void cleanup_multipath_and_paths(struct multipath **pmpp)
 }
 
 void
-free_multipathvec (vector mpvec, enum free_path_mode free_paths)
+free_multipathvec (vector mpvec)
 {
 	int i;
 	struct multipath * mpp;
@@ -357,7 +357,7 @@ free_multipathvec (vector mpvec, enum free_path_mode free_paths)
 		return;
 
 	vector_foreach_slot (mpvec, mpp, i)
-		free_multipath(mpp, free_paths);
+		free_multipath(mpp, KEEP_PATHS);
 
 	vector_free(mpvec);
 }
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index 3a52c01..738bea6 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -583,7 +583,7 @@ void free_multipath (struct multipath *, enum free_path_mode free_paths);
 void cleanup_multipath(struct multipath **pmpp);
 void cleanup_multipath_and_paths(struct multipath **pmpp);
 void free_multipath_attributes (struct multipath *);
-void free_multipathvec (vector mpvec, enum free_path_mode free_paths);
+void free_multipathvec(vector mpvec);
 
 struct adapter_group * alloc_adaptergroup(void);
 struct host_group * alloc_hostgroup(void);
diff --git a/multipath/main.c b/multipath/main.c
index 58db288..4b8d7dd 100644
--- a/multipath/main.c
+++ b/multipath/main.c
@@ -436,7 +436,7 @@ static bool released_to_systemd(void)
 static struct vectors vecs;
 static void cleanup_vecs(void)
 {
-	free_multipathvec(vecs.mpvec, KEEP_PATHS);
+	free_multipathvec(vecs.mpvec);
 	free_pathvec(vecs.pathvec, FREE_PATHS);
 }
 
@@ -580,9 +580,9 @@ out:
 	if (refwwid)
 		free(refwwid);
 
-	free_multipathvec(curmp, KEEP_PATHS);
+	free_multipathvec(curmp);
 	vecs.mpvec = NULL;
-	free_multipathvec(newmp, KEEP_PATHS);
+	free_multipathvec(newmp);
 	free_pathvec(pathvec, FREE_PATHS);
 	vecs.pathvec = NULL;
 
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 12/21] libmultipath: free_multipath: fix FREE_PATHS case
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (10 preceding siblings ...)
  2025-12-17 21:21 ` [PATCH 11/21] libmultipath: remove free_paths argument from free_multipathvec() Martin Wilck
@ 2025-12-17 21:21 ` Martin Wilck
  2025-12-19  0:15   ` Benjamin Marzinski
  2025-12-17 21:21 ` [PATCH 13/21] multipath-tools: Fix ISO C23 errors with strchr() Martin Wilck
                   ` (9 subsequent siblings)
  21 siblings, 1 reply; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:21 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

free_multipath was passing FREE_PATHS to both free_pgvec() and
free_pathvec(), which was wrong because a path could be referenced
in both structures. This can't happen any more now, as free_pgvec()
has lost the ability to free paths.

In general, free_multipath can't make any assumptions about whether
a given path is referenced in mpp->paths, mpp->pg, or both. Therefore
look first for paths that are referenced in mpp->pg but not in mpp->paths,
clean them, and finally loop over mpp->paths.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/structs.c | 44 +++++++++++++++++++++++++++++-------------
 1 file changed, 31 insertions(+), 13 deletions(-)

diff --git a/libmultipath/structs.c b/libmultipath/structs.c
index 1ee29f6..29b62e4 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -298,8 +298,12 @@ void free_multipath_attributes(struct multipath *mpp)
 }
 
 void
-free_multipath (struct multipath * mpp, enum free_path_mode free_paths)
+free_multipath (struct multipath *mpp, enum free_path_mode free_paths)
 {
+	struct pathgroup *pgp;
+	struct path *pp;
+	int i, j;
+
 	if (!mpp)
 		return;
 
@@ -310,22 +314,36 @@ free_multipath (struct multipath * mpp, enum free_path_mode free_paths)
 		mpp->alias = NULL;
 	}
 
-	if (free_paths == KEEP_PATHS && mpp->pg) {
-		struct pathgroup *pgp;
-		struct path *pp;
-		int i, j;
-
-		/*
-		 * Make sure paths carry no reference to this mpp any more
-		 */
-		vector_foreach_slot(mpp->pg, pgp, i) {
-			vector_foreach_slot(pgp->paths, pp, j)
-				if (pp->mpp == mpp)
+	/*
+	 * If free_paths == FREE_PATHS, free all paths.
+	 * Otherwise, make sure no references to this mpp remain.
+	 *
+	 * First loop over mpp->pg and clean the paths that might
+	 * be missing in mpp->paths. Then clean the rest
+	 * by looping over mpp->paths.
+	 */
+	vector_foreach_slot(mpp->pg, pgp, i) {
+		vector_foreach_slot(pgp->paths, pp, j) {
+			if (find_slot(mpp->paths, pp) == -1) {
+				if (free_paths == FREE_PATHS)
+					free_path(pp);
+				else
 					pp->mpp = NULL;
+			}
 		}
 	}
 
-	free_pathvec(mpp->paths, free_paths);
+	vector_foreach_slot(mpp->paths, pp, i) {
+		if (free_paths == FREE_PATHS)
+			free_path(pp);
+		else
+			pp->mpp = NULL;
+	}
+
+	/*
+	 * We've freed paths above already, use KEEP_PATHS here
+	 */
+	free_pathvec(mpp->paths, KEEP_PATHS);
 	free_pgvec(mpp->pg);
 	if (mpp->hwe) {
 		vector_free(mpp->hwe);
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 13/21] multipath-tools: Fix ISO C23 errors with strchr()
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (11 preceding siblings ...)
  2025-12-17 21:21 ` [PATCH 12/21] libmultipath: free_multipath: fix FREE_PATHS case Martin Wilck
@ 2025-12-17 21:21 ` Martin Wilck
  2025-12-17 21:21 ` [PATCH 14/21] libmultipath: simplify sysfs_get_target_nodename() Martin Wilck
                   ` (8 subsequent siblings)
  21 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:21 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

In ISO C23, memchr, strchr, strpbrk, strrchr and strstr become
const-preserving macros [1], meaning that the return value
inherits the const qualifier from the function argument.

This has turned up a few glitches in our code.

[1] https://gustedt.gitlabpages.inria.fr/c23-library/#memchr

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmpathutil/parser.c | 2 +-
 libmpathutil/util.c   | 2 +-
 libmultipath/prkey.c  | 2 +-
 libmultipath/prkey.h  | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libmpathutil/parser.c b/libmpathutil/parser.c
index 131a5ed..bf7c411 100644
--- a/libmpathutil/parser.c
+++ b/libmpathutil/parser.c
@@ -144,7 +144,7 @@ snprint_keyword(struct strbuf *buff, const char *fmt, struct keyword *kw,
 		const void *data)
 {
 	int r = 0;
-	char *f;
+	const char *f;
 	struct config *conf;
 	STRBUF_ON_STACK(sbuf);
 
diff --git a/libmpathutil/util.c b/libmpathutil/util.c
index 38e984f..37412c6 100644
--- a/libmpathutil/util.c
+++ b/libmpathutil/util.c
@@ -38,7 +38,7 @@ strchop(char *str)
  */
 const char *libmp_basename(const char *filename)
 {
-	char *p = strrchr(filename, '/');
+	const char *p = strrchr(filename, '/');
 	return p ? p + 1 : filename;
 }
 
diff --git a/libmultipath/prkey.c b/libmultipath/prkey.c
index 4fbde5a..ebed0d0 100644
--- a/libmultipath/prkey.c
+++ b/libmultipath/prkey.c
@@ -50,7 +50,7 @@ static int parse_prkey(const char *ptr, uint64_t *prkey)
 	return 0;
 }
 
-int parse_prkey_flags(const char *ptr, uint64_t *prkey, uint8_t *flags)
+int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags)
 {
 	char *flagstr;
 
diff --git a/libmultipath/prkey.h b/libmultipath/prkey.h
index a89a617..806795e 100644
--- a/libmultipath/prkey.h
+++ b/libmultipath/prkey.h
@@ -15,7 +15,7 @@
 
 int print_reservation_key(struct strbuf *buff,
 			  struct be64 key, uint8_t flags, int source);
-int parse_prkey_flags(const char *ptr, uint64_t *prkey, uint8_t *flags);
+int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags);
 int set_prkey(struct config *conf, struct multipath *mpp,
 	      uint64_t prkey, uint8_t sa_flags);
 int get_prkey(struct multipath *mpp, uint64_t *prkey, uint8_t *sa_flags);
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 14/21] libmultipath: simplify sysfs_get_target_nodename()
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (12 preceding siblings ...)
  2025-12-17 21:21 ` [PATCH 13/21] multipath-tools: Fix ISO C23 errors with strchr() Martin Wilck
@ 2025-12-17 21:21 ` Martin Wilck
  2025-12-17 21:21 ` [PATCH 15/21] multipathd: join the init_unwinder dummy thread Martin Wilck
                   ` (7 subsequent siblings)
  21 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:21 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

All devices that we use to derive the target node name from are
parents of the scsi_target device. This allows some simplification
in sysfs_get_target_nodename().

This changes the sequence of libudev calls in some of the tests, which
need to be adapted.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/discovery.c | 32 ++++++++++++++++++--------------
 tests/test-lib.c         |  5 +++--
 2 files changed, 21 insertions(+), 16 deletions(-)

diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index 31db875..607fb3b 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -325,19 +325,22 @@ static int
 sysfs_get_tgt_nodename(struct path *pp, char *node)
 {
 	const char *tgtname, *value;
-	struct udev_device *parent, *tgtdev;
+	struct udev_device *parent, *tgtdev, *device, *target;
 	int host, channel, tgtid = -1;
 
 	if (!pp->udev)
 		return 1;
-	parent = udev_device_get_parent_with_subsystem_devtype(pp->udev,
+	device = udev_device_get_parent_with_subsystem_devtype(pp->udev,
 							 "scsi", "scsi_device");
-	if (!parent)
+	if (!device)
 		return 1;
+	target = udev_device_get_parent_with_subsystem_devtype(device, "scsi", "scsi_target");
 	/* Check for SAS */
-	value = udev_device_get_sysattr_value(parent, "sas_address");
+	if (!target)
+		return 1;
+	value = udev_device_get_sysattr_value(device, "sas_address");
 	if (value) {
-		tgtdev = udev_device_get_parent(parent);
+		tgtdev = udev_device_get_parent(target);
 		while (tgtdev) {
 			char c;
 
@@ -362,7 +365,7 @@ sysfs_get_tgt_nodename(struct path *pp, char *node)
 	}
 
 	/* Check for USB */
-	tgtdev = udev_device_get_parent(parent);
+	tgtdev = target;
 	while (tgtdev) {
 		value = udev_device_get_subsystem(tgtdev);
 		if (value && !strcmp(value, "usb")) {
@@ -375,11 +378,8 @@ sysfs_get_tgt_nodename(struct path *pp, char *node)
 		}
 		tgtdev = udev_device_get_parent(tgtdev);
 	}
-	parent = udev_device_get_parent_with_subsystem_devtype(pp->udev, "scsi", "scsi_target");
-	if (!parent)
-		return 1;
 	/* Check for FibreChannel */
-	tgtdev = udev_device_get_parent(parent);
+	tgtdev = udev_device_get_parent(target);
 	value = udev_device_get_sysname(tgtdev);
 	if (value && sscanf(value, "rport-%d:%d-%d",
 		   &host, &channel, &tgtid) == 3) {
@@ -399,13 +399,15 @@ sysfs_get_tgt_nodename(struct path *pp, char *node)
 				strlcpy(node, value, NODE_NAME_SIZE);
 				udev_device_unref(tgtdev);
 				return 0;
-			} else
+			} else {
 				udev_device_unref(tgtdev);
+				return 1;
+			}
 		}
 	}
 
 	/* Check for iSCSI */
-	parent = pp->udev;
+	parent = target;
 	tgtname = NULL;
 	while (parent) {
 		tgtname = udev_device_get_sysname(parent);
@@ -429,12 +431,14 @@ sysfs_get_tgt_nodename(struct path *pp, char *node)
 				udev_device_unref(tgtdev);
 				return 0;
 			}
-			else
+			else {
 				udev_device_unref(tgtdev);
+				return 1;
+			}
 		}
 	}
 	/* Check for libata */
-	parent = pp->udev;
+	parent = target;
 	tgtname = NULL;
 	while (parent) {
 		tgtname = udev_device_get_sysname(parent);
diff --git a/tests/test-lib.c b/tests/test-lib.c
index e529711..963e289 100644
--- a/tests/test-lib.c
+++ b/tests/test-lib.c
@@ -292,12 +292,13 @@ static void mock_sysfs_pathinfo(const struct mocked_path *mp)
 
 	/* sysfs_get_tgt_nodename */
 	will_return(__wrap_udev_device_get_sysattr_value, NULL);
+	will_return(__wrap_udev_device_get_subsystem, NULL);
 	will_return(__wrap_udev_device_get_parent, NULL);
 	will_return(__wrap_udev_device_get_parent, NULL);
 	will_return(__wrap_udev_device_get_sysname, "nofibre");
 	will_return(__wrap_udev_device_get_sysname, "noiscsi");
-	will_return(__wrap_udev_device_get_parent, NULL);
 	will_return(__wrap_udev_device_get_sysname, "ata25");
+	will_return(__wrap_udev_device_get_parent, NULL);
 }
 
 /*
@@ -328,7 +329,7 @@ void mock_pathinfo(int mask, const struct mocked_path *mp)
 	    (mask & DI_BLACKLIST && mask & DI_SYSFS))
 		return;
 
-	/* path_offline */
+	/* path_sysfs_state */
 	will_return(__wrap_udev_device_get_subsystem, "scsi");
 	will_return(__wrap_sysfs_attr_get_value, "running");
 
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 15/21] multipathd: join the init_unwinder dummy thread
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (13 preceding siblings ...)
  2025-12-17 21:21 ` [PATCH 14/21] libmultipath: simplify sysfs_get_target_nodename() Martin Wilck
@ 2025-12-17 21:21 ` Martin Wilck
  2025-12-17 21:21 ` [PATCH 16/21] kpartx: fix some memory leaks Martin Wilck
                   ` (6 subsequent siblings)
  21 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:21 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

This closes a minor memory leak (thread-local storage of the
dummy thread is never freed).

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/init_unwinder.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/multipathd/init_unwinder.c b/multipathd/init_unwinder.c
index b1cd283..d39e428 100644
--- a/multipathd/init_unwinder.c
+++ b/multipathd/init_unwinder.c
@@ -34,5 +34,7 @@ int init_unwinder(void)
 
 	pthread_mutex_unlock(&dummy_mtx);
 
-	return pthread_cancel(dummy);
+	rc = pthread_cancel(dummy);
+	pthread_join(dummy, NULL);
+	return rc;
 }
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 16/21] kpartx: fix some memory leaks
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (14 preceding siblings ...)
  2025-12-17 21:21 ` [PATCH 15/21] multipathd: join the init_unwinder dummy thread Martin Wilck
@ 2025-12-17 21:21 ` Martin Wilck
  2025-12-17 21:21 ` [PATCH 17/21] libmpathutil: use union for bitfield Martin Wilck
                   ` (5 subsequent siblings)
  21 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:21 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

These leaks were reported by LeakSanitizer.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 kpartx/kpartx.c | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c
index a1495e5..f5bf396 100644
--- a/kpartx/kpartx.c
+++ b/kpartx/kpartx.c
@@ -227,6 +227,11 @@ xmalloc (size_t size) {
 	return t;
 }
 
+static void cleanup_charp(char **p)
+{
+	free(*p);
+}
+
 int
 main(int argc, char **argv){
 	int i, j, m, n, op, off, arg, c, d, ro=0;
@@ -237,10 +242,10 @@ main(int argc, char **argv){
 	char *type, *diskdevice, *device, *progname;
 	int verbose = 0;
 	char partname[PARTNAME_SIZE], params[PARTNAME_SIZE + 16];
-	char * loopdev = NULL;
-	char * delim = NULL;
-	char *uuid = NULL;
-	char *mapname = NULL;
+	char * loopdev __attribute__((cleanup(cleanup_charp))) = NULL;
+	char *delim __attribute__((cleanup(cleanup_charp))) = NULL;
+	char *uuid __attribute__((cleanup(cleanup_charp))) = NULL;
+	char *mapname __attribute__((cleanup(cleanup_charp))) = NULL;
 	int hotplug = 0;
 	int loopcreated = 0;
 	struct stat buf;
@@ -292,7 +297,9 @@ main(int argc, char **argv){
 			verbose = 1;
 			break;
 		case 'p':
-			delim = optarg;
+			delim = strdup(optarg);
+			if (!delim)
+				exit(1);
 			break;
 		case 'l':
 			what = LIST;
@@ -674,7 +681,6 @@ main(int argc, char **argv){
 		if (verbose)
 			fprintf(stderr, "loop deleted : %s\n", device);
 	}
-
 end:
 	dm_lib_exit();
 
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 17/21] libmpathutil: use union for bitfield
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (15 preceding siblings ...)
  2025-12-17 21:21 ` [PATCH 16/21] kpartx: fix some memory leaks Martin Wilck
@ 2025-12-17 21:21 ` Martin Wilck
  2025-12-19  0:17   ` Benjamin Marzinski
  2025-12-17 21:21 ` [PATCH 18/21] libmpathutil: add wrapper code for libudev Martin Wilck
                   ` (4 subsequent siblings)
  21 siblings, 1 reply; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:21 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

The macro BITFIELD used a cast of a pointer to a different anonymous struct
to (struct bitfield *). This violates strict aliasing rules, because the
two structs aren't compatible types in the sense of the language
specification. With -O2, gcc 15.1 generates code that makes th pgcmp()
function fail, because the compiler doesn't initialize bf->len aka
__storage_for__bf.len to 64.

The problem will show through error messages like this:

multipathd[1974208]: is_bit_set_in_bitfield: bitfield overflow: 1 >= 0

Fix this by using a union

Fixes: 9a2f173 ("libmpathutil: change STATIC_BITFIELD to BITFIELD")
Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmpathutil/util.c       |  8 ++++----
 libmpathutil/util.h       | 41 +++++++++++++++++++++++----------------
 libmultipath/configure.c  |  4 ++--
 libmultipath/pgpolicies.c |  2 +-
 tests/util.c              | 10 +++++-----
 5 files changed, 36 insertions(+), 29 deletions(-)

diff --git a/libmpathutil/util.c b/libmpathutil/util.c
index 37412c6..5e45750 100644
--- a/libmpathutil/util.c
+++ b/libmpathutil/util.c
@@ -337,15 +337,15 @@ void cleanup_fclose(void *p)
 		fclose(p);
 }
 
-void cleanup_bitfield(struct bitfield **p)
+void cleanup_bitfield(union bitfield **p)
 {
 	free(*p);
 }
 
-struct bitfield *alloc_bitfield(unsigned int maxbit)
+union bitfield *alloc_bitfield(unsigned int maxbit)
 {
 	unsigned int n;
-	struct bitfield *bf;
+	union bitfield *bf;
 
 	if (maxbit == 0) {
 		errno = EINVAL;
@@ -353,7 +353,7 @@ struct bitfield *alloc_bitfield(unsigned int maxbit)
 	}
 
 	n = (maxbit - 1) / bits_per_slot + 1;
-	bf = calloc(1, sizeof(struct bitfield) + n * sizeof(bitfield_t));
+	bf = calloc(1, sizeof(union bitfield) + (n - 1) * sizeof(bitfield_t));
 	if (bf)
 		bf->len = maxbit;
 	return bf;
diff --git a/libmpathutil/util.h b/libmpathutil/util.h
index 3799fd4..1ec6a46 100644
--- a/libmpathutil/util.h
+++ b/libmpathutil/util.h
@@ -86,29 +86,36 @@ typedef unsigned int bitfield_t;
 #endif
 #define bits_per_slot (sizeof(bitfield_t) * CHAR_BIT)
 
-struct bitfield {
-	unsigned int len;
-	bitfield_t bits[];
+union bitfield {
+	struct {
+		unsigned int len;
+		bitfield_t bits[];
+	};
+	/* for initialization in the BITFIELD macro */
+	struct {
+		unsigned int __len;
+		bitfield_t __bits[1];
+	};
 };
 
-#define BITFIELD(name, length)					\
-	struct {							\
-		unsigned int len;					\
-		bitfield_t bits[((length) - 1) / bits_per_slot + 1];	\
-	} __storage_for__ ## name = {					\
-		.len = (length),					\
-		.bits = { 0, },						\
+/*
+ * gcc 4.9.2 (Debian Jessie) raises an error if the initializer for
+ * .__len comes first. Thus put .__bits first.
+ */
+#define BITFIELD(name, length)						\
+	union bitfield __storage_for__ ## name = {			\
+		.__bits = { 0 },					\
+		.__len = (length),					\
 	}; \
-	struct bitfield *name = (struct bitfield *)& __storage_for__ ## name
+	union bitfield *name = & __storage_for__ ## name
 
-struct bitfield *alloc_bitfield(unsigned int maxbit);
+union bitfield *alloc_bitfield(unsigned int maxbit);
 
 void log_bitfield_overflow__(const char *f, unsigned int bit, unsigned int len);
 #define log_bitfield_overflow(bit, len) \
 	log_bitfield_overflow__(__func__, bit, len)
 
-static inline bool is_bit_set_in_bitfield(unsigned int bit,
-				       const struct bitfield *bf)
+static inline bool is_bit_set_in_bitfield(unsigned int bit, const union bitfield *bf)
 {
 	if (bit >= bf->len) {
 		log_bitfield_overflow(bit, bf->len);
@@ -118,7 +125,7 @@ static inline bool is_bit_set_in_bitfield(unsigned int bit,
 		  (1ULL << (bit % bits_per_slot)));
 }
 
-static inline void set_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
+static inline void set_bit_in_bitfield(unsigned int bit, union bitfield *bf)
 {
 	if (bit >= bf->len) {
 		log_bitfield_overflow(bit, bf->len);
@@ -127,7 +134,7 @@ static inline void set_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
 	bf->bits[bit / bits_per_slot] |= (1ULL << (bit % bits_per_slot));
 }
 
-static inline void clear_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
+static inline void clear_bit_in_bitfield(unsigned int bit, union bitfield *bf)
 {
 	if (bit >= bf->len) {
 		log_bitfield_overflow(bit, bf->len);
@@ -146,5 +153,5 @@ static inline void clear_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
 void cleanup_charp(char **p);
 void cleanup_ucharp(unsigned char **p);
 void cleanup_udev_device(struct udev_device **udd);
-void cleanup_bitfield(struct bitfield **p);
+void cleanup_bitfield(union bitfield **p);
 #endif /* UTIL_H_INCLUDED */
diff --git a/libmultipath/configure.c b/libmultipath/configure.c
index a8e18a2..41b6a9f 100644
--- a/libmultipath/configure.c
+++ b/libmultipath/configure.c
@@ -440,7 +440,7 @@ static int pgcmp(struct multipath *mpp, struct multipath *cmpp)
 	int i, j;
 	struct pathgroup *pgp, *cpgp;
 	BITFIELD(bf, bits_per_slot);
-	struct bitfield *bf__  __attribute__((cleanup(cleanup_bitfield))) = NULL;
+	union bitfield *bf__ __attribute__((cleanup(cleanup_bitfield))) = NULL;
 
 	if (VECTOR_SIZE(mpp->pg) != VECTOR_SIZE(cmpp->pg))
 		return 1;
@@ -1049,7 +1049,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
 	vector newmp;
 	struct config *conf = NULL;
 	int allow_queueing;
-	struct bitfield *size_mismatch_seen;
+	union bitfield *size_mismatch_seen;
 	struct multipath * cmpp;
 
 	/* ignore refwwid if it's empty */
diff --git a/libmultipath/pgpolicies.c b/libmultipath/pgpolicies.c
index 2e1a543..57b7485 100644
--- a/libmultipath/pgpolicies.c
+++ b/libmultipath/pgpolicies.c
@@ -201,7 +201,7 @@ int group_by_match(struct multipath * mp, vector paths,
 		   bool (*path_match_fn)(struct path *, struct path *))
 {
 	int i, j;
-	struct bitfield *bitmap;
+	union bitfield *bitmap;
 	struct path * pp;
 	struct pathgroup * pgp;
 	struct path * pp2;
diff --git a/tests/util.c b/tests/util.c
index da192ba..67d8148 100644
--- a/tests/util.c
+++ b/tests/util.c
@@ -198,7 +198,7 @@ static uint64_t maybe_swap(uint64_t v)
 
 static void test_bitmask_1(void **state)
 {
-	struct bitfield *bf;
+	union bitfield *bf;
 	uint64_t *arr;
 	int i, j, k, m, b;
 
@@ -240,7 +240,7 @@ static void test_bitmask_1(void **state)
 
 static void test_bitmask_2(void **state)
 {
-	struct bitfield *bf;
+	union bitfield *bf;
 	uint64_t *arr;
 	int i, j, k, m, b;
 
@@ -307,7 +307,7 @@ static void test_bitmask_2(void **state)
  */
 static void test_bitmask_len_0(void **state)
 {
-	struct bitfield *bf;
+	union bitfield *bf;
 
 	bf = alloc_bitfield(0);
 	assert_null(bf);
@@ -329,7 +329,7 @@ static unsigned int maybe_swap_idx(unsigned int i)
 
 static void _test_bitmask_small(unsigned int n)
 {
-	struct bitfield *bf;
+	union bitfield *bf;
 	uint32_t *arr;
 	unsigned int size = maybe_swap_idx((n - 1) / 32) + 1, i;
 
@@ -378,7 +378,7 @@ static void _test_bitmask_small(unsigned int n)
 
 static void _test_bitmask_small_2(unsigned int n)
 {
-	struct bitfield *bf;
+	union bitfield *bf;
 	uint32_t *arr;
 	unsigned int size = maybe_swap_idx((n - 1) / 32) + 1, i;
 
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 18/21] libmpathutil: add wrapper code for libudev
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (16 preceding siblings ...)
  2025-12-17 21:21 ` [PATCH 17/21] libmpathutil: use union for bitfield Martin Wilck
@ 2025-12-17 21:21 ` Martin Wilck
  2025-12-19  0:38   ` Benjamin Marzinski
  2025-12-17 21:21 ` [PATCH 19/21] multipath-tools: use the libudev wrapper functions Martin Wilck
                   ` (3 subsequent siblings)
  21 siblings, 1 reply; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:21 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

The libudev documentation [1] states that libudev is not thread-safe and
shouldn't be used from multithreaded programs. While the man page also
states that using libudev in multithreaded programs is unsafe even if
locking is used, there's not much else we can do in multipath-tools to
mitigate the problem, as we are using libudev in multiple threads all over
the place.

Add wrapper code that wraps all calls to libudev in a critical section
holding a specific mutex.

[1] https://man7.org/linux/man-pages/man3/libudev.3.html

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmpathutil/libmpathutil.version |  62 +++
 libmpathutil/mt-libudev.c         | 776 ++++++++++++++++++++++++++++++
 libmpathutil/mt-libudev.h         | 120 +++++
 libmpathutil/mt-udev-wrap.h       |  90 ++++
 4 files changed, 1048 insertions(+)
 create mode 100644 libmpathutil/mt-libudev.c
 create mode 100644 libmpathutil/mt-libudev.h
 create mode 100644 libmpathutil/mt-udev-wrap.h

diff --git a/libmpathutil/libmpathutil.version b/libmpathutil/libmpathutil.version
index 14aa083..4ce1da6 100644
--- a/libmpathutil/libmpathutil.version
+++ b/libmpathutil/libmpathutil.version
@@ -93,6 +93,68 @@ global:
 	log_thread_stop;
 	logsink;
 	msort;
+
+        mt_udev_ref;
+        mt_udev_unref;
+        mt_udev_new;
+
+        mt_udev_list_entry_get_next;
+        mt_udev_list_entry_get_by_name;
+        mt_udev_list_entry_get_name;
+        mt_udev_list_entry_get_value;
+
+        mt_udev_device_ref;
+        mt_udev_device_unref;
+        
+        mt_udev_device_new_from_syspath;
+        mt_udev_device_new_from_devnum;
+        mt_udev_device_new_from_subsystem_sysname;
+        mt_udev_device_new_from_device_id;
+        mt_udev_device_new_from_environment;
+
+        mt_udev_device_get_udev;
+        mt_udev_device_get_parent;
+        mt_udev_device_get_parent_with_subsystem_devtype;
+        mt_udev_device_get_devpath;
+        mt_udev_device_get_subsystem;
+	mt_udev_device_get_devtype;
+        mt_udev_device_get_syspath;
+        mt_udev_device_get_sysname;
+        mt_udev_device_get_devnum;
+        mt_udev_device_get_is_initialized;
+        mt_udev_device_get_property_value;
+        mt_udev_device_get_seqnum;
+        mt_udev_device_set_sysattr_value;
+        mt_udev_device_get_sysattr_value;
+        mt_udev_device_get_driver;
+        mt_udev_device_get_devnode;
+        
+        mt_udev_device_get_properties_list_entry;
+
+        mt_udev_monitor_new_from_netlink;
+        mt_udev_monitor_enable_receiving;
+        mt_udev_monitor_get_fd;
+        mt_udev_monitor_receive_device;
+        mt_udev_monitor_filter_add_match_subsystem_devtype;
+        mt_udev_monitor_ref;
+        mt_udev_monitor_unref;
+        mt_udev_monitor_set_receive_buffer_size;
+
+        mt_udev_enumerate_new;
+        mt_udev_enumerate_ref;
+        mt_udev_enumerate_unref;
+        mt_udev_enumerate_add_match_subsystem;
+        mt_udev_enumerate_scan_devices;
+        mt_udev_enumerate_get_list_entry;
+	mt_udev_enumerate_add_nomatch_subsystem;
+        mt_udev_enumerate_add_match_sysattr;
+        mt_udev_enumerate_add_nomatch_sysattr;
+        mt_udev_enumerate_add_match_property;
+        mt_udev_enumerate_add_match_tag;
+        mt_udev_enumerate_add_match_parent;
+        mt_udev_enumerate_add_match_is_initialized;
+        mt_udev_enumerate_add_syspath;
+	
 	normalize_timespec;
 	parse_devt;
 	print_strbuf;
diff --git a/libmpathutil/mt-libudev.c b/libmpathutil/mt-libudev.c
new file mode 100644
index 0000000..2e37c7d
--- /dev/null
+++ b/libmpathutil/mt-libudev.c
@@ -0,0 +1,776 @@
+#include "mt-libudev.h"
+#include <stddef.h>
+#include <libudev.h>
+#include "util.h"
+
+static pthread_mutex_t libudev_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+struct udev *mt_udev_ref(struct udev *udev)
+{
+	struct udev *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_ref(udev);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev *mt_udev_unref(struct udev *udev)
+{
+	struct udev *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_unref(udev);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev *mt_udev_new(void)
+{
+	struct udev *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_new();
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_list_entry *mt_udev_list_entry_get_next(struct udev_list_entry *list_entry)
+{
+	struct udev_list_entry *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_list_entry_get_next(list_entry);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_list_entry *
+mt_udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name)
+{
+	struct udev_list_entry *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_list_entry_get_by_name(list_entry, name);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+const char *mt_udev_list_entry_get_name(struct udev_list_entry *list_entry)
+{
+	const char *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_list_entry_get_name(list_entry);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+const char *mt_udev_list_entry_get_value(struct udev_list_entry *list_entry)
+{
+	const char *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_list_entry_get_value(list_entry);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_device *
+mt_udev_device_new_from_syspath(struct udev *udev, const char *syspath)
+{
+	struct udev_device *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_new_from_syspath(udev, syspath);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_device *
+mt_udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum)
+{
+	struct udev_device *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_new_from_devnum(udev, type, devnum);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_device *
+mt_udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem,
+					  const char *sysname)
+{
+	struct udev_device *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_new_from_subsystem_sysname(udev, subsystem, sysname);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_device *mt_udev_device_new_from_device_id(struct udev *udev, char *id)
+{
+	struct udev_device *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_new_from_device_id(udev, id);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_device *mt_udev_device_new_from_environment(struct udev *udev)
+{
+	struct udev_device *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_new_from_environment(udev);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_device *mt_udev_device_ref(struct udev_device *udev_device)
+{
+	struct udev_device *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_ref(udev_device);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_device *mt_udev_device_unref(struct udev_device *udev_device)
+{
+	struct udev_device *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_unref(udev_device);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev *mt_udev_device_get_udev(struct udev_device *udev_device)
+{
+	struct udev *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_get_udev(udev_device);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_device *mt_udev_device_get_parent(struct udev_device *udev_device)
+{
+	struct udev_device *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_get_parent(udev_device);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_device *mt_udev_device_get_parent_with_subsystem_devtype(
+	struct udev_device *udev_device, const char *subsystem, const char *devtype)
+{
+	struct udev_device *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_get_parent_with_subsystem_devtype(udev_device,
+							    subsystem, devtype);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+const char *mt_udev_device_get_devpath(struct udev_device *udev_device)
+{
+	const char *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_get_devpath(udev_device);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+const char *mt_udev_device_get_subsystem(struct udev_device *udev_device)
+{
+	const char *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_get_subsystem(udev_device);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+const char *mt_udev_device_get_devtype(struct udev_device *udev_device)
+{
+	const char *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_get_devtype(udev_device);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+const char *mt_udev_device_get_syspath(struct udev_device *udev_device)
+{
+	const char *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_get_syspath(udev_device);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+const char *mt_udev_device_get_sysname(struct udev_device *udev_device)
+{
+	const char *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_get_sysname(udev_device);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+dev_t mt_udev_device_get_devnum(struct udev_device *udev_device)
+{
+	dev_t ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_get_devnum(udev_device);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+unsigned long long mt_udev_device_get_seqnum(struct udev_device *udev_device)
+{
+	unsigned long long ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_get_seqnum(udev_device);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+const char *mt_udev_device_get_driver(struct udev_device *udev_device)
+{
+	const char *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_get_driver(udev_device);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+const char *mt_udev_device_get_devnode(struct udev_device *udev_device)
+{
+	const char *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_get_devnode(udev_device);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+int mt_udev_device_get_is_initialized(struct udev_device *udev_device)
+{
+	int ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_get_is_initialized(udev_device);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+const char *
+mt_udev_device_get_property_value(struct udev_device *udev_device, const char *key)
+{
+	const char *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_get_property_value(udev_device, key);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+const char *mt_udev_device_get_sysattr_value(struct udev_device *udev_device,
+					     const char *sysattr)
+{
+	const char *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_get_sysattr_value(udev_device, sysattr);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+int mt_udev_device_set_sysattr_value(struct udev_device *udev_device,
+				     const char *sysattr, char *value)
+{
+	int ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_set_sysattr_value(udev_device, sysattr, value);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_list_entry *
+mt_udev_device_get_properties_list_entry(struct udev_device *udev_device)
+{
+	struct udev_list_entry *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_device_get_properties_list_entry(udev_device);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+/*
+ * multipath-tools doesn't use these, and some of them aren't available
+ * in older libudev versions. Keeping the code here in case we will
+ * need it in the future.
+
+struct udev_list_entry *mt_udev_device_get_devlinks_list_entry(struct
+udev_device *udev_device)
+{
+    struct udev_list_entry *ret;
+    pthread_mutex_lock(&libudev_mutex);
+    pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+    ret = udev_device_get_devlinks_list_entry(udev_device);
+
+    pthread_cleanup_pop(1);
+    return ret;
+}
+
+struct udev_list_entry *mt_udev_device_get_tags_list_entry(struct udev_device
+*udev_device)
+{
+    struct udev_list_entry *ret;
+    pthread_mutex_lock(&libudev_mutex);
+    pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+    ret = udev_device_get_tags_list_entry(udev_device);
+
+    pthread_cleanup_pop(1);
+    return ret;
+}
+
+struct udev_list_entry *mt_udev_device_get_current_tags_list_entry(struct
+udev_device *udev_device)
+{
+    struct udev_list_entry *ret;
+    pthread_mutex_lock(&libudev_mutex);
+    pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+    ret = udev_device_get_current_tags_list_entry(udev_device);
+
+    pthread_cleanup_pop(1);
+    return ret;
+}
+
+struct udev_list_entry *mt_udev_device_get_sysattr_list_entry(struct
+udev_device *udev_device)
+{
+    struct udev_list_entry *ret;
+    pthread_mutex_lock(&libudev_mutex);
+    pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+    ret = udev_device_get_sysattr_list_entry(udev_device);
+
+    pthread_cleanup_pop(1);
+    return ret;
+}
+*/
+
+struct udev_monitor *
+mt_udev_monitor_new_from_netlink(struct udev *udev, const char *name)
+{
+	struct udev_monitor *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_monitor_new_from_netlink(udev, name);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_monitor *mt_udev_monitor_ref(struct udev_monitor *udev_monitor)
+{
+	struct udev_monitor *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_monitor_ref(udev_monitor);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_monitor *mt_udev_monitor_unref(struct udev_monitor *udev_monitor)
+{
+	struct udev_monitor *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_monitor_unref(udev_monitor);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+int mt_udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
+{
+	int ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_monitor_enable_receiving(udev_monitor);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+int mt_udev_monitor_get_fd(struct udev_monitor *udev_monitor)
+{
+	int ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_monitor_get_fd(udev_monitor);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_device *mt_udev_monitor_receive_device(struct udev_monitor *udev_monitor)
+{
+	struct udev_device *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_monitor_receive_device(udev_monitor);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+int mt_udev_monitor_filter_add_match_subsystem_devtype(
+	struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype)
+{
+	int ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_monitor_filter_add_match_subsystem_devtype(udev_monitor,
+							      subsystem, devtype);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+int mt_udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size)
+{
+	int ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_monitor_set_receive_buffer_size(udev_monitor, size);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_enumerate *mt_udev_enumerate_new(struct udev *udev)
+{
+	struct udev_enumerate *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_enumerate_new(udev);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_enumerate *mt_udev_enumerate_ref(struct udev_enumerate *udev_enumerate)
+{
+	struct udev_enumerate *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_enumerate_ref(udev_enumerate);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_enumerate *mt_udev_enumerate_unref(struct udev_enumerate *udev_enumerate)
+{
+	struct udev_enumerate *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_enumerate_unref(udev_enumerate);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+int mt_udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate,
+					  const char *subsystem)
+{
+	int ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_enumerate_add_match_subsystem(udev_enumerate, subsystem);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+int mt_udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate,
+					    const char *subsystem)
+{
+	int ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_enumerate_add_nomatch_subsystem(udev_enumerate, subsystem);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+int mt_udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate,
+					const char *sysattr, const char *value)
+{
+	int ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_enumerate_add_match_sysattr(udev_enumerate, sysattr, value);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+int mt_udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate,
+					  const char *sysattr, const char *value)
+{
+	int ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_enumerate_add_nomatch_sysattr(udev_enumerate, sysattr, value);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+int mt_udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate,
+					 const char *property, const char *value)
+{
+	int ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_enumerate_add_match_property(udev_enumerate, property, value);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+int mt_udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate,
+				    const char *tag)
+{
+	int ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_enumerate_add_match_tag(udev_enumerate, tag);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+int mt_udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate,
+				       struct udev_device *parent)
+{
+	int ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_enumerate_add_match_parent(udev_enumerate, parent);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+int mt_udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate)
+{
+	int ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_enumerate_add_match_is_initialized(udev_enumerate);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+int mt_udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate,
+				  const char *syspath)
+{
+	int ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_enumerate_add_syspath(udev_enumerate, syspath);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+int mt_udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate)
+{
+	int ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_enumerate_scan_devices(udev_enumerate);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+struct udev_list_entry *
+mt_udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate)
+{
+	struct udev_list_entry *ret;
+
+	pthread_mutex_lock(&libudev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
+
+	ret = udev_enumerate_get_list_entry(udev_enumerate);
+
+	pthread_cleanup_pop(1);
+	return ret;
+}
diff --git a/libmpathutil/mt-libudev.h b/libmpathutil/mt-libudev.h
new file mode 100644
index 0000000..1c1e748
--- /dev/null
+++ b/libmpathutil/mt-libudev.h
@@ -0,0 +1,120 @@
+#ifndef MT_LIBUDEV_H
+#define MT_LIBUDEV_H
+
+#include <pthread.h>
+#include <stdarg.h>    /* for va_list */
+#include <sys/types.h> /* For dev_t */
+
+struct udev;
+struct udev_list_entry;
+struct udev_device;
+struct udev_monitor;
+struct udev_enumerate;
+
+struct udev *mt_udev_ref(struct udev *udev);
+struct udev *mt_udev_unref(struct udev *udev);
+struct udev *mt_udev_new(void);
+
+struct udev_list_entry *
+mt_udev_list_entry_get_next(struct udev_list_entry *list_entry);
+struct udev_list_entry *
+mt_udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name);
+const char *mt_udev_list_entry_get_name(struct udev_list_entry *list_entry);
+const char *mt_udev_list_entry_get_value(struct udev_list_entry *list_entry);
+
+struct udev_device *
+mt_udev_device_new_from_syspath(struct udev *udev, const char *syspath);
+struct udev_device *
+mt_udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum);
+struct udev_device *
+mt_udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem,
+					  const char *sysname);
+/*
+ * Some older libudev versions  don't use "const" for the id argument,
+ * therefore we can't use it here, either.
+ */
+struct udev_device *mt_udev_device_new_from_device_id(struct udev *udev, char *id);
+struct udev_device *mt_udev_device_new_from_environment(struct udev *udev);
+
+struct udev_device *mt_udev_device_ref(struct udev_device *udev_device);
+struct udev_device *mt_udev_device_unref(struct udev_device *udev_device);
+struct udev *mt_udev_device_get_udev(struct udev_device *udev_device);
+struct udev_device *mt_udev_device_get_parent(struct udev_device *udev_device);
+struct udev_device *mt_udev_device_get_parent_with_subsystem_devtype(
+	struct udev_device *udev_device, const char *subsystem,
+	const char *devtype);
+const char *mt_udev_device_get_devpath(struct udev_device *udev_device);
+const char *mt_udev_device_get_subsystem(struct udev_device *udev_device);
+const char *mt_udev_device_get_devtype(struct udev_device *udev_device);
+const char *mt_udev_device_get_syspath(struct udev_device *udev_device);
+const char *mt_udev_device_get_sysname(struct udev_device *udev_device);
+int mt_udev_device_get_is_initialized(struct udev_device *udev_device);
+const char *
+mt_udev_device_get_property_value(struct udev_device *udev_device, const char *key);
+dev_t mt_udev_device_get_devnum(struct udev_device *udev_device);
+unsigned long long int mt_udev_device_get_seqnum(struct udev_device *udev_device);
+const char *mt_udev_device_get_driver(struct udev_device *udev_device);
+const char *mt_udev_device_get_devnode(struct udev_device *udev_device);
+const char *mt_udev_device_get_sysattr_value(struct udev_device *udev_device,
+					     const char *sysattr);
+/*
+ * libudev-215 (Debian jessie) doesn't use "const" for the value argument,
+ * therefore we can't use it here, either.
+ */
+int mt_udev_device_set_sysattr_value(struct udev_device *udev_device,
+				     const char *sysattr, char *value);
+
+struct udev_list_entry *
+mt_udev_device_get_properties_list_entry(struct udev_device *udev_device);
+/*
+ * multipath-tools doesn't use these, and some of them aren't available
+ * in older libudev versions.
+
+ struct udev_list_entry *mt_udev_device_get_devlinks_list_entry(struct
+ udev_device *udev_device); struct udev_list_entry
+ *mt_udev_device_get_tags_list_entry(struct udev_device *udev_device); struct
+ udev_list_entry *mt_udev_device_get_current_tags_list_entry(struct udev_device
+ *udev_device); struct udev_list_entry
+ *mt_udev_device_get_sysattr_list_entry(struct udev_device *udev_device);
+*/
+struct udev_monitor *
+mt_udev_monitor_new_from_netlink(struct udev *udev, const char *name);
+struct udev_monitor *mt_udev_monitor_ref(struct udev_monitor *udev_monitor);
+struct udev_monitor *mt_udev_monitor_unref(struct udev_monitor *udev_monitor);
+int mt_udev_monitor_enable_receiving(struct udev_monitor *udev_monitor);
+int mt_udev_monitor_get_fd(struct udev_monitor *udev_monitor);
+struct udev_device *
+mt_udev_monitor_receive_device(struct udev_monitor *udev_monitor);
+int mt_udev_monitor_filter_add_match_subsystem_devtype(
+	struct udev_monitor *udev_monitor, const char *subsystem,
+	const char *devtype);
+int mt_udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor,
+					    int size);
+
+struct udev_enumerate *mt_udev_enumerate_new(struct udev *udev);
+struct udev_enumerate *mt_udev_enumerate_ref(struct udev_enumerate *udev_enumerate);
+struct udev_enumerate *
+mt_udev_enumerate_unref(struct udev_enumerate *udev_enumerate);
+int mt_udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate,
+					  const char *subsystem);
+int mt_udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate,
+					    const char *subsystem);
+int mt_udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate,
+					const char *sysattr, const char *value);
+int mt_udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate,
+					  const char *sysattr, const char *value);
+int mt_udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate,
+					 const char *property, const char *value);
+int mt_udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate,
+				    const char *tag);
+int mt_udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate,
+				       struct udev_device *parent);
+int mt_udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate);
+int mt_udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate,
+				  const char *syspath);
+int mt_udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate);
+int mt_udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate);
+struct udev_list_entry *
+mt_udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate);
+
+#endif
diff --git a/libmpathutil/mt-udev-wrap.h b/libmpathutil/mt-udev-wrap.h
new file mode 100644
index 0000000..f232821
--- /dev/null
+++ b/libmpathutil/mt-udev-wrap.h
@@ -0,0 +1,90 @@
+#ifndef MT_LIBUDEV_WRAP_HPP
+#define MT_LIBUDEV_WRAP_HPP
+#include "mt-libudev.h"
+
+#define udev_ref(udev)                       mt_udev_ref(udev)
+#define udev_unref(udev)                     mt_udev_unref(udev)
+#define udev_new()                           mt_udev_new()
+
+#define udev_list_entry_get_next(list_entry)             mt_udev_list_entry_get_next(list_entry)
+#define udev_list_entry_get_by_name(list_entry, name)    mt_udev_list_entry_get_by_name(list_entry, name)
+#define udev_list_entry_get_name(list_entry)             mt_udev_list_entry_get_name(list_entry)
+#define udev_list_entry_get_value(list_entry)            mt_udev_list_entry_get_value(list_entry)
+
+#define udev_list_entry_foreach(list_entry, first_entry)	\
+        for (list_entry = first_entry; \
+             list_entry; \
+             list_entry = udev_list_entry_get_next(list_entry))
+
+#define udev_device_new_from_syspath(udev, syspath)               mt_udev_device_new_from_syspath(udev, syspath)
+#define udev_device_new_from_devnum(udev, type, devnum)           mt_udev_device_new_from_devnum(udev, type, devnum)
+#define udev_device_new_from_subsystem_sysname(udev, subsystem, sysname) \
+                                                                  mt_udev_device_new_from_subsystem_sysname(udev, subsystem, sysname)
+#define udev_device_new_from_device_id(udev, id) mt_udev_device_new_from_device_id(udev, id)
+#define udev_device_new_from_environment(udev)                    mt_udev_device_new_from_environment(udev)
+
+#define udev_device_ref(udev_device)                      mt_udev_device_ref(udev_device)
+#define udev_device_unref(udev_device)                    mt_udev_device_unref(udev_device)
+#define udev_device_get_udev(udev_device)                 mt_udev_device_get_udev(udev_device)
+#define udev_device_get_parent(udev_device)               mt_udev_device_get_parent(udev_device)
+#define udev_device_get_parent_with_subsystem_devtype(udev_device, subsystem, devtype) \
+	mt_udev_device_get_parent_with_subsystem_devtype(udev_device, subsystem, devtype)
+#define udev_device_get_devpath(udev_device)              mt_udev_device_get_devpath(udev_device)
+#define udev_device_get_subsystem(udev_device)            mt_udev_device_get_subsystem(udev_device)
+#define udev_device_get_devtype(udev_device)              mt_udev_device_get_devtype(udev_device)
+#define udev_device_get_syspath(udev_device)              mt_udev_device_get_syspath(udev_device)
+#define udev_device_get_sysname(udev_device)              mt_udev_device_get_sysname(udev_device)
+#define udev_device_get_is_initialized(udev_device)       mt_udev_device_get_is_initialized(udev_device)
+#define udev_device_get_property_value(udev_device, key)  mt_udev_device_get_property_value(udev_device, key)
+#define udev_device_get_devnum(udev_device)               mt_udev_device_get_devnum(udev_device)
+#define udev_device_get_seqnum(udev_device)               mt_udev_device_get_seqnum(udev_device)
+#define udev_device_get_driver(udev_device)                       mt_udev_device_get_driver(udev_device)
+#define udev_device_get_devnode(udev_device)                      mt_udev_device_get_devnode(udev_device)
+#define udev_device_get_sysattr_value(udev_device, sysattr) \
+                                                          mt_udev_device_get_sysattr_value(udev_device, sysattr)
+#define udev_device_set_sysattr_value(udev_device, sysattr, value) \
+	mt_udev_device_set_sysattr_value(udev_device, sysattr, value)
+
+#define udev_device_get_devlinks_list_entry(udev_device)        mt_udev_device_get_devlinks_list_entry(udev_device)
+#define udev_device_get_properties_list_entry(udev_device)      mt_udev_device_get_properties_list_entry(udev_device)
+#define udev_device_get_tags_list_entry(udev_device)            mt_udev_device_get_tags_list_entry(udev_device)
+#define udev_device_get_current_tags_list_entry(udev_device)    mt_udev_device_get_current_tags_list_entry(udev_device)
+#define udev_device_get_sysattr_list_entry(udev_device)         mt_udev_device_get_sysattr_list_entry(udev_device)
+
+#define udev_monitor_new_from_netlink(udev, name)         mt_udev_monitor_new_from_netlink(udev, name)
+#define udev_monitor_ref(udev_monitor)                    mt_udev_monitor_ref(udev_monitor)
+#define udev_monitor_unref(udev_monitor)                  mt_udev_monitor_unref(udev_monitor)
+#define udev_monitor_enable_receiving(udev_monitor)       mt_udev_monitor_enable_receiving(udev_monitor)
+#define udev_monitor_get_fd(udev_monitor)                 mt_udev_monitor_get_fd(udev_monitor)
+#define udev_monitor_receive_device(udev_monitor)         mt_udev_monitor_receive_device(udev_monitor)
+#define udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, subsystem, devtype) \
+	mt_udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, subsystem, devtype)
+#define udev_monitor_set_receive_buffer_size(udev_monitor, size) \
+                                                          mt_udev_monitor_set_receive_buffer_size(udev_monitor, size)
+
+#define udev_enumerate_new(udev)                          mt_udev_enumerate_new(udev)
+#define udev_enumerate_unref(udev_enumerate)              mt_udev_enumerate_unref(udev_enumerate)
+#define udev_enumerate_add_match_subsystem(udev_enumerate, subsystem) \
+                                                          mt_udev_enumerate_add_match_subsystem(udev_enumerate, subsystem)
+#define udev_enumerate_add_nomatch_subsystem(udev_enumerate, subsystem) \
+                                                          mt_udev_enumerate_add_nomatch_subsystem(udev_enumerate, subsystem)
+#define udev_enumerate_add_match_sysattr(udev_enumerate, sysattr, value) \
+                                                          mt_udev_enumerate_add_match_sysattr(udev_enumerate, sysattr, value)
+#define udev_enumerate_add_nomatch_sysattr(udev_enumerate, sysattr, value) \
+                                                          mt_udev_enumerate_add_nomatch_sysattr(udev_enumerate, sysattr, value)
+#define udev_enumerate_add_match_property(udev_enumerate, property, value) \
+                                                          mt_udev_enumerate_add_match_property(udev_enumerate, property, value)
+#define udev_enumerate_add_match_tag(udev_enumerate, tag) mt_udev_enumerate_add_match_tag(udev_enumerate, tag)
+#define udev_enumerate_add_match_parent(udev_enumerate, parent) \
+                                                          mt_udev_enumerate_add_match_parent(udev_enumerate, parent)
+#define udev_enumerate_add_match_is_initialized(udev_enumerate) \
+                                                          mt_udev_enumerate_add_match_is_initialized(udev_enumerate)
+#define udev_enumerate_add_syspath(udev_enumerate, syspath) \
+                                                          mt_udev_enumerate_add_syspath(udev_enumerate, syspath)
+
+#define udev_enumerate_scan_devices(udev_enumerate)       mt_udev_enumerate_scan_devices(udev_enumerate)
+#define udev_enumerate_scan_device(udev_enumerate, syspath) \
+                                                          mt_udev_enumerate_scan_device(udev_enumerate, syspath)
+#define udev_enumerate_get_list_entry(udev_enumerate)     mt_udev_enumerate_get_list_entry(udev_enumerate)
+
+#endif
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 19/21] multipath-tools: use the libudev wrapper functions
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (17 preceding siblings ...)
  2025-12-17 21:21 ` [PATCH 18/21] libmpathutil: add wrapper code for libudev Martin Wilck
@ 2025-12-17 21:21 ` Martin Wilck
  2025-12-17 21:21 ` [PATCH 20/21] Makefile: add functionality to determine cmocka version Martin Wilck
                   ` (2 subsequent siblings)
  21 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:21 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

Replace all calls to libudev functions by their respective wrappers.
This is achieved by replacing <libudev.h> by "mt-udev-wrap.h".

Adapt the test code where necessary.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libdmmp/Makefile                      |  2 +-
 libdmmp/libdmmp.c                     |  2 +-
 libmpathpersist/mpath_persist_int.c   |  2 +-
 libmpathpersist/mpath_pr_ioctl.c      |  2 +-
 libmpathpersist/mpath_updatepr.c      |  2 +-
 libmpathutil/Makefile                 |  2 +-
 libmpathutil/globals.c                |  2 +-
 libmpathutil/util.c                   |  2 +-
 libmpathutil/util.h                   |  2 +-
 libmpathvalid/mpath_valid.c           |  2 +-
 libmultipath/blacklist.c              |  2 +-
 libmultipath/blacklist.h              |  2 +-
 libmultipath/config.c                 |  2 +-
 libmultipath/configure.c              |  2 +-
 libmultipath/dict.c                   |  2 +-
 libmultipath/discovery.c              |  4 +++-
 libmultipath/foreign.c                |  2 +-
 libmultipath/foreign.h                |  2 +-
 libmultipath/foreign/nvme.c           |  2 +-
 libmultipath/print.c                  |  2 +-
 libmultipath/prio.c                   |  2 +-
 libmultipath/prioritizers/alua_rtpg.c |  2 +-
 libmultipath/prioritizers/ana.c       |  2 +-
 libmultipath/prkey.c                  |  2 +-
 libmultipath/propsel.c                |  2 +-
 libmultipath/structs.c                |  2 +-
 libmultipath/structs_vec.c            |  2 +-
 libmultipath/sysfs.c                  |  2 +-
 libmultipath/uevent.c                 |  2 +-
 libmultipath/valid.c                  |  2 +-
 mpathpersist/main.c                   |  2 +-
 multipath/main.c                      |  2 +-
 multipathd/cli_handlers.c             |  2 +-
 multipathd/fpin_handlers.c            |  2 +-
 multipathd/main.c                     |  2 +-
 tests/Makefile                        | 22 +++++++++++++---------
 tests/devt.c                          |  2 +-
 tests/hwtable.c                       |  2 +-
 tests/mpathvalid.c                    |  2 +-
 tests/sysfs.c                         |  2 +-
 tests/test-lib.c                      |  2 +-
 41 files changed, 55 insertions(+), 49 deletions(-)

diff --git a/libdmmp/Makefile b/libdmmp/Makefile
index 7e0e250..bd3b86b 100644
--- a/libdmmp/Makefile
+++ b/libdmmp/Makefile
@@ -14,7 +14,7 @@ HEADERS := libdmmp/libdmmp.h
 
 OBJS := libdmmp.o libdmmp_mp.o libdmmp_pg.o libdmmp_path.o libdmmp_misc.o
 
-CPPFLAGS += -I$(libdmmpdir) -I$(mpathcmddir) $(shell $(PKG_CONFIG) --cflags json-c)
+CPPFLAGS += -I$(libdmmpdir) -I$(mpathutildir) -I$(mpathcmddir) $(shell $(PKG_CONFIG) --cflags json-c)
 CFLAGS += $(LIB_CFLAGS) -fvisibility=hidden
 
 LIBDEPS += $(shell $(PKG_CONFIG) --libs json-c) -L$(mpathcmddir) -lmpathcmd -lpthread
diff --git a/libdmmp/libdmmp.c b/libdmmp/libdmmp.c
index 79550f5..badd5e7 100644
--- a/libdmmp/libdmmp.c
+++ b/libdmmp/libdmmp.c
@@ -11,7 +11,7 @@
 #include <string.h>
 #include <sys/time.h>
 #include <sys/resource.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <errno.h>
 #include <libdevmapper.h>
 #include <unistd.h>
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 14f7ba8..3091c5c 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -2,7 +2,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <fcntl.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <unistd.h>
 #include <pthread.h>
 #include <stdio.h>
diff --git a/libmpathpersist/mpath_pr_ioctl.c b/libmpathpersist/mpath_pr_ioctl.c
index 6eaec7c..41e5f56 100644
--- a/libmpathpersist/mpath_pr_ioctl.c
+++ b/libmpathpersist/mpath_pr_ioctl.c
@@ -10,7 +10,7 @@
 #include <unistd.h>
 #include <string.h>
 #include <sys/ioctl.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include "mpath_pr_ioctl.h"
 #include "mpath_persist.h"
 #include "unaligned.h"
diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c
index c925dce..514000d 100644
--- a/libmpathpersist/mpath_updatepr.c
+++ b/libmpathpersist/mpath_updatepr.c
@@ -10,7 +10,7 @@
 #include <sys/un.h>
 #include <poll.h>
 #include <errno.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <mpath_persist.h>
 #include "debug.h"
 #include "mpath_cmd.h"
diff --git a/libmpathutil/Makefile b/libmpathutil/Makefile
index aebd8cc..e1cc2c6 100644
--- a/libmpathutil/Makefile
+++ b/libmpathutil/Makefile
@@ -12,7 +12,7 @@ LIBDEPS += -lpthread -ldl -ludev -L$(mpathcmddir) -lmpathcmd $(SYSTEMD_LIBDEPS)
 # they need to be recompiled for unit tests
 
 # other object files
-OBJS := parser.o vector.o util.o debug.o time-util.o \
+OBJS := mt-libudev.o parser.o vector.o util.o debug.o time-util.o \
 	uxsock.o log_pthread.o log.o strbuf.o globals.o msort.o
 
 all:	$(DEVLIB)
diff --git a/libmpathutil/globals.c b/libmpathutil/globals.c
index 4d91349..2596701 100644
--- a/libmpathutil/globals.c
+++ b/libmpathutil/globals.c
@@ -1,5 +1,5 @@
 #include <stddef.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include "globals.h"
 
 __attribute__((weak)) struct config *get_multipath_config(void)
diff --git a/libmpathutil/util.c b/libmpathutil/util.c
index 5e45750..23a9797 100644
--- a/libmpathutil/util.c
+++ b/libmpathutil/util.c
@@ -12,7 +12,7 @@
 #include <dirent.h>
 #include <unistd.h>
 #include <errno.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 
 #include "util.h"
 #include "debug.h"
diff --git a/libmpathutil/util.h b/libmpathutil/util.h
index 1ec6a46..1227bbc 100644
--- a/libmpathutil/util.h
+++ b/libmpathutil/util.h
@@ -10,12 +10,12 @@
 #include <inttypes.h>
 #include <stdbool.h>
 #include <stdio.h>
-#include <libudev.h>
 
 #ifndef __GLIBC_PREREQ
 #define __GLIBC_PREREQ(x, y) 0
 #endif
 
+struct udev_device;
 size_t strchop(char *);
 
 const char *libmp_basename(const char *filename);
diff --git a/libmpathvalid/mpath_valid.c b/libmpathvalid/mpath_valid.c
index 7073d17..132578d 100644
--- a/libmpathvalid/mpath_valid.c
+++ b/libmpathvalid/mpath_valid.c
@@ -2,7 +2,7 @@
 #include <stdio.h>
 #include <stdint.h>
 #include <libdevmapper.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <errno.h>
 
 #include "devmapper.h"
diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c
index 17e1b54..bdade4c 100644
--- a/libmultipath/blacklist.c
+++ b/libmultipath/blacklist.c
@@ -4,7 +4,7 @@
 #include <stdio.h>
 #include <unistd.h>
 #include <fcntl.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 
 #include "checkers.h"
 #include "vector.h"
diff --git a/libmultipath/blacklist.h b/libmultipath/blacklist.h
index f32b582..3a30d56 100644
--- a/libmultipath/blacklist.h
+++ b/libmultipath/blacklist.h
@@ -1,7 +1,7 @@
 #ifndef BLACKLIST_H_INCLUDED
 #define BLACKLIST_H_INCLUDED
 
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <regex.h>
 
 #define MATCH_NOTHING        0
diff --git a/libmultipath/config.c b/libmultipath/config.c
index 8b424d1..0717c83 100644
--- a/libmultipath/config.c
+++ b/libmultipath/config.c
@@ -5,7 +5,7 @@
  */
 #include <stdio.h>
 #include <string.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <dirent.h>
 #include <limits.h>
 #include <errno.h>
diff --git a/libmultipath/configure.c b/libmultipath/configure.c
index 41b6a9f..c4bfdf3 100644
--- a/libmultipath/configure.c
+++ b/libmultipath/configure.c
@@ -14,7 +14,7 @@
 #include <errno.h>
 #include <ctype.h>
 #include <libdevmapper.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include "mpath_cmd.h"
 
 #include "checkers.h"
diff --git a/libmultipath/dict.c b/libmultipath/dict.c
index a06a613..cb11465 100644
--- a/libmultipath/dict.c
+++ b/libmultipath/dict.c
@@ -23,7 +23,7 @@
 #include "util.h"
 #include <errno.h>
 #include <inttypes.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include "autoconfig.h"
 #include "mpath_cmd.h"
 #include "dict.h"
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index 607fb3b..f17893f 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -8,13 +8,15 @@
 #include <unistd.h>
 #include <limits.h>
 #include <fcntl.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <dirent.h>
 #include <errno.h>
 #include <libgen.h>
-#include <libudev.h>
 
+#include "mt-udev-wrap.h"
 #include "checkers.h"
 #include "vector.h"
 #include "util.h"
diff --git a/libmultipath/foreign.c b/libmultipath/foreign.c
index 6f7ccb4..ad063ea 100644
--- a/libmultipath/foreign.c
+++ b/libmultipath/foreign.c
@@ -13,7 +13,7 @@
 #include <dirent.h>
 #include <fnmatch.h>
 #include <dlfcn.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <regex.h>
 #include "vector.h"
 #include "debug.h"
diff --git a/libmultipath/foreign.h b/libmultipath/foreign.h
index 97c1f8a..db545e3 100644
--- a/libmultipath/foreign.h
+++ b/libmultipath/foreign.h
@@ -6,7 +6,7 @@
 #define FOREIGN_H_INCLUDED
 
 #include <stdbool.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #define LIBMP_FOREIGN_API ((1 << 8) | 2)
 
 struct strbuf;
diff --git a/libmultipath/foreign/nvme.c b/libmultipath/foreign/nvme.c
index 4cbdf60..04fa1e0 100644
--- a/libmultipath/foreign/nvme.c
+++ b/libmultipath/foreign/nvme.c
@@ -6,7 +6,7 @@
 #include "nvme-lib.h"
 #include <sys/types.h>
 #include <sys/sysmacros.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/libmultipath/print.c b/libmultipath/print.c
index 0d44a5a..054f06f 100644
--- a/libmultipath/print.c
+++ b/libmultipath/print.c
@@ -10,7 +10,7 @@
 #include <unistd.h>
 #include <errno.h>
 #include <assert.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 
 #include "checkers.h"
 #include "vector.h"
diff --git a/libmultipath/prio.c b/libmultipath/prio.c
index a54487f..24f825b 100644
--- a/libmultipath/prio.c
+++ b/libmultipath/prio.c
@@ -3,7 +3,7 @@
 #include <stddef.h>
 #include <dlfcn.h>
 #include <sys/stat.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 
 #include "debug.h"
 #include "util.h"
diff --git a/libmultipath/prioritizers/alua_rtpg.c b/libmultipath/prioritizers/alua_rtpg.c
index e53f447..053cccb 100644
--- a/libmultipath/prioritizers/alua_rtpg.c
+++ b/libmultipath/prioritizers/alua_rtpg.c
@@ -18,7 +18,7 @@
 #include <limits.h>
 #include <sys/ioctl.h>
 #include <inttypes.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <errno.h>
 
 #include <scsi/sg.h>
diff --git a/libmultipath/prioritizers/ana.c b/libmultipath/prioritizers/ana.c
index 34527b2..ac2090d 100644
--- a/libmultipath/prioritizers/ana.c
+++ b/libmultipath/prioritizers/ana.c
@@ -16,7 +16,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <stdbool.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 
 #include "debug.h"
 #include "nvme-lib.h"
diff --git a/libmultipath/prkey.c b/libmultipath/prkey.c
index ebed0d0..b84746c 100644
--- a/libmultipath/prkey.c
+++ b/libmultipath/prkey.c
@@ -12,7 +12,7 @@
 #include <string.h>
 #include <inttypes.h>
 #include <errno.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 /* MPATH_F_APTPL_MASK is publicly defined in mpath_persist.h */
 #include <../libmpathpersist/mpath_persist.h>
 
diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c
index 4c0fbcf..0f79d88 100644
--- a/libmultipath/propsel.c
+++ b/libmultipath/propsel.c
@@ -26,7 +26,7 @@
 #include "propsel.h"
 #include "strbuf.h"
 #include <inttypes.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <ctype.h>
 
 pgpolicyfn *pgpolicies[] = {
diff --git a/libmultipath/structs.c b/libmultipath/structs.c
index 29b62e4..d6a67e0 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -5,7 +5,7 @@
 #include <stdio.h>
 #include <unistd.h>
 #include <libdevmapper.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <ctype.h>
 
 #include "checkers.h"
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index c23e754..aba041e 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -1,7 +1,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 
 #include "util.h"
 #include "checkers.h"
diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c
index af27d10..2426981 100644
--- a/libmultipath/sysfs.c
+++ b/libmultipath/sysfs.c
@@ -14,7 +14,7 @@
 #include <sys/stat.h>
 #include <string.h>
 #include <dirent.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <fnmatch.h>
 #include <limits.h>
 
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
index 63cf2fb..be199af 100644
--- a/libmultipath/uevent.c
+++ b/libmultipath/uevent.c
@@ -28,7 +28,7 @@
 #include <pthread.h>
 #include <sys/mman.h>
 #include <sys/time.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 
 #include "autoconfig.h"
 #include "debug.h"
diff --git a/libmultipath/valid.c b/libmultipath/valid.c
index 7bf8616..1bf1936 100644
--- a/libmultipath/valid.c
+++ b/libmultipath/valid.c
@@ -4,7 +4,7 @@
  */
 #include <stddef.h>
 #include <errno.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <dirent.h>
 #include <libmount/libmount.h>
 
diff --git a/mpathpersist/main.c b/mpathpersist/main.c
index fea5a9c..f8c5f8e 100644
--- a/mpathpersist/main.c
+++ b/mpathpersist/main.c
@@ -8,7 +8,7 @@
 #include "config.h"
 #include "structs.h"
 #include <getopt.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include "mpath_persist.h"
 #include "mpath_persist_int.h"
 #include "main.h"
diff --git a/multipath/main.c b/multipath/main.c
index 4b8d7dd..ddbe908 100644
--- a/multipath/main.c
+++ b/multipath/main.c
@@ -19,7 +19,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <ctype.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <syslog.h>
 #include <fcntl.h>
 
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index 53be68d..f5a13c4 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -19,7 +19,7 @@
 #include "print.h"
 #include "sysfs.h"
 #include <errno.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <mpath_persist.h>
 #include "util.h"
 #include "prkey.h"
diff --git a/multipathd/fpin_handlers.c b/multipathd/fpin_handlers.c
index 2920469..92a61bb 100644
--- a/multipathd/fpin_handlers.c
+++ b/multipathd/fpin_handlers.c
@@ -3,7 +3,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 #include <sys/socket.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <scsi/scsi_netlink_fc.h>
 #include <scsi/fc/fc_els.h>
 
diff --git a/multipathd/main.c b/multipathd/main.c
index 2043ff3..7f7c86d 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -15,7 +15,7 @@
 #include <errno.h>
 #include <limits.h>
 #include <linux/oom.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <urcu.h>
 #include "fpin.h"
 #ifdef USE_SYSTEMD
diff --git a/tests/Makefile b/tests/Makefile
index 28c00ad..9f1b950 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -5,7 +5,7 @@ TESTDIR := $(CURDIR)
 
 CPPFLAGS += -I$(multipathdir) -I$(mpathutildir) -I$(mpathcmddir) -I$(daemondir) \
 	-DTESTCONFDIR=\"$(TESTDIR)/conf.d\"
-CFLAGS += $(BIN_CFLAGS) -Wno-unused-parameter $(W_MISSING_INITIALIZERS)
+CFLAGS += $(BIN_CFLAGS) -Wno-unused-parameter -Wno-unused-function $(W_MISSING_INITIALIZERS)
 LIBDEPS += -L. -L $(mpathutildir) -L$(mpathcmddir) -lmultipath -lmpathutil -lmpathcmd -lcmocka
 
 TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \
@@ -39,26 +39,30 @@ dmevents-test_OBJDEPS = $(multipathdir)/devmapper.o
 dmevents-test_LIBDEPS = -lpthread -ldevmapper -lurcu
 hwtable-test_TESTDEPS := test-lib.o
 hwtable-test_OBJDEPS := $(multipathdir)/discovery.o $(multipathdir)/blacklist.o \
-	$(multipathdir)/structs_vec.o $(multipathdir)/structs.o $(multipathdir)/propsel.o
+	$(multipathdir)/structs_vec.o $(multipathdir)/structs.o $(multipathdir)/propsel.o \
+	$(mpathutildir)/mt-libudev.o
 hwtable-test_LIBDEPS := -ludev -lpthread -ldl
 blacklist-test_TESTDEPS := test-log.o
-blacklist-test_LIBDEPS := -ludev
-vpd-test_OBJDEPS :=  $(multipathdir)/discovery.o
+blacklist-test_OBJDEPS := $(mpathutildir)/mt-libudev.o
+blacklist-test_LIBDEPS := -ludev -lpthread -ldl
+vpd-test_OBJDEPS :=  $(multipathdir)/discovery.o $(mpathutildir)/mt-libudev.o
 vpd-test_LIBDEPS := -ludev -lpthread -ldl
 alias-test_TESTDEPS := test-log.o
-alias-test_OBJDEPS := $(mpathutildir)/util.o
+alias-test_OBJDEPS := $(mpathutildir)/util.o $(mpathutildir)/mt-libudev.o
 alias-test_LIBDEPS := -ludev -lpthread -ldl
-valid-test_OBJDEPS := $(multipathdir)/valid.o $(multipathdir)/discovery.o
+valid-test_OBJDEPS := $(multipathdir)/valid.o $(multipathdir)/discovery.o $(mpathutildir)/mt-libudev.o
 valid-test_LIBDEPS := -lmount -ludev -lpthread -ldl
-devt-test_LIBDEPS := -ludev
+devt-test_LIBDEPS := -ludev -lpthread -ldl
+devt-test_OBJDEPS := $(mpathutildir)/mt-libudev.o
 mpathvalid-test_LIBDEPS := -ludev -lpthread -ldl
-mpathvalid-test_OBJDEPS := $(mpathvaliddir)/mpath_valid.o
+mpathvalid-test_OBJDEPS := $(mpathvaliddir)/mpath_valid.o $(mpathutildir)/mt-libudev.o
 directio-test_LIBDEPS := -laio
 strbuf-test_OBJDEPS := $(mpathutildir)/strbuf.o
 sysfs-test_TESTDEPS := test-log.o
-sysfs-test_OBJDEPS := $(multipathdir)/sysfs.o $(mpathutildir)/util.o
+sysfs-test_OBJDEPS := $(multipathdir)/sysfs.o $(mpathutildir)/util.o $(mpathutildir)/mt-libudev.o
 sysfs-test_LIBDEPS := -ludev -lpthread -ldl
 features-test_LIBDEPS := -ludev -lpthread
+features-test_OBJDEPS := $(mpathutildir)/mt-libudev.o
 cli-test_OBJDEPS := $(daemondir)/cli.o
 mapinfo-test_LIBDEPS = -lpthread -ldevmapper
 
diff --git a/tests/devt.c b/tests/devt.c
index aafee38..32edf51 100644
--- a/tests/devt.c
+++ b/tests/devt.c
@@ -9,7 +9,7 @@
 #include <setjmp.h>
 #include <stdlib.h>
 #include <cmocka.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <sys/sysmacros.h>
 #include <fcntl.h>
 #include <sys/stat.h>
diff --git a/tests/hwtable.c b/tests/hwtable.c
index 334b75e..631b882 100644
--- a/tests/hwtable.c
+++ b/tests/hwtable.c
@@ -8,7 +8,7 @@
 #include <setjmp.h>
 #include <stdlib.h>
 #include <cmocka.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
diff --git a/tests/mpathvalid.c b/tests/mpathvalid.c
index 134dfc9..699dce5 100644
--- a/tests/mpathvalid.c
+++ b/tests/mpathvalid.c
@@ -9,7 +9,7 @@
 #include <setjmp.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <cmocka.h>
 #include "structs.h"
 #include "config.h"
diff --git a/tests/sysfs.c b/tests/sysfs.c
index 6dfa9f3..13f985f 100644
--- a/tests/sysfs.c
+++ b/tests/sysfs.c
@@ -10,7 +10,7 @@
 #include <setjmp.h>
 #include <stdlib.h>
 #include <cmocka.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <fcntl.h>
 #include <errno.h>
 #include "debug.h"
diff --git a/tests/test-lib.c b/tests/test-lib.c
index 963e289..3f07567 100644
--- a/tests/test-lib.c
+++ b/tests/test-lib.c
@@ -2,7 +2,7 @@
 #include <setjmp.h>
 #include <stdarg.h>
 #include <cmocka.h>
-#include <libudev.h>
+#include "mt-udev-wrap.h"
 #include <sys/sysmacros.h>
 #include <linux/hdreg.h>
 #include <scsi/sg.h>
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 20/21] Makefile: add functionality to determine cmocka version
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (18 preceding siblings ...)
  2025-12-17 21:21 ` [PATCH 19/21] multipath-tools: use the libudev wrapper functions Martin Wilck
@ 2025-12-17 21:21 ` Martin Wilck
  2025-12-17 21:21 ` [PATCH 21/21] multipath-tools tests: adaptations for cmocka 2.0 Martin Wilck
  2025-12-19  0:40 ` [PATCH 00/21] Multipath-tools: various bug fixes Benjamin Marzinski
  21 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:21 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

cmocka 2.0.0 introduces a couple of changes that require version-dependent
code to be used. Add macros to determine the cmocka version. If the version
can't be determined, assume 1.1.0.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 Makefile.inc     | 2 +-
 create-config.mk | 5 +++++
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/Makefile.inc b/Makefile.inc
index 9e3dc46..3dbcdcf 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -112,7 +112,7 @@ WARNFLAGS	:= $(WERROR) -Wall -Wextra -Wformat=2 $(WFORMATOVERFLOW) -W$(ERROR)imp
 		  -W$(ERROR)implicit-function-declaration -W$(ERROR)format-security \
 		  $(WNOCLOBBERED) -W$(ERROR)cast-qual $(ERROR_DISCARDED_QUALIFIERS) $(W_URCU_TYPE_LIMITS)
 
-CPPFLAGS	:= $(FORTIFY_OPT) $(CPPFLAGS) $(D_URCU_VERSION) \
+CPPFLAGS	:= $(FORTIFY_OPT) $(CPPFLAGS) $(D_URCU_VERSION) $(D_CMOCKA_VERSION) \
 		   -D_FILE_OFFSET_BITS=64 \
 		   -DBIN_DIR=\"$(bindir)\" -DMULTIPATH_DIR=\"$(TGTDIR)$(plugindir)\" \
 		   -DRUNTIME_DIR=\"$(runtimedir)\" -DCONFIG_DIR=\"$(TGTDIR)$(configdir)\" \
diff --git a/create-config.mk b/create-config.mk
index 3c3ba06..712cef0 100644
--- a/create-config.mk
+++ b/create-config.mk
@@ -77,6 +77,10 @@ URCU_VERSION = $(shell \
 	$(PKG_CONFIG) --modversion liburcu 2>/dev/null | \
 			awk -F. '{ printf("-DURCU_VERSION=0x%06x", 256 * ( 256 * $$1 + $$2) + $$3); }')
 
+CMOCKA_VERSION = $(shell \
+	($(PKG_CONFIG) --modversion cmocka 2>/dev/null || echo "1.1.0" ) | \
+			awk -F. '{ printf("-DCMOCKA_VERSION=0x%06x", 256 * ( 256 * $$1 + $$2) + $$3); }')
+
 DEFINES :=
 
 ifneq ($(call check_func,dm_task_no_flush,$(devmapper_incdir)/libdevmapper.h),0)
@@ -185,6 +189,7 @@ $(TOPDIR)/config.mk:	$(multipathdir)/autoconfig.h
 	@echo "FPIN_SUPPORT := $(FPIN_SUPPORT)" >$@
 	@echo "FORTIFY_OPT := $(FORTIFY_OPT)" >>$@
 	@echo "D_URCU_VERSION := $(call URCU_VERSION)" >>$@
+	@echo "D_CMOCKA_VERSION := $(call CMOCKA_VERSION)" >>$@
 	@echo "SYSTEMD := $(SYSTEMD)" >>$@
 	@echo "ANA_SUPPORT := $(ANA_SUPPORT)" >>$@
 	@echo "STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector)" >>$@
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 21/21] multipath-tools tests: adaptations for cmocka 2.0
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (19 preceding siblings ...)
  2025-12-17 21:21 ` [PATCH 20/21] Makefile: add functionality to determine cmocka version Martin Wilck
@ 2025-12-17 21:21 ` Martin Wilck
  2025-12-19  0:40 ` [PATCH 00/21] Multipath-tools: various bug fixes Benjamin Marzinski
  21 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-17 21:21 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

cmocka 2.0 has deprecated a number of previously defined functions
and macros, and introduced others instead. Adapt our tests to work with
both cmocka 1.1.x and 2.0.x.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 tests/alias.c         |  44 +++++++-------
 tests/blacklist.c     |   2 +-
 tests/cli.c           |   8 +--
 tests/cmocka-compat.h |  16 ++++++
 tests/devt.c          |   4 +-
 tests/directio.c      |  23 +++++---
 tests/dmevents.c      |  74 ++++++++++++------------
 tests/features.c      |   2 +-
 tests/hwtable.c       |   4 +-
 tests/mapinfo.c       |  82 +++++++++++++-------------
 tests/mpathvalid.c    |  16 +++---
 tests/parser.c        |   2 +-
 tests/pgpolicy.c      |   2 +-
 tests/strbuf.c        | 130 ++++++++++++++++++++++--------------------
 tests/sysfs.c         |  74 ++++++++++++------------
 tests/test-lib.c      |  83 ++++++++++++++-------------
 tests/test-log.c      |  10 ++--
 tests/uevent.c        |   2 +-
 tests/unaligned.c     |   8 +--
 tests/util.c          | 106 +++++++++++++++++-----------------
 tests/valid.c         |  30 +++++-----
 tests/vpd.c           |  12 ++--
 22 files changed, 382 insertions(+), 352 deletions(-)
 create mode 100644 tests/cmocka-compat.h

diff --git a/tests/alias.c b/tests/alias.c
index 163d9a7..1e99751 100644
--- a/tests/alias.c
+++ b/tests/alias.c
@@ -2,7 +2,7 @@
 #include <stdint.h>
 #include <setjmp.h>
 #include <stdio.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include "strbuf.h"
 #include "util.h"
 #include "alias.h"
@@ -59,9 +59,9 @@ ssize_t __wrap_write(int fd, const void *buf, size_t count)
 		start = (const char *)buf + sizeof(BINDINGS_FILE_HEADER) - 1;
 	else
 		start = buf;
-	binding = mock_ptr_type(char *);
+	binding = mock_ptr_type(const char *);
 	start = strstr(start, binding);
-	check_expected(count);
+	check_expected_uint(count);
 	assert_ptr_not_equal(start, NULL);
 	return set_errno__(mock_type(int));
 }
@@ -80,12 +80,12 @@ int __wrap_dm_get_wwid(const char *name, char *uuid, int uuid_len)
 {
 	int ret;
 
-	check_expected(name);
-	check_expected(uuid_len);
+	check_expected_ptr(name);
+	check_expected_int(uuid_len);
 	assert_non_null(uuid);
 	ret = mock_type(int);
 	if (ret == DMP_OK)
-		strcpy(uuid, mock_ptr_type(char *));
+		strcpy(uuid, mock_ptr_type(const char *));
 	return ret;
 }
 
@@ -389,7 +389,7 @@ static void sd_fd_many(void **state)
 
 	for (i = 1; i < 5000; i++) {
 		rc = format_devname__(buf, i, sizeof(buf), "MPATH");
-		assert_in_range(rc, 6, 8);
+		assert_int_in_range(rc, 6, 8);
 		rc = scan_devname(buf, "MPATH");
 		assert_int_equal(rc, i);
 	}
@@ -404,7 +404,7 @@ static void sd_fd_random(void **state)
 	for (i = 1; i < 1000; i++) {
 		n = random() & 0xffff;
 		rc = format_devname__(buf, n, sizeof(buf), "MPATH");
-		assert_in_range(rc, 6, 9);
+		assert_int_in_range(rc, 6, 9);
 		rc = scan_devname(buf, "MPATH");
 		assert_int_equal(rc, n);
 	}
@@ -437,14 +437,14 @@ static int test_scan_devname(void)
 static void mock_unused_alias(const char *alias)
 {
 	expect_string(__wrap_dm_get_wwid, name, alias);
-	expect_value(__wrap_dm_get_wwid, uuid_len, WWID_SIZE);
+	expect_int_value(__wrap_dm_get_wwid, uuid_len, WWID_SIZE);
 	will_return(__wrap_dm_get_wwid, DMP_NOT_FOUND);
 }
 
 static void mock_self_alias(const char *alias, const char *wwid)
 {
 	expect_string(__wrap_dm_get_wwid, name, alias);
-	expect_value(__wrap_dm_get_wwid, uuid_len, WWID_SIZE);
+	expect_int_value(__wrap_dm_get_wwid, uuid_len, WWID_SIZE);
 	will_return(__wrap_dm_get_wwid, DMP_OK);
 	will_return(__wrap_dm_get_wwid, wwid);
 }
@@ -470,14 +470,14 @@ static void mock_self_alias(const char *alias, const char *wwid)
 #define mock_failed_alias(alias, wwid)					\
 	do {								\
 		expect_string(__wrap_dm_get_wwid, name, alias);		\
-		expect_value(__wrap_dm_get_wwid, uuid_len, WWID_SIZE);	\
+		expect_int_value(__wrap_dm_get_wwid, uuid_len, WWID_SIZE);	\
 		will_return(__wrap_dm_get_wwid, DMP_NOT_FOUND);		\
 	} while (0)
 
 #define mock_used_alias(alias, wwid)					\
 	do {								\
 		expect_string(__wrap_dm_get_wwid, name, alias);		\
-		expect_value(__wrap_dm_get_wwid, uuid_len, WWID_SIZE);	\
+		expect_int_value(__wrap_dm_get_wwid, uuid_len, WWID_SIZE);	\
 		will_return(__wrap_dm_get_wwid, DMP_OK);		\
 		will_return(__wrap_dm_get_wwid, "WWID_USED");		\
 		expect_condlog(3, USED_STR(alias, wwid));		\
@@ -504,7 +504,7 @@ static void mock_bindings_file__(const char *content, bool conflict_ok)
 			continue;
 
 		rc = add_binding(&global_bindings, alias, wwid);
-		assert_in_set(rc, values, conflict_ok ? 2 : 1);
+		assert_int_in_set(rc, values, (conflict_ok ? 2 : 1));
 	}
 }
 
@@ -1260,7 +1260,7 @@ static void al_a(void **state)
 	static const char ln[] = "MPATHa WWIDa\n";
 	char *alias;
 
-	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+	expect_uint_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
 	will_return(__wrap_write, ln);
 	will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
 	will_return(__wrap_rename, 0);
@@ -1279,7 +1279,7 @@ static void al_zz(void **state)
 	static const char ln[] = "MPATHzz WWIDzz\n";
 	char *alias;
 
-	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+	expect_uint_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
 	will_return(__wrap_write, ln);
 	will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
 	will_return(__wrap_rename, 0);
@@ -1318,10 +1318,10 @@ static void al_write_partial(void **state)
 	static const char ln[] = "MPATHa WWIDa\n";
 	char *alias;
 
-	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+	expect_uint_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
 	will_return(__wrap_write, ln);
 	will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln) - 1);
-	expect_value(__wrap_write, count, 1);
+	expect_uint_value(__wrap_write, count, 1);
 	will_return(__wrap_write, ln + sizeof(ln) - 2);
 	will_return(__wrap_write, 1);
 	will_return(__wrap_rename, 0);
@@ -1340,10 +1340,10 @@ static void al_write_short(void **state)
 	static const char ln[] = "MPATHa WWIDa\n";
 	char *alias;
 
-	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+	expect_uint_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
 	will_return(__wrap_write, ln);
 	will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln) - 1);
-	expect_value(__wrap_write, count, 1);
+	expect_uint_value(__wrap_write, count, 1);
 	will_return(__wrap_write, ln + sizeof(ln) - 2);
 	will_return(__wrap_write, 0);
 	expect_condlog(2, "write_bindings_file: short write");
@@ -1360,7 +1360,7 @@ static void al_write_err(void **state)
 	static const char ln[] = "MPATHa WWIDa\n";
 	char *alias;
 
-	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+	expect_uint_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
 	will_return(__wrap_write, ln);
 	will_return(__wrap_write, -EPERM);
 	expect_condlog(1, "failed to write new bindings file");
@@ -1376,7 +1376,7 @@ static void al_rename_err(void **state)
 	static const char ln[] = "MPATHa WWIDa\n";
 	char *alias;
 
-	expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+	expect_uint_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
 	will_return(__wrap_write, ln);
 	will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
 	will_return(__wrap_rename, -EROFS);
@@ -1408,7 +1408,7 @@ static int test_allocate_binding(void)
 	do {								\
 		static const char ln[] = BINDING_STR(alias, wwid);	\
 									\
-		expect_value(__wrap_write, count,			\
+		expect_uint_value(__wrap_write, count,			\
 			     strlen(BINDINGS_FILE_HEADER) + (len) + strlen(ln)); \
 		will_return(__wrap_write, ln);				\
 		will_return(__wrap_write,				\
diff --git a/tests/blacklist.c b/tests/blacklist.c
index ab3da61..5bf2fd1 100644
--- a/tests/blacklist.c
+++ b/tests/blacklist.c
@@ -5,7 +5,7 @@
 #include <stdarg.h>
 #include <stddef.h>
 #include <setjmp.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include "globals.c"
 #include "blacklist.h"
 #include "test-log.h"
diff --git a/tests/cli.c b/tests/cli.c
index 9e2ad3c..f008d26 100644
--- a/tests/cli.c
+++ b/tests/cli.c
@@ -8,7 +8,7 @@
 #include <stddef.h>
 #include <setjmp.h>
 #include <stdlib.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 
 #include <errno.h>
 
@@ -49,7 +49,7 @@ static void client_test_##NAME(void **state)			\
 	assert_int_equal(get_cmdvec(cmd, &v, false), R);	\
 	if (R == 0) {						\
 		assert_ptr_not_equal(v, NULL);			\
-		assert_int_equal(fingerprint(v), FPR);		\
+		assert_uint_equal(fingerprint(v), FPR);		\
 		if (GOOD)					\
 			assert_ptr_not_equal(find_handler_for_cmdvec(v), NULL); \
 		else						\
@@ -73,7 +73,7 @@ static void client_param_##NAME(void **state)			\
 								\
 	assert_int_equal(get_cmdvec(cmd, &v, false), 0);	\
 	assert_ptr_not_equal(v, NULL);				\
-	assert_int_equal(fingerprint(v), FPR);			\
+	assert_uint_equal(fingerprint(v), FPR);			\
 	assert_ptr_not_equal(find_handler_for_cmdvec(v), NULL);	\
 	assert_string_equal(((struct key *)VECTOR_SLOT(v, 1))->param, PAR); \
 	free_keys(v);						\
@@ -95,7 +95,7 @@ static void client_2param_##NAME(void **state)			\
 								\
 	assert_int_equal(get_cmdvec(cmd, &v, false), 0);	\
 	assert_ptr_not_equal(v, NULL);				\
-	assert_int_equal(fingerprint(v), FPR);			\
+	assert_uint_equal(fingerprint(v), FPR);			\
 	assert_ptr_not_equal(find_handler_for_cmdvec(v), NULL);	\
 	assert_string_equal(((struct key *)VECTOR_SLOT(v, 1))->param, PAR1); \
 	assert_string_equal(((struct key *)VECTOR_SLOT(v, N))->param, PARN); \
diff --git a/tests/cmocka-compat.h b/tests/cmocka-compat.h
new file mode 100644
index 0000000..8ec6ed2
--- /dev/null
+++ b/tests/cmocka-compat.h
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2015 Martin Wilck, SUSE LLC
+ */
+#include <cmocka.h>
+
+#if CMOCKA_VERSION < 0x020000
+#define assert_int_in_range(x, low, high) assert_in_range(x, low, high)
+#define assert_uint_in_range(x, low, high) assert_in_range(x, low, high)
+#define assert_uint_equal(x, y) assert_int_equal(x, y)
+#define check_expected_int(x) check_expected(x)
+#define check_expected_uint(x) check_expected(x)
+#define expect_int_value(f, x, y) expect_value(f, x, y)
+#define expect_uint_value(f, x, y) expect_value(f, x, y)
+#define assert_int_in_set(x, vals, count) assert_in_set(x, vals, count)
+#endif
diff --git a/tests/devt.c b/tests/devt.c
index 32edf51..3b322df 100644
--- a/tests/devt.c
+++ b/tests/devt.c
@@ -8,7 +8,7 @@
 #include <stddef.h>
 #include <setjmp.h>
 #include <stdlib.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include "mt-udev-wrap.h"
 #include <sys/sysmacros.h>
 #include <fcntl.h>
@@ -173,7 +173,7 @@ static void test_devt2devname_real(void **state)
 	enm = udev_enumerate_new(udev);
 	assert_non_null(enm);
 	r = udev_enumerate_add_match_subsystem(enm, "block");
-	assert_in_range(r, 0, INT_MAX);
+	assert_int_in_range(r, 0, INT_MAX);
 	r = udev_enumerate_scan_devices(enm);
 	first = udev_enumerate_get_list_entry(enm);
 	udev_list_entry_foreach(item, first) {
diff --git a/tests/directio.c b/tests/directio.c
index 5344434..fa1e553 100644
--- a/tests/directio.c
+++ b/tests/directio.c
@@ -10,7 +10,7 @@
 #include <stddef.h>
 #include <setjmp.h>
 #include <stdlib.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include "wrap64.h"
 #include "globals.c"
 #include "../libmultipath/checkers/directio.c"
@@ -38,7 +38,7 @@ int WRAP_IOCTL(int fd, ioctl_request_t request, void *argp)
 	int *blocksize = (int *)argp;
 
 	if (test_dev) {
-		mock_type(int);
+		(void)mock_type(int);
 		return REAL_IOCTL(fd, request, argp);
 	}
 
@@ -51,7 +51,12 @@ int WRAP_IOCTL(int fd, ioctl_request_t request, void *argp)
 	 * BLKSZGET must be cast to "int" and back to "unsigned long",
 	 * otherwise the assertion below will fail.
 	 */
+#ifdef __GLIBC__
+	assert_uint_equal(request, (ioctl_request_t)BLKBSZGET);
+#else
 	assert_int_equal(request, (ioctl_request_t)BLKBSZGET);
+#endif
+
 	assert_non_null(blocksize);
 	*blocksize = mock_type(int);
 	return 0;
@@ -138,9 +143,9 @@ int WRAP_IO_GETEVENTS(io_context_t ctx, long min_nr, long nr,
 			struct io_event *events, struct timespec *timeout)
 {
 	int nr_evs;
-	struct timespec *sleep_tmo;
+	const struct timespec *sleep_tmo;
 	int i;
-	struct io_event *evs;
+	const struct io_event *evs;
 
 	assert_non_null(timeout);
 	nr_evs = mock_type(int);
@@ -149,8 +154,8 @@ int WRAP_IO_GETEVENTS(io_context_t ctx, long min_nr, long nr,
 		return 0;
 	if (test_dev) {
 		int n = 0;
-		mock_ptr_type(struct timespec *);
-		mock_ptr_type(struct io_event *);
+		(void)mock_ptr_type(const struct timespec *);
+		(void)mock_ptr_type(const struct io_event *);
 
 		condlog(2, "min_nr = %ld nr_evs = %d", min_nr, nr_evs);
 		while (n < nr_evs) {
@@ -160,7 +165,7 @@ int WRAP_IO_GETEVENTS(io_context_t ctx, long min_nr, long nr,
 		}
 		assert_int_equal(nr_evs, n);
 	} else {
-		sleep_tmo = mock_ptr_type(struct timespec *);
+		sleep_tmo = mock_ptr_type(const struct timespec *);
 		if (sleep_tmo) {
 			if (sleep_tmo->tv_sec < 0)
 				nanosleep(timeout, NULL);
@@ -171,7 +176,7 @@ int WRAP_IO_GETEVENTS(io_context_t ctx, long min_nr, long nr,
 			errno = -nr_evs;
 			return -1;
 		}
-		evs = mock_ptr_type(struct io_event *);
+		evs = mock_ptr_type(const struct io_event *);
 		for (i = 0; i < nr_evs; i++)
 			events[i] = evs[i];
 	}
@@ -259,7 +264,7 @@ static void do_libcheck_init(struct checker *c, int blocksize, int timeout,
 		*req = ct->req;
 	if (!test_dev)
 		/* don't check fake blocksize on real devices */
-		assert_int_equal(ct->req->blksize, blocksize);
+		assert_uint_equal(ct->req->blksize, blocksize);
 }
 
 static int is_checker_running(struct checker *c)
diff --git a/tests/dmevents.c b/tests/dmevents.c
index 6df4f03..c94ab01 100644
--- a/tests/dmevents.c
+++ b/tests/dmevents.c
@@ -10,7 +10,7 @@
 #include <stddef.h>
 #include <setjmp.h>
 #include <stdlib.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -244,7 +244,7 @@ int WRAP_IOCTL(int fd, unsigned long request, void *argp)
 {
 	condlog(1, "%s %ld", __func__, request);
 	assert_int_equal(fd, waiter->fd);
-	assert_int_equal(request, DM_DEV_ARM_POLL);
+	assert_uint_equal(request, DM_DEV_ARM_POLL);
 	return mock_type(int);
 }
 
@@ -295,7 +295,7 @@ void __wrap_dm_task_destroy(struct dm_task *dmt)
 
 int __wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout)
 {
-	assert_int_equal(nfds, 1);
+	assert_uint_equal(nfds, 1);
 	assert_int_equal(timeout, -1);
 	assert_int_equal(fds->fd, waiter->fd);
 	assert_int_equal(fds->events, POLLIN);
@@ -304,7 +304,7 @@ int __wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout)
 
 void __wrap_remove_map_by_alias(const char *alias, struct vectors * vecs)
 {
-	check_expected(alias);
+	check_expected_ptr(alias);
 	assert_ptr_equal(vecs, waiter->vecs);
 }
 
@@ -315,7 +315,7 @@ int __wrap_update_multipath(struct vectors *vecs, char *mapname)
 {
 	int fail;
 
-	check_expected(mapname);
+	check_expected_ptr(mapname);
 	assert_ptr_equal(vecs, waiter->vecs);
 	fail = mock_type(int);
 	if (fail) {
@@ -435,7 +435,7 @@ static void test_watch_dmevents_good0(void **state)
 	/* verify foo is being watched */
 	dev_evt = find_dmevents("foo");
 	assert_ptr_not_equal(dev_evt, NULL);
-	assert_int_equal(dev_evt->evt_nr, 5);
+	assert_uint_equal(dev_evt->evt_nr, 5);
 	assert_int_equal(dev_evt->action, EVENT_NOTHING);
 	assert_int_equal(VECTOR_SIZE(waiter->events), 1);
 	unwatch_dmevents("foo");
@@ -459,14 +459,14 @@ static void test_watch_dmevents_good1(void **state)
 	assert_int_equal(watch_dmevents("foo"), 0);
 	dev_evt = find_dmevents("foo");
 	assert_ptr_not_equal(dev_evt, NULL);
-	assert_int_equal(dev_evt->evt_nr, 5);
+	assert_uint_equal(dev_evt->evt_nr, 5);
 	assert_int_equal(dev_evt->action, EVENT_NOTHING);
 	assert_int_equal(add_dm_device_event("foo", 1, 6), 0);
 	will_return(__wrap_dm_geteventnr, 0);
 	assert_int_equal(watch_dmevents("foo"), 0);
 	dev_evt = find_dmevents("foo");
 	assert_ptr_not_equal(dev_evt, NULL);
-	assert_int_equal(dev_evt->evt_nr, 6);
+	assert_uint_equal(dev_evt->evt_nr, 6);
 	assert_int_equal(dev_evt->action, EVENT_NOTHING);
 	assert_int_equal(VECTOR_SIZE(waiter->events), 1);
 	unwatch_dmevents("foo");
@@ -490,18 +490,18 @@ static void test_watch_dmevents_good2(void **state)
 	assert_int_equal(watch_dmevents("foo"), 0);
 	dev_evt = find_dmevents("foo");
 	assert_ptr_not_equal(dev_evt, NULL);
-	assert_int_equal(dev_evt->evt_nr, 5);
+	assert_uint_equal(dev_evt->evt_nr, 5);
 	assert_int_equal(dev_evt->action, EVENT_NOTHING);
 	assert_ptr_equal(find_dmevents("bar"), NULL);
 	will_return(__wrap_dm_geteventnr, 0);
 	assert_int_equal(watch_dmevents("bar"), 0);
 	dev_evt = find_dmevents("foo");
 	assert_ptr_not_equal(dev_evt, NULL);
-	assert_int_equal(dev_evt->evt_nr, 5);
+	assert_uint_equal(dev_evt->evt_nr, 5);
 	assert_int_equal(dev_evt->action, EVENT_NOTHING);
 	dev_evt = find_dmevents("bar");
 	assert_ptr_not_equal(dev_evt, NULL);
-	assert_int_equal(dev_evt->evt_nr, 7);
+	assert_uint_equal(dev_evt->evt_nr, 7);
 	assert_int_equal(dev_evt->action, EVENT_NOTHING);
 	assert_int_equal(VECTOR_SIZE(waiter->events), 2);
 	unwatch_all_dmevents();
@@ -597,15 +597,15 @@ static void test_get_events_good1(void **state)
 	assert_int_equal(dm_get_events(), 0);
 	dev_evt = find_dmevents("foo");
 	assert_ptr_not_equal(dev_evt, NULL);
-	assert_int_equal(dev_evt->evt_nr, 6);
+	assert_uint_equal(dev_evt->evt_nr, 6);
 	assert_int_equal(dev_evt->action, EVENT_UPDATE);
 	dev_evt = find_dmevents("bar");
 	assert_ptr_not_equal(dev_evt, NULL);
-	assert_int_equal(dev_evt->evt_nr, 7);
+	assert_uint_equal(dev_evt->evt_nr, 7);
 	assert_int_equal(dev_evt->action, EVENT_NOTHING);
 	dev_evt = find_dmevents("xyzzy");
 	assert_ptr_not_equal(dev_evt, NULL);
-	assert_int_equal(dev_evt->evt_nr, 8);
+	assert_uint_equal(dev_evt->evt_nr, 8);
 	assert_int_equal(dev_evt->action, EVENT_REMOVE);
 	assert_ptr_equal(find_dmevents("baz"), NULL);
 	assert_ptr_equal(find_dmevents("qux"), NULL);
@@ -632,12 +632,12 @@ static void test_dmevent_loop_bad0(void **state)
 	assert_int_equal(dmevent_loop(), 1);
 	dev_evt = find_dmevents("foo");
 	assert_ptr_not_equal(dev_evt, NULL);
-	assert_int_equal(dev_evt->evt_nr, 5);
+	assert_uint_equal(dev_evt->evt_nr, 5);
 	assert_int_equal(dev_evt->action, EVENT_NOTHING);
 	dev = find_dm_device("foo");
 	assert_ptr_not_equal(dev, NULL);
-	assert_int_equal(dev->evt_nr, 6);
-	assert_int_equal(dev->update_nr, 5);
+	assert_uint_equal(dev->evt_nr, 6);
+	assert_uint_equal(dev->update_nr, 5);
 }
 
 /* arm_dm_event_poll's ioctl fails. Nothing happens */
@@ -654,12 +654,12 @@ static void test_dmevent_loop_bad1(void **state)
 	assert_int_equal(dmevent_loop(), 1);
 	dev_evt = find_dmevents("foo");
 	assert_ptr_not_equal(dev_evt, NULL);
-	assert_int_equal(dev_evt->evt_nr, 5);
+	assert_uint_equal(dev_evt->evt_nr, 5);
 	assert_int_equal(dev_evt->action, EVENT_NOTHING);
 	dev = find_dm_device("foo");
 	assert_ptr_not_equal(dev, NULL);
-	assert_int_equal(dev->evt_nr, 6);
-	assert_int_equal(dev->update_nr, 5);
+	assert_uint_equal(dev->evt_nr, 6);
+	assert_uint_equal(dev->update_nr, 5);
 }
 
 /* dm_get_events fails. Nothing happens */
@@ -677,12 +677,12 @@ static void test_dmevent_loop_bad2(void **state)
 	assert_int_equal(dmevent_loop(), 1);
 	dev_evt = find_dmevents("foo");
 	assert_ptr_not_equal(dev_evt, NULL);
-	assert_int_equal(dev_evt->evt_nr, 5);
+	assert_uint_equal(dev_evt->evt_nr, 5);
 	assert_int_equal(dev_evt->action, EVENT_NOTHING);
 	dev = find_dm_device("foo");
 	assert_ptr_not_equal(dev, NULL);
-	assert_int_equal(dev->evt_nr, 6);
-	assert_int_equal(dev->update_nr, 5);
+	assert_uint_equal(dev->evt_nr, 6);
+	assert_uint_equal(dev->update_nr, 5);
 }
 
 /* verify dmevent_loop runs successfully when no devices are being
@@ -743,20 +743,20 @@ static void test_dmevent_loop_good1(void **state)
 	assert_int_equal(VECTOR_SIZE(data.dm_devices), 3);
 	dev_evt = find_dmevents("foo");
 	assert_ptr_not_equal(dev_evt, NULL);
-	assert_int_equal(dev_evt->evt_nr, 6);
+	assert_uint_equal(dev_evt->evt_nr, 6);
 	assert_int_equal(dev_evt->action, EVENT_NOTHING);
 	dev = find_dm_device("foo");
 	assert_ptr_not_equal(dev, NULL);
-	assert_int_equal(dev->evt_nr, 6);
-	assert_int_equal(dev->update_nr, 6);
+	assert_uint_equal(dev->evt_nr, 6);
+	assert_uint_equal(dev->update_nr, 6);
 	dev_evt = find_dmevents("bar");
 	assert_ptr_not_equal(dev_evt, NULL);
-	assert_int_equal(dev_evt->evt_nr, 7);
+	assert_uint_equal(dev_evt->evt_nr, 7);
 	assert_int_equal(dev_evt->action, EVENT_NOTHING);
 	dev = find_dm_device("bar");
 	assert_ptr_not_equal(dev, NULL);
-	assert_int_equal(dev->evt_nr, 7);
-	assert_int_equal(dev->update_nr, 7);
+	assert_uint_equal(dev->evt_nr, 7);
+	assert_uint_equal(dev->update_nr, 7);
 	assert_ptr_equal(find_dmevents("xyzzy"), NULL);
 	assert_ptr_equal(find_dm_device("xyzzy"), NULL);
 }
@@ -791,20 +791,20 @@ static void test_dmevent_loop_good2(void **state)
 	assert_int_equal(VECTOR_SIZE(data.dm_devices), 2);
 	dev_evt = find_dmevents("foo");
 	assert_ptr_not_equal(dev_evt, NULL);
-	assert_int_equal(dev_evt->evt_nr, 6);
+	assert_uint_equal(dev_evt->evt_nr, 6);
 	assert_int_equal(dev_evt->action, EVENT_NOTHING);
 	dev = find_dm_device("foo");
 	assert_ptr_not_equal(dev, NULL);
-	assert_int_equal(dev->evt_nr, 6);
-	assert_int_equal(dev->update_nr, 6);
+	assert_uint_equal(dev->evt_nr, 6);
+	assert_uint_equal(dev->update_nr, 6);
 	dev_evt = find_dmevents("bar");
 	assert_ptr_not_equal(dev_evt, NULL);
-	assert_int_equal(dev_evt->evt_nr, 9);
+	assert_uint_equal(dev_evt->evt_nr, 9);
 	assert_int_equal(dev_evt->action, EVENT_NOTHING);
 	dev = find_dm_device("bar");
 	assert_ptr_not_equal(dev, NULL);
-	assert_int_equal(dev->evt_nr, 9);
-	assert_int_equal(dev->update_nr, 9);
+	assert_uint_equal(dev->evt_nr, 9);
+	assert_uint_equal(dev->update_nr, 9);
 	assert_ptr_equal(find_dmevents("baz"), NULL);
 	assert_ptr_equal(find_dm_device("baz"), NULL);
 }
@@ -831,8 +831,8 @@ static void test_dmevent_loop_good3(void **state)
 	assert_int_equal(VECTOR_SIZE(data.dm_devices), 1);
 	dev = find_dm_device("bar");
 	assert_ptr_not_equal(dev, NULL);
-	assert_int_equal(dev->evt_nr, 9);
-	assert_int_equal(dev->update_nr, 9);
+	assert_uint_equal(dev->evt_nr, 9);
+	assert_uint_equal(dev->update_nr, 9);
 	assert_ptr_equal(find_dmevents("foo"), NULL);
 	assert_ptr_equal(find_dmevents("bar"), NULL);
 	assert_ptr_equal(find_dm_device("foo"), NULL);
diff --git a/tests/features.c b/tests/features.c
index 31f978f..35f15de 100644
--- a/tests/features.c
+++ b/tests/features.c
@@ -2,7 +2,7 @@
 #include <stddef.h>
 #include <stdarg.h>
 #include <setjmp.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 
 #include "../libmultipath/propsel.c"
 #include "globals.c"
diff --git a/tests/hwtable.c b/tests/hwtable.c
index 631b882..6021bfd 100644
--- a/tests/hwtable.c
+++ b/tests/hwtable.c
@@ -7,7 +7,7 @@
 #include <stddef.h>
 #include <setjmp.h>
 #include <stdlib.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include "mt-udev-wrap.h"
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -505,7 +505,7 @@ static void replicate_config(const struct hwt_state *hwt, bool local)
 	DUMP_CFG_STR(cfg2);
 #endif
 
-	assert_int_equal(strlen(cfg2), strlen(cfg1));
+	assert_uint_equal(strlen(cfg2), strlen(cfg1));
 	assert_string_equal(cfg2, cfg1);
 	free(cfg1);
 	free(cfg2);
diff --git a/tests/mapinfo.c b/tests/mapinfo.c
index 3651160..912d4f4 100644
--- a/tests/mapinfo.c
+++ b/tests/mapinfo.c
@@ -25,7 +25,7 @@
 #include <stdlib.h>
 #include <fcntl.h>
 #include <errno.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include "util.h"
 #include "devmapper.h"
 #include "globals.c"
@@ -87,10 +87,10 @@ void __wrap_dm_task_destroy(struct dm_task *t)
 {
 }
 
-struct dm_task *__wrap_dm_task_create(int task)
+const struct dm_task *__wrap_dm_task_create(int task)
 {
-	check_expected(task);
-	return mock_ptr_type(void *);
+	check_expected_int(task);
+	return mock_ptr_type(const struct dm_task *);
 }
 
 int __wrap_dm_task_run(struct dm_task *t)
@@ -114,25 +114,25 @@ int __wrap_dm_task_get_errno(struct dm_task *t)
 
 int __wrap_dm_task_set_name(struct dm_task *t, const char *name)
 {
-	check_expected(name);
+	check_expected_ptr(name);
 	return mock_type(int);
 }
 
 int __wrap_dm_task_set_uuid(struct dm_task *t, const char *uuid)
 {
-	check_expected(uuid);
+	check_expected_ptr(uuid);
 	return mock_type(int);
 }
 
 int __wrap_dm_task_set_major(struct dm_task *t, int val)
 {
-	check_expected(val);
+	check_expected_int(val);
 	return mock_type(int);
 }
 
 int __wrap_dm_task_set_minor(struct dm_task *t, int val)
 {
-	check_expected(val);
+	check_expected_int(val);
 	return mock_type(int);
 }
 
@@ -151,22 +151,22 @@ int __wrap_dm_task_get_info(struct dm_task *t, struct dm_info *dmi)
 
 	assert_non_null(dmi);
 	if (rc) {
-		struct dm_info *info = mock_ptr_type(struct dm_info *);
+		const struct dm_info *info = mock_ptr_type(const struct dm_info *);
 
 		memcpy(dmi, info, sizeof(*dmi));
 	}
 	return rc;
 }
 
-void * __wrap_dm_get_next_target(struct dm_task *dmt, void *next,
+const void * __wrap_dm_get_next_target(struct dm_task *dmt, void *next,
 				uint64_t *start, uint64_t *length,
-				char **target_type, char **params)
+				const char **target_type, const char **params)
 {
 	*start = 0;
 	*length = mock_type(uint64_t);
-	*target_type = mock_ptr_type(char *);
-	*params = mock_ptr_type(char *);
-	return mock_ptr_type(void *);
+	*target_type = mock_ptr_type(const char *);
+	*params = mock_ptr_type(const char *);
+	return mock_ptr_type(const void *);
 }
 
 static void mock_dm_get_next_target(uint64_t len, const char *target_type,
@@ -191,11 +191,11 @@ const char *__wrap_dm_task_get_uuid(struct dm_task *t)
 static void mock_mapinfo_name_1(int ioctl_nr, int create_rc, const char *name,
 				int name_rc, int run_rc, int err)
 {
-	expect_value(__wrap_dm_task_create, task, ioctl_nr);
+	expect_int_value(__wrap_dm_task_create, task, ioctl_nr);
 	will_return(__wrap_dm_task_create, create_rc);
 	if (create_rc == 0)
 		return;
-	expect_value(__wrap_dm_task_set_name, name, name);
+	expect_string(__wrap_dm_task_set_name, name, name);
 	will_return(__wrap_dm_task_set_name, name_rc);
 	if (name_rc == 0)
 		return;
@@ -223,7 +223,7 @@ static void test_mapinfo_bad_mapid(void **state)
 	int rc;
 
 	/* can't use mock_mapinfo_name() here because of invalid id type */
-	expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
+	expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
 	will_return(__wrap_dm_task_create, 1);
 	rc = libmp_mapinfo(DM_MAP_BY_NAME + 100,
 			   (mapid_t) { .str = "foo", },
@@ -646,9 +646,9 @@ static void test_mapinfo_bad_set_uuid(void **state)
 {
 	int rc;
 
-	expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
+	expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
 	will_return(__wrap_dm_task_create, 1);
-	expect_value(__wrap_dm_task_set_uuid, uuid, "foo");
+	expect_string(__wrap_dm_task_set_uuid, uuid, "foo");
 	will_return(__wrap_dm_task_set_uuid, 0);
 	rc = libmp_mapinfo(DM_MAP_BY_UUID,
 			   (mapid_t) { .str = "foo", },
@@ -660,9 +660,9 @@ static void test_mapinfo_bad_set_dev_01(void **state)
 {
 	int rc;
 
-	expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
+	expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
 	will_return(__wrap_dm_task_create, 1);
-	expect_value(__wrap_dm_task_set_major, val, 254);
+	expect_int_value(__wrap_dm_task_set_major, val, 254);
 	will_return(__wrap_dm_task_set_major, 0);
 	rc = libmp_mapinfo(DM_MAP_BY_DEV,
 			   (mapid_t) { ._u = { 254, 123 } },
@@ -674,11 +674,11 @@ static void test_mapinfo_bad_set_dev_02(void **state)
 {
 	int rc;
 
-	expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
+	expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
 	will_return(__wrap_dm_task_create, 1);
-	expect_value(__wrap_dm_task_set_major, val, 254);
+	expect_int_value(__wrap_dm_task_set_major, val, 254);
 	will_return(__wrap_dm_task_set_major, 1);
-	expect_value(__wrap_dm_task_set_minor, val, 123);
+	expect_int_value(__wrap_dm_task_set_minor, val, 123);
 	will_return(__wrap_dm_task_set_minor, 0);
 	rc = libmp_mapinfo(DM_MAP_BY_DEV,
 			   (mapid_t) { ._u = { 254, 123 } },
@@ -691,9 +691,9 @@ static void test_mapinfo_bad_set_dev_03(void **state)
 	int rc;
 	dev_t devt = makedev(254, 123);
 
-	expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
+	expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
 	will_return(__wrap_dm_task_create, 1);
-	expect_value(__wrap_dm_task_set_major, val, 254);
+	expect_int_value(__wrap_dm_task_set_major, val, 254);
 	will_return(__wrap_dm_task_set_major, 0);
 	rc = libmp_mapinfo(DM_MAP_BY_DEVT,
 			   (mapid_t) { .devt = devt },
@@ -706,11 +706,11 @@ static void test_mapinfo_bad_set_dev_04(void **state)
 	int rc;
 	dev_t devt = makedev(254, 123);
 
-	expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
+	expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
 	will_return(__wrap_dm_task_create, 1);
-	expect_value(__wrap_dm_task_set_major, val, 254);
+	expect_int_value(__wrap_dm_task_set_major, val, 254);
 	will_return(__wrap_dm_task_set_major, 1);
-	expect_value(__wrap_dm_task_set_minor, val, 123);
+	expect_int_value(__wrap_dm_task_set_minor, val, 123);
 	will_return(__wrap_dm_task_set_minor, 0);
 	rc = libmp_mapinfo(DM_MAP_BY_DEVT,
 			   (mapid_t) { .devt = devt },
@@ -738,9 +738,9 @@ static void test_mapinfo_good_by_uuid_info(void **state)
 	int rc;
 	struct dm_info dmi;
 
-	expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
+	expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
 	will_return(__wrap_dm_task_create, 1);
-	expect_value(__wrap_dm_task_set_uuid, uuid, "foo");
+	expect_string(__wrap_dm_task_set_uuid, uuid, "foo");
 	will_return(__wrap_dm_task_set_uuid, 1);
 	will_return(__wrap_dm_task_run, 1);
 	WRAP_DM_TASK_GET_INFO(1);
@@ -757,11 +757,11 @@ static void test_mapinfo_good_by_dev_info(void **state)
 	int rc;
 	struct dm_info dmi;
 
-	expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
+	expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
 	will_return(__wrap_dm_task_create, 1);
-	expect_value(__wrap_dm_task_set_major, val, 254);
+	expect_int_value(__wrap_dm_task_set_major, val, 254);
 	will_return(__wrap_dm_task_set_major, 1);
-	expect_value(__wrap_dm_task_set_minor, val, 123);
+	expect_int_value(__wrap_dm_task_set_minor, val, 123);
 	will_return(__wrap_dm_task_set_minor, 1);
 	will_return(__wrap_dm_task_run, 1);
 	WRAP_DM_TASK_GET_INFO(1);
@@ -779,11 +779,11 @@ static void test_mapinfo_good_by_devt_info(void **state)
 	int rc;
 	struct dm_info dmi;
 
-	expect_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
+	expect_int_value(__wrap_dm_task_create, task, DM_DEVICE_INFO);
 	will_return(__wrap_dm_task_create, 1);
-	expect_value(__wrap_dm_task_set_major, val, 254);
+	expect_int_value(__wrap_dm_task_set_major, val, 254);
 	will_return(__wrap_dm_task_set_major, 1);
-	expect_value(__wrap_dm_task_set_minor, val, 123);
+	expect_int_value(__wrap_dm_task_set_minor, val, 123);
 	will_return(__wrap_dm_task_set_minor, 1);
 	will_return(__wrap_dm_task_run, 1);
 	WRAP_DM_TASK_GET_INFO(1);
@@ -871,7 +871,7 @@ static void test_mapinfo_good_size(void **state)
 			   (mapid_t) { .str = "foo", },
 			   (mapinfo_t) { .size = &size });
 	assert_int_equal(rc, DMP_OK);
-	assert_int_equal(size, 12345);
+	assert_uint_equal(size, 12345);
 }
 
 static void test_mapinfo_bad_next_target_01(void **state)
@@ -909,7 +909,7 @@ static void test_mapinfo_bad_next_target_02(void **state)
 			   (mapid_t) { .str = "foo", },
 			   (mapinfo_t) { .dmi = &dmi, .name = name, .uuid = uuid, .size = &size });
 	assert_int_equal(rc, DMP_EMPTY);
-	assert_int_equal(size, 0);
+	assert_uint_equal(size, 0);
 	assert_memory_equal(&dmi, &MPATH_DMI_02, sizeof(dmi));
 	assert_true(!strcmp(name, MPATH_NAME_01));
 	assert_true(!strcmp(uuid, MPATH_UUID_01));
@@ -1102,7 +1102,7 @@ static void test_mapinfo_no_table_02(void **state)
 			   (mapid_t) { .str = "foo", },
 			   (mapinfo_t) { .dmi = &dmi, .name = name, .uuid = uuid, .size = &size });
 	assert_int_equal(rc, DMP_EMPTY);
-	assert_int_equal(size, 0);
+	assert_uint_equal(size, 0);
 	assert_memory_equal(&dmi, &MPATH_DMI_02, sizeof(dmi));
 	assert_true(!strcmp(name, MPATH_NAME_01));
 	assert_true(!strcmp(uuid, MPATH_UUID_01));
@@ -1369,7 +1369,7 @@ static void test_mapinfo_good_all_01(void **state)
 	assert_int_equal(rc, DMP_OK);
 	assert_non_null(status);
 	assert_non_null(target);
-	assert_int_equal(size, 12345);
+	assert_uint_equal(size, 12345);
 	assert_memory_equal(&dmi, &MPATH_DMI_01, sizeof(dmi));
 	assert_true(!strcmp(target, MPATH_TARGET_01));
 	assert_true(!strcmp(status, MPATH_STATUS_01));
diff --git a/tests/mpathvalid.c b/tests/mpathvalid.c
index 699dce5..1b060fe 100644
--- a/tests/mpathvalid.c
+++ b/tests/mpathvalid.c
@@ -10,7 +10,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include "mt-udev-wrap.h"
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include "structs.h"
 #include "config.h"
 #include "mpath_valid.h"
@@ -66,11 +66,11 @@ int __wrap_is_path_valid(const char *name, struct config *conf, struct path *pp,
 	assert_ptr_not_equal(pp, NULL);
 	assert_true(check_multipathd);
 
-	assert_int_equal(findmp, conf->find_multipaths);	
+	assert_int_equal(findmp, conf->find_multipaths);
 	if (r == MPATH_IS_ERROR || r == MPATH_IS_NOT_VALID)
 		return r;
-	
-	strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE);
+
+	strlcpy(pp->wwid, mock_ptr_type(const char *), WWID_SIZE);
 	return r;
 }
 
@@ -223,7 +223,7 @@ static void check_mpathvalid_init(int findmp, int prio, int log_style)
 	check_config(true);
 	assert_int_equal(logsink, log_style);
 	assert_int_equal(libmp_verbosity, prio);
-	assert_int_equal(findmp_to_mode(findmp), mpathvalid_get_mode());
+	assert_uint_equal(findmp_to_mode(findmp), mpathvalid_get_mode());
 }
 
 static void check_mpathvalid_exit(void)
@@ -262,9 +262,9 @@ static void test_mpathvalid_exit(void **state)
 static void test_mpathvalid_get_mode_bad(void **state)
 {
 #if 1
-	assert_int_equal(mpathvalid_get_mode(), MPATH_MODE_ERROR);
+	assert_uint_equal(mpathvalid_get_mode(), MPATH_MODE_ERROR);
 #else
-	assert_int_equal(mpathvalid_get_mode(), 1);
+	assert_uint_equal(mpathvalid_get_mode(), 1);
 #endif
 }
 
@@ -298,7 +298,7 @@ static void check_mpathvalid_reload_config(int findmp)
 	will_return(__wrap_init_config, findmp);
 	assert_int_equal(mpathvalid_reload_config(), 0);
 	check_config(true);
-	assert_int_equal(findmp_to_mode(findmp), mpathvalid_get_mode());
+	assert_uint_equal(findmp_to_mode(findmp), mpathvalid_get_mode());
 }
 
 static void test_mpathvalid_reload_config_good(void **state)
diff --git a/tests/parser.c b/tests/parser.c
index efa7bf2..0e718c9 100644
--- a/tests/parser.c
+++ b/tests/parser.c
@@ -8,7 +8,7 @@
 #include <stddef.h>
 #include <setjmp.h>
 #include <stdlib.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 // #include "list.h"
 #include "parser.h"
 #include "vector.h"
diff --git a/tests/pgpolicy.c b/tests/pgpolicy.c
index ed050fa..aec16ec 100644
--- a/tests/pgpolicy.c
+++ b/tests/pgpolicy.c
@@ -9,7 +9,7 @@
 #include <stddef.h>
 #include <setjmp.h>
 #include <stdlib.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include <stdio.h>
 
 #include "globals.c"
diff --git a/tests/strbuf.c b/tests/strbuf.c
index 1cc1618..e4a6871 100644
--- a/tests/strbuf.c
+++ b/tests/strbuf.c
@@ -9,7 +9,7 @@
 #include <stddef.h>
 #include <setjmp.h>
 #include <stdlib.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include <errno.h>
 #include "strbuf.h"
 #include "debug.h"
@@ -24,7 +24,11 @@ void *__wrap_realloc(void *ptr, size_t size)
 	if (!mock_realloc)
 		return __real_realloc(ptr, size);
 
+#pragma GCC diagnostic push
+#pragma GCC   diagnostic ignored "-Wcast-qual"
 	p = mock_ptr_type(void *);
+#pragma GCC diagnostic pop
+
 	condlog(4, "%s: %p, %zu -> %p", __func__, ptr, size, p);
 	return p;
 }
@@ -35,44 +39,44 @@ static void test_strbuf_00(void **state)
 	char *p;
 
 	assert_ptr_equal(buf.buf, NULL);
-	assert_int_equal(buf.size, 0);
-	assert_int_equal(buf.offs, 0);
-	assert_int_equal(get_strbuf_len(&buf), 0);
+	assert_uint_equal(buf.size, 0);
+	assert_uint_equal(buf.offs, 0);
+	assert_uint_equal(get_strbuf_len(&buf), 0);
 	assert_string_equal(get_strbuf_str(&buf), "");
 	p = steal_strbuf_str(&buf);
 	assert_ptr_equal(p, NULL);
 
 	assert_ptr_equal(buf.buf, NULL);
-	assert_int_equal(buf.size, 0);
-	assert_int_equal(buf.offs, 0);
-	assert_int_equal(get_strbuf_len(&buf), 0);
+	assert_uint_equal(buf.size, 0);
+	assert_uint_equal(buf.offs, 0);
+	assert_uint_equal(get_strbuf_len(&buf), 0);
 	assert_string_equal(get_strbuf_str(&buf), "");
 
 	assert_int_equal(append_strbuf_str(&buf, "moin"), 4);
-	assert_int_equal(get_strbuf_len(&buf), 4);
-	assert_in_range(buf.size, 5, SIZE_MAX);
+	assert_uint_equal(get_strbuf_len(&buf), 4);
+	assert_uint_in_range(buf.size, 5, SIZE_MAX);
 	assert_string_equal(get_strbuf_str(&buf), "moin");
 	p = steal_strbuf_str(&buf);
 	assert_string_equal(p, "moin");
 	free(p);
 
 	assert_ptr_equal(buf.buf, NULL);
-	assert_int_equal(buf.size, 0);
-	assert_int_equal(buf.offs, 0);
-	assert_int_equal(get_strbuf_len(&buf), 0);
+	assert_uint_equal(buf.size, 0);
+	assert_uint_equal(buf.offs, 0);
+	assert_uint_equal(get_strbuf_len(&buf), 0);
 	assert_string_equal(get_strbuf_str(&buf), "");
 
 	assert_int_equal(append_strbuf_str(&buf, NULL), -EINVAL);
-	assert_int_equal(buf.size, 0);
-	assert_int_equal(buf.offs, 0);
-	assert_int_equal(get_strbuf_len(&buf), 0);
+	assert_uint_equal(buf.size, 0);
+	assert_uint_equal(buf.offs, 0);
+	assert_uint_equal(get_strbuf_len(&buf), 0);
 	assert_string_equal(get_strbuf_str(&buf), "");
 
 	assert_int_equal(append_strbuf_str(&buf, ""), 0);
 	/* appending a 0-length string allocates memory */
-	assert_in_range(buf.size, 1, SIZE_MAX);
-	assert_int_equal(buf.offs, 0);
-	assert_int_equal(get_strbuf_len(&buf), 0);
+	assert_uint_in_range(buf.size, 1, SIZE_MAX);
+	assert_uint_equal(buf.offs, 0);
+	assert_uint_equal(get_strbuf_len(&buf), 0);
 	assert_string_equal(get_strbuf_str(&buf), "");
 	p = steal_strbuf_str(&buf);
 	assert_string_equal(p, "");
@@ -80,9 +84,9 @@ static void test_strbuf_00(void **state)
 
 	assert_int_equal(append_strbuf_str__(&buf, "x", 0), 0);
 	/* appending a 0-length string allocates memory */
-	assert_in_range(buf.size, 1, SIZE_MAX);
-	assert_int_equal(buf.offs, 0);
-	assert_int_equal(get_strbuf_len(&buf), 0);
+	assert_uint_in_range(buf.size, 1, SIZE_MAX);
+	assert_uint_equal(buf.offs, 0);
+	assert_uint_equal(get_strbuf_len(&buf), 0);
 	assert_string_equal(get_strbuf_str(&buf), "");
 }
 
@@ -95,17 +99,17 @@ static void test_strbuf_alloc_err(void **state)
 	mock_realloc = true;
 	will_return(__wrap_realloc, NULL);
 	assert_int_equal(append_strbuf_str(&buf, "moin"), -ENOMEM);
-	assert_int_equal(buf.size, 0);
-	assert_int_equal(buf.offs, 0);
-	assert_int_equal(get_strbuf_len(&buf), 0);
+	assert_uint_equal(buf.size, 0);
+	assert_uint_equal(buf.offs, 0);
+	assert_uint_equal(get_strbuf_len(&buf), 0);
 	assert_string_equal(get_strbuf_str(&buf), "");
 
 	mock_realloc = false;
 	assert_int_equal(append_strbuf_str(&buf, "moin"), 4);
 	sz = buf.size;
-	assert_in_range(sz, 5, SIZE_MAX);
-	assert_int_equal(buf.offs, 4);
-	assert_int_equal(get_strbuf_len(&buf), 4);
+	assert_uint_in_range(sz, 5, SIZE_MAX);
+	assert_uint_equal(buf.offs, 4);
+	assert_uint_equal(get_strbuf_len(&buf), 4);
 	assert_string_equal(get_strbuf_str(&buf), "moin");
 
 	mock_realloc = true;
@@ -114,22 +118,22 @@ static void test_strbuf_alloc_err(void **state)
 	while ((rc = append_strbuf_str(&buf, " hello")) >= 0) {
 		condlog(3, "%s", get_strbuf_str(&buf));
 		assert_int_equal(rc, 6);
-		assert_int_equal(get_strbuf_len(&buf), ofs + 6);
+		assert_uint_equal(get_strbuf_len(&buf), ofs + 6);
 		assert_memory_equal(get_strbuf_str(&buf), "moin", 4);
 		assert_string_equal(get_strbuf_str(&buf) + ofs, " hello");
 		ofs = get_strbuf_len(&buf);
 	}
 	assert_int_equal(rc, -ENOMEM);
-	assert_int_equal(buf.size, sz);
-	assert_int_equal(get_strbuf_len(&buf), ofs);
+	assert_uint_equal(buf.size, sz);
+	assert_uint_equal(get_strbuf_len(&buf), ofs);
 	assert_memory_equal(get_strbuf_str(&buf), "moin", 4);
 	assert_string_equal(get_strbuf_str(&buf) + ofs - 6, " hello");
 
 	reset_strbuf(&buf);
 	assert_ptr_equal(buf.buf, NULL);
-	assert_int_equal(buf.size, 0);
-	assert_int_equal(buf.offs, 0);
-	assert_int_equal(get_strbuf_len(&buf), 0);
+	assert_uint_equal(buf.size, 0);
+	assert_uint_equal(buf.offs, 0);
+	assert_uint_equal(get_strbuf_len(&buf), 0);
 	assert_string_equal(get_strbuf_str(&buf), "");
 
 	mock_realloc = false;
@@ -158,7 +162,7 @@ static void test_strbuf_big(void **state)
 		if (i % 1000 == 0)
 			condlog(4, "%d", i);
 		assert_int_equal(append_strbuf_str(&buf, big), sizeof(big) - 1);
-		assert_int_equal(get_strbuf_len(&buf), (sizeof(big) - 1) * (i + 1));
+		assert_uint_equal(get_strbuf_len(&buf), (sizeof(big) - 1) * (i + 1));
 		assert_memory_equal(get_strbuf_str(&buf), big, sizeof(big) - 1);
 		assert_string_equal(get_strbuf_str(&buf) + get_strbuf_len(&buf)
 				    - (sizeof(big) - 1), big);
@@ -166,12 +170,12 @@ static void test_strbuf_big(void **state)
 	bbig = steal_strbuf_str(&buf);
 
 	assert_ptr_equal(buf.buf, NULL);
-	assert_int_equal(buf.size, 0);
-	assert_int_equal(buf.offs, 0);
-	assert_int_equal(get_strbuf_len(&buf), 0);
+	assert_uint_equal(buf.size, 0);
+	assert_uint_equal(buf.offs, 0);
+	assert_uint_equal(get_strbuf_len(&buf), 0);
 	assert_string_equal(get_strbuf_str(&buf), "");
 
-	assert_int_equal(strlen(bbig), i * (sizeof(big) - 1));
+	assert_uint_equal(strlen(bbig), i * (sizeof(big) - 1));
 	assert_memory_equal(bbig, big, sizeof(big) - 1);
 	free(bbig);
 }
@@ -190,23 +194,23 @@ static void test_strbuf_nul(void **state)
 	greet[5] = '\0';
 	reset_strbuf(&buf);
 	assert_int_equal(append_strbuf_str(&buf, greet), 5);
-	assert_int_equal(get_strbuf_len(&buf), 5);
+	assert_uint_equal(get_strbuf_len(&buf), 5);
 	assert_string_equal(get_strbuf_str(&buf), "hello");
 	assert_int_equal(append_strbuf_str(&buf, greet), 5);
-	assert_int_equal(get_strbuf_len(&buf), 10);
+	assert_uint_equal(get_strbuf_len(&buf), 10);
 	assert_string_equal(get_strbuf_str(&buf), "hellohello");
 
 	/* append_strbuf_str__() appends full memory, including NUL bytes */
 	reset_strbuf(&buf);
 	assert_int_equal(append_strbuf_str__(&buf, greet, sizeof(greet) - 1),
 			 sizeof(greet) - 1);
-	assert_int_equal(get_strbuf_len(&buf), sizeof(greet) - 1);
+	assert_uint_equal(get_strbuf_len(&buf), sizeof(greet) - 1);
 	assert_string_equal(get_strbuf_str(&buf), "hello");
 	assert_string_equal(get_strbuf_str(&buf) + get_strbuf_len(&buf) - 5, " sir!");
 	assert_int_equal(append_strbuf_str__(&buf, greet, sizeof(greet) - 1),
 			 sizeof(greet) - 1);
 	assert_string_equal(get_strbuf_str(&buf), "hello");
-	assert_int_equal(get_strbuf_len(&buf), 2 * (sizeof(greet) - 1));
+	assert_uint_equal(get_strbuf_len(&buf), 2 * (sizeof(greet) - 1));
 	assert_string_equal(get_strbuf_str(&buf) + get_strbuf_len(&buf) - 5, " sir!");
 }
 
@@ -318,7 +322,7 @@ static void test_print_strbuf_3(void **state)
 
 	s = get_strbuf_str(&buf);
 	condlog(3, "%s", s);
-	assert_int_equal(strlen(s), repeat * (sizeof(sentence) - 1));
+	assert_uint_equal(strlen(s), repeat * (sizeof(sentence) - 1));
 	for (i = 0; i < repeat; i++)
 		assert_int_equal(strncmp(s + i * (sizeof(sentence) - 1),
 					 sentence, sizeof(sentence) - 1), 0);
@@ -340,7 +344,7 @@ static void test_print_strbuf_4(void **state)
 
 	s = get_strbuf_str(&buf);
 	condlog(3, "%s", s);
-	assert_int_equal(strlen(s), repeat * (sizeof(sentence) - 1));
+	assert_uint_equal(strlen(s), repeat * (sizeof(sentence) - 1));
 	for (i = 0; i < repeat; i++)
 		assert_int_equal(strncmp(s + i * (sizeof(sentence) - 1),
 					 sentence, sizeof(sentence) - 1), 0);
@@ -356,24 +360,24 @@ static void test_truncate_strbuf(void **state)
 	assert_int_equal(truncate_strbuf(&buf, 0), -EFAULT);
 
 	assert_int_equal(append_strbuf_str(&buf, str), sizeof(str) - 1);
-	assert_int_equal(get_strbuf_len(&buf), sizeof(str) - 1);
+	assert_uint_equal(get_strbuf_len(&buf), sizeof(str) - 1);
 	assert_string_equal(get_strbuf_str(&buf), str);
 
 	assert_int_equal(truncate_strbuf(&buf, sizeof(str)), -ERANGE);
-	assert_int_equal(get_strbuf_len(&buf), sizeof(str) - 1);
+	assert_uint_equal(get_strbuf_len(&buf), sizeof(str) - 1);
 	assert_string_equal(get_strbuf_str(&buf), str);
 
 	assert_int_equal(truncate_strbuf(&buf, sizeof(str) - 1), 0);
-	assert_int_equal(get_strbuf_len(&buf), sizeof(str) - 1);
+	assert_uint_equal(get_strbuf_len(&buf), sizeof(str) - 1);
 	assert_string_equal(get_strbuf_str(&buf), str);
 
 	assert_int_equal(truncate_strbuf(&buf, sizeof(str) - 2), 0);
-	assert_int_equal(get_strbuf_len(&buf), sizeof(str) - 2);
+	assert_uint_equal(get_strbuf_len(&buf), sizeof(str) - 2);
 	assert_string_not_equal(get_strbuf_str(&buf), str);
 	assert_memory_equal(get_strbuf_str(&buf), str, sizeof(str) - 2);
 
 	assert_int_equal(truncate_strbuf(&buf, 5), 0);
-	assert_int_equal(get_strbuf_len(&buf), 5);
+	assert_uint_equal(get_strbuf_len(&buf), 5);
 	assert_string_not_equal(get_strbuf_str(&buf), str);
 	assert_string_equal(get_strbuf_str(&buf), "hello");
 
@@ -385,7 +389,7 @@ static void test_truncate_strbuf(void **state)
 		assert_int_equal(append_strbuf_str(&buf, str), sizeof(str) - 1);
 
 	sz1  = buf.size;
-	assert_in_range(get_strbuf_len(&buf), sz + 1, SIZE_MAX);
+	assert_uint_in_range(get_strbuf_len(&buf), sz + 1, SIZE_MAX);
 	assert_string_equal(get_strbuf_str(&buf) +
 			    get_strbuf_len(&buf) - (sizeof(str) - 1), str);
 	assert_int_equal(truncate_strbuf(&buf, get_strbuf_len(&buf) + 1),
@@ -393,20 +397,20 @@ static void test_truncate_strbuf(void **state)
 	assert_int_equal(truncate_strbuf(&buf, get_strbuf_len(&buf)), 0);
 	assert_int_equal(truncate_strbuf(&buf, get_strbuf_len(&buf)
 					 - (sizeof(str) - 1)), 0);
-	assert_in_range(get_strbuf_len(&buf), 1, sz);
+	assert_uint_in_range(get_strbuf_len(&buf), 1, sz);
 	assert_string_equal(get_strbuf_str(&buf) +
 			    get_strbuf_len(&buf) - (sizeof(str) - 1), str);
-	assert_int_equal(buf.size, sz1);
+	assert_uint_equal(buf.size, sz1);
 
 	assert_int_equal(truncate_strbuf(&buf, 5), 0);
-	assert_int_equal(get_strbuf_len(&buf), 5);
+	assert_uint_equal(get_strbuf_len(&buf), 5);
 	assert_string_equal(get_strbuf_str(&buf), "hello");
-	assert_int_equal(buf.size, sz1);
+	assert_uint_equal(buf.size, sz1);
 
 	assert_int_equal(truncate_strbuf(&buf, 0), 0);
-	assert_int_equal(get_strbuf_len(&buf), 0);
+	assert_uint_equal(get_strbuf_len(&buf), 0);
 	assert_string_equal(get_strbuf_str(&buf), "");
-	assert_int_equal(buf.size, sz1);
+	assert_uint_equal(buf.size, sz1);
 }
 
 static void test_fill_strbuf(void **state)
@@ -418,31 +422,31 @@ static void test_fill_strbuf(void **state)
 	assert_int_equal(fill_strbuf(&buf, '+', -5), -EINVAL);
 
 	assert_int_equal(fill_strbuf(&buf, '+', 0), 0);
-	assert_int_equal(get_strbuf_len(&buf), 0);
+	assert_uint_equal(get_strbuf_len(&buf), 0);
 	assert_string_equal(get_strbuf_str(&buf), "");
 
 	assert_int_equal(fill_strbuf(&buf, '+', 1), 1);
-	assert_int_equal(get_strbuf_len(&buf), 1);
+	assert_uint_equal(get_strbuf_len(&buf), 1);
 	assert_string_equal(get_strbuf_str(&buf), "+");
 
 	assert_int_equal(fill_strbuf(&buf, '-', 3), 3);
-	assert_int_equal(get_strbuf_len(&buf), 4);
+	assert_uint_equal(get_strbuf_len(&buf), 4);
 	assert_string_equal(get_strbuf_str(&buf), "+---");
 
 	assert_int_equal(fill_strbuf(&buf, '\0', 3), 3);
-	assert_int_equal(get_strbuf_len(&buf), 7);
+	assert_uint_equal(get_strbuf_len(&buf), 7);
 	assert_string_equal(get_strbuf_str(&buf), "+---");
 
 	truncate_strbuf(&buf, 4);
 	assert_int_equal(fill_strbuf(&buf, '+', 4), 4);
-	assert_int_equal(get_strbuf_len(&buf), 8);
+	assert_uint_equal(get_strbuf_len(&buf), 8);
 	assert_string_equal(get_strbuf_str(&buf), "+---++++");
 
 	reset_strbuf(&buf);
 	assert_int_equal(fill_strbuf(&buf, 'x', 30000), 30000);
-	assert_int_equal(get_strbuf_len(&buf), 30000);
+	assert_uint_equal(get_strbuf_len(&buf), 30000);
 	p = steal_strbuf_str(&buf);
-	assert_int_equal(strlen(p), 30000);
+	assert_uint_equal(strlen(p), 30000);
 	for (i = 0; i < 30000; i++)
 		assert_int_equal(p[i], 'x');
 	free(p);
diff --git a/tests/sysfs.c b/tests/sysfs.c
index 13f985f..1b9c6a6 100644
--- a/tests/sysfs.c
+++ b/tests/sysfs.c
@@ -9,7 +9,7 @@
 #include <stddef.h>
 #include <setjmp.h>
 #include <stdlib.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include "mt-udev-wrap.h"
 #include <fcntl.h>
 #include <errno.h>
@@ -22,9 +22,9 @@
 
 #define TEST_FD 123
 
-char *__wrap_udev_device_get_syspath(struct udev_device *ud)
+const char *__wrap_udev_device_get_syspath(struct udev_device *ud)
 {
-	char *val  = mock_ptr_type(char *);
+	const char  *val  = mock_ptr_type(const char  *);
 
 	return val;
 }
@@ -33,8 +33,8 @@ int WRAP_OPEN(const char *pathname, int flags)
 {
 	int ret;
 
-	check_expected(pathname);
-	check_expected(flags);
+	check_expected_ptr(pathname);
+	check_expected_int(flags);
 	ret = mock_type(int);
 	return ret;
 }
@@ -42,12 +42,12 @@ int WRAP_OPEN(const char *pathname, int flags)
 ssize_t __wrap_read(int fd, void *buf, size_t count)
 {
 	ssize_t ret;
-	char *val;
+	const char *val;
 
-	check_expected(fd);
-	check_expected(count);
+	check_expected_int(fd);
+	check_expected_uint(count);
 	ret = mock_type(int);
-	val = mock_ptr_type(char *);
+	val = mock_ptr_type(const char *);
 	if (ret >= (ssize_t)count)
 		ret = count;
 	if (ret >= 0 && val) {
@@ -61,8 +61,8 @@ ssize_t __wrap_write(int fd, void *buf, size_t count)
 {
 	ssize_t ret;
 
-	check_expected(fd);
-	check_expected(count);
+	check_expected_int(fd);
+	check_expected_uint(count);
 	ret = mock_type(int);
 	if (ret >= (ssize_t)count)
 		ret = count;
@@ -168,7 +168,7 @@ static void test_sagv_open_fail(void **state)
 	will_return(__wrap_udev_device_get_syspath, "/foo");
 	expect_condlog(4, "open '/foo/bar'");
 	expect_string(WRAP_OPEN, pathname, "/foo/bar");
-	expect_value(WRAP_OPEN, flags, O_RDONLY);
+	expect_int_value(WRAP_OPEN, flags, O_RDONLY);
 	errno = ENOENT;
 	wrap_will_return(WRAP_OPEN, -1);
 	expect_condlog(3, "sysfs_attr_get_value__: attribute '/foo/bar' cannot be opened");
@@ -183,10 +183,10 @@ static void test_sagv_read_fail(void **state)
 	will_return(__wrap_udev_device_get_syspath, "/foo");
 	expect_condlog(4, "open '/foo/bar'");
 	expect_string(WRAP_OPEN, pathname, "/foo/bar");
-	expect_value(WRAP_OPEN, flags, O_RDONLY);
+	expect_int_value(WRAP_OPEN, flags, O_RDONLY);
 	wrap_will_return(WRAP_OPEN, TEST_FD);
-	expect_value(__wrap_read, fd, TEST_FD);
-	expect_value(__wrap_read, count, sizeof(buf));
+	expect_int_value(__wrap_read, fd, TEST_FD);
+	expect_uint_value(__wrap_read, count, sizeof(buf));
 	errno = EISDIR;
 	will_return(__wrap_read, -1);
 	will_return(__wrap_read, NULL);
@@ -198,10 +198,10 @@ static void test_sagv_read_fail(void **state)
 	will_return(__wrap_udev_device_get_syspath, "/foo");
 	expect_condlog(4, "open '/foo/baz'");
 	expect_string(WRAP_OPEN, pathname, "/foo/baz");
-	expect_value(WRAP_OPEN, flags, O_RDONLY);
+	expect_int_value(WRAP_OPEN, flags, O_RDONLY);
 	wrap_will_return(WRAP_OPEN, TEST_FD);
-	expect_value(__wrap_read, fd, TEST_FD);
-	expect_value(__wrap_read, count, sizeof(buf));
+	expect_int_value(__wrap_read, fd, TEST_FD);
+	expect_uint_value(__wrap_read, count, sizeof(buf));
 	errno = EPERM;
 	will_return(__wrap_read, -1);
 	will_return(__wrap_read, NULL);
@@ -219,15 +219,15 @@ static void _test_sagv_read(void **state, unsigned int bufsz)
 	char input[] = "01234567";
 	unsigned int n, trunc;
 
-	assert_in_range(bufsz, 1, sizeof(buf));
+	assert_uint_in_range(bufsz, 1, sizeof(buf));
 	memset(buf, '.', sizeof(buf));
 	will_return(__wrap_udev_device_get_syspath, "/foo");
 	expect_condlog(4, "open '/foo/bar'");
 	expect_string(WRAP_OPEN, pathname, "/foo/bar");
-	expect_value(WRAP_OPEN, flags, O_RDONLY);
+	expect_int_value(WRAP_OPEN, flags, O_RDONLY);
 	wrap_will_return(WRAP_OPEN, TEST_FD);
-	expect_value(__wrap_read, fd, TEST_FD);
-	expect_value(__wrap_read, count, bufsz);
+	expect_int_value(__wrap_read, fd, TEST_FD);
+	expect_uint_value(__wrap_read, count, bufsz);
 	will_return(__wrap_read, sizeof(input) - 1);
 	will_return(__wrap_read, input);
 
@@ -251,10 +251,10 @@ static void _test_sagv_read(void **state, unsigned int bufsz)
 	will_return(__wrap_udev_device_get_syspath, "/foo");
 	expect_condlog(4, "open '/foo/baz'");
 	expect_string(WRAP_OPEN, pathname, "/foo/baz");
-	expect_value(WRAP_OPEN, flags, O_RDONLY);
+	expect_int_value(WRAP_OPEN, flags, O_RDONLY);
 	wrap_will_return(WRAP_OPEN, TEST_FD);
-	expect_value(__wrap_read, fd, TEST_FD);
-	expect_value(__wrap_read, count, bufsz);
+	expect_int_value(__wrap_read, fd, TEST_FD);
+	expect_uint_value(__wrap_read, count, bufsz);
 	will_return(__wrap_read, sizeof(input) - 1);
 	will_return(__wrap_read, input);
 	will_return(__wrap_close, 0);
@@ -297,15 +297,15 @@ static void _test_sagv_read_zeroes(void **state, unsigned int bufsz)
 	char input[] = { '\0','\0','\0','\0','\0','\0','\0','\0' };
 	unsigned int n;
 
-	assert_in_range(bufsz, 1, sizeof(buf));
+	assert_uint_in_range(bufsz, 1, sizeof(buf));
 	memset(buf, '.', sizeof(buf));
 	will_return(__wrap_udev_device_get_syspath, "/foo");
 	expect_condlog(4, "open '/foo/bar'");
 	expect_string(WRAP_OPEN, pathname, "/foo/bar");
-	expect_value(WRAP_OPEN, flags, O_RDONLY);
+	expect_int_value(WRAP_OPEN, flags, O_RDONLY);
 	wrap_will_return(WRAP_OPEN, TEST_FD);
-	expect_value(__wrap_read, fd, TEST_FD);
-	expect_value(__wrap_read, count, bufsz);
+	expect_int_value(__wrap_read, fd, TEST_FD);
+	expect_uint_value(__wrap_read, count, bufsz);
 	will_return(__wrap_read, sizeof(input) - 1);
 	will_return(__wrap_read, input);
 
@@ -387,7 +387,7 @@ static void test_sasv_open_fail(void **state)
 	will_return(__wrap_udev_device_get_syspath, "/foo");
 	expect_condlog(4, "open '/foo/bar'");
 	expect_string(WRAP_OPEN, pathname, "/foo/bar");
-	expect_value(WRAP_OPEN, flags, O_WRONLY);
+	expect_int_value(WRAP_OPEN, flags, O_WRONLY);
 	errno = EPERM;
 	wrap_will_return(WRAP_OPEN, -1);
 	expect_condlog(3, "sysfs_attr_set_value: attribute '/foo/bar' cannot be opened");
@@ -402,10 +402,10 @@ static void test_sasv_write_fail(void **state)
 	will_return(__wrap_udev_device_get_syspath, "/foo");
 	expect_condlog(4, "open '/foo/bar'");
 	expect_string(WRAP_OPEN, pathname, "/foo/bar");
-	expect_value(WRAP_OPEN, flags, O_WRONLY);
+	expect_int_value(WRAP_OPEN, flags, O_WRONLY);
 	wrap_will_return(WRAP_OPEN, TEST_FD);
-	expect_value(__wrap_write, fd, TEST_FD);
-	expect_value(__wrap_write, count, sizeof(buf));
+	expect_int_value(__wrap_write, fd, TEST_FD);
+	expect_uint_value(__wrap_write, count, sizeof(buf));
 	errno = EISDIR;
 	will_return(__wrap_write, -1);
 	expect_condlog(3, "sysfs_attr_set_value: write to /foo/bar failed:");
@@ -419,14 +419,14 @@ static void _test_sasv_write(void **state, unsigned int n_written)
 {
 	char buf[8];
 
-	assert_in_range(n_written, 0, sizeof(buf));
+	assert_uint_in_range(n_written, 0, sizeof(buf));
 	will_return(__wrap_udev_device_get_syspath, "/foo");
 	expect_condlog(4, "open '/foo/bar'");
 	expect_string(WRAP_OPEN, pathname, "/foo/bar");
-	expect_value(WRAP_OPEN, flags, O_WRONLY);
+	expect_int_value(WRAP_OPEN, flags, O_WRONLY);
 	wrap_will_return(WRAP_OPEN, TEST_FD);
-	expect_value(__wrap_write, fd, TEST_FD);
-	expect_value(__wrap_write, count, sizeof(buf));
+	expect_int_value(__wrap_write, fd, TEST_FD);
+	expect_uint_value(__wrap_write, count, sizeof(buf));
 	will_return(__wrap_write, n_written);
 
 	if (n_written < sizeof(buf))
diff --git a/tests/test-lib.c b/tests/test-lib.c
index 3f07567..95a3231 100644
--- a/tests/test-lib.c
+++ b/tests/test-lib.c
@@ -1,7 +1,7 @@
 #include <string.h>
 #include <setjmp.h>
 #include <stdarg.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include "mt-udev-wrap.h"
 #include <sys/sysmacros.h>
 #include <linux/hdreg.h>
@@ -55,7 +55,7 @@ int WRAP_OPEN(const char *path, int flags, int mode)
 
 int __wrap_libmp_get_version(int which, unsigned int version[3])
 {
-	unsigned int *vers = mock_ptr_type(unsigned int *);
+	const unsigned int *vers = mock_ptr_type(const unsigned int *);
 
 	condlog(4, "%s: %d", __func__, which);
 	memcpy(version, vers, 3 * sizeof(unsigned int));
@@ -82,7 +82,7 @@ struct udev_list_entry
 
 const char *__wrap_udev_list_entry_get_name(struct udev_list_entry *udle)
 {
-	char *val = mock_ptr_type(char *);
+	const char *val = mock_ptr_type(const char *);
 
 	condlog(5, "%s: %s", __func__, val);
 	return val;
@@ -98,54 +98,67 @@ struct udev_device *__wrap_udev_device_unref(struct udev_device *ud)
 	return ud;
 }
 
-char *__wrap_udev_device_get_subsystem(struct udev_device *ud)
+/*
+ * cmocka 2.0 uses "const void *" for the pointer argument.
+ * As we have to return char *, we must accept casting
+ * (const void *) to (char *) here.
+ */
+const char *__wrap_udev_device_get_subsystem(struct udev_device *ud)
 {
-	char *val = mock_ptr_type(char *);
+	const char *val = mock_ptr_type(const char *);
 
 	condlog(5, "%s: %s", __func__, val);
 	return val;
 }
 
-char *__wrap_udev_device_get_sysname(struct udev_device *ud)
+const char  *__wrap_udev_device_get_sysname(struct udev_device *ud)
 {
-	char *val  = mock_ptr_type(char *);
+	const char  *val  = mock_ptr_type(const char  *);
 
 	condlog(5, "%s: %s", __func__, val);
 	return val;
 }
 
-char *__wrap_udev_device_get_devnode(struct udev_device *ud)
+const char  *__wrap_udev_device_get_devnode(struct udev_device *ud)
 {
-	char *val  = mock_ptr_type(char *);
+	const char  *val  = mock_ptr_type(const char  *);
 
 	condlog(5, "%s: %s", __func__, val);
 	return val;
 }
 
+const char  *__wrap_udev_device_get_sysattr_value(struct udev_device *ud,
+					     const char *attr)
+{
+	const char  *val  = mock_ptr_type(const char  *);
+
+	condlog(5, "%s: %s->%s", __func__, attr, val);
+	return val;
+}
+
+const char  *__wrap_udev_device_get_property_value(struct udev_device *ud,
+					    const char *attr)
+{
+	const char  *val  = mock_ptr_type(const char  *);
+
+	condlog(5, "%s: %s->%s", __func__, attr, val);
+	return val;
+}
+
+const void *__wrap_udev_device_get_parent(struct udev_device *ud)
+{
+	const void *val  = mock_ptr_type(const void *);
+
+	condlog(5, "%s: %p", __func__, val);
+	return val;
+}
+
 dev_t __wrap_udev_device_get_devnum(struct udev_device *ud)
 {
 	condlog(5, "%s: %p", __func__, ud);
 	return makedev(17, 17);
 }
 
-char *__wrap_udev_device_get_sysattr_value(struct udev_device *ud,
-					     const char *attr)
-{
-	char *val  = mock_ptr_type(char *);
-
-	condlog(5, "%s: %s->%s", __func__, attr, val);
-	return val;
-}
-
-char *__wrap_udev_device_get_property_value(struct udev_device *ud,
-					    const char *attr)
-{
-	char *val  = mock_ptr_type(char *);
-
-	condlog(5, "%s: %s->%s", __func__, attr, val);
-	return val;
-}
-
 int __wrap_sysfs_get_size(struct path *pp, unsigned long long *sz)
 {
 	*sz = 12345678UL;
@@ -166,19 +179,11 @@ void *__wrap_udev_device_get_parent_with_subsystem_devtype(
 	return type;
 }
 
-void *__wrap_udev_device_get_parent(struct udev_device *ud)
-{
-	char *val  = mock_ptr_type(void *);
-
-	condlog(5, "%s: %p", __func__, val);
-	return val;
-}
-
-ssize_t __wrap_sysfs_attr_get_value(struct udev_device *dev,
+size_t __wrap_sysfs_attr_get_value(struct udev_device *dev,
 				    const char *attr_name,
 				    char *value, size_t sz)
 {
-	char *val  = mock_ptr_type(char *);
+	const char *val  = mock_ptr_type(const char *);
 
 	condlog(5, "%s: %s", __func__, val);
 	strlcpy(value, val, sz);
@@ -193,7 +198,7 @@ ssize_t __wrap_sysfs_bin_attr_get_value(struct udev_device *dev,
 	static const char serial[] = "mptest_serial";
 
 	assert_string_equal(attr_name, "vpd_pg80");
-	assert_in_range(sz, sizeof(serial) + 3, INT_MAX);
+	assert_uint_in_range(sz, sizeof(serial) + 3, INT_MAX);
 	memset(buf, 0, sizeof(serial) + 3);
 	buf[1] = 0x80;
 	put_unaligned_be16(sizeof(serial) - 1, &buf[2]);
@@ -238,7 +243,7 @@ int __wrap_ioctl(int fd, unsigned long request, void *param)
 
 		if (hdr->interface_id == 'S' && hdr->cmdp[0] == 0x12
 		    && (hdr->cmdp[1] & 1) == 1 && hdr->cmdp[2] == HP3PAR_VPD) {
-			assert_in_range(hdr->dxfer_len,
+			assert_uint_in_range(hdr->dxfer_len,
 					sizeof(vpd_data) + 3, INT_MAX);
 			memset(buf, 0, hdr->dxfer_len);
 			buf[1] = HP3PAR_VPD;
diff --git a/tests/test-log.c b/tests/test-log.c
index 9ace478..b907e7b 100644
--- a/tests/test-log.c
+++ b/tests/test-log.c
@@ -4,7 +4,7 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include "log.h"
 #include "test-log.h"
 #include "debug.h"
@@ -15,16 +15,16 @@ void __wrap_dlog (int prio, const char * fmt, ...)
 {
 	char buff[MAX_MSG_SIZE];
 	va_list ap;
-	char *expected;
+	const char *expected;
 
 	va_start(ap, fmt);
 	vsnprintf(buff, MAX_MSG_SIZE, fmt, ap);
 	va_end(ap);
 	fprintf(stderr, "%s(%d): %s", __func__, prio, buff);
-	expected = mock_ptr_type(char *);
+	expected = mock_ptr_type(const char *);
 	if (memcmp(expected, buff, strlen(expected)))
 		fprintf(stderr, "%s(expected): %s", __func__, expected);
-	check_expected(prio);
+	check_expected_int(prio);
 	assert_memory_equal(buff, expected, strlen(expected));
 }
 
@@ -32,6 +32,6 @@ void expect_condlog(int prio, char *string)
 {
 	if (prio > MAX_VERBOSITY || prio > libmp_verbosity)
 		return;
-	expect_value(__wrap_dlog, prio, prio);
+	expect_int_value(__wrap_dlog, prio, prio);
 	will_return(__wrap_dlog, string);
 }
diff --git a/tests/uevent.c b/tests/uevent.c
index 39f5dbd..044d490 100644
--- a/tests/uevent.c
+++ b/tests/uevent.c
@@ -8,7 +8,7 @@
 #include <stddef.h>
 #include <setjmp.h>
 #include <stdlib.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include "list.h"
 #include "uevent.h"
 
diff --git a/tests/unaligned.c b/tests/unaligned.c
index e43b64d..252f8f4 100644
--- a/tests/unaligned.c
+++ b/tests/unaligned.c
@@ -6,7 +6,7 @@
 #include <stddef.h>
 #include <setjmp.h>
 #include <stdlib.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include "unaligned.h"
 
 #define SIZE 16
@@ -40,13 +40,13 @@ static int teardown(void **state)
 	uint8_t *p = *state + SIZE;				\
 	uint64_t u;						\
 								\
-	assert_in_range(len, 1, SIZE);				\
-	assert_in_range(offset + len, 1, SIZE);			\
+	assert_int_in_range(len, 1, SIZE);				\
+	assert_int_in_range(offset + len, 1, SIZE);			\
 	memset(c, 0, 2 * SIZE);					\
 	memcpy(c + offset, memory, len);			\
 								\
 	u = get_unaligned_be##bits(c + offset);			\
-	assert_int_equal(u, intval##bits);			\
+	assert_uint_equal(u, intval##bits);			\
 	put_unaligned_be##bits(u, p + offset);			\
 	assert_memory_equal(c + offset, p  + offset, len);	\
 }
diff --git a/tests/util.c b/tests/util.c
index 67d8148..66ba7f4 100644
--- a/tests/util.c
+++ b/tests/util.c
@@ -9,7 +9,7 @@
 #include <stddef.h>
 #include <setjmp.h>
 #include <stdlib.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include <endian.h>
 #include <string.h>
 #include "util.h"
@@ -204,7 +204,7 @@ static void test_bitmask_1(void **state)
 
 	bf = alloc_bitfield(BITARR_SZ * 64);
 	assert_non_null(bf);
-	assert_int_equal(bf->len, BITARR_SZ * 64);
+	assert_uint_equal(bf->len, BITARR_SZ * 64);
 	arr = (uint64_t *)bf->bits;
 
 	for (j = 0; j < BITARR_SZ; j++) {
@@ -218,9 +218,9 @@ static void test_bitmask_1(void **state)
 				       b, j, k, arr[k]);
 #endif
 				if (k == j)
-					assert_int_equal(maybe_swap(arr[j]), 1ULL << i);
+					assert_uint_equal(maybe_swap(arr[j]), 1ULL << i);
 				else
-					assert_int_equal(arr[k], 0ULL);
+					assert_uint_equal(arr[k], 0ULL);
 			}
 			for (m = 0; m < 64; m++)
 				if (i == m)
@@ -232,7 +232,7 @@ static void test_bitmask_1(void **state)
 			clear_bit_in_bitfield(b, bf);
 			assert_false(is_bit_set_in_bitfield(b, bf));
 			for (k = 0; k < BITARR_SZ; k++)
-				assert_int_equal(arr[k], 0ULL);
+				assert_uint_equal(arr[k], 0ULL);
 		}
 	}
 	free(bf);
@@ -246,7 +246,7 @@ static void test_bitmask_2(void **state)
 
 	bf = alloc_bitfield(BITARR_SZ * 64);
 	assert_non_null(bf);
-	assert_int_equal(bf->len, BITARR_SZ * 64);
+	assert_uint_equal(bf->len, BITARR_SZ * 64);
 	arr = (uint64_t *)bf->bits;
 
 	for (j = 0; j < BITARR_SZ; j++) {
@@ -264,11 +264,11 @@ static void test_bitmask_2(void **state)
 			assert_true(is_bit_set_in_bitfield(b, bf));
 			for (k = 0; k < BITARR_SZ; k++) {
 				if (k < j || (k == j && i == 63))
-					assert_int_equal(arr[k], ~0ULL);
+					assert_uint_equal(arr[k], ~0ULL);
 				else if (k > j)
-					assert_int_equal(arr[k], 0ULL);
+					assert_uint_equal(arr[k], 0ULL);
 				else
-					assert_int_equal(
+					assert_uint_equal(
 						maybe_swap(arr[k]),
 						(1ULL << (i + 1)) - 1);
 			}
@@ -289,11 +289,11 @@ static void test_bitmask_2(void **state)
 			assert_false(is_bit_set_in_bitfield(b, bf));
 			for (k = 0; k < BITARR_SZ; k++) {
 				if (k < j || (k == j && i == 63))
-					assert_int_equal(arr[k], 0ULL);
+					assert_uint_equal(arr[k], 0ULL);
 				else if (k > j)
-					assert_int_equal(arr[k], ~0ULL);
+					assert_uint_equal(arr[k], ~0ULL);
 				else
-					assert_int_equal(
+					assert_uint_equal(
 						maybe_swap(arr[k]),
 						~((1ULL << (i + 1)) - 1));
 			}
@@ -334,23 +334,23 @@ static void _test_bitmask_small(unsigned int n)
 	unsigned int size = maybe_swap_idx((n - 1) / 32) + 1, i;
 
 	assert_true(sizeof(bitfield_t) == 4 || sizeof(bitfield_t) == 8);
-	assert_in_range(n, 1, 64);
+	assert_uint_in_range(n, 1, 64);
 
 	bf = alloc_bitfield(n);
 	assert_non_null(bf);
-	assert_int_equal(bf->len, n);
+	assert_uint_equal(bf->len, n);
 	arr = (uint32_t *)bf->bits;
 
 	for (i = 0; i < size; i++)
-		assert_int_equal(arr[i], 0);
+		assert_uint_equal(arr[i], 0);
 
 	set_bit_in_bitfield(n + 1, bf);
 	for (i = 0; i < size; i++)
-		assert_int_equal(arr[i], 0);
+		assert_uint_equal(arr[i], 0);
 
 	set_bit_in_bitfield(n, bf);
 	for (i = 0; i < size; i++)
-		assert_int_equal(arr[i], 0);
+		assert_uint_equal(arr[i], 0);
 
 	set_bit_in_bitfield(n - 1, bf);
 	for (i = 0; i < size; i++) {
@@ -359,19 +359,19 @@ static void _test_bitmask_small(unsigned int n)
 		unsigned int i1 = maybe_swap_idx(i);
 
 		if (i == k)
-			assert_int_equal(arr[i1], 1UL << j);
+			assert_uint_equal(arr[i1], 1UL << j);
 		else
-			assert_int_equal(arr[i1], 0);
+			assert_uint_equal(arr[i1], 0);
 	}
 
 	clear_bit_in_bitfield(n - 1, bf);
 	for (i = 0; i < size; i++)
-		assert_int_equal(arr[i], 0);
+		assert_uint_equal(arr[i], 0);
 
 	set_bit_in_bitfield(0, bf);
-	assert_int_equal(arr[maybe_swap_idx(0)], 1);
+	assert_uint_equal(arr[maybe_swap_idx(0)], 1);
 	for (i = 1; i < size; i++)
-		assert_int_equal(arr[maybe_swap_idx(i)], 0);
+		assert_uint_equal(arr[maybe_swap_idx(i)], 0);
 
 	free(bf);
 }
@@ -382,35 +382,35 @@ static void _test_bitmask_small_2(unsigned int n)
 	uint32_t *arr;
 	unsigned int size = maybe_swap_idx((n - 1) / 32) + 1, i;
 
-	assert_in_range(n, 65, 128);
+	assert_uint_in_range(n, 65, 128);
 
 	bf = alloc_bitfield(n);
 	assert_non_null(bf);
-	assert_int_equal(bf->len, n);
+	assert_uint_equal(bf->len, n);
 	arr = (uint32_t *)bf->bits;
 
 	for (i = 0; i < size; i++)
-		assert_int_equal(arr[i], 0);
+		assert_uint_equal(arr[i], 0);
 
 	set_bit_in_bitfield(n + 1, bf);
 	for (i = 0; i < size; i++)
-		assert_int_equal(arr[i], 0);
+		assert_uint_equal(arr[i], 0);
 
 	set_bit_in_bitfield(n, bf);
 	for (i = 0; i < size; i++)
-		assert_int_equal(arr[i], 0);
+		assert_uint_equal(arr[i], 0);
 
 	set_bit_in_bitfield(n - 1, bf);
-	assert_int_equal(arr[0], 0);
+	assert_uint_equal(arr[0], 0);
 	for (i = 0; i < size; i++) {
 		unsigned int k = (n - 1) / 32;
 		unsigned int j = (n - 1) - k * 32;
 		unsigned int i1 = maybe_swap_idx(i);
 
 		if (i == k)
-			assert_int_equal(arr[i1], 1UL << j);
+			assert_uint_equal(arr[i1], 1UL << j);
 		else
-			assert_int_equal(arr[i1], 0);
+			assert_uint_equal(arr[i1], 0);
 	}
 
 	set_bit_in_bitfield(0, bf);
@@ -420,13 +420,13 @@ static void _test_bitmask_small_2(unsigned int n)
 		unsigned int i1 = maybe_swap_idx(i);
 
 		if (i == k && k == 0)
-			assert_int_equal(arr[i1], (1UL << j) | 1);
+			assert_uint_equal(arr[i1], (1UL << j) | 1);
 		else if (i == k)
-			assert_int_equal(arr[i1], 1UL << j);
+			assert_uint_equal(arr[i1], 1UL << j);
 		else if (i == 0)
-			assert_int_equal(arr[i1], 1);
+			assert_uint_equal(arr[i1], 1);
 		else
-			assert_int_equal(arr[i1], 0);
+			assert_uint_equal(arr[i1], 0);
 	}
 
 	set_bit_in_bitfield(64, bf);
@@ -436,13 +436,13 @@ static void _test_bitmask_small_2(unsigned int n)
 		unsigned int i1 = maybe_swap_idx(i);
 
 		if (i == k && (k == 0 || k == 2))
-			assert_int_equal(arr[i1], (1UL << j) | 1);
+			assert_uint_equal(arr[i1], (1UL << j) | 1);
 		else if (i == k)
-			assert_int_equal(arr[i1], 1UL << j);
+			assert_uint_equal(arr[i1], 1UL << j);
 		else if (i == 2 || i == 0)
-			assert_int_equal(arr[i1], 1);
+			assert_uint_equal(arr[i1], 1);
 		else
-			assert_int_equal(arr[i1], 0);
+			assert_uint_equal(arr[i1], 0);
 	}
 
 	clear_bit_in_bitfield(0, bf);
@@ -452,13 +452,13 @@ static void _test_bitmask_small_2(unsigned int n)
 		unsigned int i1 = maybe_swap_idx(i);
 
 		if (i == k && k == 2)
-			assert_int_equal(arr[i1], (1UL << j) | 1);
+			assert_uint_equal(arr[i1], (1UL << j) | 1);
 		else if (i == k)
-			assert_int_equal(arr[i1], 1UL << j);
+			assert_uint_equal(arr[i1], 1UL << j);
 		else if (i == 2)
-			assert_int_equal(arr[i1], 1);
+			assert_uint_equal(arr[i1], 1);
 		else
-			assert_int_equal(arr[i1], 0);
+			assert_uint_equal(arr[i1], 0);
 	}
 
 	free(bf);
@@ -568,7 +568,7 @@ static void test_strlcpy_0(void **state)
 	int rc;
 
 	rc = strlcpy(tst, src_str, 0);
-	assert_int_equal(rc, strlen(src_str));
+	assert_uint_equal(rc, strlen(src_str));
 	assert_string_equal(tst, dst_str);
 }
 
@@ -579,7 +579,7 @@ static void test_strlcpy_1(void **state)
 	int rc;
 
 	rc = strlcpy(tst, src_str, 1);
-	assert_int_equal(rc, strlen(src_str));
+	assert_uint_equal(rc, strlen(src_str));
 	assert_int_equal(tst[0], '\0');
 	assert_string_equal(tst + 1, dst_str + 1);
 }
@@ -591,7 +591,7 @@ static void test_strlcpy_2(void **state)
 	int rc;
 
 	rc = strlcpy(tst, src_str, 2);
-	assert_int_equal(rc, strlen(src_str));
+	assert_uint_equal(rc, strlen(src_str));
 	assert_int_equal(tst[0], src_str[0]);
 	assert_int_equal(tst[1], '\0');
 	assert_string_equal(tst + 2, dst_str + 2);
@@ -605,7 +605,7 @@ static void test_strlcpy_3(void **state)
 
 	rc = strlcpy(tst, src_str, sizeof(tst));
 	assert_int_equal(rc, strlen(src_str));
-	assert_int_equal(sizeof(tst) - 1, strlen(tst));
+	assert_uint_equal(sizeof(tst) - 1, strlen(tst));
 	assert_true(strncmp(tst, src_str, sizeof(tst) - 1) == 0);
 }
 
@@ -709,7 +709,7 @@ static int test_strlcpy(void)
 static void prep_buf(char *buf, size_t size, const char *word)
 {
 	memset(buf, FILL, size);
-	assert_in_range(strlen(word), 0, size - 1);
+	assert_uint_in_range(strlen(word), 0, size - 1);
 	memcpy(buf, word, strlen(word) + 1);
 }
 
@@ -899,7 +899,7 @@ static void test_strchop_nochop(void **state)
 {
 	char hello[] = "hello";
 
-	assert_int_equal(strchop(hello), 5);
+	assert_uint_equal(strchop(hello), 5);
 	assert_string_equal(hello, "hello");
 }
 
@@ -907,7 +907,7 @@ static void test_strchop_newline(void **state)
 {
 	char hello[] = "hello\n";
 
-	assert_int_equal(strchop(hello), 5);
+	assert_uint_equal(strchop(hello), 5);
 	assert_string_equal(hello, "hello");
 }
 
@@ -915,7 +915,7 @@ static void test_strchop_space(void **state)
 {
 	char hello[] = " ello      ";
 
-	assert_int_equal(strchop(hello), 5);
+	assert_uint_equal(strchop(hello), 5);
 	assert_string_equal(hello, " ello");
 }
 
@@ -923,7 +923,7 @@ static void test_strchop_mix(void **state)
 {
 	char hello[] = " el\no \t  \n\n \t    \n";
 
-	assert_int_equal(strchop(hello), 5);
+	assert_uint_equal(strchop(hello), 5);
 	assert_string_equal(hello, " el\no");
 }
 
@@ -931,7 +931,7 @@ static void test_strchop_blank(void **state)
 {
 	char hello[] = "  \t  \n\n \t    \n";
 
-	assert_int_equal(strchop(hello), 0);
+	assert_uint_equal(strchop(hello), 0);
 	assert_string_equal(hello, "");
 }
 
@@ -939,7 +939,7 @@ static void test_strchop_empty(void **state)
 {
 	char hello[] = "";
 
-	assert_int_equal(strchop(hello), 0);
+	assert_uint_equal(strchop(hello), 0);
 	assert_string_equal(hello, "");
 }
 
diff --git a/tests/valid.c b/tests/valid.c
index 3f15ce6..d6c8ee9 100644
--- a/tests/valid.c
+++ b/tests/valid.c
@@ -11,7 +11,7 @@
 #include <setjmp.h>
 #include <stdlib.h>
 #include <errno.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include <sys/sysmacros.h>
 
 #include "globals.c"
@@ -35,7 +35,7 @@ bool __wrap_sysfs_is_multipathed(struct path *pp, bool set_wwid)
 	assert_non_null(pp);
 	assert_int_not_equal(strlen(pp->dev), 0);
 	if (is_multipathed && set_wwid)
-		strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE);
+		strlcpy(pp->wwid, mock_ptr_type(const char *), WWID_SIZE);
 	return is_multipathed;
 }
 
@@ -59,7 +59,7 @@ int __wrap_mpath_disconnect(int fd)
 struct udev_device *__wrap_udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname)
 {
 	bool passed = mock_type(bool);
-	assert_string_equal(sysname, mock_ptr_type(char *));
+	assert_string_equal(sysname, mock_ptr_type(const char *));
 	if (passed)
 		return &test_udev;
 	return NULL;
@@ -68,16 +68,16 @@ struct udev_device *__wrap_udev_device_new_from_subsystem_sysname(struct udev *u
 /* For devtype check */
 const char *__wrap_udev_device_get_property_value(struct udev_device *udev_device, const char *property)
 {
-	check_expected(property);
-	return mock_ptr_type(char *);
+	check_expected_ptr(property);
+	return mock_ptr_type(const char *);
 }
 
 /* For the "hidden" check in pathinfo() */
 const char *__wrap_udev_device_get_sysattr_value(struct udev_device *udev_device,
 					 const char *sysattr)
 {
-	check_expected(sysattr);
-	return mock_ptr_type(char *);
+	check_expected_ptr(sysattr);
+	return mock_ptr_type(const char *);
 }
 
 /* For pathinfo() -> is_claimed_by_foreign() */
@@ -89,7 +89,7 @@ int __wrap_add_foreign(struct udev_device *udev_device)
 /* For is_device_used() */
 const char *__wrap_udev_device_get_sysname(struct udev_device *udev_device)
 {
-	return mock_ptr_type(char *);
+	return mock_ptr_type(const char *);
 }
 
 /* called from pathinfo() */
@@ -121,7 +121,7 @@ int __wrap_sysfs_get_size(struct path *pp, unsigned long long * size)
 /* called in pathinfo() before filter_property() */
 int __wrap_select_getuid(struct config *conf, struct path *pp)
 {
-	pp->uid_attribute = mock_ptr_type(char *);
+	pp->uid_attribute = mock_ptr_type(const char *);
 	return 0;
 }
 
@@ -131,7 +131,7 @@ int __wrap_pathinfo(struct path *pp, struct config *conf, int mask)
 {
 	int ret = mock_type(int);
 
-	assert_string_equal(pp->dev, mock_ptr_type(char *));
+	assert_string_equal(pp->dev, mock_ptr_type(const char *));
 	assert_int_equal(mask, DI_SYSFS | DI_WWID | DI_BLACKLIST);
 	if (ret == PATHINFO_REAL) {
 		/* for test_filter_property() */
@@ -139,7 +139,7 @@ int __wrap_pathinfo(struct path *pp, struct config *conf, int mask)
 		return ret;
 	} else if (ret == PATHINFO_OK) {
 		pp->uid_attribute = "ID_TEST";
-		strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE);
+		strlcpy(pp->wwid, mock_ptr_type(const char *), WWID_SIZE);
 	} else
 		memset(pp->wwid, 0, WWID_SIZE);
 	return ret;
@@ -156,20 +156,20 @@ int __wrap_filter_property(struct config *conf, struct udev_device *udev,
 int __wrap_is_failed_wwid(const char *wwid)
 {
 	int ret = mock_type(int);
-	assert_string_equal(wwid, mock_ptr_type(char *));
+	assert_string_equal(wwid, mock_ptr_type(const char *));
 	return ret;
 }
 
 const char *__wrap_udev_device_get_syspath(struct udev_device *udevice)
 {
-	return mock_ptr_type(char *);
+	return mock_ptr_type(const char *);
 }
 
 int __wrap_check_wwids_file(char *wwid, int write_wwid)
 {
 	bool passed = mock_type(bool);
 	assert_int_equal(write_wwid, 0);
-	assert_string_equal(wwid, mock_ptr_type(char *));
+	assert_string_equal(wwid, mock_ptr_type(const char *));
 	if (passed)
 		return 0;
 	else
@@ -180,7 +180,7 @@ int __wrap_dm_find_map_by_wwid(const char *wwid, char *name,
 			       struct dm_info *dmi)
 {
 	int ret = mock_type(int);
-	assert_string_equal(wwid, mock_ptr_type(char *));
+	assert_string_equal(wwid, mock_ptr_type(const char *));
 	return ret;
 }
 
diff --git a/tests/vpd.c b/tests/vpd.c
index e3212e6..043b6aa 100644
--- a/tests/vpd.c
+++ b/tests/vpd.c
@@ -13,7 +13,7 @@
 #include <stdarg.h>
 #include <stddef.h>
 #include <setjmp.h>
-#include <cmocka.h>
+#include "cmocka-compat.h"
 #include <scsi/sg.h>
 #include "unaligned.h"
 #include "debug.h"
@@ -63,12 +63,12 @@ int WRAP_IOCTL(int fd, unsigned long request, void *param)
 {
 	int len;
 	struct sg_io_hdr *io_hdr;
-	unsigned char *val;
+	const unsigned char *val;
 
 	len = mock();
 	io_hdr = (struct sg_io_hdr *)param;
-	assert_in_range(len, 0, io_hdr->dxfer_len);
-	val = mock_ptr_type(unsigned char *);
+	assert_int_in_range(len, 0, io_hdr->dxfer_len);
+	val = mock_ptr_type(const unsigned char *);
 	io_hdr->status = 0;
 	memcpy(io_hdr->dxferp, val, len);
 	return 0;
@@ -260,7 +260,7 @@ static int create_scsi_string_desc(unsigned char *desc,
 	desc[1] = 8;
 	desc[2] = 0;
 
-	assert_in_range(type, STR_EUI, STR_IQN);
+	assert_int_in_range(type, STR_EUI, STR_IQN);
 	assert_true(maxlen % 4 == 0);
 	len = snprintf((char *)(desc + 4), maxlen, "%s%s",
 		       str_prefix[type], id);
@@ -354,7 +354,7 @@ static void assert_correct_wwid(const char *test,
 	}
 	/* check matching length, and length of WWID string */
 	assert_int_equal(expected, returned);
-	assert_int_equal(returned, strlen(wwid));
+	assert_uint_equal(returned, strlen(wwid));
 	/* check expected string value up to expected length */
 	for (i = 0; i < returned - ofs; i++)
 		assert_int_equal(wwid[ofs + i],
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCH 03/21] multipathd: free paths in checker_finished()
  2025-12-17 21:20 ` [PATCH 03/21] multipathd: free paths in checker_finished() Martin Wilck
@ 2025-12-18 23:34   ` Benjamin Marzinski
  2025-12-18 23:40     ` Martin Wilck
  0 siblings, 1 reply; 33+ messages in thread
From: Benjamin Marzinski @ 2025-12-18 23:34 UTC (permalink / raw)
  To: Martin Wilck; +Cc: Christophe Varoqui, dm-devel, Martin Wilck

On Wed, Dec 17, 2025 at 10:20:55PM +0100, Martin Wilck wrote:
> Modifying the pathvec in a deep call stack is unexpected and
> error-prone. Free paths in the checkerloop instead, after
> having synced all maps.

I'm not fond of the fact that this leaves paths around, when callers
expect that the path will be removed. For instance, right now
cli_add_path() and uev_add_path() can call ev_remove_path to handle
INIT_REMOVED paths, and expect that success means the path is removed.
They can end up storing duplicate paths with this code.

What do you think about having calls to ev_remove_path() actually remove
the path if multipath device successfully orphans it. All the callers
of that function already expect that it likely will remove the path, and
that means that whenever multipathd is explicitly told to remove a path,
it will, if it can. If it can't, then the path was always going to get
removed later, and delaying that later incidental remove till the next
path check shouldn't make much difference.

-Ben
 
> Suggested-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/libmultipath.version | 1 +
>  libmultipath/structs_vec.c        | 7 +++----
>  libmultipath/structs_vec.h        | 1 +
>  multipathd/main.c                 | 1 +
>  4 files changed, 6 insertions(+), 4 deletions(-)
> 
> diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
> index 89ae2a3..e5b7b83 100644
> --- a/libmultipath/libmultipath.version
> +++ b/libmultipath/libmultipath.version
> @@ -58,6 +58,7 @@ global:
>  	change_foreign;
>  	check_alias_settings;
>  	check_daemon;
> +	check_removed_paths;
>  	checker_clear_message;
>  	checker_disable;
>  	checker_enable;
> diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
> index f651b29..3a9ab58 100644
> --- a/libmultipath/structs_vec.c
> +++ b/libmultipath/structs_vec.c
> @@ -573,16 +573,16 @@ static struct path *find_devt_in_pathgroups(const struct multipath *mpp,
>  	return NULL;
>  }
>  
> -static void check_removed_paths(const struct multipath *mpp, vector pathvec)
> +void check_removed_paths(vector pathvec)
>  {
>  	struct path *pp;
>  	int i;
>  
>  	vector_foreach_slot(pathvec, pp, i) {
> -		if (pp->mpp == mpp &&
> +		if (pp->mpp &&
>  		    (pp->initialized == INIT_REMOVED ||
>  		     pp->initialized == INIT_PARTIAL) &&
> -		    !find_devt_in_pathgroups(mpp, pp->dev_t)) {
> +		    !find_devt_in_pathgroups(pp->mpp, pp->dev_t)) {
>  			condlog(2, "%s: %s: freeing path in %s state",
>  				__func__, pp->dev,
>  				pp->initialized == INIT_REMOVED ?
> @@ -615,7 +615,6 @@ void sync_paths(struct multipath *mpp, vector pathvec)
>  			orphan_path(pp, "path removed externally");
>  		}
>  	}
> -	check_removed_paths(mpp, pathvec);
>  	update_mpp_paths(mpp, pathvec);
>  	vector_foreach_slot (mpp->paths, pp, i)
>  		pp->mpp = mpp;
> diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h
> index 1188ada..cb02d75 100644
> --- a/libmultipath/structs_vec.h
> +++ b/libmultipath/structs_vec.h
> @@ -18,6 +18,7 @@ int adopt_paths (vector pathvec, struct multipath *mpp,
>  void orphan_path (struct path * pp, const char *reason);
>  void set_path_removed(struct path *pp);
>  
> +void check_removed_paths(vector pathvec);
>  int verify_paths(struct multipath *mpp);
>  int update_mpp_paths(struct multipath * mpp, vector pathvec);
>  int update_multipath_strings (struct multipath *mpp, vector pathvec);
> diff --git a/multipathd/main.c b/multipathd/main.c
> index d3bf4d0..49a1f25 100644
> --- a/multipathd/main.c
> +++ b/multipathd/main.c
> @@ -3127,6 +3127,7 @@ static void checker_finished(struct vectors *vecs, unsigned int ticks)
>  	}
>  	if (uev_timed_out && !need_to_delay_reconfig(vecs))
>  		unblock_reconfigure();
> +	check_removed_paths(vecs->pathvec);
>  	partial_retrigger_tick(vecs->pathvec);
>  }
>  
> -- 
> 2.52.0


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 03/21] multipathd: free paths in checker_finished()
  2025-12-18 23:34   ` Benjamin Marzinski
@ 2025-12-18 23:40     ` Martin Wilck
  2025-12-19 10:38       ` Martin Wilck
  0 siblings, 1 reply; 33+ messages in thread
From: Martin Wilck @ 2025-12-18 23:40 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: Christophe Varoqui, dm-devel

On Thu, 2025-12-18 at 18:34 -0500, Benjamin Marzinski wrote:
> On Wed, Dec 17, 2025 at 10:20:55PM +0100, Martin Wilck wrote:
> > Modifying the pathvec in a deep call stack is unexpected and
> > error-prone. Free paths in the checkerloop instead, after
> > having synced all maps.
> 
> I'm not fond of the fact that this leaves paths around, when callers
> expect that the path will be removed. For instance, right now
> cli_add_path() and uev_add_path() can call ev_remove_path to handle
> INIT_REMOVED paths, and expect that success means the path is
> removed.

Thanks for pointing this out.

> They can end up storing duplicate paths with this code.

I'm not sure if that really hurts, as long as multipathd basically 
ignores INIT_REMOVED paths, and eventually frees them. But I agree that
it's awkward and should be avoided if possible.

> What do you think about having calls to ev_remove_path() actually
> remove
> the path if multipath device successfully orphans it.

Sure, that should be possible. I'll think about it.
I just want to stop removing paths somewhere deep in the stack.

Thanks,
Martin

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 09/21] libmultipath: fix numeric value of free_paths in free_multipaths()
  2025-12-17 21:21 ` [PATCH 09/21] libmultipath: fix numeric value of free_paths in free_multipaths() Martin Wilck
@ 2025-12-19  0:10   ` Benjamin Marzinski
  0 siblings, 0 replies; 33+ messages in thread
From: Benjamin Marzinski @ 2025-12-19  0:10 UTC (permalink / raw)
  To: Martin Wilck; +Cc: Christophe Varoqui, dm-devel, Martin Wilck

On Wed, Dec 17, 2025 at 10:21:01PM +0100, Martin Wilck wrote:
> free_paths is an enum, use the enum value.

I think free_multipath() should always keep the paths. It only doesn't
in cleanup_multipath_and_paths(), which is used in two places, and is
wrong in one and unnecessary in the other. check_usable_paths() could
just use cleanup_multipath(). The paths will be added to the pathvec by
update_multipath_table__(), and we can clean that up with a cleanup
handler that calls free_pathvec(). In add_map_without_path() using
cleanup_multipath_and_paths() is just wrong. We don't want it to free
all the paths. Instead it should orphan the paths before calling
free_multipath(). That will flag paths that need to get removed for
removing, but it won't actually free paths that are in the pathvec
(without removing them from the pathvec, to make matters worse).
 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/structs.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/libmultipath/structs.c b/libmultipath/structs.c
> index aaa7e50..059d454 100644
> --- a/libmultipath/structs.c
> +++ b/libmultipath/structs.c
> @@ -310,7 +310,7 @@ free_multipath (struct multipath * mpp, enum free_path_mode free_paths)
>  		mpp->alias = NULL;
>  	}
>  
> -	if (!free_paths && mpp->pg) {
> +	if (free_paths == KEEP_PATHS && mpp->pg) {
>  		struct pathgroup *pgp;
>  		struct path *pp;
>  		int i, j;
> -- 
> 2.52.0


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 12/21] libmultipath: free_multipath: fix FREE_PATHS case
  2025-12-17 21:21 ` [PATCH 12/21] libmultipath: free_multipath: fix FREE_PATHS case Martin Wilck
@ 2025-12-19  0:15   ` Benjamin Marzinski
  0 siblings, 0 replies; 33+ messages in thread
From: Benjamin Marzinski @ 2025-12-19  0:15 UTC (permalink / raw)
  To: Martin Wilck; +Cc: Christophe Varoqui, dm-devel, Martin Wilck

On Wed, Dec 17, 2025 at 10:21:04PM +0100, Martin Wilck wrote:
> free_multipath was passing FREE_PATHS to both free_pgvec() and
> free_pathvec(), which was wrong because a path could be referenced
> in both structures. This can't happen any more now, as free_pgvec()
> has lost the ability to free paths.
> 
> In general, free_multipath can't make any assumptions about whether
> a given path is referenced in mpp->paths, mpp->pg, or both. Therefore
> look first for paths that are referenced in mpp->pg but not in mpp->paths,
> clean them, and finally loop over mpp->paths.

Like I said in 07/21, I don't think we should free paths here. The paths
linked from the multipath device are also in the pathvec. We should
always be freeing them from there, so we don't have a pathvec with freed
paths.

-Ben
 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/structs.c | 44 +++++++++++++++++++++++++++++-------------
>  1 file changed, 31 insertions(+), 13 deletions(-)
> 
> diff --git a/libmultipath/structs.c b/libmultipath/structs.c
> index 1ee29f6..29b62e4 100644
> --- a/libmultipath/structs.c
> +++ b/libmultipath/structs.c
> @@ -298,8 +298,12 @@ void free_multipath_attributes(struct multipath *mpp)
>  }
>  
>  void
> -free_multipath (struct multipath * mpp, enum free_path_mode free_paths)
> +free_multipath (struct multipath *mpp, enum free_path_mode free_paths)
>  {
> +	struct pathgroup *pgp;
> +	struct path *pp;
> +	int i, j;
> +
>  	if (!mpp)
>  		return;
>  
> @@ -310,22 +314,36 @@ free_multipath (struct multipath * mpp, enum free_path_mode free_paths)
>  		mpp->alias = NULL;
>  	}
>  
> -	if (free_paths == KEEP_PATHS && mpp->pg) {
> -		struct pathgroup *pgp;
> -		struct path *pp;
> -		int i, j;
> -
> -		/*
> -		 * Make sure paths carry no reference to this mpp any more
> -		 */
> -		vector_foreach_slot(mpp->pg, pgp, i) {
> -			vector_foreach_slot(pgp->paths, pp, j)
> -				if (pp->mpp == mpp)
> +	/*
> +	 * If free_paths == FREE_PATHS, free all paths.
> +	 * Otherwise, make sure no references to this mpp remain.
> +	 *
> +	 * First loop over mpp->pg and clean the paths that might
> +	 * be missing in mpp->paths. Then clean the rest
> +	 * by looping over mpp->paths.
> +	 */
> +	vector_foreach_slot(mpp->pg, pgp, i) {
> +		vector_foreach_slot(pgp->paths, pp, j) {
> +			if (find_slot(mpp->paths, pp) == -1) {
> +				if (free_paths == FREE_PATHS)
> +					free_path(pp);
> +				else
>  					pp->mpp = NULL;
> +			}
>  		}
>  	}
>  
> -	free_pathvec(mpp->paths, free_paths);
> +	vector_foreach_slot(mpp->paths, pp, i) {
> +		if (free_paths == FREE_PATHS)
> +			free_path(pp);
> +		else
> +			pp->mpp = NULL;
> +	}
> +
> +	/*
> +	 * We've freed paths above already, use KEEP_PATHS here
> +	 */
> +	free_pathvec(mpp->paths, KEEP_PATHS);
>  	free_pgvec(mpp->pg);
>  	if (mpp->hwe) {
>  		vector_free(mpp->hwe);
> -- 
> 2.52.0


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 17/21] libmpathutil: use union for bitfield
  2025-12-17 21:21 ` [PATCH 17/21] libmpathutil: use union for bitfield Martin Wilck
@ 2025-12-19  0:17   ` Benjamin Marzinski
  2025-12-19  8:08     ` Martin Wilck
  0 siblings, 1 reply; 33+ messages in thread
From: Benjamin Marzinski @ 2025-12-19  0:17 UTC (permalink / raw)
  To: Martin Wilck; +Cc: Christophe Varoqui, dm-devel, Martin Wilck

On Wed, Dec 17, 2025 at 10:21:09PM +0100, Martin Wilck wrote:
> The macro BITFIELD used a cast of a pointer to a different anonymous struct
> to (struct bitfield *). This violates strict aliasing rules, because the
> two structs aren't compatible types in the sense of the language
> specification. With -O2, gcc 15.1 generates code that makes th pgcmp()
> function fail, because the compiler doesn't initialize bf->len aka
> __storage_for__bf.len to 64.
> 
> The problem will show through error messages like this:
> 
> multipathd[1974208]: is_bit_set_in_bitfield: bitfield overflow: 1 >= 0
> 
> Fix this by using a union
> 
> Fixes: 9a2f173 ("libmpathutil: change STATIC_BITFIELD to BITFIELD")
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmpathutil/util.c       |  8 ++++----
>  libmpathutil/util.h       | 41 +++++++++++++++++++++++----------------
>  libmultipath/configure.c  |  4 ++--
>  libmultipath/pgpolicies.c |  2 +-
>  tests/util.c              | 10 +++++-----
>  5 files changed, 36 insertions(+), 29 deletions(-)
> 
> diff --git a/libmpathutil/util.c b/libmpathutil/util.c
> index 37412c6..5e45750 100644
> --- a/libmpathutil/util.c
> +++ b/libmpathutil/util.c
> @@ -337,15 +337,15 @@ void cleanup_fclose(void *p)
>  		fclose(p);
>  }
>  
> -void cleanup_bitfield(struct bitfield **p)
> +void cleanup_bitfield(union bitfield **p)
>  {
>  	free(*p);
>  }
>  
> -struct bitfield *alloc_bitfield(unsigned int maxbit)
> +union bitfield *alloc_bitfield(unsigned int maxbit)
>  {
>  	unsigned int n;
> -	struct bitfield *bf;
> +	union bitfield *bf;
>  
>  	if (maxbit == 0) {
>  		errno = EINVAL;
> @@ -353,7 +353,7 @@ struct bitfield *alloc_bitfield(unsigned int maxbit)
>  	}
>  
>  	n = (maxbit - 1) / bits_per_slot + 1;
> -	bf = calloc(1, sizeof(struct bitfield) + n * sizeof(bitfield_t));
> +	bf = calloc(1, sizeof(union bitfield) + (n - 1) * sizeof(bitfield_t));
>  	if (bf)
>  		bf->len = maxbit;
>  	return bf;
> diff --git a/libmpathutil/util.h b/libmpathutil/util.h
> index 3799fd4..1ec6a46 100644
> --- a/libmpathutil/util.h
> +++ b/libmpathutil/util.h
> @@ -86,29 +86,36 @@ typedef unsigned int bitfield_t;
>  #endif
>  #define bits_per_slot (sizeof(bitfield_t) * CHAR_BIT)
>  
> -struct bitfield {
> -	unsigned int len;
> -	bitfield_t bits[];
> +union bitfield {
> +	struct {
> +		unsigned int len;
> +		bitfield_t bits[];
> +	};
> +	/* for initialization in the BITFIELD macro */
> +	struct {
> +		unsigned int __len;
> +		bitfield_t __bits[1];
> +	};
>  };
>  
> -#define BITFIELD(name, length)					\
> -	struct {							\
> -		unsigned int len;					\
> -		bitfield_t bits[((length) - 1) / bits_per_slot + 1];	\
> -	} __storage_for__ ## name = {					\
> -		.len = (length),					\
> -		.bits = { 0, },						\
> +/*
> + * gcc 4.9.2 (Debian Jessie) raises an error if the initializer for
> + * .__len comes first. Thus put .__bits first.
> + */
> +#define BITFIELD(name, length)						\
> +	union bitfield __storage_for__ ## name = {			\
> +		.__bits = { 0 },					\
> +		.__len = (length),					\
>  	}; \

It looks like this only allocates one bitfield_t, regardless of length.
Am I missing something here?

-Ben

> -	struct bitfield *name = (struct bitfield *)& __storage_for__ ## name
> +	union bitfield *name = & __storage_for__ ## name
>  
> -struct bitfield *alloc_bitfield(unsigned int maxbit);
> +union bitfield *alloc_bitfield(unsigned int maxbit);
>  
>  void log_bitfield_overflow__(const char *f, unsigned int bit, unsigned int len);
>  #define log_bitfield_overflow(bit, len) \
>  	log_bitfield_overflow__(__func__, bit, len)
>  
> -static inline bool is_bit_set_in_bitfield(unsigned int bit,
> -				       const struct bitfield *bf)
> +static inline bool is_bit_set_in_bitfield(unsigned int bit, const union bitfield *bf)
>  {
>  	if (bit >= bf->len) {
>  		log_bitfield_overflow(bit, bf->len);
> @@ -118,7 +125,7 @@ static inline bool is_bit_set_in_bitfield(unsigned int bit,
>  		  (1ULL << (bit % bits_per_slot)));
>  }
>  
> -static inline void set_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
> +static inline void set_bit_in_bitfield(unsigned int bit, union bitfield *bf)
>  {
>  	if (bit >= bf->len) {
>  		log_bitfield_overflow(bit, bf->len);
> @@ -127,7 +134,7 @@ static inline void set_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
>  	bf->bits[bit / bits_per_slot] |= (1ULL << (bit % bits_per_slot));
>  }
>  
> -static inline void clear_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
> +static inline void clear_bit_in_bitfield(unsigned int bit, union bitfield *bf)
>  {
>  	if (bit >= bf->len) {
>  		log_bitfield_overflow(bit, bf->len);
> @@ -146,5 +153,5 @@ static inline void clear_bit_in_bitfield(unsigned int bit, struct bitfield *bf)
>  void cleanup_charp(char **p);
>  void cleanup_ucharp(unsigned char **p);
>  void cleanup_udev_device(struct udev_device **udd);
> -void cleanup_bitfield(struct bitfield **p);
> +void cleanup_bitfield(union bitfield **p);
>  #endif /* UTIL_H_INCLUDED */
> diff --git a/libmultipath/configure.c b/libmultipath/configure.c
> index a8e18a2..41b6a9f 100644
> --- a/libmultipath/configure.c
> +++ b/libmultipath/configure.c
> @@ -440,7 +440,7 @@ static int pgcmp(struct multipath *mpp, struct multipath *cmpp)
>  	int i, j;
>  	struct pathgroup *pgp, *cpgp;
>  	BITFIELD(bf, bits_per_slot);
> -	struct bitfield *bf__  __attribute__((cleanup(cleanup_bitfield))) = NULL;
> +	union bitfield *bf__ __attribute__((cleanup(cleanup_bitfield))) = NULL;
>  
>  	if (VECTOR_SIZE(mpp->pg) != VECTOR_SIZE(cmpp->pg))
>  		return 1;
> @@ -1049,7 +1049,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
>  	vector newmp;
>  	struct config *conf = NULL;
>  	int allow_queueing;
> -	struct bitfield *size_mismatch_seen;
> +	union bitfield *size_mismatch_seen;
>  	struct multipath * cmpp;
>  
>  	/* ignore refwwid if it's empty */
> diff --git a/libmultipath/pgpolicies.c b/libmultipath/pgpolicies.c
> index 2e1a543..57b7485 100644
> --- a/libmultipath/pgpolicies.c
> +++ b/libmultipath/pgpolicies.c
> @@ -201,7 +201,7 @@ int group_by_match(struct multipath * mp, vector paths,
>  		   bool (*path_match_fn)(struct path *, struct path *))
>  {
>  	int i, j;
> -	struct bitfield *bitmap;
> +	union bitfield *bitmap;
>  	struct path * pp;
>  	struct pathgroup * pgp;
>  	struct path * pp2;
> diff --git a/tests/util.c b/tests/util.c
> index da192ba..67d8148 100644
> --- a/tests/util.c
> +++ b/tests/util.c
> @@ -198,7 +198,7 @@ static uint64_t maybe_swap(uint64_t v)
>  
>  static void test_bitmask_1(void **state)
>  {
> -	struct bitfield *bf;
> +	union bitfield *bf;
>  	uint64_t *arr;
>  	int i, j, k, m, b;
>  
> @@ -240,7 +240,7 @@ static void test_bitmask_1(void **state)
>  
>  static void test_bitmask_2(void **state)
>  {
> -	struct bitfield *bf;
> +	union bitfield *bf;
>  	uint64_t *arr;
>  	int i, j, k, m, b;
>  
> @@ -307,7 +307,7 @@ static void test_bitmask_2(void **state)
>   */
>  static void test_bitmask_len_0(void **state)
>  {
> -	struct bitfield *bf;
> +	union bitfield *bf;
>  
>  	bf = alloc_bitfield(0);
>  	assert_null(bf);
> @@ -329,7 +329,7 @@ static unsigned int maybe_swap_idx(unsigned int i)
>  
>  static void _test_bitmask_small(unsigned int n)
>  {
> -	struct bitfield *bf;
> +	union bitfield *bf;
>  	uint32_t *arr;
>  	unsigned int size = maybe_swap_idx((n - 1) / 32) + 1, i;
>  
> @@ -378,7 +378,7 @@ static void _test_bitmask_small(unsigned int n)
>  
>  static void _test_bitmask_small_2(unsigned int n)
>  {
> -	struct bitfield *bf;
> +	union bitfield *bf;
>  	uint32_t *arr;
>  	unsigned int size = maybe_swap_idx((n - 1) / 32) + 1, i;
>  
> -- 
> 2.52.0


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 18/21] libmpathutil: add wrapper code for libudev
  2025-12-17 21:21 ` [PATCH 18/21] libmpathutil: add wrapper code for libudev Martin Wilck
@ 2025-12-19  0:38   ` Benjamin Marzinski
  2025-12-19  8:06     ` Martin Wilck
  0 siblings, 1 reply; 33+ messages in thread
From: Benjamin Marzinski @ 2025-12-19  0:38 UTC (permalink / raw)
  To: Martin Wilck; +Cc: Christophe Varoqui, dm-devel, Martin Wilck

On Wed, Dec 17, 2025 at 10:21:10PM +0100, Martin Wilck wrote:
> The libudev documentation [1] states that libudev is not thread-safe and
> shouldn't be used from multithreaded programs. While the man page also
> states that using libudev in multithreaded programs is unsafe even if
> locking is used, there's not much else we can do in multipath-tools to
> mitigate the problem, as we are using libudev in multiple threads all over
> the place.

Ugh. I'm a little confused on what exactly could go wrong. Do you know
what they are concerned with, that locking cannot fix? It sounds like
the functions themselves are safe to call from multiple threads, as long
as they are working on separate data. If they were manipulating global
state that wouldn't be true.

> Add wrapper code that wraps all calls to libudev in a critical section
> holding a specific mutex.
> 
> [1] https://man7.org/linux/man-pages/man3/libudev.3.html

I assume that there must be times when use call a udev outside of the
vecs lock. Did you check how often that is? Not that would be great to
pile even more work on the vecs lock.

Also, git flagged some whitespace errors.

> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmpathutil/libmpathutil.version |  62 +++
>  libmpathutil/mt-libudev.c         | 776 ++++++++++++++++++++++++++++++
>  libmpathutil/mt-libudev.h         | 120 +++++
>  libmpathutil/mt-udev-wrap.h       |  90 ++++
>  4 files changed, 1048 insertions(+)
>  create mode 100644 libmpathutil/mt-libudev.c
>  create mode 100644 libmpathutil/mt-libudev.h
>  create mode 100644 libmpathutil/mt-udev-wrap.h
> 
> diff --git a/libmpathutil/libmpathutil.version b/libmpathutil/libmpathutil.version
> index 14aa083..4ce1da6 100644
> --- a/libmpathutil/libmpathutil.version
> +++ b/libmpathutil/libmpathutil.version
> @@ -93,6 +93,68 @@ global:
>  	log_thread_stop;
>  	logsink;
>  	msort;
> +
> +        mt_udev_ref;
> +        mt_udev_unref;
> +        mt_udev_new;
> +
> +        mt_udev_list_entry_get_next;
> +        mt_udev_list_entry_get_by_name;
> +        mt_udev_list_entry_get_name;
> +        mt_udev_list_entry_get_value;
> +
> +        mt_udev_device_ref;
> +        mt_udev_device_unref;
> +        
> +        mt_udev_device_new_from_syspath;
> +        mt_udev_device_new_from_devnum;
> +        mt_udev_device_new_from_subsystem_sysname;
> +        mt_udev_device_new_from_device_id;
> +        mt_udev_device_new_from_environment;
> +
> +        mt_udev_device_get_udev;
> +        mt_udev_device_get_parent;
> +        mt_udev_device_get_parent_with_subsystem_devtype;
> +        mt_udev_device_get_devpath;
> +        mt_udev_device_get_subsystem;
> +	mt_udev_device_get_devtype;
> +        mt_udev_device_get_syspath;
> +        mt_udev_device_get_sysname;
> +        mt_udev_device_get_devnum;
> +        mt_udev_device_get_is_initialized;
> +        mt_udev_device_get_property_value;
> +        mt_udev_device_get_seqnum;
> +        mt_udev_device_set_sysattr_value;
> +        mt_udev_device_get_sysattr_value;
> +        mt_udev_device_get_driver;
> +        mt_udev_device_get_devnode;
> +        
> +        mt_udev_device_get_properties_list_entry;
> +
> +        mt_udev_monitor_new_from_netlink;
> +        mt_udev_monitor_enable_receiving;
> +        mt_udev_monitor_get_fd;
> +        mt_udev_monitor_receive_device;
> +        mt_udev_monitor_filter_add_match_subsystem_devtype;
> +        mt_udev_monitor_ref;
> +        mt_udev_monitor_unref;
> +        mt_udev_monitor_set_receive_buffer_size;
> +
> +        mt_udev_enumerate_new;
> +        mt_udev_enumerate_ref;
> +        mt_udev_enumerate_unref;
> +        mt_udev_enumerate_add_match_subsystem;
> +        mt_udev_enumerate_scan_devices;
> +        mt_udev_enumerate_get_list_entry;
> +	mt_udev_enumerate_add_nomatch_subsystem;
> +        mt_udev_enumerate_add_match_sysattr;
> +        mt_udev_enumerate_add_nomatch_sysattr;
> +        mt_udev_enumerate_add_match_property;
> +        mt_udev_enumerate_add_match_tag;
> +        mt_udev_enumerate_add_match_parent;
> +        mt_udev_enumerate_add_match_is_initialized;
> +        mt_udev_enumerate_add_syspath;
> +	
>  	normalize_timespec;
>  	parse_devt;
>  	print_strbuf;
> diff --git a/libmpathutil/mt-libudev.c b/libmpathutil/mt-libudev.c
> new file mode 100644
> index 0000000..2e37c7d
> --- /dev/null
> +++ b/libmpathutil/mt-libudev.c
> @@ -0,0 +1,776 @@
> +#include "mt-libudev.h"
> +#include <stddef.h>
> +#include <libudev.h>
> +#include "util.h"
> +
> +static pthread_mutex_t libudev_mutex = PTHREAD_MUTEX_INITIALIZER;
> +
> +struct udev *mt_udev_ref(struct udev *udev)
> +{
> +	struct udev *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_ref(udev);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev *mt_udev_unref(struct udev *udev)
> +{
> +	struct udev *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_unref(udev);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev *mt_udev_new(void)
> +{
> +	struct udev *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_new();
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_list_entry *mt_udev_list_entry_get_next(struct udev_list_entry *list_entry)
> +{
> +	struct udev_list_entry *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_list_entry_get_next(list_entry);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_list_entry *
> +mt_udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name)
> +{
> +	struct udev_list_entry *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_list_entry_get_by_name(list_entry, name);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +const char *mt_udev_list_entry_get_name(struct udev_list_entry *list_entry)
> +{
> +	const char *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_list_entry_get_name(list_entry);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +const char *mt_udev_list_entry_get_value(struct udev_list_entry *list_entry)
> +{
> +	const char *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_list_entry_get_value(list_entry);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_device *
> +mt_udev_device_new_from_syspath(struct udev *udev, const char *syspath)
> +{
> +	struct udev_device *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_new_from_syspath(udev, syspath);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_device *
> +mt_udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum)
> +{
> +	struct udev_device *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_new_from_devnum(udev, type, devnum);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_device *
> +mt_udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem,
> +					  const char *sysname)
> +{
> +	struct udev_device *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_new_from_subsystem_sysname(udev, subsystem, sysname);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_device *mt_udev_device_new_from_device_id(struct udev *udev, char *id)
> +{
> +	struct udev_device *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_new_from_device_id(udev, id);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_device *mt_udev_device_new_from_environment(struct udev *udev)
> +{
> +	struct udev_device *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_new_from_environment(udev);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_device *mt_udev_device_ref(struct udev_device *udev_device)
> +{
> +	struct udev_device *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_ref(udev_device);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_device *mt_udev_device_unref(struct udev_device *udev_device)
> +{
> +	struct udev_device *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_unref(udev_device);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev *mt_udev_device_get_udev(struct udev_device *udev_device)
> +{
> +	struct udev *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_get_udev(udev_device);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_device *mt_udev_device_get_parent(struct udev_device *udev_device)
> +{
> +	struct udev_device *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_get_parent(udev_device);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_device *mt_udev_device_get_parent_with_subsystem_devtype(
> +	struct udev_device *udev_device, const char *subsystem, const char *devtype)
> +{
> +	struct udev_device *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_get_parent_with_subsystem_devtype(udev_device,
> +							    subsystem, devtype);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +const char *mt_udev_device_get_devpath(struct udev_device *udev_device)
> +{
> +	const char *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_get_devpath(udev_device);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +const char *mt_udev_device_get_subsystem(struct udev_device *udev_device)
> +{
> +	const char *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_get_subsystem(udev_device);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +const char *mt_udev_device_get_devtype(struct udev_device *udev_device)
> +{
> +	const char *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_get_devtype(udev_device);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +const char *mt_udev_device_get_syspath(struct udev_device *udev_device)
> +{
> +	const char *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_get_syspath(udev_device);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +const char *mt_udev_device_get_sysname(struct udev_device *udev_device)
> +{
> +	const char *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_get_sysname(udev_device);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +dev_t mt_udev_device_get_devnum(struct udev_device *udev_device)
> +{
> +	dev_t ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_get_devnum(udev_device);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +unsigned long long mt_udev_device_get_seqnum(struct udev_device *udev_device)
> +{
> +	unsigned long long ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_get_seqnum(udev_device);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +const char *mt_udev_device_get_driver(struct udev_device *udev_device)
> +{
> +	const char *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_get_driver(udev_device);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +const char *mt_udev_device_get_devnode(struct udev_device *udev_device)
> +{
> +	const char *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_get_devnode(udev_device);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +int mt_udev_device_get_is_initialized(struct udev_device *udev_device)
> +{
> +	int ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_get_is_initialized(udev_device);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +const char *
> +mt_udev_device_get_property_value(struct udev_device *udev_device, const char *key)
> +{
> +	const char *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_get_property_value(udev_device, key);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +const char *mt_udev_device_get_sysattr_value(struct udev_device *udev_device,
> +					     const char *sysattr)
> +{
> +	const char *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_get_sysattr_value(udev_device, sysattr);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +int mt_udev_device_set_sysattr_value(struct udev_device *udev_device,
> +				     const char *sysattr, char *value)
> +{
> +	int ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_set_sysattr_value(udev_device, sysattr, value);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_list_entry *
> +mt_udev_device_get_properties_list_entry(struct udev_device *udev_device)
> +{
> +	struct udev_list_entry *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_device_get_properties_list_entry(udev_device);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +/*
> + * multipath-tools doesn't use these, and some of them aren't available
> + * in older libudev versions. Keeping the code here in case we will
> + * need it in the future.
> +
> +struct udev_list_entry *mt_udev_device_get_devlinks_list_entry(struct
> +udev_device *udev_device)
> +{
> +    struct udev_list_entry *ret;
> +    pthread_mutex_lock(&libudev_mutex);
> +    pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +    ret = udev_device_get_devlinks_list_entry(udev_device);
> +
> +    pthread_cleanup_pop(1);
> +    return ret;
> +}
> +
> +struct udev_list_entry *mt_udev_device_get_tags_list_entry(struct udev_device
> +*udev_device)
> +{
> +    struct udev_list_entry *ret;
> +    pthread_mutex_lock(&libudev_mutex);
> +    pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +    ret = udev_device_get_tags_list_entry(udev_device);
> +
> +    pthread_cleanup_pop(1);
> +    return ret;
> +}
> +
> +struct udev_list_entry *mt_udev_device_get_current_tags_list_entry(struct
> +udev_device *udev_device)
> +{
> +    struct udev_list_entry *ret;
> +    pthread_mutex_lock(&libudev_mutex);
> +    pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +    ret = udev_device_get_current_tags_list_entry(udev_device);
> +
> +    pthread_cleanup_pop(1);
> +    return ret;
> +}
> +
> +struct udev_list_entry *mt_udev_device_get_sysattr_list_entry(struct
> +udev_device *udev_device)
> +{
> +    struct udev_list_entry *ret;
> +    pthread_mutex_lock(&libudev_mutex);
> +    pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +    ret = udev_device_get_sysattr_list_entry(udev_device);
> +
> +    pthread_cleanup_pop(1);
> +    return ret;
> +}
> +*/
> +
> +struct udev_monitor *
> +mt_udev_monitor_new_from_netlink(struct udev *udev, const char *name)
> +{
> +	struct udev_monitor *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_monitor_new_from_netlink(udev, name);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_monitor *mt_udev_monitor_ref(struct udev_monitor *udev_monitor)
> +{
> +	struct udev_monitor *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_monitor_ref(udev_monitor);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_monitor *mt_udev_monitor_unref(struct udev_monitor *udev_monitor)
> +{
> +	struct udev_monitor *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_monitor_unref(udev_monitor);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +int mt_udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
> +{
> +	int ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_monitor_enable_receiving(udev_monitor);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +int mt_udev_monitor_get_fd(struct udev_monitor *udev_monitor)
> +{
> +	int ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_monitor_get_fd(udev_monitor);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_device *mt_udev_monitor_receive_device(struct udev_monitor *udev_monitor)
> +{
> +	struct udev_device *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_monitor_receive_device(udev_monitor);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +int mt_udev_monitor_filter_add_match_subsystem_devtype(
> +	struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype)
> +{
> +	int ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_monitor_filter_add_match_subsystem_devtype(udev_monitor,
> +							      subsystem, devtype);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +int mt_udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size)
> +{
> +	int ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_monitor_set_receive_buffer_size(udev_monitor, size);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_enumerate *mt_udev_enumerate_new(struct udev *udev)
> +{
> +	struct udev_enumerate *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_enumerate_new(udev);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_enumerate *mt_udev_enumerate_ref(struct udev_enumerate *udev_enumerate)
> +{
> +	struct udev_enumerate *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_enumerate_ref(udev_enumerate);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_enumerate *mt_udev_enumerate_unref(struct udev_enumerate *udev_enumerate)
> +{
> +	struct udev_enumerate *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_enumerate_unref(udev_enumerate);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +int mt_udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate,
> +					  const char *subsystem)
> +{
> +	int ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_enumerate_add_match_subsystem(udev_enumerate, subsystem);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +int mt_udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate,
> +					    const char *subsystem)
> +{
> +	int ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_enumerate_add_nomatch_subsystem(udev_enumerate, subsystem);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +int mt_udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate,
> +					const char *sysattr, const char *value)
> +{
> +	int ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_enumerate_add_match_sysattr(udev_enumerate, sysattr, value);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +int mt_udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate,
> +					  const char *sysattr, const char *value)
> +{
> +	int ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_enumerate_add_nomatch_sysattr(udev_enumerate, sysattr, value);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +int mt_udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate,
> +					 const char *property, const char *value)
> +{
> +	int ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_enumerate_add_match_property(udev_enumerate, property, value);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +int mt_udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate,
> +				    const char *tag)
> +{
> +	int ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_enumerate_add_match_tag(udev_enumerate, tag);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +int mt_udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate,
> +				       struct udev_device *parent)
> +{
> +	int ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_enumerate_add_match_parent(udev_enumerate, parent);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +int mt_udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate)
> +{
> +	int ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_enumerate_add_match_is_initialized(udev_enumerate);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +int mt_udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate,
> +				  const char *syspath)
> +{
> +	int ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_enumerate_add_syspath(udev_enumerate, syspath);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +int mt_udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate)
> +{
> +	int ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_enumerate_scan_devices(udev_enumerate);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> +
> +struct udev_list_entry *
> +mt_udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate)
> +{
> +	struct udev_list_entry *ret;
> +
> +	pthread_mutex_lock(&libudev_mutex);
> +	pthread_cleanup_push(cleanup_mutex, &libudev_mutex);
> +
> +	ret = udev_enumerate_get_list_entry(udev_enumerate);
> +
> +	pthread_cleanup_pop(1);
> +	return ret;
> +}
> diff --git a/libmpathutil/mt-libudev.h b/libmpathutil/mt-libudev.h
> new file mode 100644
> index 0000000..1c1e748
> --- /dev/null
> +++ b/libmpathutil/mt-libudev.h
> @@ -0,0 +1,120 @@
> +#ifndef MT_LIBUDEV_H
> +#define MT_LIBUDEV_H
> +
> +#include <pthread.h>
> +#include <stdarg.h>    /* for va_list */
> +#include <sys/types.h> /* For dev_t */
> +
> +struct udev;
> +struct udev_list_entry;
> +struct udev_device;
> +struct udev_monitor;
> +struct udev_enumerate;
> +
> +struct udev *mt_udev_ref(struct udev *udev);
> +struct udev *mt_udev_unref(struct udev *udev);
> +struct udev *mt_udev_new(void);
> +
> +struct udev_list_entry *
> +mt_udev_list_entry_get_next(struct udev_list_entry *list_entry);
> +struct udev_list_entry *
> +mt_udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name);
> +const char *mt_udev_list_entry_get_name(struct udev_list_entry *list_entry);
> +const char *mt_udev_list_entry_get_value(struct udev_list_entry *list_entry);
> +
> +struct udev_device *
> +mt_udev_device_new_from_syspath(struct udev *udev, const char *syspath);
> +struct udev_device *
> +mt_udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum);
> +struct udev_device *
> +mt_udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem,
> +					  const char *sysname);
> +/*
> + * Some older libudev versions  don't use "const" for the id argument,
> + * therefore we can't use it here, either.
> + */
> +struct udev_device *mt_udev_device_new_from_device_id(struct udev *udev, char *id);
> +struct udev_device *mt_udev_device_new_from_environment(struct udev *udev);
> +
> +struct udev_device *mt_udev_device_ref(struct udev_device *udev_device);
> +struct udev_device *mt_udev_device_unref(struct udev_device *udev_device);
> +struct udev *mt_udev_device_get_udev(struct udev_device *udev_device);
> +struct udev_device *mt_udev_device_get_parent(struct udev_device *udev_device);
> +struct udev_device *mt_udev_device_get_parent_with_subsystem_devtype(
> +	struct udev_device *udev_device, const char *subsystem,
> +	const char *devtype);
> +const char *mt_udev_device_get_devpath(struct udev_device *udev_device);
> +const char *mt_udev_device_get_subsystem(struct udev_device *udev_device);
> +const char *mt_udev_device_get_devtype(struct udev_device *udev_device);
> +const char *mt_udev_device_get_syspath(struct udev_device *udev_device);
> +const char *mt_udev_device_get_sysname(struct udev_device *udev_device);
> +int mt_udev_device_get_is_initialized(struct udev_device *udev_device);
> +const char *
> +mt_udev_device_get_property_value(struct udev_device *udev_device, const char *key);
> +dev_t mt_udev_device_get_devnum(struct udev_device *udev_device);
> +unsigned long long int mt_udev_device_get_seqnum(struct udev_device *udev_device);
> +const char *mt_udev_device_get_driver(struct udev_device *udev_device);
> +const char *mt_udev_device_get_devnode(struct udev_device *udev_device);
> +const char *mt_udev_device_get_sysattr_value(struct udev_device *udev_device,
> +					     const char *sysattr);
> +/*
> + * libudev-215 (Debian jessie) doesn't use "const" for the value argument,
> + * therefore we can't use it here, either.
> + */
> +int mt_udev_device_set_sysattr_value(struct udev_device *udev_device,
> +				     const char *sysattr, char *value);
> +
> +struct udev_list_entry *
> +mt_udev_device_get_properties_list_entry(struct udev_device *udev_device);
> +/*
> + * multipath-tools doesn't use these, and some of them aren't available
> + * in older libudev versions.
> +
> + struct udev_list_entry *mt_udev_device_get_devlinks_list_entry(struct
> + udev_device *udev_device); struct udev_list_entry
> + *mt_udev_device_get_tags_list_entry(struct udev_device *udev_device); struct
> + udev_list_entry *mt_udev_device_get_current_tags_list_entry(struct udev_device
> + *udev_device); struct udev_list_entry
> + *mt_udev_device_get_sysattr_list_entry(struct udev_device *udev_device);
> +*/
> +struct udev_monitor *
> +mt_udev_monitor_new_from_netlink(struct udev *udev, const char *name);
> +struct udev_monitor *mt_udev_monitor_ref(struct udev_monitor *udev_monitor);
> +struct udev_monitor *mt_udev_monitor_unref(struct udev_monitor *udev_monitor);
> +int mt_udev_monitor_enable_receiving(struct udev_monitor *udev_monitor);
> +int mt_udev_monitor_get_fd(struct udev_monitor *udev_monitor);
> +struct udev_device *
> +mt_udev_monitor_receive_device(struct udev_monitor *udev_monitor);
> +int mt_udev_monitor_filter_add_match_subsystem_devtype(
> +	struct udev_monitor *udev_monitor, const char *subsystem,
> +	const char *devtype);
> +int mt_udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor,
> +					    int size);
> +
> +struct udev_enumerate *mt_udev_enumerate_new(struct udev *udev);
> +struct udev_enumerate *mt_udev_enumerate_ref(struct udev_enumerate *udev_enumerate);
> +struct udev_enumerate *
> +mt_udev_enumerate_unref(struct udev_enumerate *udev_enumerate);
> +int mt_udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate,
> +					  const char *subsystem);
> +int mt_udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate,
> +					    const char *subsystem);
> +int mt_udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate,
> +					const char *sysattr, const char *value);
> +int mt_udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate,
> +					  const char *sysattr, const char *value);
> +int mt_udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate,
> +					 const char *property, const char *value);
> +int mt_udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate,
> +				    const char *tag);
> +int mt_udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate,
> +				       struct udev_device *parent);
> +int mt_udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate);
> +int mt_udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate,
> +				  const char *syspath);
> +int mt_udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate);
> +int mt_udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate);
> +struct udev_list_entry *
> +mt_udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate);
> +
> +#endif
> diff --git a/libmpathutil/mt-udev-wrap.h b/libmpathutil/mt-udev-wrap.h
> new file mode 100644
> index 0000000..f232821
> --- /dev/null
> +++ b/libmpathutil/mt-udev-wrap.h
> @@ -0,0 +1,90 @@
> +#ifndef MT_LIBUDEV_WRAP_HPP
> +#define MT_LIBUDEV_WRAP_HPP
> +#include "mt-libudev.h"
> +
> +#define udev_ref(udev)                       mt_udev_ref(udev)
> +#define udev_unref(udev)                     mt_udev_unref(udev)
> +#define udev_new()                           mt_udev_new()
> +
> +#define udev_list_entry_get_next(list_entry)             mt_udev_list_entry_get_next(list_entry)
> +#define udev_list_entry_get_by_name(list_entry, name)    mt_udev_list_entry_get_by_name(list_entry, name)
> +#define udev_list_entry_get_name(list_entry)             mt_udev_list_entry_get_name(list_entry)
> +#define udev_list_entry_get_value(list_entry)            mt_udev_list_entry_get_value(list_entry)
> +
> +#define udev_list_entry_foreach(list_entry, first_entry)	\
> +        for (list_entry = first_entry; \
> +             list_entry; \
> +             list_entry = udev_list_entry_get_next(list_entry))
> +
> +#define udev_device_new_from_syspath(udev, syspath)               mt_udev_device_new_from_syspath(udev, syspath)
> +#define udev_device_new_from_devnum(udev, type, devnum)           mt_udev_device_new_from_devnum(udev, type, devnum)
> +#define udev_device_new_from_subsystem_sysname(udev, subsystem, sysname) \
> +                                                                  mt_udev_device_new_from_subsystem_sysname(udev, subsystem, sysname)
> +#define udev_device_new_from_device_id(udev, id) mt_udev_device_new_from_device_id(udev, id)
> +#define udev_device_new_from_environment(udev)                    mt_udev_device_new_from_environment(udev)
> +
> +#define udev_device_ref(udev_device)                      mt_udev_device_ref(udev_device)
> +#define udev_device_unref(udev_device)                    mt_udev_device_unref(udev_device)
> +#define udev_device_get_udev(udev_device)                 mt_udev_device_get_udev(udev_device)
> +#define udev_device_get_parent(udev_device)               mt_udev_device_get_parent(udev_device)
> +#define udev_device_get_parent_with_subsystem_devtype(udev_device, subsystem, devtype) \
> +	mt_udev_device_get_parent_with_subsystem_devtype(udev_device, subsystem, devtype)
> +#define udev_device_get_devpath(udev_device)              mt_udev_device_get_devpath(udev_device)
> +#define udev_device_get_subsystem(udev_device)            mt_udev_device_get_subsystem(udev_device)
> +#define udev_device_get_devtype(udev_device)              mt_udev_device_get_devtype(udev_device)
> +#define udev_device_get_syspath(udev_device)              mt_udev_device_get_syspath(udev_device)
> +#define udev_device_get_sysname(udev_device)              mt_udev_device_get_sysname(udev_device)
> +#define udev_device_get_is_initialized(udev_device)       mt_udev_device_get_is_initialized(udev_device)
> +#define udev_device_get_property_value(udev_device, key)  mt_udev_device_get_property_value(udev_device, key)
> +#define udev_device_get_devnum(udev_device)               mt_udev_device_get_devnum(udev_device)
> +#define udev_device_get_seqnum(udev_device)               mt_udev_device_get_seqnum(udev_device)
> +#define udev_device_get_driver(udev_device)                       mt_udev_device_get_driver(udev_device)
> +#define udev_device_get_devnode(udev_device)                      mt_udev_device_get_devnode(udev_device)
> +#define udev_device_get_sysattr_value(udev_device, sysattr) \
> +                                                          mt_udev_device_get_sysattr_value(udev_device, sysattr)
> +#define udev_device_set_sysattr_value(udev_device, sysattr, value) \
> +	mt_udev_device_set_sysattr_value(udev_device, sysattr, value)
> +
> +#define udev_device_get_devlinks_list_entry(udev_device)        mt_udev_device_get_devlinks_list_entry(udev_device)
> +#define udev_device_get_properties_list_entry(udev_device)      mt_udev_device_get_properties_list_entry(udev_device)
> +#define udev_device_get_tags_list_entry(udev_device)            mt_udev_device_get_tags_list_entry(udev_device)
> +#define udev_device_get_current_tags_list_entry(udev_device)    mt_udev_device_get_current_tags_list_entry(udev_device)
> +#define udev_device_get_sysattr_list_entry(udev_device)         mt_udev_device_get_sysattr_list_entry(udev_device)
> +
> +#define udev_monitor_new_from_netlink(udev, name)         mt_udev_monitor_new_from_netlink(udev, name)
> +#define udev_monitor_ref(udev_monitor)                    mt_udev_monitor_ref(udev_monitor)
> +#define udev_monitor_unref(udev_monitor)                  mt_udev_monitor_unref(udev_monitor)
> +#define udev_monitor_enable_receiving(udev_monitor)       mt_udev_monitor_enable_receiving(udev_monitor)
> +#define udev_monitor_get_fd(udev_monitor)                 mt_udev_monitor_get_fd(udev_monitor)
> +#define udev_monitor_receive_device(udev_monitor)         mt_udev_monitor_receive_device(udev_monitor)
> +#define udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, subsystem, devtype) \
> +	mt_udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, subsystem, devtype)
> +#define udev_monitor_set_receive_buffer_size(udev_monitor, size) \
> +                                                          mt_udev_monitor_set_receive_buffer_size(udev_monitor, size)
> +
> +#define udev_enumerate_new(udev)                          mt_udev_enumerate_new(udev)
> +#define udev_enumerate_unref(udev_enumerate)              mt_udev_enumerate_unref(udev_enumerate)
> +#define udev_enumerate_add_match_subsystem(udev_enumerate, subsystem) \
> +                                                          mt_udev_enumerate_add_match_subsystem(udev_enumerate, subsystem)
> +#define udev_enumerate_add_nomatch_subsystem(udev_enumerate, subsystem) \
> +                                                          mt_udev_enumerate_add_nomatch_subsystem(udev_enumerate, subsystem)
> +#define udev_enumerate_add_match_sysattr(udev_enumerate, sysattr, value) \
> +                                                          mt_udev_enumerate_add_match_sysattr(udev_enumerate, sysattr, value)
> +#define udev_enumerate_add_nomatch_sysattr(udev_enumerate, sysattr, value) \
> +                                                          mt_udev_enumerate_add_nomatch_sysattr(udev_enumerate, sysattr, value)
> +#define udev_enumerate_add_match_property(udev_enumerate, property, value) \
> +                                                          mt_udev_enumerate_add_match_property(udev_enumerate, property, value)
> +#define udev_enumerate_add_match_tag(udev_enumerate, tag) mt_udev_enumerate_add_match_tag(udev_enumerate, tag)
> +#define udev_enumerate_add_match_parent(udev_enumerate, parent) \
> +                                                          mt_udev_enumerate_add_match_parent(udev_enumerate, parent)
> +#define udev_enumerate_add_match_is_initialized(udev_enumerate) \
> +                                                          mt_udev_enumerate_add_match_is_initialized(udev_enumerate)
> +#define udev_enumerate_add_syspath(udev_enumerate, syspath) \
> +                                                          mt_udev_enumerate_add_syspath(udev_enumerate, syspath)
> +
> +#define udev_enumerate_scan_devices(udev_enumerate)       mt_udev_enumerate_scan_devices(udev_enumerate)
> +#define udev_enumerate_scan_device(udev_enumerate, syspath) \
> +                                                          mt_udev_enumerate_scan_device(udev_enumerate, syspath)
> +#define udev_enumerate_get_list_entry(udev_enumerate)     mt_udev_enumerate_get_list_entry(udev_enumerate)
> +
> +#endif
> -- 
> 2.52.0


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 00/21] Multipath-tools: various bug fixes
  2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
                   ` (20 preceding siblings ...)
  2025-12-17 21:21 ` [PATCH 21/21] multipath-tools tests: adaptations for cmocka 2.0 Martin Wilck
@ 2025-12-19  0:40 ` Benjamin Marzinski
  21 siblings, 0 replies; 33+ messages in thread
From: Benjamin Marzinski @ 2025-12-19  0:40 UTC (permalink / raw)
  To: Martin Wilck; +Cc: Christophe Varoqui, dm-devel, Martin Wilck

On Wed, Dec 17, 2025 at 10:20:52PM +0100, Martin Wilck wrote:
> This series contains a number of fixes for various recent issues with
> multipath-tools. The starting point was a use-after-free issue reported on
> GitHub [1]. The actual fixes for that are 02/21 and 06/21. Because this
> Patches 3-12 generally rework the freeing of maps, trying to avoid unexpected
> freeing of paths while freeing multipath structures.
> 
> Because this changes memory handling in multipathd, I ran a set of tests to
> make sure the series doesn't open up new memory leaks. The good news is that I
> haven't found any, except some trivial ones (15/21, 16/21). But I did see one
> minor issue related to libudev [2]. After I found a warning in the libudev man
> page about the library not being thread-safe, I suspected that this might be
> causing the leak, and came up with code wrapping all libudev calls with a
> mutex (18/21, 19/21).  Unfortunately it didn't fix the observed leak, but I
> suppose it's still useful because multipathd is using libudev in a way that
> the authors of the library explicitly dismiss as unsupported.
> 
> The release of cmocka 2.0 [3] necessitated rather large-ish adaptations in our
> unit test code (20/21, 21/21).
> 
> Finally 13/21 and 17/21 are bug fixes; in particular the latter is rather nasty.
> 
> [1] https://github.com/opensvc/multipath-tools/issues/128
> [2] https://github.com/opensvc/multipath-tools/issues/130
> [3] https://github.com/opensvc/multipath-tools/issues/129
> 

For everything but 3,9,12, and 17

Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>

> Martin Wilck (21):
>   libmultipath: drop drop_multipath
>   libmultipath: don't access path members in free_pgvec()
>   multipathd: free paths in checker_finished()
>   libmultipath: don't touch mpvec in remove_map()
>   libmpathutil: constify find_slot()
>   libmultipath: don't free paths in orphan_paths()
>   libmultipath: free orphaned paths in check_removed_paths()
>   libmultipath: remove free_paths argument from free_pathgroup()
>   libmultipath: fix numeric value of free_paths in free_multipaths()
>   libmultipath: remove free_paths argument from free_pgvec()
>   libmultipath: remove free_paths argument from free_multipathvec()
>   libmultipath: free_multipath: fix FREE_PATHS case
>   multipath-tools: Fix ISO C23 errors with strchr()
>   libmultipath: simplify sysfs_get_target_nodename()
>   multipathd: join the init_unwinder dummy thread
>   kpartx: fix some memory leaks
>   libmpathutil: use union for bitfield
>   libmpathutil: add wrapper code for libudev
>   multipath-tools: use the libudev wrapper functions
>   Makefile: add functionality to determine cmocka version
>   multipath-tools tests: adaptations for cmocka 2.0
> 
>  Makefile.inc                          |   2 +-
>  create-config.mk                      |   5 +
>  kpartx/kpartx.c                       |  18 +-
>  libdmmp/Makefile                      |   2 +-
>  libdmmp/libdmmp.c                     |   2 +-
>  libmpathpersist/mpath_persist.c       |   2 +-
>  libmpathpersist/mpath_persist_int.c   |   2 +-
>  libmpathpersist/mpath_pr_ioctl.c      |   2 +-
>  libmpathpersist/mpath_updatepr.c      |   2 +-
>  libmpathutil/Makefile                 |   2 +-
>  libmpathutil/globals.c                |   2 +-
>  libmpathutil/libmpathutil.version     |  62 ++
>  libmpathutil/mt-libudev.c             | 776 ++++++++++++++++++++++++++
>  libmpathutil/mt-libudev.h             | 120 ++++
>  libmpathutil/mt-udev-wrap.h           |  90 +++
>  libmpathutil/parser.c                 |   2 +-
>  libmpathutil/util.c                   |  12 +-
>  libmpathutil/util.h                   |  43 +-
>  libmpathutil/vector.c                 |   3 +-
>  libmpathutil/vector.h                 |   2 +-
>  libmpathvalid/mpath_valid.c           |   2 +-
>  libmultipath/blacklist.c              |   2 +-
>  libmultipath/blacklist.h              |   2 +-
>  libmultipath/config.c                 |   2 +-
>  libmultipath/configure.c              |  22 +-
>  libmultipath/dict.c                   |   2 +-
>  libmultipath/discovery.c              |  36 +-
>  libmultipath/dmparser.c               |   6 +-
>  libmultipath/foreign.c                |   2 +-
>  libmultipath/foreign.h                |   2 +-
>  libmultipath/foreign/nvme.c           |   2 +-
>  libmultipath/libmultipath.version     |   2 +
>  libmultipath/pgpolicies.c             |  14 +-
>  libmultipath/print.c                  |   2 +-
>  libmultipath/prio.c                   |   2 +-
>  libmultipath/prioritizers/alua_rtpg.c |   2 +-
>  libmultipath/prioritizers/ana.c       |   2 +-
>  libmultipath/prkey.c                  |   4 +-
>  libmultipath/prkey.h                  |   2 +-
>  libmultipath/propsel.c                |   2 +-
>  libmultipath/structs.c                |  94 ++--
>  libmultipath/structs.h                |   7 +-
>  libmultipath/structs_vec.c            |  58 +-
>  libmultipath/structs_vec.h            |   4 +-
>  libmultipath/sysfs.c                  |   2 +-
>  libmultipath/uevent.c                 |   2 +-
>  libmultipath/valid.c                  |   2 +-
>  mpathpersist/main.c                   |   2 +-
>  multipath/main.c                      |  16 +-
>  multipathd/cli_handlers.c             |   2 +-
>  multipathd/fpin_handlers.c            |   2 +-
>  multipathd/init_unwinder.c            |   4 +-
>  multipathd/main.c                     |  20 +-
>  tests/Makefile                        |  22 +-
>  tests/alias.c                         |  44 +-
>  tests/blacklist.c                     |   2 +-
>  tests/cli.c                           |   8 +-
>  tests/cmocka-compat.h                 |  16 +
>  tests/devt.c                          |   6 +-
>  tests/directio.c                      |  23 +-
>  tests/dmevents.c                      |  74 +--
>  tests/features.c                      |   2 +-
>  tests/hwtable.c                       |   6 +-
>  tests/mapinfo.c                       |  82 +--
>  tests/mpathvalid.c                    |  18 +-
>  tests/parser.c                        |   2 +-
>  tests/pgpolicy.c                      |   4 +-
>  tests/strbuf.c                        | 130 ++---
>  tests/sysfs.c                         |  76 +--
>  tests/test-lib.c                      |  90 +--
>  tests/test-log.c                      |  10 +-
>  tests/uevent.c                        |   2 +-
>  tests/unaligned.c                     |   8 +-
>  tests/util.c                          | 116 ++--
>  tests/valid.c                         |  30 +-
>  tests/vpd.c                           |  12 +-
>  76 files changed, 1680 insertions(+), 581 deletions(-)
>  create mode 100644 libmpathutil/mt-libudev.c
>  create mode 100644 libmpathutil/mt-libudev.h
>  create mode 100644 libmpathutil/mt-udev-wrap.h
>  create mode 100644 tests/cmocka-compat.h
> 
> -- 
> 2.52.0


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 18/21] libmpathutil: add wrapper code for libudev
  2025-12-19  0:38   ` Benjamin Marzinski
@ 2025-12-19  8:06     ` Martin Wilck
  2025-12-19 17:27       ` Benjamin Marzinski
  0 siblings, 1 reply; 33+ messages in thread
From: Martin Wilck @ 2025-12-19  8:06 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: Christophe Varoqui, dm-devel

Hi Ben,

On Thu, 2025-12-18 at 19:38 -0500, Benjamin Marzinski wrote:
> On Wed, Dec 17, 2025 at 10:21:10PM +0100, Martin Wilck wrote:
> > The libudev documentation [1] states that libudev is not thread-
> > safe and
> > shouldn't be used from multithreaded programs. While the man page
> > also
> > states that using libudev in multithreaded programs is unsafe even
> > if
> > locking is used, there's not much else we can do in multipath-tools
> > to
> > mitigate the problem, as we are using libudev in multiple threads
> > all over
> > the place.
> 
> Ugh. I'm a little confused on what exactly could go wrong. Do you
> know
> what they are concerned with, that locking cannot fix? 

No. I've asked our SUSE systemd experts but they didn't know either.
The man page mentions the use of setenv()/getenv(), but I don't think
that matters for us.

> It sounds like
> the functions themselves are safe to call from multiple threads, as
> long
> as they are working on separate data. If they were manipulating
> global
> state that wouldn't be true.

They aren't working on completely separate data, that's the problem. We
only have one "struct udev", which provides the context for the entire
operation of libudev in our code. The udev_device structs we use are
created from that object and link to it internally. Individual
udev_device structs are linked together e.g. through parent
releationships. That's why devices obtained by udev_device_get_parent()
et al. don't need to be unref()'d; they're referenced by their children
and supposed to be freed together with the last child.

As the udev operations are opaque, we can't make any assumptions about
which of the libudev funtions allocate memory, or modify shared data
structures otherwise. Some properties are fetched "lazily", when the
user actually needs them, and then cached in libudev. The 2-layer
architecture with udev_device being actually a shallow wrapper around
sd_device etc. doesn't make it easier to assess the code flow inside
the systemd libraries.

These issues should be fixed by serializing the libudev accesses with a
mutex. I agree with you that I don't understand what could still go
wrong if that's done.

In theory we could have separate "struct udev" devices per thread, but
keeping these in sync with each other would be a nightmare. We could
rather give up running multithreaded altogether.

> 
> > Add wrapper code that wraps all calls to libudev in a critical
> > section
> > holding a specific mutex.
> > 
> > [1] https://man7.org/linux/man-pages/man3/libudev.3.html
> 
> I assume that there must be times when use call a udev outside of the
> vecs lock. Did you check how often that is? Not that would be great
> to
> pile even more work on the vecs lock.

I wouldn't want to do that. Even if we could create a comprehensive
assessment now, it could become incorrect any time just by adding an
innocent libudev call somewhere. Also, I wouldn't want to overload the
vecs lock further. This code is ugly, but I am pretty sure that the
mutex operations will be light-weight. The mutex is always at the
bottom of our call stack, so no ABBA deadlock is possibe.

I started out by just wrapping some libudev calls, but I soon realized
that we can't do that without making assumptions about libudev's inner
workings, which we shouldn't (see above).

That said, I agree that it's unclear if this is really worth the
effort. I did it because I was hoping to fix the memory leak issue #129
with it. But that attempt was unsuccessful. So we have no evidence that
this serialization fixes any actual issue. multipathd has been using
libudev from multi-theaded context for 20 years, and we have never
encountered a problem that was caused by that (or if we did, we didn't
realize). At least in theory, the mutex could cause a deadlock or
noticeable delay if some libudev operation blocks or crashes, so the
change is not without risk. 

So, consider this part of the patch set an RFC.

OTOH, the libudev man page is pretty clear about being not thread-safe.

> Also, git flagged some whitespace errors.

Ups, will fix.

Thanks,
Martin

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 17/21] libmpathutil: use union for bitfield
  2025-12-19  0:17   ` Benjamin Marzinski
@ 2025-12-19  8:08     ` Martin Wilck
  0 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-19  8:08 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: Christophe Varoqui, dm-devel

On Thu, 2025-12-18 at 19:17 -0500, Benjamin Marzinski wrote:
> On Wed, Dec 17, 2025 at 10:21:09PM +0100, Martin Wilck wrote:
> > The macro BITFIELD used a cast of a pointer to a different
> > anonymous struct
> > to (struct bitfield *). This violates strict aliasing rules,
> > because the
> > two structs aren't compatible types in the sense of the language
> > specification. With -O2, gcc 15.1 generates code that makes th
> > pgcmp()
> > function fail, because the compiler doesn't initialize bf->len aka
> > __storage_for__bf.len to 64.
> > 
> > The problem will show through error messages like this:
> > 
> > multipathd[1974208]: is_bit_set_in_bitfield: bitfield overflow: 1
> > >= 0
> > 
> > Fix this by using a union
> > 
> > Fixes: 9a2f173 ("libmpathutil: change STATIC_BITFIELD to BITFIELD")
> > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > ---
> >  libmpathutil/util.c       |  8 ++++----
> >  libmpathutil/util.h       | 41 +++++++++++++++++++++++------------
> > ----
> >  libmultipath/configure.c  |  4 ++--
> >  libmultipath/pgpolicies.c |  2 +-
> >  tests/util.c              | 10 +++++-----
> >  5 files changed, 36 insertions(+), 29 deletions(-)
> > 
> > diff --git a/libmpathutil/util.c b/libmpathutil/util.c
> > index 37412c6..5e45750 100644
> > --- a/libmpathutil/util.c
> > +++ b/libmpathutil/util.c
> > */
> > +#define BITFIELD(name,
> > length)						\
> > +	union bitfield __storage_for__ ## name =
> > {			\
> > +		.__bits = { 0
> > },					\
> > +		.__len =
> > (length),					\
> >  	}; \
> 
> It looks like this only allocates one bitfield_t, regardless of
> length.
> Am I missing something here?

Hm, yes. While this is kind of intentional, the code is confusing. The
whole BITFIELD thing is a mess. Given that it's only used in two lines,
I guess I might as well just remove it.

Martin

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 03/21] multipathd: free paths in checker_finished()
  2025-12-18 23:40     ` Martin Wilck
@ 2025-12-19 10:38       ` Martin Wilck
  0 siblings, 0 replies; 33+ messages in thread
From: Martin Wilck @ 2025-12-19 10:38 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: Christophe Varoqui, dm-devel

On Fri, 2025-12-19 at 00:40 +0100, Martin Wilck wrote:
> On Thu, 2025-12-18 at 18:34 -0500, Benjamin Marzinski wrote:
> > On Wed, Dec 17, 2025 at 10:20:55PM +0100, Martin Wilck wrote:
> > > Modifying the pathvec in a deep call stack is unexpected and
> > > error-prone. Free paths in the checkerloop instead, after
> > > having synced all maps.
> > 
> > I'm not fond of the fact that this leaves paths around, when
> > callers
> > expect that the path will be removed. For instance, right now
> > cli_add_path() and uev_add_path() can call ev_remove_path to handle
> > INIT_REMOVED paths, and expect that success means the path is
> > removed.
> 
> Thanks for pointing this out.
> 
> > They can end up storing duplicate paths with this code.
> 
> I'm not sure if that really hurts, as long as multipathd basically 
> ignores INIT_REMOVED paths, and eventually frees them. But I agree
> that
> it's awkward and should be avoided if possible.
> 
> > What do you think about having calls to ev_remove_path() actually
> > remove
> > the path if multipath device successfully orphans it.
> 
> Sure, that should be possible. I'll think about it.
> I just want to stop removing paths somewhere deep in the stack.

Thinking about it, I believe we should keep the call to
check_removed_paths() in sync_paths(). We've put it there for a reason.
We just need to be sure from where to call it.

Martin

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 18/21] libmpathutil: add wrapper code for libudev
  2025-12-19  8:06     ` Martin Wilck
@ 2025-12-19 17:27       ` Benjamin Marzinski
  0 siblings, 0 replies; 33+ messages in thread
From: Benjamin Marzinski @ 2025-12-19 17:27 UTC (permalink / raw)
  To: Martin Wilck; +Cc: Christophe Varoqui, dm-devel

On Fri, Dec 19, 2025 at 09:06:49AM +0100, Martin Wilck wrote:
> On Thu, 2025-12-18 at 19:38 -0500, Benjamin Marzinski wrote:
> > On Wed, Dec 17, 2025 at 10:21:10PM +0100, Martin Wilck wrote:
> > It sounds like
> > the functions themselves are safe to call from multiple threads, as
> > long
> > as they are working on separate data. If they were manipulating
> > global
> > state that wouldn't be true.
> 
> They aren't working on completely separate data, that's the problem. We
> only have one "struct udev", which provides the context for the entire
> operation of libudev in our code. The udev_device structs we use are
> created from that object and link to it internally. Individual
> udev_device structs are linked together e.g. through parent
> releationships. That's why devices obtained by udev_device_get_parent()
> et al. don't need to be unref()'d; they're referenced by their children
> and supposed to be freed together with the last child.
> 
> As the udev operations are opaque, we can't make any assumptions about
> which of the libudev funtions allocate memory, or modify shared data
> structures otherwise. Some properties are fetched "lazily", when the
> user actually needs them, and then cached in libudev. The 2-layer
> architecture with udev_device being actually a shallow wrapper around
> sd_device etc. doesn't make it easier to assess the code flow inside
> the systemd libraries.
> 
> These issues should be fixed by serializing the libudev accesses with a
> mutex. I agree with you that I don't understand what could still go
> wrong if that's done.
> 
> In theory we could have separate "struct udev" devices per thread, but
> keeping these in sync with each other would be a nightmare. We could
> rather give up running multithreaded altogether.

Yeah. I started looking into this, and quickly realized the issue with
the shared struct udev and gave up. I can easily believe that two
threads running at the same time could corrupt a sharted object, and
since everything is linked to one struct udev, there's no way that we
can claim that there can't be shared objects in any of our calls.

I assume that we've likely been pretty safe because most (maybe all) of
our udev device accesses (which is where I assume things could get
corrupted the easiest) have been protected by the vecs lock. But the
purge code is about to add a bunch of unprotected accesses, so clearly
we need some locking there. 
 
> > 
> > > Add wrapper code that wraps all calls to libudev in a critical
> > > section
> > > holding a specific mutex.
> > > 
> > > [1] https://man7.org/linux/man-pages/man3/libudev.3.html
> > 
> > I assume that there must be times when use call a udev outside of the
> > vecs lock. Did you check how often that is? Not that would be great
> > to
> > pile even more work on the vecs lock.
> 
> I wouldn't want to do that. Even if we could create a comprehensive
> assessment now, it could become incorrect any time just by adding an
> innocent libudev call somewhere. Also, I wouldn't want to overload the
> vecs lock further. This code is ugly, but I am pretty sure that the
> mutex operations will be light-weight. The mutex is always at the
> bottom of our call stack, so no ABBA deadlock is possibe.
> 
> I started out by just wrapping some libudev calls, but I soon realized
> that we can't do that without making assumptions about libudev's inner
> workings, which we shouldn't (see above).
> 
> That said, I agree that it's unclear if this is really worth the
> effort. I did it because I was hoping to fix the memory leak issue #129
> with it. But that attempt was unsuccessful. So we have no evidence that
> this serialization fixes any actual issue. multipathd has been using
> libudev from multi-theaded context for 20 years, and we have never
> encountered a problem that was caused by that (or if we did, we didn't
> realize). At least in theory, the mutex could cause a deadlock or
> noticeable delay if some libudev operation blocks or crashes, so the
> change is not without risk. 
> 
> So, consider this part of the patch set an RFC.
> 
> OTOH, the libudev man page is pretty clear about being not thread-safe.

Especially with the purge code adding otherwise unprotected udev device
accesses, I think this is probably a good idea. I don't recall seeing
libudev calls block. I'm betting the real risk is for systems where the
root filesystem is multipathed, and you lose all paths. But that's
always the scary case, and we do enough udev accesses with the vecs lock
held, that if we can get wedged in one, we're likely already gonna get
locked up, even without these mutexes.

-Ben
 
> > Also, git flagged some whitespace errors.
> 
> Ups, will fix.
> 
> Thanks,
> Martin


^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2025-12-19 17:27 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-17 21:20 [PATCH 00/21] Multipath-tools: various bug fixes Martin Wilck
2025-12-17 21:20 ` [PATCH 01/21] libmultipath: drop drop_multipath Martin Wilck
2025-12-17 21:20 ` [PATCH 02/21] libmultipath: don't access path members in free_pgvec() Martin Wilck
2025-12-17 21:20 ` [PATCH 03/21] multipathd: free paths in checker_finished() Martin Wilck
2025-12-18 23:34   ` Benjamin Marzinski
2025-12-18 23:40     ` Martin Wilck
2025-12-19 10:38       ` Martin Wilck
2025-12-17 21:20 ` [PATCH 04/21] libmultipath: don't touch mpvec in remove_map() Martin Wilck
2025-12-17 21:20 ` [PATCH 05/21] libmpathutil: constify find_slot() Martin Wilck
2025-12-17 21:20 ` [PATCH 06/21] libmultipath: don't free paths in orphan_paths() Martin Wilck
2025-12-17 21:20 ` [PATCH 07/21] libmultipath: free orphaned paths in check_removed_paths() Martin Wilck
2025-12-17 21:21 ` [PATCH 08/21] libmultipath: remove free_paths argument from free_pathgroup() Martin Wilck
2025-12-17 21:21 ` [PATCH 09/21] libmultipath: fix numeric value of free_paths in free_multipaths() Martin Wilck
2025-12-19  0:10   ` Benjamin Marzinski
2025-12-17 21:21 ` [PATCH 10/21] libmultipath: remove free_paths argument from free_pgvec() Martin Wilck
2025-12-17 21:21 ` [PATCH 11/21] libmultipath: remove free_paths argument from free_multipathvec() Martin Wilck
2025-12-17 21:21 ` [PATCH 12/21] libmultipath: free_multipath: fix FREE_PATHS case Martin Wilck
2025-12-19  0:15   ` Benjamin Marzinski
2025-12-17 21:21 ` [PATCH 13/21] multipath-tools: Fix ISO C23 errors with strchr() Martin Wilck
2025-12-17 21:21 ` [PATCH 14/21] libmultipath: simplify sysfs_get_target_nodename() Martin Wilck
2025-12-17 21:21 ` [PATCH 15/21] multipathd: join the init_unwinder dummy thread Martin Wilck
2025-12-17 21:21 ` [PATCH 16/21] kpartx: fix some memory leaks Martin Wilck
2025-12-17 21:21 ` [PATCH 17/21] libmpathutil: use union for bitfield Martin Wilck
2025-12-19  0:17   ` Benjamin Marzinski
2025-12-19  8:08     ` Martin Wilck
2025-12-17 21:21 ` [PATCH 18/21] libmpathutil: add wrapper code for libudev Martin Wilck
2025-12-19  0:38   ` Benjamin Marzinski
2025-12-19  8:06     ` Martin Wilck
2025-12-19 17:27       ` Benjamin Marzinski
2025-12-17 21:21 ` [PATCH 19/21] multipath-tools: use the libudev wrapper functions Martin Wilck
2025-12-17 21:21 ` [PATCH 20/21] Makefile: add functionality to determine cmocka version Martin Wilck
2025-12-17 21:21 ` [PATCH 21/21] multipath-tools tests: adaptations for cmocka 2.0 Martin Wilck
2025-12-19  0:40 ` [PATCH 00/21] Multipath-tools: various bug fixes Benjamin Marzinski

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