All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/26] Multipath-tools: various bug fixes
@ 2025-12-19 14:40 Martin Wilck
  2025-12-19 14:40 ` [PATCH v2 01/26] libmultipath: drop drop_multipath Martin Wilck
                   ` (26 more replies)
  0 siblings, 27 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

Changes v1 -> v2

Fixed the issues pointed out by Benjamin Marzinski in v1.

- paths are still removed in sync_paths(), as before. I added some comments
  lest we forget about this.
- in the checker loop, only orphan paths in REMOVED/PARTIAL state are freed
- removed the free_paths argument from free_multipath, and changed
  add_map_without_path and check_usable_paths() as suggested by Ben.
  free_multipath() doesn't free paths any more.
- the BITFIELD macro now creates a field of fixed length.
- fixed whitespace issues.

These changes required shuffling the patches around. Therefore I'm resending
the entire series. I kept Ben's Reviewed-by: trailers.

I also fixed some formatting issues reported by clang-format.
clang-format also changes context line of diffs, which I accepted.

v1 cover letter
---------------

(note: I did not fix the patch numbers)

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 (26):
  libmultipath: drop drop_multipath
  libmultipath: don't access path members in free_pgvec()
  libmpathutil: constify find_slot()
  libmultipath: don't touch mpvec in remove_map()
  libmultipath: export orphan_paths()
  libmultipath: export cleanup_multipath()
  libmultipath: add cleanup_pathvec_and_free_paths()
  libmultipath: don't free paths in orphan_paths()
  multipathd: free orphaned paths in checker_finished()
  libmultipath: remove free_paths argument from free_pathgroup()
  libmultipath: remove free_paths argument from free_pgvec()
  libmultipath: remove free_paths argument from free_multipathvec()
  multipath: free paths through pathvec in check_usable_paths()
  multipathd: add_map_without_path(): orphan paths instead of freeing
    them
  libmultipath: remove free_paths argument from free_multipath()
  libmultipath: remove cleanup_multipath_and_paths()
  libmultipaths: annotate functions that may free paths
  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                   |  49 +-
 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              |  24 +-
 libmultipath/devmapper.c              |   2 +-
 libmultipath/dict.c                   |   2 +-
 libmultipath/discovery.c              |  44 +-
 libmultipath/dmparser.c               |   6 +-
 libmultipath/foreign.c                |   2 +-
 libmultipath/foreign.h                |   2 +-
 libmultipath/foreign/nvme.c           |   2 +-
 libmultipath/libmultipath.version     |   5 +-
 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                |  82 +--
 libmultipath/structs.h                |  13 +-
 libmultipath/structs_vec.c            |  88 +--
 libmultipath/structs_vec.h            |   4 +-
 libmultipath/sysfs.c                  |   2 +-
 libmultipath/uevent.c                 |   2 +-
 libmultipath/valid.c                  |   2 +-
 mpathpersist/main.c                   |   2 +-
 multipath/main.c                      |  22 +-
 multipathd/cli_handlers.c             |   2 +-
 multipathd/fpin_handlers.c            |   2 +-
 multipathd/init_unwinder.c            |   4 +-
 multipathd/main.c                     |  59 +-
 tests/Makefile                        |  22 +-
 tests/alias.c                         |  50 +-
 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                       |  85 +--
 tests/mpathvalid.c                    |  18 +-
 tests/parser.c                        |   2 +-
 tests/pgpolicy.c                      |   4 +-
 tests/strbuf.c                        | 131 ++---
 tests/sysfs.c                         |  76 +--
 tests/test-lib.c                      |  95 ++--
 tests/test-log.c                      |  10 +-
 tests/uevent.c                        |   2 +-
 tests/unaligned.c                     |   8 +-
 tests/util.c                          | 123 ++--
 tests/valid.c                         |  30 +-
 tests/vpd.c                           |  12 +-
 77 files changed, 1748 insertions(+), 625 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] 37+ messages in thread

* [PATCH v2 01/26] libmultipath: drop drop_multipath
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
@ 2025-12-19 14:40 ` Martin Wilck
  2025-12-19 14:40 ` [PATCH v2 02/26] libmultipath: don't access path members in free_pgvec() Martin Wilck
                   ` (25 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 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>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 libmultipath/structs.c | 18 ------------------
 libmultipath/structs.h |  3 +--
 2 files changed, 1 insertion(+), 20 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..e7a0585 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -582,8 +582,7 @@ void free_pgvec (vector pgvec, enum free_path_mode free_paths);
 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_multipath_attributes(struct multipath *);
 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] 37+ messages in thread

* [PATCH v2 02/26] libmultipath: don't access path members in free_pgvec()
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
  2025-12-19 14:40 ` [PATCH v2 01/26] libmultipath: drop drop_multipath Martin Wilck
@ 2025-12-19 14:40 ` Martin Wilck
  2025-12-19 14:40 ` [PATCH v2 03/26] libmpathutil: constify find_slot() Martin Wilck
                   ` (24 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 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>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 libmultipath/structs.c | 12 +-----------
 1 file changed, 1 insertion(+), 11 deletions(-)

diff --git a/libmultipath/structs.c b/libmultipath/structs.c
index 1cbbea8..cde5991 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] 37+ messages in thread

* [PATCH v2 03/26] libmpathutil: constify find_slot()
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
  2025-12-19 14:40 ` [PATCH v2 01/26] libmultipath: drop drop_multipath Martin Wilck
  2025-12-19 14:40 ` [PATCH v2 02/26] libmultipath: don't access path members in free_pgvec() Martin Wilck
@ 2025-12-19 14:40 ` Martin Wilck
  2025-12-19 14:40 ` [PATCH v2 04/26] libmultipath: don't touch mpvec in remove_map() Martin Wilck
                   ` (23 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.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] 37+ messages in thread

* [PATCH v2 04/26] libmultipath: don't touch mpvec in remove_map()
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (2 preceding siblings ...)
  2025-12-19 14:40 ` [PATCH v2 03/26] libmpathutil: constify find_slot() Martin Wilck
@ 2025-12-19 14:40 ` Martin Wilck
  2025-12-19 14:40 ` [PATCH v2 05/26] libmultipath: export orphan_paths() Martin Wilck
                   ` (22 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 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>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.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 89ae2a3..30b282f 100644
--- a/libmultipath/libmultipath.version
+++ b/libmultipath/libmultipath.version
@@ -159,6 +159,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 f651b29..7bceeb6 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;
@@ -832,7 +831,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 1188ada..1eedfc8 100644
--- a/libmultipath/structs_vec.h
+++ b/libmultipath/structs_vec.h
@@ -23,7 +23,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 d3bf4d0..6c29d5e 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;
@@ -3352,8 +3355,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] 37+ messages in thread

* [PATCH v2 05/26] libmultipath: export orphan_paths()
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (3 preceding siblings ...)
  2025-12-19 14:40 ` [PATCH v2 04/26] libmultipath: don't touch mpvec in remove_map() Martin Wilck
@ 2025-12-19 14:40 ` Martin Wilck
  2025-12-19 14:40 ` [PATCH v2 06/26] libmultipath: export cleanup_multipath() Martin Wilck
                   ` (21 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

This function will be called from multipathd in a follow-up patch.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/libmultipath.version | 1 +
 libmultipath/structs_vec.c        | 2 +-
 libmultipath/structs_vec.h        | 1 +
 3 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
index 30b282f..19e4f39 100644
--- a/libmultipath/libmultipath.version
+++ b/libmultipath/libmultipath.version
@@ -145,6 +145,7 @@ global:
 	mpath_in_use;
 	need_io_err_check;
 	orphan_path;
+	orphan_paths;
 	parse_prkey_flags;
 	pathcount;
 	path_discovery;
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 7bceeb6..0717888 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -377,7 +377,7 @@ void orphan_path(struct path *pp, const char *reason)
 	uninitialize_path(pp);
 }
 
-static void orphan_paths(vector pathvec, struct multipath *mpp, const char *reason)
+void orphan_paths(vector pathvec, struct multipath *mpp, const char *reason)
 {
 	int i;
 	struct path * pp;
diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h
index 1eedfc8..5f8fd63 100644
--- a/libmultipath/structs_vec.h
+++ b/libmultipath/structs_vec.h
@@ -16,6 +16,7 @@ void set_no_path_retry(struct multipath *mpp);
 int adopt_paths (vector pathvec, struct multipath *mpp,
 		 const struct multipath *current_mpp);
 void orphan_path (struct path * pp, const char *reason);
+void orphan_paths(vector pathvec, struct multipath *mpp, const char *reason);
 void set_path_removed(struct path *pp);
 
 int verify_paths(struct multipath *mpp);
-- 
2.52.0


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

* [PATCH v2 06/26] libmultipath: export cleanup_multipath()
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (4 preceding siblings ...)
  2025-12-19 14:40 ` [PATCH v2 05/26] libmultipath: export orphan_paths() Martin Wilck
@ 2025-12-19 14:40 ` Martin Wilck
  2025-12-19 14:40 ` [PATCH v2 07/26] libmultipath: add cleanup_pathvec_and_free_paths() Martin Wilck
                   ` (20 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

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

diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
index 19e4f39..1b67b16 100644
--- a/libmultipath/libmultipath.version
+++ b/libmultipath/libmultipath.version
@@ -68,6 +68,7 @@ global:
 	check_foreign;
 	cleanup_bindings;
 	cleanup_lock;
+	cleanup_multipath;
 	cleanup_multipath_and_paths;
 	cleanup_udev_device_ptr;
 	cleanup_udev_enumerate_ptr;
-- 
2.52.0


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

* [PATCH v2 07/26] libmultipath: add cleanup_pathvec_and_free_paths()
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (5 preceding siblings ...)
  2025-12-19 14:40 ` [PATCH v2 06/26] libmultipath: export cleanup_multipath() Martin Wilck
@ 2025-12-19 14:40 ` Martin Wilck
  2025-12-19 14:40 ` [PATCH v2 08/26] libmultipath: don't free paths in orphan_paths() Martin Wilck
                   ` (19 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

Cleanup function for a pathvec. Will be used in a follow-up
patch.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/libmultipath.version | 1 +
 libmultipath/structs.c            | 5 +++++
 libmultipath/structs.h            | 1 +
 3 files changed, 7 insertions(+)

diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
index 1b67b16..366b829 100644
--- a/libmultipath/libmultipath.version
+++ b/libmultipath/libmultipath.version
@@ -70,6 +70,7 @@ global:
 	cleanup_lock;
 	cleanup_multipath;
 	cleanup_multipath_and_paths;
+	cleanup_pathvec_and_free_paths;
 	cleanup_udev_device_ptr;
 	cleanup_udev_enumerate_ptr;
 	coalesce_paths;
diff --git a/libmultipath/structs.c b/libmultipath/structs.c
index cde5991..08d8b87 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -199,6 +199,11 @@ free_pathvec (vector vec, enum free_path_mode free_paths)
 	vector_free(vec);
 }
 
+void cleanup_pathvec_and_free_paths(vector *vec)
+{
+	free_pathvec(*vec, FREE_PATHS);
+}
+
 struct pathgroup *
 alloc_pathgroup (void)
 {
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index e7a0585..d89582c 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -577,6 +577,7 @@ 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 cleanup_pathvec_and_free_paths(vector *vec);
 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_multipath (struct multipath *, enum free_path_mode free_paths);
-- 
2.52.0


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

* [PATCH v2 08/26] libmultipath: don't free paths in orphan_paths()
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (6 preceding siblings ...)
  2025-12-19 14:40 ` [PATCH v2 07/26] libmultipath: add cleanup_pathvec_and_free_paths() Martin Wilck
@ 2025-12-19 14:40 ` Martin Wilck
  2025-12-19 14:40 ` [PATCH v2 09/26] multipathd: free orphaned paths in checker_finished() Martin Wilck
                   ` (18 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 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>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 libmultipath/structs_vec.c | 17 ++++-------------
 1 file changed, 4 insertions(+), 13 deletions(-)

diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 0717888..1259aec 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -383,19 +383,10 @@ void orphan_paths(vector pathvec, struct multipath *mpp, const char *reason)
 	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 &&
-			   strncmp(mpp->wwid, pp->wwid, WWID_SIZE) == 0) {
+		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] 37+ messages in thread

* [PATCH v2 09/26] multipathd: free orphaned paths in checker_finished()
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (7 preceding siblings ...)
  2025-12-19 14:40 ` [PATCH v2 08/26] libmultipath: don't free paths in orphan_paths() Martin Wilck
@ 2025-12-19 14:40 ` Martin Wilck
  2025-12-19 14:40 ` [PATCH v2 10/26] libmultipath: remove free_paths argument from free_pathgroup() Martin Wilck
                   ` (17 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

As we now don't free orphaned paths which are in INIT_REMOVED or
INIT_PARTIAL state in orphan_paths() directly any more, do it from the
checker loop to be sure we don't carry them around forever.

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

diff --git a/multipathd/main.c b/multipathd/main.c
index 6c29d5e..c03546b 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -3083,12 +3083,30 @@ static void enable_pathgroups(struct multipath *mpp)
 	}
 }
 
+static void free_orphan_paths(vector pathvec)
+{
+	struct path *pp;
+	int i;
+
+	vector_foreach_slot (pathvec, pp, i) {
+		if (!pp->mpp && (pp->initialized == INIT_REMOVED ||
+				 pp->initialized == INIT_PARTIAL)) {
+			condlog(2, "%s: freeing orphan %s in %s state",
+				__func__, pp->dev,
+				pp->initialized == INIT_REMOVED ? "removed" : "partial");
+			vector_del_slot(pathvec, i--);
+			free_path(pp);
+		}
+	}
+}
+
 static void checker_finished(struct vectors *vecs, unsigned int ticks)
 {
 	struct multipath *mpp;
 	bool uev_timed_out = false;
 	int i;
 
+	free_orphan_paths(vecs->pathvec);
 	vector_foreach_slot(vecs->mpvec, mpp, i) {
 		bool inconsistent, prio_reload, failback_reload;
 		bool uev_wait_reload, ghost_reload;
-- 
2.52.0


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

* [PATCH v2 10/26] libmultipath: remove free_paths argument from free_pathgroup()
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (8 preceding siblings ...)
  2025-12-19 14:40 ` [PATCH v2 09/26] multipathd: free orphaned paths in checker_finished() Martin Wilck
@ 2025-12-19 14:40 ` Martin Wilck
  2025-12-19 14:40 ` [PATCH v2 11/26] libmultipath: remove free_paths argument from free_pgvec() Martin Wilck
                   ` (16 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 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>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 libmultipath/configure.c   |  2 +-
 libmultipath/dmparser.c    |  2 +-
 libmultipath/pgpolicies.c  |  6 +++---
 libmultipath/structs.c     | 11 +++++------
 libmultipath/structs.h     |  2 +-
 libmultipath/structs_vec.c |  2 +-
 6 files changed, 12 insertions(+), 13 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 08d8b87..a7c68f2 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -225,18 +225,17 @@ alloc_pathgroup (void)
 	return pgp;
 }
 
-void
-free_pathgroup (struct pathgroup * pgp, enum free_path_mode free_paths)
+void 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)
+void free_pgvec(vector pgvec,
+		enum free_path_mode free_paths __attribute__((unused)))
 {
 	int i;
 	struct pathgroup * pgp;
@@ -245,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 d89582c..796201a 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -578,7 +578,7 @@ void uninitialize_path(struct path *pp);
 void free_path (struct path *);
 void free_pathvec (vector vec, enum free_path_mode free_paths);
 void cleanup_pathvec_and_free_paths(vector *vec);
-void free_pathgroup (struct pathgroup * pgp, enum free_path_mode free_paths);
+void free_pathgroup(struct pathgroup *pgp);
 void free_pgvec (vector pgvec, enum free_path_mode free_paths);
 void free_multipath (struct multipath *, enum free_path_mode free_paths);
 void cleanup_multipath(struct multipath **pmpp);
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 1259aec..91c8d58 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] 37+ messages in thread

* [PATCH v2 11/26] libmultipath: remove free_paths argument from free_pgvec()
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (9 preceding siblings ...)
  2025-12-19 14:40 ` [PATCH v2 10/26] libmultipath: remove free_paths argument from free_pathgroup() Martin Wilck
@ 2025-12-19 14:40 ` Martin Wilck
  2025-12-19 14:40 ` [PATCH v2 12/26] libmultipath: remove free_paths argument from free_multipathvec() Martin Wilck
                   ` (15 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 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>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 libmultipath/dmparser.c    | 4 ++--
 libmultipath/pgpolicies.c  | 6 +++---
 libmultipath/structs.c     | 5 ++---
 libmultipath/structs.h     | 2 +-
 libmultipath/structs_vec.c | 4 ++--
 tests/pgpolicy.c           | 2 +-
 6 files changed, 11 insertions(+), 12 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 a7c68f2..511173d 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -234,8 +234,7 @@ void free_pathgroup(struct pathgroup *pgp)
 	free(pgp);
 }
 
-void free_pgvec(vector pgvec,
-		enum free_path_mode free_paths __attribute__((unused)))
+void free_pgvec(vector pgvec)
 {
 	int i;
 	struct pathgroup * pgp;
@@ -326,7 +325,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 796201a..064a76b 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -579,7 +579,7 @@ void free_path (struct path *);
 void free_pathvec (vector vec, enum free_path_mode free_paths);
 void cleanup_pathvec_and_free_paths(vector *vec);
 void free_pathgroup(struct pathgroup *pgp);
-void free_pgvec (vector pgvec, enum free_path_mode free_paths);
+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 91c8d58..8d9c0be 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;
 
 	/*
@@ -624,7 +624,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] 37+ messages in thread

* [PATCH v2 12/26] libmultipath: remove free_paths argument from free_multipathvec()
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (10 preceding siblings ...)
  2025-12-19 14:40 ` [PATCH v2 11/26] libmultipath: remove free_paths argument from free_pgvec() Martin Wilck
@ 2025-12-19 14:40 ` Martin Wilck
  2025-12-19 14:40 ` [PATCH v2 13/26] multipath: free paths through pathvec in check_usable_paths() Martin Wilck
                   ` (14 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 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>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 libmpathpersist/mpath_persist.c | 2 +-
 libmultipath/structs.c          | 5 ++---
 libmultipath/structs.h          | 2 +-
 multipath/main.c                | 6 +++---
 4 files changed, 7 insertions(+), 8 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 511173d..ec8779c 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -346,8 +346,7 @@ void cleanup_multipath_and_paths(struct multipath **pmpp)
 		free_multipath(*pmpp, FREE_PATHS);
 }
 
-void
-free_multipathvec (vector mpvec, enum free_path_mode free_paths)
+void free_multipathvec(vector mpvec)
 {
 	int i;
 	struct multipath * mpp;
@@ -356,7 +355,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 064a76b..b16bd9e 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -584,7 +584,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] 37+ messages in thread

* [PATCH v2 13/26] multipath: free paths through pathvec in check_usable_paths()
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (11 preceding siblings ...)
  2025-12-19 14:40 ` [PATCH v2 12/26] libmultipath: remove free_paths argument from free_multipathvec() Martin Wilck
@ 2025-12-19 14:40 ` Martin Wilck
  2025-12-20  4:19   ` Benjamin Marzinski
  2025-12-19 14:40 ` [PATCH v2 14/26] multipathd: add_map_without_path(): orphan paths instead of freeing them Martin Wilck
                   ` (13 subsequent siblings)
  26 siblings, 1 reply; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

Paths belong to the pathvec and should be freed from it.
Use cleanup_multipath() instead of cleanup_multipath_and_paths(),
and free the paths via the pathvec instead.

Suggested-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipath/main.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/multipath/main.c b/multipath/main.c
index 4b8d7dd..d28628b 100644
--- a/multipath/main.c
+++ b/multipath/main.c
@@ -214,12 +214,12 @@ static int check_usable_paths(struct config *conf,
 			      const char *devpath, enum devtypes dev_type)
 {
 	struct udev_device __attribute__((cleanup(cleanup_udev_device))) *ud = NULL;
-	struct multipath __attribute__((cleanup(cleanup_multipath_and_paths))) *mpp = NULL;
+	struct multipath __attribute__((cleanup(cleanup_multipath))) *mpp = NULL;
 	struct pathgroup *pg;
 	struct path *pp;
 	char __attribute__((cleanup(cleanup_charp))) *params = NULL;
 	char __attribute__((cleanup(cleanup_charp))) *status = NULL;
-	vector __attribute((cleanup(cleanup_vector))) pathvec = NULL;
+	vector __attribute((cleanup(cleanup_pathvec_and_free_paths))) pathvec = NULL;
 	char uuid[DM_UUID_LEN];
 	dev_t devt;
 	int r = 1, i, j;
-- 
2.52.0


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

* [PATCH v2 14/26] multipathd: add_map_without_path(): orphan paths instead of freeing them
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (12 preceding siblings ...)
  2025-12-19 14:40 ` [PATCH v2 13/26] multipath: free paths through pathvec in check_usable_paths() Martin Wilck
@ 2025-12-19 14:40 ` Martin Wilck
  2025-12-19 14:46   ` Martin Wilck
  2025-12-20  4:23   ` Benjamin Marzinski
  2025-12-19 14:40 ` [PATCH v2 15/26] libmultipath: remove free_paths argument from free_multipath() Martin Wilck
                   ` (12 subsequent siblings)
  26 siblings, 2 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski
  Cc: dm-devel, Martin Wilck, Benjamin Marzinski

If add_map_without_path() fails to set up the multipath data structures,
don't free the paths associated with the map as
cleanup_multipath_and_paths() would; just orphan the paths.
update_pathvec_from_dm() will handle the paths and see if they
need to be removed.

Suggested-by: Benjamin Marzinski <bmarzins@suse.com>
Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/main.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/multipathd/main.c b/multipathd/main.c
index c03546b..7b522e8 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -771,8 +771,8 @@ fail:
 
 static int add_map_without_path (struct vectors *vecs, const char *alias)
 {
-	struct multipath __attribute__((cleanup(cleanup_multipath_and_paths)))
-		*mpp = alloc_multipath();
+	struct multipath __attribute__((cleanup(cleanup_multipath))) *mpp =
+		alloc_multipath();
 	char __attribute__((cleanup(cleanup_charp))) *params = NULL;
 	char __attribute__((cleanup(cleanup_charp))) *status = NULL;
 	struct config *conf;
@@ -802,11 +802,13 @@ static int add_map_without_path (struct vectors *vecs, const char *alias)
 	mpp->mpe = find_mpe(conf->mptable, mpp->wwid);
 	put_multipath_config(conf);
 
-	if ((rc = update_multipath_table__(mpp, vecs->pathvec, 0, params, status)) != DMP_OK)
+	if (update_multipath_table__(mpp, vecs->pathvec, 0, params, status) != DMP_OK ||
+	    !vector_alloc_slot(vecs->mpvec)) {
+		orphan_paths(vecs->pathvec, mpp, "failed to add map");
 		return DMP_ERR;
+	}
 
-	if (!vector_alloc_slot(vecs->mpvec))
-		return DMP_ERR;
+	/* Make sure mpp is not cleaned up on return */
 	vector_set_slot(vecs->mpvec, steal_ptr(mpp));
 
 	/*
-- 
2.52.0


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

* [PATCH v2 15/26] libmultipath: remove free_paths argument from free_multipath()
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (13 preceding siblings ...)
  2025-12-19 14:40 ` [PATCH v2 14/26] multipathd: add_map_without_path(): orphan paths instead of freeing them Martin Wilck
@ 2025-12-19 14:40 ` Martin Wilck
  2025-12-19 14:40 ` [PATCH v2 16/26] libmultipath: remove cleanup_multipath_and_paths() Martin Wilck
                   ` (11 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

free_multipath() is now always called with free_paths == KEEP_PATHS.
Remove the argument and the code for freeing paths.

Suggested-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/devmapper.c   |  2 +-
 libmultipath/structs.c     | 26 +++++---------------------
 libmultipath/structs.h     |  2 +-
 libmultipath/structs_vec.c |  2 +-
 multipath/main.c           |  2 +-
 5 files changed, 9 insertions(+), 25 deletions(-)

diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index 8e632ba..e3870f5 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -1279,7 +1279,7 @@ int dm_get_maps(vector mp)
 		switch (dm_get_multipath(names->name, &mpp)) {
 		case DMP_OK:
 			if (!vector_alloc_slot(mp)) {
-				free_multipath(mpp, KEEP_PATHS);
+				free_multipath(mpp);
 				return 1;
 			}
 			vector_set_slot(mp, mpp);
diff --git a/libmultipath/structs.c b/libmultipath/structs.c
index ec8779c..9e1734d 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -296,8 +296,7 @@ void free_multipath_attributes(struct multipath *mpp)
 	}
 }
 
-void
-free_multipath (struct multipath * mpp, enum free_path_mode free_paths)
+void free_multipath(struct multipath *mpp)
 {
 	if (!mpp)
 		return;
@@ -309,22 +308,7 @@ free_multipath (struct multipath * mpp, enum free_path_mode free_paths)
 		mpp->alias = NULL;
 	}
 
-	if (!free_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)
-					pp->mpp = NULL;
-		}
-	}
-
-	free_pathvec(mpp->paths, free_paths);
+	free_pathvec(mpp->paths, KEEP_PATHS);
 	free_pgvec(mpp->pg);
 	if (mpp->hwe) {
 		vector_free(mpp->hwe);
@@ -337,13 +321,13 @@ free_multipath (struct multipath * mpp, enum free_path_mode free_paths)
 void cleanup_multipath(struct multipath **pmpp)
 {
 	if (*pmpp)
-		free_multipath(*pmpp, KEEP_PATHS);
+		free_multipath(*pmpp);
 }
 
 void cleanup_multipath_and_paths(struct multipath **pmpp)
 {
 	if (*pmpp)
-		free_multipath(*pmpp, FREE_PATHS);
+		free_multipath(*pmpp);
 }
 
 void free_multipathvec(vector mpvec)
@@ -355,7 +339,7 @@ void free_multipathvec(vector mpvec)
 		return;
 
 	vector_foreach_slot (mpvec, mpp, i)
-		free_multipath(mpp, KEEP_PATHS);
+		free_multipath(mpp);
 
 	vector_free(mpvec);
 }
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index b16bd9e..a637af7 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -580,7 +580,7 @@ void free_pathvec (vector vec, enum free_path_mode free_paths);
 void cleanup_pathvec_and_free_paths(vector *vec);
 void free_pathgroup(struct pathgroup *pgp);
 void free_pgvec(vector pgvec);
-void free_multipath (struct multipath *, enum free_path_mode free_paths);
+void free_multipath(struct multipath *mpp);
 void cleanup_multipath(struct multipath **pmpp);
 void cleanup_multipath_and_paths(struct multipath **pmpp);
 void free_multipath_attributes(struct multipath *);
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 8d9c0be..62ee450 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -432,7 +432,7 @@ void remove_map(struct multipath *mpp, vector pathvec)
 	 */
 	orphan_paths(pathvec, mpp, "map removed internally");
 
-	free_multipath(mpp, KEEP_PATHS);
+	free_multipath(mpp);
 }
 
 void
diff --git a/multipath/main.c b/multipath/main.c
index d28628b..5557732 100644
--- a/multipath/main.c
+++ b/multipath/main.c
@@ -1069,7 +1069,7 @@ main (int argc, char *argv[])
 			printf("successfully reset wwids\n");
 		vector_foreach_slot_backwards(curmp, mpp, i) {
 			vector_del_slot(curmp, i);
-			free_multipath(mpp, KEEP_PATHS);
+			free_multipath(mpp);
 		}
 		vector_free(curmp);
 		goto out;
-- 
2.52.0


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

* [PATCH v2 16/26] libmultipath: remove cleanup_multipath_and_paths()
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (14 preceding siblings ...)
  2025-12-19 14:40 ` [PATCH v2 15/26] libmultipath: remove free_paths argument from free_multipath() Martin Wilck
@ 2025-12-19 14:40 ` Martin Wilck
  2025-12-20  4:26   ` Benjamin Marzinski
  2025-12-19 14:40 ` [PATCH v2 17/26] libmultipaths: annotate functions that may free paths Martin Wilck
                   ` (10 subsequent siblings)
  26 siblings, 1 reply; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

This function is not used any more.

Suggested-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/libmultipath.version | 1 -
 libmultipath/structs.c            | 6 ------
 libmultipath/structs.h            | 1 -
 3 files changed, 8 deletions(-)

diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
index 366b829..39384d6 100644
--- a/libmultipath/libmultipath.version
+++ b/libmultipath/libmultipath.version
@@ -69,7 +69,6 @@ global:
 	cleanup_bindings;
 	cleanup_lock;
 	cleanup_multipath;
-	cleanup_multipath_and_paths;
 	cleanup_pathvec_and_free_paths;
 	cleanup_udev_device_ptr;
 	cleanup_udev_enumerate_ptr;
diff --git a/libmultipath/structs.c b/libmultipath/structs.c
index 9e1734d..868c3d6 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -324,12 +324,6 @@ void cleanup_multipath(struct multipath **pmpp)
 		free_multipath(*pmpp);
 }
 
-void cleanup_multipath_and_paths(struct multipath **pmpp)
-{
-	if (*pmpp)
-		free_multipath(*pmpp);
-}
-
 void free_multipathvec(vector mpvec)
 {
 	int i;
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index a637af7..251e672 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -582,7 +582,6 @@ void free_pathgroup(struct pathgroup *pgp);
 void free_pgvec(vector pgvec);
 void free_multipath(struct multipath *mpp);
 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);
 
-- 
2.52.0


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

* [PATCH v2 17/26] libmultipaths: annotate functions that may free paths
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (15 preceding siblings ...)
  2025-12-19 14:40 ` [PATCH v2 16/26] libmultipath: remove cleanup_multipath_and_paths() Martin Wilck
@ 2025-12-19 14:40 ` Martin Wilck
  2025-12-20  4:35   ` Benjamin Marzinski
  2025-12-19 14:41 ` [PATCH v2 18/26] multipath-tools: Fix ISO C23 errors with strchr() Martin Wilck
                   ` (9 subsequent siblings)
  26 siblings, 1 reply; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:40 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

Paths are freed in check_removed_paths() quite deeply in the call
stack. Annotate this function and some of its callers, lest we forget
about it.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/structs_vec.c | 33 +++++++++++++++++++++++++++++++--
 multipathd/main.c          | 10 ++++++----
 2 files changed, 37 insertions(+), 6 deletions(-)

diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 62ee450..9905ff2 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -563,6 +563,34 @@ static struct path *find_devt_in_pathgroups(const struct multipath *mpp,
 	return NULL;
 }
 
+/*
+ * check_removed_paths()
+ *
+ * This function removes paths from the pathvec and frees them if they have
+ * been marked for removal (INIT_REMOVED, INIT_PARTIAL).
+ * This is important because some callers (e.g. uev_add_path->ev_remove_path())
+ * rely on the paths being actually gone when the call stack returns.
+ * Be sure not to call it while these paths are still referenced elsewhere
+ * (e.g. from coalesce_paths(), where curmp may still reference them).
+ *
+ * Most important call stacks in multipath-tools 0.13.0:
+ *
+ * checker_finished()
+ *   sync_mpp()
+ *     do_sync_mpp()
+ *       update_multipath_strings()
+ *         sync_paths()
+ *           check_removed_paths()
+ *
+ * [multiple callers including update_map(), ev_remove_path(), ...]
+ *   setup_multipath()
+ *     refresh_multipath()
+ *       update_multipath_strings()
+ *         sync_paths()
+ *           check_removed_paths()
+ *
+ * refresh_multipath() is also called from a couple of CLI handlers.
+ */
 static void check_removed_paths(const struct multipath *mpp, vector pathvec)
 {
 	struct path *pp;
@@ -583,6 +611,7 @@ static void check_removed_paths(const struct multipath *mpp, vector pathvec)
 	}
 }
 
+/* This function may free paths. See check_removed_paths(). */
 void sync_paths(struct multipath *mpp, vector pathvec)
 {
 	struct path *pp;
@@ -611,8 +640,8 @@ void sync_paths(struct multipath *mpp, vector pathvec)
 		pp->mpp = mpp;
 }
 
-int
-update_multipath_strings(struct multipath *mpp, vector pathvec)
+/* This function may free paths. See check_removed_paths(). */
+int update_multipath_strings(struct multipath *mpp, vector pathvec)
 {
 	struct pathgroup *pgp;
 	int i, r = DMP_ERR;
diff --git a/multipathd/main.c b/multipathd/main.c
index 7b522e8..697e269 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -503,6 +503,7 @@ remove_maps_and_stop_waiters(struct vectors *vecs)
 	remove_maps(vecs);
 }
 
+/* This function may free paths. See check_removed_paths(). */
 int refresh_multipath(struct vectors *vecs, struct multipath *mpp)
 {
 	if (update_multipath_strings(mpp, vecs->pathvec) != DMP_OK) {
@@ -513,6 +514,7 @@ int refresh_multipath(struct vectors *vecs, struct multipath *mpp)
 	return 0;
 }
 
+/* This function may free paths. See check_removed_paths(). */
 int setup_multipath(struct vectors *vecs, struct multipath *mpp)
 {
 	if (refresh_multipath(vecs, mpp) != 0)
@@ -2519,8 +2521,8 @@ get_new_state(struct path *pp)
 	return newstate;
 }
 
-static int
-do_sync_mpp(struct vectors * vecs, struct multipath *mpp)
+/* This function may free paths. See check_removed_paths(). */
+static int do_sync_mpp(struct vectors *vecs, struct multipath *mpp)
 {
 	int i, ret;
 	struct path *pp;
@@ -2538,8 +2540,8 @@ do_sync_mpp(struct vectors * vecs, struct multipath *mpp)
 	return ret;
 }
 
-static int
-sync_mpp(struct vectors * vecs, struct multipath *mpp, unsigned int ticks)
+/* This function may free paths. See check_removed_paths(). */
+static int sync_mpp(struct vectors *vecs, struct multipath *mpp, unsigned int ticks)
 {
 	if (mpp->sync_tick)
 		mpp->sync_tick -= (mpp->sync_tick > ticks) ? ticks :
-- 
2.52.0


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

* [PATCH v2 18/26] multipath-tools: Fix ISO C23 errors with strchr()
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (16 preceding siblings ...)
  2025-12-19 14:40 ` [PATCH v2 17/26] libmultipaths: annotate functions that may free paths Martin Wilck
@ 2025-12-19 14:41 ` Martin Wilck
  2025-12-19 14:41 ` [PATCH v2 19/26] libmultipath: simplify sysfs_get_target_nodename() Martin Wilck
                   ` (8 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:41 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>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.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] 37+ messages in thread

* [PATCH v2 19/26] libmultipath: simplify sysfs_get_target_nodename()
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (17 preceding siblings ...)
  2025-12-19 14:41 ` [PATCH v2 18/26] multipath-tools: Fix ISO C23 errors with strchr() Martin Wilck
@ 2025-12-19 14:41 ` Martin Wilck
  2025-12-19 14:41 ` [PATCH v2 20/26] multipathd: join the init_unwinder dummy thread Martin Wilck
                   ` (7 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:41 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>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 libmultipath/discovery.c | 36 ++++++++++++++++++++----------------
 tests/test-lib.c         |  5 +++--
 2 files changed, 23 insertions(+), 18 deletions(-)

diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index 31db875..85ea1aa 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -325,19 +325,23 @@ 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,
-							 "scsi", "scsi_device");
-	if (!parent)
+	device = udev_device_get_parent_with_subsystem_devtype(pp->udev, "scsi",
+							       "scsi_device");
+	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 +366,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 +379,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 +400,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);
@@ -428,13 +431,14 @@ 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 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] 37+ messages in thread

* [PATCH v2 20/26] multipathd: join the init_unwinder dummy thread
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (18 preceding siblings ...)
  2025-12-19 14:41 ` [PATCH v2 19/26] libmultipath: simplify sysfs_get_target_nodename() Martin Wilck
@ 2025-12-19 14:41 ` Martin Wilck
  2025-12-19 14:41 ` [PATCH v2 21/26] kpartx: fix some memory leaks Martin Wilck
                   ` (6 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:41 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>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.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] 37+ messages in thread

* [PATCH v2 21/26] kpartx: fix some memory leaks
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (19 preceding siblings ...)
  2025-12-19 14:41 ` [PATCH v2 20/26] multipathd: join the init_unwinder dummy thread Martin Wilck
@ 2025-12-19 14:41 ` Martin Wilck
  2025-12-19 14:41 ` [PATCH v2 22/26] libmpathutil: use union for bitfield Martin Wilck
                   ` (5 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:41 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>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 kpartx/kpartx.c | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c
index a1495e5..7ec89b4 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] 37+ messages in thread

* [PATCH v2 22/26] libmpathutil: use union for bitfield
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (20 preceding siblings ...)
  2025-12-19 14:41 ` [PATCH v2 21/26] kpartx: fix some memory leaks Martin Wilck
@ 2025-12-19 14:41 ` Martin Wilck
  2025-12-19 14:41 ` [PATCH v2 23/26] libmpathutil: add wrapper code for libudev Martin Wilck
                   ` (4 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:41 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.

The "length" argument to BITFIELD has no effect any more. Remove it.
BITFIELD will now always have space for bits_per_slot bits (usually
64 on modern 64-bit distros).

Introduce BUILD_BUG_ON and use it in ordert to make sure static bit fields
are large enough.

Fixes: 9a2f173 ("libmpathutil: change STATIC_BITFIELD to BITFIELD")
Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmpathutil/util.c       |  8 +++----
 libmpathutil/util.h       | 47 +++++++++++++++++++++++++--------------
 libmultipath/configure.c  |  6 ++---
 libmultipath/discovery.c  |  4 +++-
 libmultipath/pgpolicies.c |  2 +-
 tests/util.c              | 10 ++++-----
 6 files changed, 46 insertions(+), 31 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..d2a6c52 100644
--- a/libmpathutil/util.h
+++ b/libmpathutil/util.h
@@ -16,6 +16,8 @@
 #define __GLIBC_PREREQ(x, y) 0
 #endif
 
+#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
+
 size_t strchop(char *);
 
 const char *libmp_basename(const char *filename);
@@ -86,29 +88,40 @@ 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, },						\
+/*
+ * BITFIELD: define a static bitfield of length bits_per_slot
+ * (aka 64 on 64-bit architectures).
+ * gcc 4.9.2 (Debian Jessie) raises an error if the initializer for
+ * .__len comes first. Thus put .__bits first.
+ * Use e.g. BUILD_BUG_ON() to make sure the bitfield size is sufficient
+ * to hold the number of bits required.
+ */
+#define BITFIELD(name)							\
+	union bitfield __storage_for__ ## name = {			\
+		.__bits = { 0 },					\
+		.__len = (bits_per_slot),				\
 	}; \
-	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 +131,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 +140,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 +159,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..b5f1bab 100644
--- a/libmultipath/configure.c
+++ b/libmultipath/configure.c
@@ -439,8 +439,8 @@ 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;
+	BITFIELD(bf);
+	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/discovery.c b/libmultipath/discovery.c
index 85ea1aa..72e64da 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -880,10 +880,12 @@ sysfs_set_nexus_loss_tmo(struct path *pp)
 static void
 scsi_tmo_error_msg(struct path *pp)
 {
-	static BITFIELD(bf, LAST_BUS_PROTOCOL_ID + 1);
+	static BITFIELD(bf);
 	STRBUF_ON_STACK(proto_buf);
 	unsigned int proto_id = bus_protocol_id(pp);
 
+	/* make sure the bitfield is large enough */
+	BUILD_BUG_ON((LAST_BUS_PROTOCOL_ID + 1) > bits_per_slot);
 	snprint_path_protocol(&proto_buf, pp);
 	condlog(2, "%s: setting scsi timeouts is unsupported for protocol %s",
 		pp->dev, get_strbuf_str(&proto_buf));
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] 37+ messages in thread

* [PATCH v2 23/26] libmpathutil: add wrapper code for libudev
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (21 preceding siblings ...)
  2025-12-19 14:41 ` [PATCH v2 22/26] libmpathutil: use union for bitfield Martin Wilck
@ 2025-12-19 14:41 ` Martin Wilck
  2025-12-19 14:41 ` [PATCH v2 24/26] multipath-tools: use the libudev wrapper functions Martin Wilck
                   ` (3 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:41 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>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.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..8fab1a6 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] 37+ messages in thread

* [PATCH v2 24/26] multipath-tools: use the libudev wrapper functions
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (22 preceding siblings ...)
  2025-12-19 14:41 ` [PATCH v2 23/26] libmpathutil: add wrapper code for libudev Martin Wilck
@ 2025-12-19 14:41 ` Martin Wilck
  2025-12-19 14:41 ` [PATCH v2 25/26] Makefile: add functionality to determine cmocka version Martin Wilck
                   ` (2 subsequent siblings)
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:41 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>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.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 d2a6c52..aed1bc1 100644
--- a/libmpathutil/util.h
+++ b/libmpathutil/util.h
@@ -10,7 +10,6 @@
 #include <inttypes.h>
 #include <stdbool.h>
 #include <stdio.h>
-#include <libudev.h>
 
 #ifndef __GLIBC_PREREQ
 #define __GLIBC_PREREQ(x, y) 0
@@ -18,6 +17,7 @@
 
 #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
 
+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 b5f1bab..2f6db49 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 72e64da..9d0f6d9 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 868c3d6..d406356 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 9905ff2..c76b1e8 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 5557732..ecf2f2d 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 697e269..b909242 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] 37+ messages in thread

* [PATCH v2 25/26] Makefile: add functionality to determine cmocka version
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (23 preceding siblings ...)
  2025-12-19 14:41 ` [PATCH v2 24/26] multipath-tools: use the libudev wrapper functions Martin Wilck
@ 2025-12-19 14:41 ` Martin Wilck
  2025-12-19 14:41 ` [PATCH v2 26/26] multipath-tools tests: adaptations for cmocka 2.0 Martin Wilck
  2025-12-20  4:41 ` [PATCH v2 00/26] Multipath-tools: various bug fixes Benjamin Marzinski
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:41 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>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.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] 37+ messages in thread

* [PATCH v2 26/26] multipath-tools tests: adaptations for cmocka 2.0
  2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
                   ` (24 preceding siblings ...)
  2025-12-19 14:41 ` [PATCH v2 25/26] Makefile: add functionality to determine cmocka version Martin Wilck
@ 2025-12-19 14:41 ` Martin Wilck
  2025-12-20  4:41 ` [PATCH v2 00/26] Multipath-tools: various bug fixes Benjamin Marzinski
  26 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:41 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>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 tests/alias.c         |  50 +++++++++-------
 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       |  85 +++++++++++++--------------
 tests/mpathvalid.c    |  16 +++---
 tests/parser.c        |   2 +-
 tests/pgpolicy.c      |   2 +-
 tests/strbuf.c        | 131 ++++++++++++++++++++++--------------------
 tests/sysfs.c         |  74 ++++++++++++------------
 tests/test-lib.c      |  88 ++++++++++++++--------------
 tests/test-log.c      |  10 ++--
 tests/uevent.c        |   2 +-
 tests/unaligned.c     |   8 +--
 tests/util.c          | 113 ++++++++++++++++++------------------
 tests/valid.c         |  30 +++++-----
 tests/vpd.c           |  12 ++--
 22 files changed, 396 insertions(+), 360 deletions(-)
 create mode 100644 tests/cmocka-compat.h

diff --git a/tests/alias.c b/tests/alias.c
index 163d9a7..faafcd2 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,8 @@ 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 +1280,8 @@ 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 +1320,11 @@ 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 +1343,11 @@ 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 +1364,8 @@ 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 +1381,8 @@ 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 +1414,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..d8461b4 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,23 @@ 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,
-				uint64_t *start, uint64_t *length,
-				char **target_type, char **params)
+const void *
+__wrap_dm_get_next_target(struct dm_task *dmt, void *next, uint64_t *start,
+			  uint64_t *length, 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 +192,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 +224,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 +647,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 +661,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 +675,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 +692,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 +707,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 +739,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 +758,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 +780,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 +872,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 +910,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 +1103,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 +1370,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..06fa71c 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,8 @@ 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 +171,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 +195,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 +323,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 +345,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 +361,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 +390,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 +398,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 +423,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..5f02942 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..ea99977 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,10 @@ void *__wrap_udev_device_get_parent_with_subsystem_devtype(
 	return type;
 }
 
-void *__wrap_udev_device_get_parent(struct udev_device *ud)
+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(void *);
-
-	condlog(5, "%s: %p", __func__, val);
-	return val;
-}
-
-ssize_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 +197,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,8 +242,8 @@ 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,
-					sizeof(vpd_data) + 3, INT_MAX);
+			assert_uint_in_range(hdr->dxfer_len,
+					     sizeof(vpd_data) + 3, INT_MAX);
 			memset(buf, 0, hdr->dxfer_len);
 			buf[1] = HP3PAR_VPD;
 			put_unaligned_be16(sizeof(vpd_data), &buf[2]);
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..9f4a5d9 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,10 @@ 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 +233,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 +247,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,13 +265,12 @@ 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(
-						maybe_swap(arr[k]),
-						(1ULL << (i + 1)) - 1);
+					assert_uint_equal(maybe_swap(arr[k]),
+							  (1ULL << (i + 1)) - 1);
 			}
 		}
 	}
@@ -289,13 +289,12 @@ 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(
-						maybe_swap(arr[k]),
-						~((1ULL << (i + 1)) - 1));
+					assert_uint_equal(maybe_swap(arr[k]),
+							  ~((1ULL << (i + 1)) - 1));
 			}
 		}
 	}
@@ -334,23 +333,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 +358,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 +381,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 +419,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 +435,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 +451,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 +567,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 +578,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 +590,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 +604,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 +708,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 +898,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 +906,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 +914,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 +922,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 +930,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 +938,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] 37+ messages in thread

* Re: [PATCH v2 14/26] multipathd: add_map_without_path(): orphan paths instead of freeing them
  2025-12-19 14:40 ` [PATCH v2 14/26] multipathd: add_map_without_path(): orphan paths instead of freeing them Martin Wilck
@ 2025-12-19 14:46   ` Martin Wilck
  2025-12-20  4:23   ` Benjamin Marzinski
  1 sibling, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2025-12-19 14:46 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

On Fri, 2025-12-19 at 15:40 +0100, Martin Wilck wrote:
> If add_map_without_path() fails to set up the multipath data
> structures,
> don't free the paths associated with the map as
> cleanup_multipath_and_paths() would; just orphan the paths.
> update_pathvec_from_dm() will handle the paths and see if they
> need to be removed.
> 
> Suggested-by: Benjamin Marzinski <bmarzins@suse.com>

Ups, sorry. ;-)
I won't resend just for this, it's fixed on my tip branch.

Martin

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

* Re: [PATCH v2 13/26] multipath: free paths through pathvec in check_usable_paths()
  2025-12-19 14:40 ` [PATCH v2 13/26] multipath: free paths through pathvec in check_usable_paths() Martin Wilck
@ 2025-12-20  4:19   ` Benjamin Marzinski
  2026-01-05 14:38     ` Martin Wilck
  0 siblings, 1 reply; 37+ messages in thread
From: Benjamin Marzinski @ 2025-12-20  4:19 UTC (permalink / raw)
  To: Martin Wilck; +Cc: Christophe Varoqui, dm-devel, Martin Wilck

On Fri, Dec 19, 2025 at 03:40:55PM +0100, Martin Wilck wrote:
> Paths belong to the pathvec and should be freed from it.
> Use cleanup_multipath() instead of cleanup_multipath_and_paths(),
> and free the paths via the pathvec instead.
> 

I don't know if this matters, but until you stop free_multipaths() from
clearing pp->mpp in patch 15/26, this will temporarily cause UAF errors,
since you free the paths from the pathvec first, and then attempt to
reset their pp->mpp value in free_multipaths() later.

You could avoid the temporary problem by moving the pathvec definition
before the mpp definition, since cleanup functions are run in reverse
order of their declaration.

But after applying patch 15/26, this is fine, so
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>

> Suggested-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipath/main.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/multipath/main.c b/multipath/main.c
> index 4b8d7dd..d28628b 100644
> --- a/multipath/main.c
> +++ b/multipath/main.c
> @@ -214,12 +214,12 @@ static int check_usable_paths(struct config *conf,
>  			      const char *devpath, enum devtypes dev_type)
>  {
>  	struct udev_device __attribute__((cleanup(cleanup_udev_device))) *ud = NULL;
> -	struct multipath __attribute__((cleanup(cleanup_multipath_and_paths))) *mpp = NULL;
> +	struct multipath __attribute__((cleanup(cleanup_multipath))) *mpp = NULL;
>  	struct pathgroup *pg;
>  	struct path *pp;
>  	char __attribute__((cleanup(cleanup_charp))) *params = NULL;
>  	char __attribute__((cleanup(cleanup_charp))) *status = NULL;
> -	vector __attribute((cleanup(cleanup_vector))) pathvec = NULL;
> +	vector __attribute((cleanup(cleanup_pathvec_and_free_paths))) pathvec = NULL;
>  	char uuid[DM_UUID_LEN];
>  	dev_t devt;
>  	int r = 1, i, j;
> -- 
> 2.52.0


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

* Re: [PATCH v2 14/26] multipathd: add_map_without_path(): orphan paths instead of freeing them
  2025-12-19 14:40 ` [PATCH v2 14/26] multipathd: add_map_without_path(): orphan paths instead of freeing them Martin Wilck
  2025-12-19 14:46   ` Martin Wilck
@ 2025-12-20  4:23   ` Benjamin Marzinski
  2026-01-05 15:15     ` Martin Wilck
  1 sibling, 1 reply; 37+ messages in thread
From: Benjamin Marzinski @ 2025-12-20  4:23 UTC (permalink / raw)
  To: Martin Wilck
  Cc: Christophe Varoqui, dm-devel, Martin Wilck, Benjamin Marzinski

On Fri, Dec 19, 2025 at 03:40:56PM +0100, Martin Wilck wrote:
> If add_map_without_path() fails to set up the multipath data structures,
> don't free the paths associated with the map as
> cleanup_multipath_and_paths() would; just orphan the paths.
> update_pathvec_from_dm() will handle the paths and see if they
> need to be removed.
> 
> Suggested-by: Benjamin Marzinski <bmarzins@suse.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/main.c | 12 +++++++-----
>  1 file changed, 7 insertions(+), 5 deletions(-)
> 
> diff --git a/multipathd/main.c b/multipathd/main.c
> index c03546b..7b522e8 100644
> --- a/multipathd/main.c
> +++ b/multipathd/main.c
> @@ -771,8 +771,8 @@ fail:
>  
>  static int add_map_without_path (struct vectors *vecs, const char *alias)
>  {
> -	struct multipath __attribute__((cleanup(cleanup_multipath_and_paths)))
> -		*mpp = alloc_multipath();
> +	struct multipath __attribute__((cleanup(cleanup_multipath))) *mpp =
> +		alloc_multipath();
>  	char __attribute__((cleanup(cleanup_charp))) *params = NULL;
>  	char __attribute__((cleanup(cleanup_charp))) *status = NULL;
>  	struct config *conf;
> @@ -802,11 +802,13 @@ static int add_map_without_path (struct vectors *vecs, const char *alias)
>  	mpp->mpe = find_mpe(conf->mptable, mpp->wwid);
>  	put_multipath_config(conf);
>  
> -	if ((rc = update_multipath_table__(mpp, vecs->pathvec, 0, params, status)) != DMP_OK)
> +	if (update_multipath_table__(mpp, vecs->pathvec, 0, params, status) != DMP_OK ||
> +	    !vector_alloc_slot(vecs->mpvec)) {
> +		orphan_paths(vecs->pathvec, mpp, "failed to add map");
>  		return DMP_ERR;

I'm not sure we should just call orphan_paths() after we call
update_multipath_table__(). If we hit a cancellation point in the middle
of update_multipath_table__(), we want to fully orphan any paths that
got added to the device before we cancelled.  Perhaps the easiest way to
solve this is to make cleanup_multipath_and_paths() loop through all the
path in mpp->pg and orphan them before calling free_multipath(). We
shouldn't need to search through the entire pathvec here. Since this
multipath device was just created, the only paths that could have
pp->mpp set to this device will be in mpp->pg.

-Ben

> +	}
>  
> -	if (!vector_alloc_slot(vecs->mpvec))
> -		return DMP_ERR;
> +	/* Make sure mpp is not cleaned up on return */
>  	vector_set_slot(vecs->mpvec, steal_ptr(mpp));
>  
>  	/*
> -- 
> 2.52.0


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

* Re: [PATCH v2 16/26] libmultipath: remove cleanup_multipath_and_paths()
  2025-12-19 14:40 ` [PATCH v2 16/26] libmultipath: remove cleanup_multipath_and_paths() Martin Wilck
@ 2025-12-20  4:26   ` Benjamin Marzinski
  0 siblings, 0 replies; 37+ messages in thread
From: Benjamin Marzinski @ 2025-12-20  4:26 UTC (permalink / raw)
  To: Martin Wilck; +Cc: Christophe Varoqui, dm-devel, Martin Wilck

On Fri, Dec 19, 2025 at 03:40:58PM +0100, Martin Wilck wrote:
> This function is not used any more.
> 
> Suggested-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/libmultipath.version | 1 -
>  libmultipath/structs.c            | 6 ------
>  libmultipath/structs.h            | 1 -
>  3 files changed, 8 deletions(-)
> 
> diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
> index 366b829..39384d6 100644
> --- a/libmultipath/libmultipath.version
> +++ b/libmultipath/libmultipath.version
> @@ -69,7 +69,6 @@ global:
>  	cleanup_bindings;
>  	cleanup_lock;
>  	cleanup_multipath;
> -	cleanup_multipath_and_paths;
>  	cleanup_pathvec_and_free_paths;
>  	cleanup_udev_device_ptr;
>  	cleanup_udev_enumerate_ptr;
> diff --git a/libmultipath/structs.c b/libmultipath/structs.c
> index 9e1734d..868c3d6 100644
> --- a/libmultipath/structs.c
> +++ b/libmultipath/structs.c
> @@ -324,12 +324,6 @@ void cleanup_multipath(struct multipath **pmpp)
>  		free_multipath(*pmpp);
>  }
>  
> -void cleanup_multipath_and_paths(struct multipath **pmpp)
> -{
> -	if (*pmpp)
> -		free_multipath(*pmpp);
> -}
> -

Like I said in 14/26, I think we want to keep this to clean up
add_map_without_path(). It should loop through the paths in (*pmpp)->pg,
orphaning them, before calling free_multipath().

-Ben

>  void free_multipathvec(vector mpvec)
>  {
>  	int i;
> diff --git a/libmultipath/structs.h b/libmultipath/structs.h
> index a637af7..251e672 100644
> --- a/libmultipath/structs.h
> +++ b/libmultipath/structs.h
> @@ -582,7 +582,6 @@ void free_pathgroup(struct pathgroup *pgp);
>  void free_pgvec(vector pgvec);
>  void free_multipath(struct multipath *mpp);
>  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);
>  
> -- 
> 2.52.0


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

* Re: [PATCH v2 17/26] libmultipaths: annotate functions that may free paths
  2025-12-19 14:40 ` [PATCH v2 17/26] libmultipaths: annotate functions that may free paths Martin Wilck
@ 2025-12-20  4:35   ` Benjamin Marzinski
  0 siblings, 0 replies; 37+ messages in thread
From: Benjamin Marzinski @ 2025-12-20  4:35 UTC (permalink / raw)
  To: Martin Wilck; +Cc: Christophe Varoqui, dm-devel, Martin Wilck

On Fri, Dec 19, 2025 at 03:40:59PM +0100, Martin Wilck wrote:
> Paths are freed in check_removed_paths() quite deeply in the call
> stack. Annotate this function and some of its callers, lest we forget
> about it.

This looks fine. I just feel like I should point out that if you're
doing this to satisfy my objection, I don't think that it's necessary to
immediately remove these paths on every map reload. I was fine with
check_remove_paths() being removed from sync_paths(). All I was
envisioning was a check in ev_remove_path() to see if the path had been
removed from mpp->pg, and removing it there if it had. It actually seems
a little strange that we immeditately remove paths on reloads, but not
when the whole map is deleted. But I get that it's designed to avoid
freeing the paths in coalesce_paths(), and like I said, I'm fine with
it, so:

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

> 
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/structs_vec.c | 33 +++++++++++++++++++++++++++++++--
>  multipathd/main.c          | 10 ++++++----
>  2 files changed, 37 insertions(+), 6 deletions(-)
> 
> diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
> index 62ee450..9905ff2 100644
> --- a/libmultipath/structs_vec.c
> +++ b/libmultipath/structs_vec.c
> @@ -563,6 +563,34 @@ static struct path *find_devt_in_pathgroups(const struct multipath *mpp,
>  	return NULL;
>  }
>  
> +/*
> + * check_removed_paths()
> + *
> + * This function removes paths from the pathvec and frees them if they have
> + * been marked for removal (INIT_REMOVED, INIT_PARTIAL).
> + * This is important because some callers (e.g. uev_add_path->ev_remove_path())
> + * rely on the paths being actually gone when the call stack returns.
> + * Be sure not to call it while these paths are still referenced elsewhere
> + * (e.g. from coalesce_paths(), where curmp may still reference them).
> + *
> + * Most important call stacks in multipath-tools 0.13.0:
> + *
> + * checker_finished()
> + *   sync_mpp()
> + *     do_sync_mpp()
> + *       update_multipath_strings()
> + *         sync_paths()
> + *           check_removed_paths()
> + *
> + * [multiple callers including update_map(), ev_remove_path(), ...]
> + *   setup_multipath()
> + *     refresh_multipath()
> + *       update_multipath_strings()
> + *         sync_paths()
> + *           check_removed_paths()
> + *
> + * refresh_multipath() is also called from a couple of CLI handlers.
> + */
>  static void check_removed_paths(const struct multipath *mpp, vector pathvec)
>  {
>  	struct path *pp;
> @@ -583,6 +611,7 @@ static void check_removed_paths(const struct multipath *mpp, vector pathvec)
>  	}
>  }
>  
> +/* This function may free paths. See check_removed_paths(). */
>  void sync_paths(struct multipath *mpp, vector pathvec)
>  {
>  	struct path *pp;
> @@ -611,8 +640,8 @@ void sync_paths(struct multipath *mpp, vector pathvec)
>  		pp->mpp = mpp;
>  }
>  
> -int
> -update_multipath_strings(struct multipath *mpp, vector pathvec)
> +/* This function may free paths. See check_removed_paths(). */
> +int update_multipath_strings(struct multipath *mpp, vector pathvec)
>  {
>  	struct pathgroup *pgp;
>  	int i, r = DMP_ERR;
> diff --git a/multipathd/main.c b/multipathd/main.c
> index 7b522e8..697e269 100644
> --- a/multipathd/main.c
> +++ b/multipathd/main.c
> @@ -503,6 +503,7 @@ remove_maps_and_stop_waiters(struct vectors *vecs)
>  	remove_maps(vecs);
>  }
>  
> +/* This function may free paths. See check_removed_paths(). */
>  int refresh_multipath(struct vectors *vecs, struct multipath *mpp)
>  {
>  	if (update_multipath_strings(mpp, vecs->pathvec) != DMP_OK) {
> @@ -513,6 +514,7 @@ int refresh_multipath(struct vectors *vecs, struct multipath *mpp)
>  	return 0;
>  }
>  
> +/* This function may free paths. See check_removed_paths(). */
>  int setup_multipath(struct vectors *vecs, struct multipath *mpp)
>  {
>  	if (refresh_multipath(vecs, mpp) != 0)
> @@ -2519,8 +2521,8 @@ get_new_state(struct path *pp)
>  	return newstate;
>  }
>  
> -static int
> -do_sync_mpp(struct vectors * vecs, struct multipath *mpp)
> +/* This function may free paths. See check_removed_paths(). */
> +static int do_sync_mpp(struct vectors *vecs, struct multipath *mpp)
>  {
>  	int i, ret;
>  	struct path *pp;
> @@ -2538,8 +2540,8 @@ do_sync_mpp(struct vectors * vecs, struct multipath *mpp)
>  	return ret;
>  }
>  
> -static int
> -sync_mpp(struct vectors * vecs, struct multipath *mpp, unsigned int ticks)
> +/* This function may free paths. See check_removed_paths(). */
> +static int sync_mpp(struct vectors *vecs, struct multipath *mpp, unsigned int ticks)
>  {
>  	if (mpp->sync_tick)
>  		mpp->sync_tick -= (mpp->sync_tick > ticks) ? ticks :
> -- 
> 2.52.0


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

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

On Fri, Dec 19, 2025 at 03:40:42PM +0100, Martin Wilck wrote:

For everything but patches 14 & 16:
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>

> Changes v1 -> v2
> 
> Fixed the issues pointed out by Benjamin Marzinski in v1.
> 
> - paths are still removed in sync_paths(), as before. I added some comments
>   lest we forget about this.
> - in the checker loop, only orphan paths in REMOVED/PARTIAL state are freed
> - removed the free_paths argument from free_multipath, and changed
>   add_map_without_path and check_usable_paths() as suggested by Ben.
>   free_multipath() doesn't free paths any more.
> - the BITFIELD macro now creates a field of fixed length.
> - fixed whitespace issues.
> 
> These changes required shuffling the patches around. Therefore I'm resending
> the entire series. I kept Ben's Reviewed-by: trailers.
> 
> I also fixed some formatting issues reported by clang-format.
> clang-format also changes context line of diffs, which I accepted.
> 
> v1 cover letter
> ---------------
> 
> (note: I did not fix the patch numbers)
> 
> 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 (26):
>   libmultipath: drop drop_multipath
>   libmultipath: don't access path members in free_pgvec()
>   libmpathutil: constify find_slot()
>   libmultipath: don't touch mpvec in remove_map()
>   libmultipath: export orphan_paths()
>   libmultipath: export cleanup_multipath()
>   libmultipath: add cleanup_pathvec_and_free_paths()
>   libmultipath: don't free paths in orphan_paths()
>   multipathd: free orphaned paths in checker_finished()
>   libmultipath: remove free_paths argument from free_pathgroup()
>   libmultipath: remove free_paths argument from free_pgvec()
>   libmultipath: remove free_paths argument from free_multipathvec()
>   multipath: free paths through pathvec in check_usable_paths()
>   multipathd: add_map_without_path(): orphan paths instead of freeing
>     them
>   libmultipath: remove free_paths argument from free_multipath()
>   libmultipath: remove cleanup_multipath_and_paths()
>   libmultipaths: annotate functions that may free paths
>   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                   |  49 +-
>  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              |  24 +-
>  libmultipath/devmapper.c              |   2 +-
>  libmultipath/dict.c                   |   2 +-
>  libmultipath/discovery.c              |  44 +-
>  libmultipath/dmparser.c               |   6 +-
>  libmultipath/foreign.c                |   2 +-
>  libmultipath/foreign.h                |   2 +-
>  libmultipath/foreign/nvme.c           |   2 +-
>  libmultipath/libmultipath.version     |   5 +-
>  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                |  82 +--
>  libmultipath/structs.h                |  13 +-
>  libmultipath/structs_vec.c            |  88 +--
>  libmultipath/structs_vec.h            |   4 +-
>  libmultipath/sysfs.c                  |   2 +-
>  libmultipath/uevent.c                 |   2 +-
>  libmultipath/valid.c                  |   2 +-
>  mpathpersist/main.c                   |   2 +-
>  multipath/main.c                      |  22 +-
>  multipathd/cli_handlers.c             |   2 +-
>  multipathd/fpin_handlers.c            |   2 +-
>  multipathd/init_unwinder.c            |   4 +-
>  multipathd/main.c                     |  59 +-
>  tests/Makefile                        |  22 +-
>  tests/alias.c                         |  50 +-
>  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                       |  85 +--
>  tests/mpathvalid.c                    |  18 +-
>  tests/parser.c                        |   2 +-
>  tests/pgpolicy.c                      |   4 +-
>  tests/strbuf.c                        | 131 ++---
>  tests/sysfs.c                         |  76 +--
>  tests/test-lib.c                      |  95 ++--
>  tests/test-log.c                      |  10 +-
>  tests/uevent.c                        |   2 +-
>  tests/unaligned.c                     |   8 +-
>  tests/util.c                          | 123 ++--
>  tests/valid.c                         |  30 +-
>  tests/vpd.c                           |  12 +-
>  77 files changed, 1748 insertions(+), 625 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] 37+ messages in thread

* Re: [PATCH v2 13/26] multipath: free paths through pathvec in check_usable_paths()
  2025-12-20  4:19   ` Benjamin Marzinski
@ 2026-01-05 14:38     ` Martin Wilck
  0 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2026-01-05 14:38 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: Christophe Varoqui, dm-devel

On Fri, 2025-12-19 at 23:19 -0500, Benjamin Marzinski wrote:
> On Fri, Dec 19, 2025 at 03:40:55PM +0100, Martin Wilck wrote:
> > Paths belong to the pathvec and should be freed from it.
> > Use cleanup_multipath() instead of cleanup_multipath_and_paths(),
> > and free the paths via the pathvec instead.
> > 
> 
> I don't know if this matters, but until you stop free_multipaths()
> from
> clearing pp->mpp in patch 15/26, this will temporarily cause UAF
> errors,
> since you free the paths from the pathvec first, and then attempt to
> reset their pp->mpp value in free_multipaths() later.
> 
> You could avoid the temporary problem by moving the pathvec
> definition
> before the mpp definition, since cleanup functions are run in reverse
> order of their declaration.
> 
> But after applying patch 15/26, this is fine, so
> Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>

Thanks for pointing this out. I will move 13 after 14, 15.

Martin

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

* Re: [PATCH v2 14/26] multipathd: add_map_without_path(): orphan paths instead of freeing them
  2025-12-20  4:23   ` Benjamin Marzinski
@ 2026-01-05 15:15     ` Martin Wilck
  2026-01-05 23:17       ` Benjamin Marzinski
  0 siblings, 1 reply; 37+ messages in thread
From: Martin Wilck @ 2026-01-05 15:15 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: Christophe Varoqui, dm-devel, Benjamin Marzinski

On Fri, 2025-12-19 at 23:23 -0500, Benjamin Marzinski wrote:
> On Fri, Dec 19, 2025 at 03:40:56PM +0100, Martin Wilck wrote:
> > If add_map_without_path() fails to set up the multipath data
> > structures,
> > don't free the paths associated with the map as
> > cleanup_multipath_and_paths() would; just orphan the paths.
> > update_pathvec_from_dm() will handle the paths and see if they
> > need to be removed.
> > 
> > Suggested-by: Benjamin Marzinski <bmarzins@suse.com>
> > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > ---
> >  multipathd/main.c | 12 +++++++-----
> >  1 file changed, 7 insertions(+), 5 deletions(-)
> > 
> > diff --git a/multipathd/main.c b/multipathd/main.c
> > index c03546b..7b522e8 100644
> > --- a/multipathd/main.c
> > +++ b/multipathd/main.c
> > @@ -771,8 +771,8 @@ fail:
> >  
> >  static int add_map_without_path (struct vectors *vecs, const char
> > *alias)
> >  {
> > -	struct multipath
> > __attribute__((cleanup(cleanup_multipath_and_paths)))
> > -		*mpp = alloc_multipath();
> > +	struct multipath
> > __attribute__((cleanup(cleanup_multipath))) *mpp =
> > +		alloc_multipath();
> >  	char __attribute__((cleanup(cleanup_charp))) *params =
> > NULL;
> >  	char __attribute__((cleanup(cleanup_charp))) *status =
> > NULL;
> >  	struct config *conf;
> > @@ -802,11 +802,13 @@ static int add_map_without_path (struct
> > vectors *vecs, const char *alias)
> >  	mpp->mpe = find_mpe(conf->mptable, mpp->wwid);
> >  	put_multipath_config(conf);
> >  
> > -	if ((rc = update_multipath_table__(mpp, vecs->pathvec, 0,
> > params, status)) != DMP_OK)
> > +	if (update_multipath_table__(mpp, vecs->pathvec, 0,
> > params, status) != DMP_OK ||
> > +	    !vector_alloc_slot(vecs->mpvec)) {
> > +		orphan_paths(vecs->pathvec, mpp, "failed to add
> > map");
> >  		return DMP_ERR;
> 
> I'm not sure we should just call orphan_paths() after we call
> update_multipath_table__(). If we hit a cancellation point in the
> middle
> of update_multipath_table__(), we want to fully orphan any paths that
> got added to the device before we cancelled.  

Sorry, I don't understand.

The __attribute__((cleanup())) functions don't protect against thread
cancellation. If we want to achieve what you describe, we must add a
pthread_cleanup_push()/pop() pair around the update_multipath_table__()
call.

But would that buy us anything? update_multipath_table__() only
manipulates multipathd's internal data structures, which, if a
cancellation happens, will almost immediately all be freed anyway. What
harm would be done if these paths weren't orphaned in such a case? Yes,
the some paths might now link to a non-existing struct multipath. I'd
see your point if we accessed pp->mpp in the path-freeing code path,
but we don't do this any more.

> Perhaps the easiest way to
> solve this is to make cleanup_multipath_and_paths() loop through all
> the
> path in mpp->pg and orphan them before calling free_multipath().

Like I said above, that would have no effect in the case of
cancellation.

I can add pthread_cleanup_push()/pop() pair if you insist, but so far I
don't see the benefit.

Regards
Martin

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

* Re: [PATCH v2 14/26] multipathd: add_map_without_path(): orphan paths instead of freeing them
  2026-01-05 15:15     ` Martin Wilck
@ 2026-01-05 23:17       ` Benjamin Marzinski
  2026-01-06 10:31         ` Martin Wilck
  0 siblings, 1 reply; 37+ messages in thread
From: Benjamin Marzinski @ 2026-01-05 23:17 UTC (permalink / raw)
  To: Martin Wilck; +Cc: Christophe Varoqui, dm-devel, Benjamin Marzinski

On Mon, Jan 05, 2026 at 04:15:37PM +0100, Martin Wilck wrote:
> On Fri, 2025-12-19 at 23:23 -0500, Benjamin Marzinski wrote:
> > On Fri, Dec 19, 2025 at 03:40:56PM +0100, Martin Wilck wrote:
> > > If add_map_without_path() fails to set up the multipath data
> > > structures,
> > > don't free the paths associated with the map as
> > > cleanup_multipath_and_paths() would; just orphan the paths.
> > > update_pathvec_from_dm() will handle the paths and see if they
> > > need to be removed.
> > > 
> > > Suggested-by: Benjamin Marzinski <bmarzins@suse.com>
> > > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > > ---
> > >  multipathd/main.c | 12 +++++++-----
> > >  1 file changed, 7 insertions(+), 5 deletions(-)
> > > 
> > > diff --git a/multipathd/main.c b/multipathd/main.c
> > > index c03546b..7b522e8 100644
> > > --- a/multipathd/main.c
> > > +++ b/multipathd/main.c
> > > @@ -771,8 +771,8 @@ fail:
> > >  
> > >  static int add_map_without_path (struct vectors *vecs, const char
> > > *alias)
> > >  {
> > > -	struct multipath
> > > __attribute__((cleanup(cleanup_multipath_and_paths)))
> > > -		*mpp = alloc_multipath();
> > > +	struct multipath
> > > __attribute__((cleanup(cleanup_multipath))) *mpp =
> > > +		alloc_multipath();
> > >  	char __attribute__((cleanup(cleanup_charp))) *params =
> > > NULL;
> > >  	char __attribute__((cleanup(cleanup_charp))) *status =
> > > NULL;
> > >  	struct config *conf;
> > > @@ -802,11 +802,13 @@ static int add_map_without_path (struct
> > > vectors *vecs, const char *alias)
> > >  	mpp->mpe = find_mpe(conf->mptable, mpp->wwid);
> > >  	put_multipath_config(conf);
> > >  
> > > -	if ((rc = update_multipath_table__(mpp, vecs->pathvec, 0,
> > > params, status)) != DMP_OK)
> > > +	if (update_multipath_table__(mpp, vecs->pathvec, 0,
> > > params, status) != DMP_OK ||
> > > +	    !vector_alloc_slot(vecs->mpvec)) {
> > > +		orphan_paths(vecs->pathvec, mpp, "failed to add
> > > map");
> > >  		return DMP_ERR;
> > 
> > I'm not sure we should just call orphan_paths() after we call
> > update_multipath_table__(). If we hit a cancellation point in the
> > middle
> > of update_multipath_table__(), we want to fully orphan any paths that
> > got added to the device before we cancelled.  
> 
> Sorry, I don't understand.
> 
> The __attribute__((cleanup())) functions don't protect against thread
> cancellation. If we want to achieve what you describe, we must add a
> pthread_cleanup_push()/pop() pair around the update_multipath_table__()
> call.

I'm pretty sure they do run during cancellation after I added ca957f2a
("multipath-tools: Makefile.inc: compile with -fexceptions").
 
> But would that buy us anything? update_multipath_table__() only
> manipulates multipathd's internal data structures, which, if a
> cancellation happens, will almost immediately all be freed anyway. What
> harm would be done if these paths weren't orphaned in such a case? Yes,
> the some paths might now link to a non-existing struct multipath. I'd
> see your point if we accessed pp->mpp in the path-freeing code path,
> but we don't do this any more.

We check whether pp->mpp is set in lots of places in the code to decide
what to do with the paths. But you are correct that we don't ever call
add_map_without_path() from a thread that can be cancelled and have
multipathd continue running, and we aren't likely to do so.

> > Perhaps the easiest way to
> > solve this is to make cleanup_multipath_and_paths() loop through all
> > the
> > path in mpp->pg and orphan them before calling free_multipath().
> 
> Like I said above, that would have no effect in the case of
> cancellation.
> 
> I can add pthread_cleanup_push()/pop() pair if you insist, but so far I
> don't see the benefit.

Sure. I drop my objection.

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

> Regards
> Martin


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

* Re: [PATCH v2 14/26] multipathd: add_map_without_path(): orphan paths instead of freeing them
  2026-01-05 23:17       ` Benjamin Marzinski
@ 2026-01-06 10:31         ` Martin Wilck
  0 siblings, 0 replies; 37+ messages in thread
From: Martin Wilck @ 2026-01-06 10:31 UTC (permalink / raw)
  To: Benjamin Marzinski; +Cc: Christophe Varoqui, dm-devel, Benjamin Marzinski

On Mon, 2026-01-05 at 18:17 -0500, Benjamin Marzinski wrote:
> On Mon, Jan 05, 2026 at 04:15:37PM +0100, Martin Wilck wrote:
> > On Fri, 2025-12-19 at 23:23 -0500, Benjamin Marzinski wrote:
> > > On Fri, Dec 19, 2025 at 03:40:56PM +0100, Martin Wilck wrote:
> > > > If add_map_without_path() fails to set up the multipath data
> > > > structures,
> > > > don't free the paths associated with the map as
> > > > cleanup_multipath_and_paths() would; just orphan the paths.
> > > > update_pathvec_from_dm() will handle the paths and see if they
> > > > need to be removed.
> > > > 
> > > > Suggested-by: Benjamin Marzinski <bmarzins@suse.com>
> > > > Signed-off-by: Martin Wilck <mwilck@suse.com>
> > > > ---
> > > >  multipathd/main.c | 12 +++++++-----
> > > >  1 file changed, 7 insertions(+), 5 deletions(-)
> > > > 
> > > > diff --git a/multipathd/main.c b/multipathd/main.c
> > > > index c03546b..7b522e8 100644
> > > > --- a/multipathd/main.c
> > > > +++ b/multipathd/main.c
> > > > @@ -771,8 +771,8 @@ fail:
> > > >  
> > > >  static int add_map_without_path (struct vectors *vecs, const
> > > > char
> > > > *alias)
> > > >  {
> > > > -	struct multipath
> > > > __attribute__((cleanup(cleanup_multipath_and_paths)))
> > > > -		*mpp = alloc_multipath();
> > > > +	struct multipath
> > > > __attribute__((cleanup(cleanup_multipath))) *mpp =
> > > > +		alloc_multipath();
> > > >  	char __attribute__((cleanup(cleanup_charp))) *params =
> > > > NULL;
> > > >  	char __attribute__((cleanup(cleanup_charp))) *status =
> > > > NULL;
> > > >  	struct config *conf;
> > > > @@ -802,11 +802,13 @@ static int add_map_without_path (struct
> > > > vectors *vecs, const char *alias)
> > > >  	mpp->mpe = find_mpe(conf->mptable, mpp->wwid);
> > > >  	put_multipath_config(conf);
> > > >  
> > > > -	if ((rc = update_multipath_table__(mpp, vecs->pathvec,
> > > > 0,
> > > > params, status)) != DMP_OK)
> > > > +	if (update_multipath_table__(mpp, vecs->pathvec, 0,
> > > > params, status) != DMP_OK ||
> > > > +	    !vector_alloc_slot(vecs->mpvec)) {
> > > > +		orphan_paths(vecs->pathvec, mpp, "failed to
> > > > add
> > > > map");
> > > >  		return DMP_ERR;
> > > 
> > > I'm not sure we should just call orphan_paths() after we call
> > > update_multipath_table__(). If we hit a cancellation point in the
> > > middle
> > > of update_multipath_table__(), we want to fully orphan any paths
> > > that
> > > got added to the device before we cancelled.  
> > 
> > Sorry, I don't understand.
> > 
> > The __attribute__((cleanup())) functions don't protect against
> > thread
> > cancellation. If we want to achieve what you describe, we must add
> > a
> > pthread_cleanup_push()/pop() pair around the
> > update_multipath_table__()
> > call.
> 
> I'm pretty sure they do run during cancellation after I added
> ca957f2a
> ("multipath-tools: Makefile.inc: compile with -fexceptions").

Right, I forgot about that, sorry. Re-reading the docs, I'd like
to double-check if this is really guaranteed.

I think we should get rid of cancellation for our long-running threads.
Later ;-)

> > But would that buy us anything? update_multipath_table__() only
> > manipulates multipathd's internal data structures, which, if a
> > cancellation happens, will almost immediately all be freed anyway.
> > What
> > harm would be done if these paths weren't orphaned in such a case?
> > Yes,
> > the some paths might now link to a non-existing struct multipath.
> > I'd
> > see your point if we accessed pp->mpp in the path-freeing code
> > path,
> > but we don't do this any more.
> 
> We check whether pp->mpp is set in lots of places in the code to
> decide
> what to do with the paths. But you are correct that we don't ever
> call
> add_map_without_path() from a thread that can be cancelled and have
> multipathd continue running, and we aren't likely to do so.

Yes, this was my thinking. I didn't write it explicitly. The function
is only called from the uevent_dispatch thread, which is cancelled if
and only if multipathd exits.

However, you are right that this isn't clean. What actually need 
here is remove_map(), which does exactly what we want, AFAICS.
But I'll give the struct-multipath-freeing code another review.

Regards
Martin

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

end of thread, other threads:[~2026-01-06 10:31 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-19 14:40 [PATCH v2 00/26] Multipath-tools: various bug fixes Martin Wilck
2025-12-19 14:40 ` [PATCH v2 01/26] libmultipath: drop drop_multipath Martin Wilck
2025-12-19 14:40 ` [PATCH v2 02/26] libmultipath: don't access path members in free_pgvec() Martin Wilck
2025-12-19 14:40 ` [PATCH v2 03/26] libmpathutil: constify find_slot() Martin Wilck
2025-12-19 14:40 ` [PATCH v2 04/26] libmultipath: don't touch mpvec in remove_map() Martin Wilck
2025-12-19 14:40 ` [PATCH v2 05/26] libmultipath: export orphan_paths() Martin Wilck
2025-12-19 14:40 ` [PATCH v2 06/26] libmultipath: export cleanup_multipath() Martin Wilck
2025-12-19 14:40 ` [PATCH v2 07/26] libmultipath: add cleanup_pathvec_and_free_paths() Martin Wilck
2025-12-19 14:40 ` [PATCH v2 08/26] libmultipath: don't free paths in orphan_paths() Martin Wilck
2025-12-19 14:40 ` [PATCH v2 09/26] multipathd: free orphaned paths in checker_finished() Martin Wilck
2025-12-19 14:40 ` [PATCH v2 10/26] libmultipath: remove free_paths argument from free_pathgroup() Martin Wilck
2025-12-19 14:40 ` [PATCH v2 11/26] libmultipath: remove free_paths argument from free_pgvec() Martin Wilck
2025-12-19 14:40 ` [PATCH v2 12/26] libmultipath: remove free_paths argument from free_multipathvec() Martin Wilck
2025-12-19 14:40 ` [PATCH v2 13/26] multipath: free paths through pathvec in check_usable_paths() Martin Wilck
2025-12-20  4:19   ` Benjamin Marzinski
2026-01-05 14:38     ` Martin Wilck
2025-12-19 14:40 ` [PATCH v2 14/26] multipathd: add_map_without_path(): orphan paths instead of freeing them Martin Wilck
2025-12-19 14:46   ` Martin Wilck
2025-12-20  4:23   ` Benjamin Marzinski
2026-01-05 15:15     ` Martin Wilck
2026-01-05 23:17       ` Benjamin Marzinski
2026-01-06 10:31         ` Martin Wilck
2025-12-19 14:40 ` [PATCH v2 15/26] libmultipath: remove free_paths argument from free_multipath() Martin Wilck
2025-12-19 14:40 ` [PATCH v2 16/26] libmultipath: remove cleanup_multipath_and_paths() Martin Wilck
2025-12-20  4:26   ` Benjamin Marzinski
2025-12-19 14:40 ` [PATCH v2 17/26] libmultipaths: annotate functions that may free paths Martin Wilck
2025-12-20  4:35   ` Benjamin Marzinski
2025-12-19 14:41 ` [PATCH v2 18/26] multipath-tools: Fix ISO C23 errors with strchr() Martin Wilck
2025-12-19 14:41 ` [PATCH v2 19/26] libmultipath: simplify sysfs_get_target_nodename() Martin Wilck
2025-12-19 14:41 ` [PATCH v2 20/26] multipathd: join the init_unwinder dummy thread Martin Wilck
2025-12-19 14:41 ` [PATCH v2 21/26] kpartx: fix some memory leaks Martin Wilck
2025-12-19 14:41 ` [PATCH v2 22/26] libmpathutil: use union for bitfield Martin Wilck
2025-12-19 14:41 ` [PATCH v2 23/26] libmpathutil: add wrapper code for libudev Martin Wilck
2025-12-19 14:41 ` [PATCH v2 24/26] multipath-tools: use the libudev wrapper functions Martin Wilck
2025-12-19 14:41 ` [PATCH v2 25/26] Makefile: add functionality to determine cmocka version Martin Wilck
2025-12-19 14:41 ` [PATCH v2 26/26] multipath-tools tests: adaptations for cmocka 2.0 Martin Wilck
2025-12-20  4:41 ` [PATCH v2 00/26] Multipath-tools: various bug fixes Benjamin Marzinski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.