Additional patches for 9.9.0 lvm2

Patches from upstream up to 2.03.41.

Resolves: RHEL-174324
This commit is contained in:
Marian Csontos 2026-06-04 21:06:04 +02:00
parent a49ba1df62
commit 0d41e7e8af
146 changed files with 7209 additions and 3 deletions

View File

@ -15,14 +15,14 @@ index 2d35b7660..4993d445a 100644
+++ b/VERSION
@@ -1 +1 @@
-2.03.33(2) (2025-06-27)
+2.03.33(2)-RHEL9 (2025-09-25)
+2.03.33(2)-RHEL9 (2026-06-04)
diff --git a/VERSION_DM b/VERSION_DM
index 0e8dcd346..c0564e0ea 100644
--- a/VERSION_DM
+++ b/VERSION_DM
@@ -1 +1 @@
-1.02.207 (2025-06-27)
+1.02.207-RHEL9 (2025-09-25)
+1.02.207-RHEL9 (2026-06-04)
--
2.51.0

View File

@ -0,0 +1,61 @@
From 442f1cff0766899544dbdb6a7ae88166ef7016e5 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 21:52:58 +0200
Subject: [PATCH 062/211] cov: pvck: fix TOCTOU race in get_devicefile
Remove stat() check before open() to eliminate TOCTOU race.
Instead, open the file first, then use fstat() on the fd to
verify it's a regular file. Close fd on all error paths.
This ensures we're checking the actual file we opened, not
a path that could have changed between stat() and open().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 2079fc370d05b174f43a501617acba89e86240f4)
---
tools/pvck.c | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/tools/pvck.c b/tools/pvck.c
index 71171092c..8c7bbc92f 100644
--- a/tools/pvck.c
+++ b/tools/pvck.c
@@ -352,21 +352,29 @@ static struct devicefile *get_devicefile(struct cmd_context *cmd, const char *pa
struct stat sb;
struct devicefile *def;
size_t len;
+ int fd;
+
+ if ((fd = open(path, O_RDONLY)) < 0)
+ return_NULL;
- if (stat(path, &sb))
+ if (fstat(fd, &sb) < 0) {
+ (void) close(fd);
return_NULL;
+ }
- if ((sb.st_mode & S_IFMT) != S_IFREG)
+ if ((sb.st_mode & S_IFMT) != S_IFREG) {
+ (void) close(fd);
return_NULL;
+ }
len = strlen(path) + 1;
- if (!(def = dm_pool_alloc(cmd->mem, sizeof(struct devicefile) + len)))
+ if (!(def = dm_pool_alloc(cmd->mem, sizeof(struct devicefile) + len))) {
+ (void) close(fd);
return_NULL;
+ }
memcpy(def->path, path, len);
-
- if ((def->fd = open(path, O_RDONLY)) < 0)
- return_NULL;
+ def->fd = fd;
return def;
}
--
2.54.0

View File

@ -0,0 +1,129 @@
From 0c5fb9c14a75042620ddd43de513f1a1119e60be Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 21:37:54 +0200
Subject: [PATCH 063/211] cov: libdm: fix TOCTOU race in
_sysfs_find_kernel_name
Remove stat() check before opendir() to eliminate TOCTOU race.
Instead, call opendir() directly and check errno:
- ENOTDIR: silently skip (path is not a directory)
- Other errors: log with log_sys_debug()
This simplifies the code and eliminates the race condition.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit f2f410e28db30a37e70fb7b2920ea1bd95d96d15)
---
libdm/libdm-common.c | 80 +++++++++++++++++++++-----------------------
1 file changed, 38 insertions(+), 42 deletions(-)
diff --git a/libdm/libdm-common.c b/libdm/libdm-common.c
index e4fdbbf05..21bdd826e 100644
--- a/libdm/libdm-common.c
+++ b/libdm/libdm-common.c
@@ -1906,7 +1906,6 @@ static int _sysfs_find_kernel_name(uint32_t major, uint32_t minor, char *buf, si
char path[PATH_MAX];
struct dirent *dirent, *dirent_dev;
DIR *d, *d_dev;
- struct stat st;
int r = 0, sz;
if (!*_sysfs_dir ||
@@ -1938,56 +1937,53 @@ static int _sysfs_find_kernel_name(uint32_t major, uint32_t minor, char *buf, si
}
path[sz - 4] = 0; /* strip /dev from end of path string */
- if (stat(path, &st))
+
+ /* let's assume there is no tree-complex device in past systems */
+ if (!(d_dev = opendir(path))) {
+ /* Silently skip non-directories, log other errors */
+ if (errno != ENOTDIR)
+ log_sys_debug("opendir", path);
continue;
+ }
- if (S_ISDIR(st.st_mode)) {
+ while ((dirent_dev = readdir(d_dev))) {
+ name_dev = dirent_dev->d_name;
+
+ /* skip known ignorable paths */
+ if (!strcmp(name_dev, ".") || !strcmp(name_dev, "..") ||
+ !strcmp(name_dev, "bdi") ||
+ !strcmp(name_dev, "dev") ||
+ !strcmp(name_dev, "device") ||
+ !strcmp(name_dev, "holders") ||
+ !strcmp(name_dev, "integrity") ||
+ !strcmp(name_dev, "loop") ||
+ !strcmp(name_dev, "queue") ||
+ !strcmp(name_dev, "md") ||
+ !strcmp(name_dev, "mq") ||
+ !strcmp(name_dev, "power") ||
+ !strcmp(name_dev, "removable") ||
+ !strcmp(name_dev, "slave") ||
+ !strcmp(name_dev, "slaves") ||
+ !strcmp(name_dev, "subsystem") ||
+ !strcmp(name_dev, "trace") ||
+ !strcmp(name_dev, "uevent"))
+ continue;
- /* let's assume there is no tree-complex device in past systems */
- if (!(d_dev = opendir(path))) {
- log_sys_debug("opendir", path);
+ if (dm_snprintf(path, sizeof(path), "%sblock/%s/%s/dev",
+ _sysfs_dir, name, name_dev) == -1) {
+ log_warn("Couldn't create path for %s/%s.", name, name_dev);
continue;
}
- while ((dirent_dev = readdir(d_dev))) {
- name_dev = dirent_dev->d_name;
-
- /* skip known ignorable paths */
- if (!strcmp(name_dev, ".") || !strcmp(name_dev, "..") ||
- !strcmp(name_dev, "bdi") ||
- !strcmp(name_dev, "dev") ||
- !strcmp(name_dev, "device") ||
- !strcmp(name_dev, "holders") ||
- !strcmp(name_dev, "integrity") ||
- !strcmp(name_dev, "loop") ||
- !strcmp(name_dev, "queue") ||
- !strcmp(name_dev, "md") ||
- !strcmp(name_dev, "mq") ||
- !strcmp(name_dev, "power") ||
- !strcmp(name_dev, "removable") ||
- !strcmp(name_dev, "slave") ||
- !strcmp(name_dev, "slaves") ||
- !strcmp(name_dev, "subsystem") ||
- !strcmp(name_dev, "trace") ||
- !strcmp(name_dev, "uevent"))
- continue;
-
- if (dm_snprintf(path, sizeof(path), "%sblock/%s/%s/dev",
- _sysfs_dir, name, name_dev) == -1) {
- log_warn("Couldn't create path for %s/%s.", name, name_dev);
- continue;
- }
-
- if (_sysfs_get_dev_major_minor(path, major, minor)) {
- r = dm_strncpy(buf, name_dev, buf_size);
- break; /* found */
- }
+ if (_sysfs_get_dev_major_minor(path, major, minor)) {
+ r = dm_strncpy(buf, name_dev, buf_size);
+ break; /* found */
}
+ }
- if (closedir(d_dev))
- log_sys_debug("closedir", name);
+ if (closedir(d_dev))
+ log_sys_debug("closedir", name);
}
- }
if (closedir(d))
log_sys_debug("closedir", path);
--
2.54.0

View File

@ -0,0 +1,56 @@
From 8483c7d96d831c06968ef585533ba06da8c36a05 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 22:01:14 +0200
Subject: [PATCH 064/211] cov: dev-mpath: fix TOCTOU race in holders directory
check
Remove stat() check before opendir() to eliminate TOCTOU race.
Call opendir() directly and check errno:
- ENOENT: silently skip (normal for partitions without holders)
- ENOTDIR: warn (path exists but is not a directory)
- Other errors: log debug message
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 44b9934d92c2e1ef723d58753ee1633b528582d1)
---
lib/device/dev-mpath.c | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)
diff --git a/lib/device/dev-mpath.c b/lib/device/dev-mpath.c
index 7e99e17d0..6e71ab080 100644
--- a/lib/device/dev-mpath.c
+++ b/lib/device/dev-mpath.c
@@ -459,21 +459,19 @@ static int _dev_is_mpath_component_sysfs(struct cmd_context *cmd, struct device
return 0;
}
- /* also will filter out partitions */
- if (stat(holders_path, &info))
- return 0;
-
- if (!S_ISDIR(info.st_mode)) {
- log_warn("Path %s is not a directory.", holders_path);
- return 0;
- }
-
/*
* If any holder is a dm mpath device, then return 1;
+ * This also filters out partitions (no holders directory).
*/
-
if (!(dr = opendir(holders_path))) {
- log_debug("Device %s has no holders dir", dev_name(dev));
+ /* Distinguish between non-existent (partitions) and errors */
+ if (errno == ENOENT)
+ return 0; /* Normal for partitions */
+ if (errno == ENOTDIR) {
+ log_warn("Path %s is not a directory.", holders_path);
+ return 0;
+ }
+ log_debug("Device %s has no holders dir.", dev_name(dev));
return 0;
}
--
2.54.0

View File

@ -0,0 +1,52 @@
From 7b9f2be292c3b6c6fd4985e5b56cd61bc44b1608 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 22:13:18 +0200
Subject: [PATCH 065/211] cov: dev-cache: fix TOCTOU race in
devices_file_rename_unused
Remove stat() check before rename() to eliminate TOCTOU race.
Instead, call rename() directly and silently ignore ENOENT
(file doesn't exist). Other errors still call stack.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 142aa7b6ceec37b72b6d39b3c1356909b4cf4bff)
---
lib/device/dev-cache.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/lib/device/dev-cache.c b/lib/device/dev-cache.c
index 15cbc1d84..24a4d91c5 100644
--- a/lib/device/dev-cache.c
+++ b/lib/device/dev-cache.c
@@ -2019,7 +2019,6 @@ static void devices_file_rename_unused(struct cmd_context *cmd)
const char *filename;
time_t t;
struct tm *tm;
- struct stat st;
filename = find_config_tree_str(cmd, devices_devicesfile_CFG, NULL);
@@ -2029,9 +2028,6 @@ static void devices_file_rename_unused(struct cmd_context *cmd)
if (dm_snprintf(path, sizeof(path), "%s/devices/%s", cmd->system_dir, filename) < 0)
return;
- if (stat(path, &st))
- return;
-
t = time(NULL);
if (!(tm = localtime(&t)))
return;
@@ -2043,7 +2039,9 @@ static void devices_file_rename_unused(struct cmd_context *cmd)
return;
if (rename(path, path2) < 0) {
- stack;
+ /* Silently ignore if file doesn't exist */
+ if (errno != ENOENT)
+ stack;
return;
}
log_debug("Devices file moved to %s", path2);
--
2.54.0

View File

@ -0,0 +1,32 @@
From 304e2acd7b40c9ebbebca1af474f5f7ba6b8a73e Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 12:01:35 +0200
Subject: [PATCH 066/211] libdm: dbg_malloc: fix buffer overflow in
dm_realloc_aux
memcpy used the old allocation size (mb->length) unconditionally.
When shrinking (new size < old size), this overflows the new buffer.
Copy the minimum of old and new sizes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 767157754b694035d051093861cad5b9ac3494e7)
---
libdm/mm/dbg_malloc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libdm/mm/dbg_malloc.c b/libdm/mm/dbg_malloc.c
index 96d2311d5..c6a9ae3ed 100644
--- a/libdm/mm/dbg_malloc.c
+++ b/libdm/mm/dbg_malloc.c
@@ -212,7 +212,7 @@ void *dm_realloc_aux(void *p, unsigned int s, const char *file, int line)
r = dm_malloc_aux_debug(s, file, line);
if (r && p) {
- memcpy(r, p, mb->length);
+ memcpy(r, p, (s < mb->length) ? s : mb->length);
dm_free_aux(p);
}
--
2.54.0

View File

@ -0,0 +1,33 @@
From 724efc360fb2f54611bbb56adfed56e0699de880 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 12:38:57 +0200
Subject: [PATCH 067/211] libdevmapper-event: fix read() buffer overflow in
_daemon_read
read() was called with full 'size' instead of remaining 'size - bytes',
so after a partial read, it could write past the end of the buffer.
The server-side counterpart in dmeventd.c correctly uses 'size - bytes'.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 17ae34da124c96922b266ab7accdbcfdc11791b4)
---
daemons/dmeventd/libdevmapper-event.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/daemons/dmeventd/libdevmapper-event.c b/daemons/dmeventd/libdevmapper-event.c
index 485f04605..36f119261 100644
--- a/daemons/dmeventd/libdevmapper-event.c
+++ b/daemons/dmeventd/libdevmapper-event.c
@@ -249,7 +249,7 @@ static int _daemon_read(struct dm_event_fifos *fifos,
goto bad;
}
- ret = read(fifos->server, buf + bytes, size);
+ ret = read(fifos->server, buf + bytes, size - bytes);
if (ret < 0) {
if ((errno == EINTR) || (errno == EAGAIN))
continue;
--
2.54.0

View File

@ -0,0 +1,31 @@
From b897d04990f31f299aab883d5055f9ca351dfe6a Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 13 Mar 2026 16:00:40 +0100
Subject: [PATCH 068/211] dmsetup: fix dangling pointer in _slurp_stdin after
realloc
After realloc() the buffer may move to a new address, but pos
was not updated, causing read() to write into freed memory.
Triggers EFAULT when stdin input exceeds ~128KB.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 130ddc48ea2a084852e78c232990ed8d58aaeca9)
---
libdm/dm-tools/dmsetup.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/libdm/dm-tools/dmsetup.c b/libdm/dm-tools/dmsetup.c
index 2d6ed0a71..eb77ebce4 100644
--- a/libdm/dm-tools/dmsetup.c
+++ b/libdm/dm-tools/dmsetup.c
@@ -1263,6 +1263,7 @@ static char *_slurp_stdin(void)
return NULL;
}
buf = newbuf;
+ pos = buf + total;
}
} while (1);
--
2.54.0

View File

@ -0,0 +1,31 @@
From 81b04fb6706d19fe9cc85cf7c7fbbe1e4a714b82 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 10:05:12 +0200
Subject: [PATCH 069/211] export: fix out-of-bounds access in _sectors_to_units
The loop could increment i to DM_ARRAY_SIZE(_units) before
exiting, causing out-of-bounds access on _units[i].
However issue cannot be hit, since lvm2 supports only 8EiB.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 4f1a29639abaa3ee7fd095116627b65c83e369ab)
---
lib/format_text/export.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/format_text/export.c b/lib/format_text/export.c
index ccc3167ca..5c120f145 100644
--- a/lib/format_text/export.c
+++ b/lib/format_text/export.c
@@ -240,7 +240,7 @@ static int _sectors_to_units(uint64_t sectors, char *buffer, size_t s)
/* to convert to K */
d /= 2.0;
- for (i = 0; (d > 1024.0) && i < DM_ARRAY_SIZE(_units); ++i)
+ for (i = 0; (d > 1024.0) && i < DM_ARRAY_SIZE(_units) - 1; ++i)
d /= 1024.0;
return dm_snprintf(buffer, s, "# %g %s", d, _units[i]) > 0;
--
2.54.0

View File

@ -0,0 +1,34 @@
From b55144da93ab7a0dc8ba750afade39f4144f3f12 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 13:40:49 +0200
Subject: [PATCH 070/211] id: fix out-of-bounds read in _id_valid with corrupt
metadata
id->uuid is int8_t[], so a corrupt on-disk byte >= 0x80 becomes
negative and indexes before the _inverse_c[256] array.
Cast to unsigned char to keep the index within bounds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 7966fa3369dcd5671deb732615dfe0fe5633477c)
---
lib/id/id.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lib/id/id.c b/lib/id/id.c
index b52121cd9..5c386ce0f 100644
--- a/lib/id/id.c
+++ b/lib/id/id.c
@@ -96,7 +96,9 @@ static int _id_valid(struct id *id, int e)
_build_inverse();
for (i = 0; i < ID_LEN; i++)
- if (!_inverse_c[id->uuid[i]]) {
+ /* Cast to unsigned char: int8_t uuid with corrupt byte >= 0x80
+ * would be negative and index before _inverse_c[] array. */
+ if (!_inverse_c[(unsigned char)id->uuid[i]]) {
if (e)
log_error("UUID contains invalid character '%c'", id->uuid[i]);
return 0;
--
2.54.0

View File

@ -0,0 +1,38 @@
From b95b1d3fb1a0eb132dbef466305bd33f78186a19 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Tue, 24 Mar 2026 21:31:58 +0100
Subject: [PATCH 071/211] clang: lvmcache: fix use-after-free in
lvmcache_update_vg_from_read
_drop_vginfo() detaches info from vginfo->infos and then frees
vginfo if the infos list becomes empty. The next line accesses
vginfo->outdated_infos which is use-after-free when the last PV
info was just removed.
Use _vginfo_detach_info() directly since we only need to unlink
info from vginfo->infos without the conditional vginfo cleanup.
Found by clang scan-build.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 374ec4c76ed7ad596cf22f70fc8f447854c17e82)
---
lib/cache/lvmcache.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index 86d252e45..da9a55d09 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -2318,7 +2318,7 @@ void lvmcache_update_vg_from_read(struct volume_group *vg, int *incorrect_pv_cla
if (!_outdated_warning++)
log_warn("See vgck --updatemetadata to clear outdated metadata.");
- _drop_vginfo(info, vginfo); /* remove from vginfo->infos */
+ _vginfo_detach_info(info); /* remove from vginfo->infos */
dm_list_add(&vginfo->outdated_infos, &info->list);
}
--
2.54.0

View File

@ -0,0 +1,40 @@
From fae11decdb12d9da660748bb181063dde5200d1f Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Tue, 24 Mar 2026 21:31:49 +0100
Subject: [PATCH 072/211] clang: lvmlockd: fix use-after-free in res_process
add_client_result() may call free_action(act) when LD_AF_NO_CLIENT
flag is set. The subsequent access of act->op to check for
LD_OP_DISABLE is then a use-after-free. Save the op value before
passing act to add_client_result().
Found by clang scan-build.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 3095302e97b9c48f2df4296a449e5ee76580f103)
---
daemons/lvmlockd/lvmlockd-core.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/daemons/lvmlockd/lvmlockd-core.c b/daemons/lvmlockd/lvmlockd-core.c
index f7340ff3e..b3960feb7 100644
--- a/daemons/lvmlockd/lvmlockd-core.c
+++ b/daemons/lvmlockd/lvmlockd-core.c
@@ -1989,12 +1989,13 @@ static void res_process(struct lockspace *ls, struct resource *r,
list_for_each_entry_safe(act, safe, &r->actions, list) {
if (act->op == LD_OP_ENABLE || act->op == LD_OP_DISABLE) {
+ int is_disable = (act->op == LD_OP_DISABLE);
rv = res_able(ls, r, act);
act->result = rv;
list_del(&act->list);
add_client_result(act);
- if (!rv && act->op == LD_OP_DISABLE) {
+ if (!rv && is_disable) {
log_debug("%s:%s free disabled", ls->name, r->name);
goto r_free;
}
--
2.54.0

View File

@ -0,0 +1,32 @@
From 58d413836eab837f113dfd741c999719d9e3ca16 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Wed, 1 Apr 2026 20:33:27 +0200
Subject: [PATCH 073/211] cov: device_id: fix NULL dereference in _dev_has_id
Add NULL check for idname parameter before strcmp() call.
The function could crash when called with NULL idname from
device_ids_check_serial() if du->idname was unexpectedly NULL.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 035d0bf5b8e044198f4233e42f3f8f9de011c297)
---
lib/device/device_id.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/lib/device/device_id.c b/lib/device/device_id.c
index 4bb92cb09..6ec8f43ba 100644
--- a/lib/device/device_id.c
+++ b/lib/device/device_id.c
@@ -1199,6 +1199,9 @@ static int _dev_has_id(struct device *dev, uint16_t idtype, const char *idname)
{
struct dev_id *id;
+ if (!idname)
+ return 0;
+
dm_list_iterate_items(id, &dev->ids) {
if (id->idtype != idtype)
continue;
--
2.54.0

View File

@ -0,0 +1,31 @@
From ec768d527e2f33f919d7482ae722cf837433cfd6 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 12:47:30 +0200
Subject: [PATCH 074/211] dmeventd: snapshot: fix NULL target_type passed to
log_error %s
When target_type is NULL, passing it to %s format is undefined behavior.
Use ??? as fallback to still report actual target_type when available.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 057434a6dc5def17379725b8b9c25453b286f250)
---
daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c b/daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c
index e47747c1a..40d197297 100644
--- a/daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c
+++ b/daemons/dmeventd/plugins/snapshot/dmeventd_snapshot.c
@@ -183,7 +183,7 @@ void process_event(struct dm_task *dmt,
dm_get_next_target(dmt, next, &start, &length, &target_type, &params);
if (!target_type || strcmp(target_type, "snapshot")) {
- log_error("Target %s is not snapshot.", target_type);
+ log_error("Target %s is not snapshot.", target_type ?: "???");
return;
}
--
2.54.0

View File

@ -0,0 +1,31 @@
From 58160a59cafb671695d4c1149b504640d7953ed9 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 12:23:53 +0200
Subject: [PATCH 075/211] libdaemon: config-util: fix NULL dereference in error
message
When fmt (from strchr) is NULL, the error message was printing
fmt instead of next, passing NULL to printf %s.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 7458d4755e724ec49dcf9f0e36566f1e80d49f48)
---
libdaemon/client/config-util.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libdaemon/client/config-util.c b/libdaemon/client/config-util.c
index b0813f176..ceefc06a4 100644
--- a/libdaemon/client/config-util.c
+++ b/libdaemon/client/config-util.c
@@ -246,7 +246,7 @@ struct dm_config_node *config_make_nodes_v(struct dm_config_tree *cft,
fmt = strchr(next, '=');
if (!fmt) {
- log_error(INTERNAL_ERROR "Bad format string '%s'", fmt);
+ log_error(INTERNAL_ERROR "Bad format string '%s'.", next);
return NULL;
}
--
2.54.0

View File

@ -0,0 +1,37 @@
From 18a1e4c8f486eacde276de49a75241578865c9e8 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Tue, 24 Mar 2026 21:38:48 +0100
Subject: [PATCH 076/211] clang: metadata: add NULL check for pva in
_alloc_parallel_area
The allocator's gap-filling logic can leave alloc_state areas
with NULL pva in certain edge cases. Add a defensive NULL check
before dereferencing pva->map->pv to avoid a potential NULL
pointer dereference.
Found by clang scan-build.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 52319437d50614f782640eb43a29d56d5af98eda)
---
lib/metadata/lv_manip.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index 7296f5e66..201836d73 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -2151,6 +2151,10 @@ static int _alloc_parallel_area(struct alloc_handle *ah, uint32_t max_to_allocat
}
pva = alloc_state->areas[s + ix_log_skip].pva;
+ if (!pva) {
+ log_error("Missing PV area for parallel allocation.");
+ return 0;
+ }
if (ah->alloc_and_split_meta && !ah->split_metadata_is_allocated) {
/*
* The metadata area goes at the front of the allocated
--
2.54.0

View File

@ -0,0 +1,49 @@
From f066ceec5967f7ac1000b707cee68adc4c7abb44 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Tue, 24 Mar 2026 21:32:21 +0100
Subject: [PATCH 077/211] clang: libdm: fix dangling parent pointer to stack
variable in config flatten
_override_path() uses a stack-local dummy node as the root anchor
for _find_or_make_node(). When _make_node() creates a new top-level
node, it sets node->parent = &dummy. After _override_path() returns,
the dummy goes out of scope leaving a dangling parent pointer in
the config tree returned by dm_config_flatten().
Clear the parent pointer for any top-level nodes that still
reference the stack variable.
Found by clang scan-build.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 07572f306384bbc5604c0035f6297e7cd99e7b31)
---
libdm/libdm-config.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/libdm/libdm-config.c b/libdm/libdm-config.c
index 0e8baea73..25cfa268c 100644
--- a/libdm/libdm-config.c
+++ b/libdm/libdm-config.c
@@ -1510,13 +1510,17 @@ struct dm_pool *dm_config_memory(struct dm_config_tree *cft)
static int _override_path(const char *path, struct dm_config_node *node, void *baton)
{
struct dm_config_tree *cft = baton;
- struct dm_config_node dummy, *target;
+ struct dm_config_node dummy, *target, *cn;
dummy.child = cft->root;
if (!(target = _find_or_make_node(cft->mem, &dummy, path, 0)))
return_0;
if (!(target->v = _clone_config_value(cft->mem, node->v)))
return_0;
cft->root = dummy.child;
+ /* Clear dangling parent pointers to stack variable */
+ for (cn = cft->root; cn; cn = cn->sib)
+ if (cn->parent == &dummy)
+ cn->parent = NULL;
return 1;
}
--
2.54.0

View File

@ -0,0 +1,73 @@
From ce1d80012d4237ea181747b78fe125164a889f3d Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Thu, 2 Apr 2026 00:03:37 +0200
Subject: [PATCH 078/211] parse_vpd: fix VPD descriptor bounds checking
parse_vpd_ids had broken bounds checks in cases 0x1 (T10) and 0x8
(SCSI name string) that compared cur_id_size against the output buffer
size (id_len = 1024) instead of the VPD input buffer. Since cur_id_size
is uint8_t (max 255 per VPD spec byte 3), those checks were always
false (dead code), and the overflow assignments were flagged by Coverity.
The real risk is reading past vpd_datalen with malformed VPD data.
Fix by:
- Tightening loop condition to d + 4 <= vpd_end so the 4-byte
descriptor header is always readable
- Clamping cur_id_size to available VPD bytes (vpd_end - d - 4)
instead of output buffer size
- Removing unused id_len variable
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 37ec8951e8881e906aa0b9dcd688bb71fe0d178a)
---
lib/device/parse_vpd.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/lib/device/parse_vpd.c b/lib/device/parse_vpd.c
index 16a653a14..b67e36763 100644
--- a/lib/device/parse_vpd.c
+++ b/lib/device/parse_vpd.c
@@ -153,14 +153,14 @@ int parse_vpd_ids(const unsigned char *vpd_data, int vpd_datalen, struct dm_list
char id[ID_BUFSIZE];
unsigned char tmp_str[ID_BUFSIZE];
const unsigned char *d, *cur_id_str;
- size_t id_len = ID_BUFSIZE;
+ const unsigned char *vpd_end = vpd_data + vpd_datalen;
int id_size = -1;
int type;
uint8_t cur_id_size = 0;
memset(id, 0, ID_BUFSIZE);
for (d = vpd_data + 4;
- d < vpd_data + vpd_datalen;
+ d + 4 <= vpd_end;
d += d[3] + 4) {
memset(tmp_str, 0, sizeof(tmp_str));
@@ -168,8 +168,8 @@ int parse_vpd_ids(const unsigned char *vpd_data, int vpd_datalen, struct dm_list
case 0x1:
/* T10 Vendor ID */
cur_id_size = d[3];
- if ((size_t)(cur_id_size + 4) > id_len)
- cur_id_size = id_len - 4;
+ if (d + 4 + cur_id_size > vpd_end)
+ cur_id_size = vpd_end - d - 4;
cur_id_str = d + 4;
format_t10_id(cur_id_str, cur_id_size, tmp_str, sizeof(tmp_str));
id_size = snprintf(id, ID_BUFSIZE, "t10.%s", tmp_str);
@@ -230,9 +230,9 @@ int parse_vpd_ids(const unsigned char *vpd_data, int vpd_datalen, struct dm_list
case 0x8:
/* SCSI name string */
cur_id_size = d[3];
+ if (d + 4 + cur_id_size > vpd_end)
+ cur_id_size = vpd_end - d - 4;
cur_id_str = d + 4;
- if (cur_id_size >= id_len)
- cur_id_size = id_len - 1;
memcpy(id, cur_id_str, cur_id_size);
id_size = cur_id_size;
--
2.54.0

View File

@ -0,0 +1,35 @@
From 4341180f04be061e991fa40f215f000069c54243 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Wed, 1 Apr 2026 22:33:49 +0200
Subject: [PATCH 079/211] cov: dev-ext: validate src bounds in dev_ext_enable
Check that src is within _ext_registry array bounds before
using it as index. Prevents out of bounds access if src has
an unexpected value.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 9cbc486bdc838612c7e3381bb8c198bede81ca77)
---
lib/device/dev-ext.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/lib/device/dev-ext.c b/lib/device/dev-ext.c
index 8f0f519b5..8521b6e0b 100644
--- a/lib/device/dev-ext.c
+++ b/lib/device/dev-ext.c
@@ -140,6 +140,12 @@ int dev_ext_release(struct device *dev)
int dev_ext_enable(struct device *dev, dev_ext_t src)
{
+ if (src >= DEV_EXT_NUM) {
+ log_error(INTERNAL_ERROR "%s: Invalid external source [%d].",
+ dev_name(dev), src);
+ return 0;
+ }
+
if (dev->ext.enabled && (dev->ext.src != src) && !dev_ext_release(dev)) {
log_error("%s: Failed to enable external handle [%s].",
dev_name(dev), _ext_registry[src].name);
--
2.54.0

View File

@ -0,0 +1,35 @@
From 5a9623ddea7094bb7fd3dc1124a495be9762bf67 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Wed, 1 Apr 2026 20:56:58 +0200
Subject: [PATCH 080/211] cov: config: check for size overflow in
config_file_read_fd
Add overflow check for size + size2 before buffer allocation.
On 32-bit systems, the size_t addition could wrap, allocating
a small buffer followed by a large read into it.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit f9dfc2223ccf94e3e9589d8249f48a658a214339)
---
lib/config/config.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/lib/config/config.c b/lib/config/config.c
index 53d6fc9a0..9cc010e46 100644
--- a/lib/config/config.c
+++ b/lib/config/config.c
@@ -513,6 +513,11 @@ int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, dev_io_r
/* Ensure there is extra '\0' after end of buffer since we pass
* buffer to functions like strtoll() */
+ if (size + size2 < size) {
+ log_error("Metadata buffer size overflow.");
+ return 0;
+ }
+
if (!(buf = zalloc(size + size2 + 1))) {
log_error("Failed to allocate circular buffer.");
return 0;
--
2.54.0

View File

@ -0,0 +1,62 @@
From 9eb047aa1742031f5999a8a1d22720dcd16dd432 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Wed, 1 Apr 2026 20:33:32 +0200
Subject: [PATCH 081/211] cov: format_text: validate rlocn bounds before
uint32_t cast
Validate rlocn offset and size fields against mda bounds before
casting to uint32_t. The on-disk rlocn fields are uint64_t but
wrap and size parameters are uint32_t, so crafted metadata with
values exceeding UINT32_MAX would silently truncate, leading to
incorrect buffer sizes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit c42c285e1b7c062e9db736e8b8ad3646b477661a)
---
lib/format_text/format-text.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/lib/format_text/format-text.c b/lib/format_text/format-text.c
index 615b92df9..3165312d7 100644
--- a/lib/format_text/format-text.c
+++ b/lib/format_text/format-text.c
@@ -416,6 +416,17 @@ static struct volume_group *_vg_read_raw_area(struct cmd_context *cmd,
goto out;
}
+ /* Validate rlocn fields fit within mda bounds before uint32_t cast */
+ if (rlocn->offset >= mdah->size ||
+ rlocn->size > mdah->size - MDA_HEADER_SIZE) {
+ log_error("Metadata location out of bounds (offset %llu size %llu mda %llu) on %s.",
+ (unsigned long long)rlocn->offset,
+ (unsigned long long)rlocn->size,
+ (unsigned long long)mdah->size,
+ dev_name(area->dev));
+ goto out;
+ }
+
if (rlocn->offset + rlocn->size > mdah->size)
wrap = (uint32_t) ((rlocn->offset + rlocn->size) - mdah->size);
@@ -1501,6 +1512,18 @@ int read_metadata_location_summary(const struct format_type *fmt,
* at the beginning. The end of this wrapped metadata is located at an
* offset of wrap+MDA_HEADER_SIZE from area.start.
*/
+
+ /* Validate rlocn fields fit within mda bounds before uint32_t cast */
+ if (rlocn->offset >= mdah->size ||
+ rlocn->size > mdah->size - MDA_HEADER_SIZE) {
+ log_warn("WARNING: Metadata location out of bounds (offset %llu size %llu mda %llu) on %s.",
+ (unsigned long long)rlocn->offset,
+ (unsigned long long)rlocn->size,
+ (unsigned long long)mdah->size,
+ dev_name(dev_area->dev));
+ return 0;
+ }
+
if (rlocn->offset + rlocn->size > mdah->size)
wrap = (uint32_t) ((rlocn->offset + rlocn->size) - mdah->size);
--
2.54.0

View File

@ -0,0 +1,30 @@
From aea719b00789152a2213f4791ac698836b2bd439 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 13:14:40 +0200
Subject: [PATCH 082/211] label: clear DEV_IN_BCACHE on bcache_set_fd failure
When bcache_set_fd() fails, DEV_IN_BCACHE flag was left set while
bcache_fd was reset to -1. This causes _in_bcache() to return true,
making label_scan_open() skip the actual open.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit b69f9cc41a1f5b806cf1fc088876fa89cc14c0a2)
---
lib/label/label.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/label/label.c b/lib/label/label.c
index 91243494b..12a825c62 100644
--- a/lib/label/label.c
+++ b/lib/label/label.c
@@ -563,6 +563,7 @@ static int _scan_dev_open(struct device *dev)
if (close(fd))
log_sys_debug("close", name);
dev->bcache_fd = -1;
+ dev->flags &= ~DEV_IN_BCACHE;
return 0;
}
--
2.54.0

View File

@ -0,0 +1,31 @@
From c78d380024e6972dac5ea79aa2989cd2276c5c5a Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 21:28:49 +0200
Subject: [PATCH 083/211] lv_manip: fix copy-paste error in thin pool metadata
extend
Assigning poolmetadata_sign to lp_meta.size instead of lp_meta.sign,
overwriting previously set size value.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit eea1ea49fb41352cf4f1fe59784dc08bc1669024)
---
lib/metadata/lv_manip.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index 201836d73..61816bd8c 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -6803,7 +6803,7 @@ int lv_resize(struct cmd_context *cmd, struct logical_volume *lv,
_setup_params_for_extend_metadata(lv_meta, &lp_meta);
if (lp->poolmetadata_size) {
lp_meta.size = lp->poolmetadata_size;
- lp_meta.size = lp->poolmetadata_sign;
+ lp_meta.sign = lp->poolmetadata_sign;
lp->poolmetadata_size = 0;
lp->poolmetadata_sign = SIGN_NONE;
}
--
2.54.0

View File

@ -0,0 +1,36 @@
From d84d1cb3056afa25792e332fa813012760958c62 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Wed, 4 Feb 2026 15:32:27 +0100
Subject: [PATCH 084/211] activate: fix cachevol cmeta/cdata device offsets
Use actual metadata_start and data_start offsets when creating the
cmeta and cdata linear devices for cachevol, instead of assuming
metadata always starts at 0 and data at metadata_len.
This allows for flexible cachevol layouts as specified in metadata.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
(cherry picked from commit cd2f25ee0deb40161a17c7e5f427e237a0bda7bb)
---
lib/activate/dev_manager.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c
index 8caa9f997..db3b24595 100644
--- a/lib/activate/dev_manager.c
+++ b/lib/activate/dev_manager.c
@@ -3459,9 +3459,9 @@ static int _add_new_cvol_subdev_to_dtree(struct dev_manager *dm,
if (!(dlid_pool = build_dm_uuid(dm->mem, pool_lv, NULL)))
return_0;
- /* add seg_area to prev load_seg: offset 0 maps to cachevol lv offset 0 */
+ /* add seg_area to prev load_seg: map to correct offset in cachevol */
if (!dm_tree_node_add_target_area(dnode, NULL, dlid_pool,
- meta_or_data ? 0 : lvseg->metadata_len))
+ meta_or_data ? lvseg->metadata_start : lvseg->data_start))
return_0;
}
--
2.54.0

View File

@ -0,0 +1,36 @@
From 50740fb435f1cab88363bf3dc38a331a68dc8934 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 08:39:38 +0200
Subject: [PATCH 085/211] merge: fix wrong variable in mirror size check
log_error
The error message was printing area index 's' as the segment number.
Use seg_count for the segment number and add 's' for the image index,
matching the format used by the adjacent mirror pointer check message.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit fcae4db4074d6d9c7d059759a2a04596ba06801a)
---
lib/metadata/merge.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lib/metadata/merge.c b/lib/metadata/merge.c
index b2829f9d0..2cb9b4c08 100644
--- a/lib/metadata/merge.c
+++ b/lib/metadata/merge.c
@@ -639,9 +639,10 @@ int check_lv_segments_complete_vg(struct logical_volume *lv)
if (seg_is_mirrored(seg) && !seg_is_raid(seg) &&
seg_type(seg, s) == AREA_LV && seg_lv(seg, s) &&
seg_lv(seg, s)->le_count != seg->area_len) {
- log_error("LV %s: mirrored LV segment %u has "
+ log_error("LV %s: segment %u mirrored image %u has "
"wrong size %u (should be %u).",
- lv->name, s, seg_lv(seg, s)->le_count,
+ lv->name, seg_count, s,
+ seg_lv(seg, s)->le_count,
seg->area_len);
inc_error_count;
}
--
2.54.0

View File

@ -0,0 +1,31 @@
From 7b4579ac06e2debcd889666723f39bd0de800766 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 10:24:46 +0200
Subject: [PATCH 086/211] dev_manager: fix wrong sizeof in raid status
allocation
Copy-paste error: sizeof(struct lv_status_cache) was used instead
of sizeof(struct lv_status_raid) in dev_manager_raid_status.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 591d822bf496133a68bdd2ee98fdb4093199144a)
---
lib/activate/dev_manager.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c
index db3b24595..46c626de5 100644
--- a/lib/activate/dev_manager.c
+++ b/lib/activate/dev_manager.c
@@ -1664,7 +1664,7 @@ int dev_manager_raid_status(struct dev_manager *dm,
struct dm_status_raid *sr;
*exists = -1;
- if (!(*status = dm_pool_zalloc(dm->mem, sizeof(struct lv_status_cache))))
+ if (!(*status = dm_pool_zalloc(dm->mem, sizeof(struct lv_status_raid))))
return_0;
if (!(dlid = build_dm_uuid(dm->mem, lv, layer)))
--
2.54.0

View File

@ -0,0 +1,30 @@
From 3c3f8eac835e42ee6802e7e66bc059b91995ae50 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 14:34:04 +0200
Subject: [PATCH 087/211] cov: thin: fix wrong variable in chunk_size error
message
The error validates seg->chunk_size but printed seg->device_id.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 32d6cb664a38fb66fa361e385fc09812d9459b16)
---
lib/thin/thin.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/thin/thin.c b/lib/thin/thin.c
index c9b40f635..85a90db38 100644
--- a/lib/thin/thin.c
+++ b/lib/thin/thin.c
@@ -123,7 +123,7 @@ static int _thin_pool_text_import(struct lv_segment *seg,
if ((seg->chunk_size < DM_THIN_MIN_DATA_BLOCK_SIZE) ||
(seg->chunk_size > DM_THIN_MAX_DATA_BLOCK_SIZE))
return SEG_LOG_ERROR("Unsupported value %u for chunk_size",
- seg->device_id);
+ seg->chunk_size);
if (dm_config_has_node(sn, "zero_new_blocks") &&
!dm_config_get_uint32(sn, "zero_new_blocks", &zero))
--
2.54.0

View File

@ -0,0 +1,33 @@
From cc43f53099abafce472746deba9015a9f9db4373 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 08:40:26 +0200
Subject: [PATCH 088/211] vdo_manip: fix missing si.mem_unit in total memory
calculation
_get_sysinfo_memory() was computing total_mb without multiplying by
si.mem_unit, unlike the available_mb calculation on the line above.
When si.mem_unit != 1 (e.g. on 32-bit systems) this gave a wrong
total memory value.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit ff30ca50aca84903c802a44db8e39aa13993aeaf)
---
lib/metadata/vdo_manip.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/metadata/vdo_manip.c b/lib/metadata/vdo_manip.c
index 21084468f..f5d04918b 100644
--- a/lib/metadata/vdo_manip.c
+++ b/lib/metadata/vdo_manip.c
@@ -651,7 +651,7 @@ static int _get_sysinfo_memory(uint64_t *total_mb, uint64_t *available_mb)
(unsigned long long)si.freehigh >> 20, si.mem_unit);
*available_mb = ((uint64_t)(si.freeram + si.bufferram) * si.mem_unit) >> 30;
- *total_mb = si.totalram >> 30;
+ *total_mb = ((uint64_t) si.totalram * si.mem_unit) >> 30;
return 1;
}
--
2.54.0

View File

@ -0,0 +1,45 @@
From 64029cb552f1b65bad1b2385bdbbfd7a63d6082c Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 12:45:03 +0200
Subject: [PATCH 089/211] dmeventd: vdo: fix name overwrite and missing
max_fails init
Fix two bugs in VDO plugin register_device():
1. 'state->name = "volume"' was unconditionally overwritten by
'state->name = name' (where name="pool"). Fix by setting the
local 'name' variable instead of state->name directly.
2. Missing 'state->max_fails = 1' initialization. Since state is
zero-allocated, max_fails starts at 0 and the exponential backoff
logic (which uses left-shift) never advances: 0 << 1 = 0.
This causes failing policy commands to retry on every single event
instead of backing off. Match the thin plugin which correctly
initializes max_fails to 1.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 1d9edbfd6d3a1ad5da0a93765350407717436461)
---
daemons/dmeventd/plugins/vdo/dmeventd_vdo.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/daemons/dmeventd/plugins/vdo/dmeventd_vdo.c b/daemons/dmeventd/plugins/vdo/dmeventd_vdo.c
index 093e71908..536870a16 100644
--- a/daemons/dmeventd/plugins/vdo/dmeventd_vdo.c
+++ b/daemons/dmeventd/plugins/vdo/dmeventd_vdo.c
@@ -353,10 +353,11 @@ int register_device(const char *device,
state->argv[1] = str + 1; /* 1 argument - vg/lv */
_init_thread_signals(state);
} else if (cmd[0] == 0) {
- state->name = "volume"; /* What to use with 'others?' */
+ name = "volume"; /* What to use with 'others?' */
} else/* Unsupported command format */
goto inval;
+ state->max_fails = 1;
state->pid = -1;
state->name = name;
*user = state;
--
2.54.0

View File

@ -0,0 +1,44 @@
From 4eb6ecf8f54b60f12c181a590e0eb32c19a4cd9b Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 12:54:24 +0200
Subject: [PATCH 090/211] lvmlockd-dlm: fix wrong variable in fopen and error
log
1. get_local_nodeid() constructed per-entry path in 'path' but opened
'ls_comms_path' (the parent directory) instead, making local node ID
detection non-functional.
2. read_cluster_name() logged fd (file descriptor) instead of rv
(the read error) on read failure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 4aa202dbe28fd2df9dc235a651cd7001b2904afb)
---
daemons/lvmlockd/lvmlockd-dlm.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/daemons/lvmlockd/lvmlockd-dlm.c b/daemons/lvmlockd/lvmlockd-dlm.c
index 7529ad327..ec3a3b3c5 100644
--- a/daemons/lvmlockd/lvmlockd-dlm.c
+++ b/daemons/lvmlockd/lvmlockd-dlm.c
@@ -113,7 +113,7 @@ static int read_cluster_name(char *clustername)
rv = read(fd, clustername, MAX_ARGS);
if (rv < 0) {
- log_error("read_cluster_name: cluster name read error %d, check dlm_controld", fd);
+ log_error("read_cluster_name: cluster name read error %d, check dlm_controld", rv);
goto out;
}
clustername[rv] = 0;
@@ -247,7 +247,7 @@ static int get_local_nodeid(void)
snprintf(path, sizeof(path), "%s/%s/local",
DLM_COMMS_PATH, de->d_name);
- if (!(file = fopen(ls_comms_path, "r")))
+ if (!(file = fopen(path, "r")))
continue;
str1 = fgets(line, sizeof(line), file);
if (fclose(file))
--
2.54.0

View File

@ -0,0 +1,30 @@
From 22fd8e58f06adfdd212ec3c0d40ade2c274588a5 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 12:55:45 +0200
Subject: [PATCH 091/211] lvmlockd-sanlock: fix wrong variable in error log
Error message printed rv (error code) twice instead of the offset value.
The success path two lines above correctly uses offset.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 975b7886a17004fb5dcf0baf1c35848d1a57cbc8)
---
daemons/lvmlockd/lvmlockd-sanlock.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/daemons/lvmlockd/lvmlockd-sanlock.c b/daemons/lvmlockd/lvmlockd-sanlock.c
index f06af6c60..2a5720b07 100644
--- a/daemons/lvmlockd/lvmlockd-sanlock.c
+++ b/daemons/lvmlockd/lvmlockd-sanlock.c
@@ -1004,7 +1004,7 @@ int lm_init_lv_sanlock(struct lockspace *ls, char *ls_name, char *vg_name, char
lock_args_version, (unsigned long long)offset);
} else {
log_error("S %s init_lv_san write error %d offset %llu",
- ls_name, rv, (unsigned long long)rv);
+ ls_name, rv, (unsigned long long)offset);
}
break;
}
--
2.54.0

View File

@ -0,0 +1,34 @@
From d324d870e7409d2c3a84b26b37c13b60dd331869 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 20:32:03 +0200
Subject: [PATCH 092/211] daemon-client: fix swapped parameters in log_debug
Line 31-32: Fixed swapped parameters in debug message. The format
string suggests "%s: Opening daemon socket to %s" where the first %s
is the daemon name and the second %s is the socket path. The arguments
were swapped (i.socket, i.path) when they should be (i.path, i.socket)
according to the daemon_info struct definition where path is the daemon
binary and socket is the socket path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit a47d90285a1172f37a713ab0969d4c8b87c314e6)
---
libdaemon/client/daemon-client.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libdaemon/client/daemon-client.c b/libdaemon/client/daemon-client.c
index 9ee88fa69..14edee63e 100644
--- a/libdaemon/client/daemon-client.c
+++ b/libdaemon/client/daemon-client.c
@@ -28,7 +28,7 @@ daemon_handle daemon_open(daemon_info i)
struct sockaddr_un sockaddr = { .sun_family = AF_UNIX };
log_debug("%s: Opening daemon socket to %s for protocol %s version %d.",
- i.socket, i.path, i.protocol, i.protocol_version);
+ i.path, i.socket, i.protocol, i.protocol_version);
if ((h.socket_fd = socket(PF_UNIX, SOCK_STREAM /* | SOCK_NONBLOCK */, 0)) < 0) {
h.error = errno;
--
2.54.0

View File

@ -0,0 +1,47 @@
From 8e1bfa318078918b98dd16db253518c620df73e9 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 19:14:42 +0200
Subject: [PATCH 093/211] raid_manip: fix swapped parameters in error message
Fix swapped parameters in error message after match_count decrement.
The execution flow is:
1. Try to allocate match_count devices -> FAILS
2. Decrement match_count--
3. Log error message
4. Retry with reduced count
Before the decrement, match_count had the value that just failed.
After the decrement, match_count has the new reduced value we'll retry.
The message should say:
"Failed to replace X (the old value). Attempting Y (the new value)."
Current code incorrectly prints:
"Failed to replace 5 devices. Attempting to replace 6 instead."
Fixed code correctly prints:
"Failed to replace 6 devices. Attempting to replace 5 instead."
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit cde1ea1ff15a5e1f80f55229f8b4b23f209157ea)
---
lib/metadata/raid_manip.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/metadata/raid_manip.c b/lib/metadata/raid_manip.c
index 2a78a0bec..99c71e967 100644
--- a/lib/metadata/raid_manip.c
+++ b/lib/metadata/raid_manip.c
@@ -6995,7 +6995,7 @@ try_again:
if (match_count > 0) {
log_error("Failed to replace %u devices."
" Attempting to replace %u instead.",
- match_count, match_count+1);
+ match_count+1, match_count);
/*
* Since we are replacing some but not all of the bad
* devices, we must set partial_activation
--
2.54.0

View File

@ -0,0 +1,30 @@
From 1ada320659c8c1879cecae817853b643e31b071a Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 14:24:49 +0200
Subject: [PATCH 094/211] cov: memlock: fix open() return value check
open() returns -1 on error and 0 is a valid file descriptor.
Using !() would incorrectly treat fd 0 as failure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 4c64e5b70ab9803f60ce2110c7f05367f98c3de9)
---
lib/mm/memlock.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/mm/memlock.c b/lib/mm/memlock.c
index 8b45f10ee..0e43bf624 100644
--- a/lib/mm/memlock.c
+++ b/lib/mm/memlock.c
@@ -551,7 +551,7 @@ static void _lock_mem(struct cmd_context *cmd)
return;
}
- if (!(_maps_fd = open(_procselfmaps, O_RDONLY))) {
+ if ((_maps_fd = open(_procselfmaps, O_RDONLY)) < 0) {
log_sys_debug("open", _procselfmaps);
return;
}
--
2.54.0

View File

@ -0,0 +1,39 @@
From 80326bcb7fa8638fd14ce8900a30e59b9c9dc2f4 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 13:32:46 +0200
Subject: [PATCH 095/211] filesystem: fix nofs early-return checking wrong
struct
fs_get_info() checked fsi->nofs (caller's zeroed output parameter)
instead of info.nofs (the local struct populated by fs_get_blkid).
The early-return for no-filesystem case never triggered, causing
unnecessary crypto_LUKS, mount, and resize checks.
Result was still correct because *fsi = info copy at line 342
propagated nofs to the caller, but the early exit was dead code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 3606b9749042fb02117bebb0ba84f4e95ebeef57)
---
lib/device/filesystem.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lib/device/filesystem.c b/lib/device/filesystem.c
index adb8f0174..87f97130d 100644
--- a/lib/device/filesystem.c
+++ b/lib/device/filesystem.c
@@ -284,8 +284,10 @@ int fs_get_info(struct cmd_context *cmd, struct logical_volume *lv, struct fs_in
return 0;
}
- if (fsi->nofs)
+ if (info.nofs) {
+ fsi->nofs = 1;
return 1;
+ }
/*
* If there's a LUKS dm-crypt layer over the LV, then
--
2.54.0

View File

@ -0,0 +1,43 @@
From cb232e39db78f5a11a8aefd6502ffd394c90eed9 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 11:08:44 +0200
Subject: [PATCH 096/211] lvconvert: fix inverted proc_dir check and wrong
sizeof
Fix two bugs:
- The proc_dir emptiness check was inverted, skipping /proc/meminfo
reading when proc_dir was available instead of when it was empty.
- memset used sizeof(cvname) instead of sizeof(format) to clear the
format buffer.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 54ec1fefa043522e4d04152998b3d66a059defe2)
---
tools/lvconvert.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/lvconvert.c b/tools/lvconvert.c
index 270bae7d0..7a17028e5 100644
--- a/tools/lvconvert.c
+++ b/tools/lvconvert.c
@@ -4538,7 +4538,7 @@ static int _lv_create_cachevol(struct cmd_context *cmd,
}
if (find_lv(vg, cvname)) {
- memset(format, 0, sizeof(cvname));
+ memset(format, 0, sizeof(format));
memset(cvname, 0, sizeof(cvname));
if (dm_snprintf(format, sizeof(format), "%s_cache%%d", lv->name) < 0) {
log_error("Failed to generate cachevol LV format.");
@@ -6236,7 +6236,7 @@ static int _check_writecache_memory(struct cmd_context *cmd, struct logical_volu
unsigned long long proc_mem_kb = 0;
char proc_meminfo[PATH_MAX];
- if (*cmd->proc_dir)
+ if (!*cmd->proc_dir)
goto skip_proc;
if (dm_snprintf(proc_meminfo, sizeof(proc_meminfo),
--
2.54.0

View File

@ -0,0 +1,44 @@
From 437fa65fa0438d92c44e9ee647d1cee6cfdf416d Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 12:55:14 +0200
Subject: [PATCH 097/211] lvmlockd-idm: fix stale rv check and missing & in
convert
1. lm_lock_idm() checked stale 'rv' instead of 'rdi->op.mode' after
to_idm_mode(), so invalid lock modes were never caught.
2. lm_convert_idm() passed vb_timestamp value as pointer (missing &),
causing ilm_write_lvb() to read from an arbitrary address.
The correct pattern with & is used in lm_unlock_idm().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 0a04cb3d1f1cf377c1f49453d90381ca25857957)
---
daemons/lvmlockd/lvmlockd-idm.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/daemons/lvmlockd/lvmlockd-idm.c b/daemons/lvmlockd/lvmlockd-idm.c
index bfba4f59b..92c7e56ab 100644
--- a/daemons/lvmlockd/lvmlockd-idm.c
+++ b/daemons/lvmlockd/lvmlockd-idm.c
@@ -547,7 +547,7 @@ int lm_lock_idm(struct lockspace *ls, struct resource *r, int ld_mode,
}
rdi->op.mode = to_idm_mode(ld_mode);
- if (rv < 0) {
+ if (rdi->op.mode < 0) {
log_error("lock_idm invalid mode %d", ld_mode);
return -EINVAL;
}
@@ -725,7 +725,7 @@ int lm_convert_idm(struct lockspace *ls, struct resource *r,
if (rdi->vb && r_version && (r->mode == LD_LK_EX)) {
rv = ilm_write_lvb(lmi->sock, &rdi->id,
- (char *)rdi->vb_timestamp, sizeof(uint64_t));
+ (char *)&rdi->vb_timestamp, sizeof(uint64_t));
if (rv < 0) {
log_error("S %s R %s convert_idm write lvb error %d",
ls->name, r->name, rv);
--
2.54.0

View File

@ -0,0 +1,32 @@
From 6bbf2f55027d9a4db50572c514fb6a37cd988431 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 14:37:02 +0200
Subject: [PATCH 098/211] cov: lv: fix off-by-one in
_sublvs_remove_after_reshape
The backward loop 'for (s = area_count-1; s; s--)' never checked
area index 0. While area 0 is unlikely to be marked for removal
after reshape, use a standard forward loop for clarity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit ba2fef12d6351347e0bd2ab6cef950d47617a727)
---
lib/metadata/lv.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c
index 2ecc4cf4e..c7b213067 100644
--- a/lib/metadata/lv.c
+++ b/lib/metadata/lv.c
@@ -1296,7 +1296,7 @@ static int _sublvs_remove_after_reshape(const struct logical_volume *lv)
uint32_t s;
struct lv_segment *seg = first_seg(lv);
- for (s = seg->area_count -1; s; s--)
+ for (s = 0; s < seg->area_count; s++)
if (seg_lv(seg, s)->status & LV_REMOVE_AFTER_RESHAPE)
return 1;
--
2.54.0

View File

@ -0,0 +1,38 @@
From 7fd9d29a454797512bbd18b6d560813d34a1df25 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 13:17:30 +0200
Subject: [PATCH 099/211] hints: fix off-by-one in orphan vgname check
Hint file lines have format: scan:/dev/sda pvid:xxx devn:8:0 vg:NAME
For orphan PVs (no VG) it writes "vg:-".
The "vg:" prefix is 3 characters, so vgname[3] is the first char of
the actual VG name. The orphan check used vgname[4] instead of
vgname[3], so for "vg:-" it checked '\0' != '-' which is true,
letting the orphan name "-" through into hint.vgname.
Currently masked by a downstream check (hint->vgname[0] == '-')
that independently filters orphan hints.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 3201a6fa88595b2abbec00049ce6826cda7175b5)
---
lib/label/hints.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/label/hints.c b/lib/label/hints.c
index de36da504..43103b440 100644
--- a/lib/label/hints.c
+++ b/lib/label/hints.c
@@ -846,7 +846,7 @@ static int _read_hint_file(struct cmd_context *cmd, struct dm_list *hints, int *
if (devn && sscanf(devn, "devn:%d:%d", &major, &minor) == 2)
hint.devt = makedev(major, minor);
- if (vgname && (strlen(vgname) > 3) && (vgname[4] != '-'))
+ if (vgname && (strlen(vgname) > 3) && (vgname[3] != '-'))
if (!_dm_strncpy(hint.vgname, vgname + 3, sizeof(hint.vgname)))
continue;
--
2.54.0

View File

@ -0,0 +1,47 @@
From d74e3b3f8b3b57cb1c12642fc4a317dd0e075a19 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 12:27:33 +0200
Subject: [PATCH 100/211] lvmpolld: fix comma typo and off-by-one in timeout
check
- lvmpolld-data-utils.c: comma instead of semicolon after
_get_lvid assignment (worked by accident as comma expression)
- lvmpolld-core.c: use > UINT_MAX instead of >= UINT_MAX
in timeout argument parser
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 2869bd746d57929b099d67c1b17cbcaabc3da5f8)
---
daemons/lvmpolld/lvmpolld-core.c | 2 +-
daemons/lvmpolld/lvmpolld-data-utils.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/daemons/lvmpolld/lvmpolld-core.c b/daemons/lvmpolld/lvmpolld-core.c
index 7ab0db8b4..ff47e1fbc 100644
--- a/daemons/lvmpolld/lvmpolld-core.c
+++ b/daemons/lvmpolld/lvmpolld-core.c
@@ -771,7 +771,7 @@ static int process_timeout_arg(const char *str, unsigned *max_timeouts)
errno = 0;
l = strtoul(str, &endptr, 10);
- if (errno || *endptr || l >= UINT_MAX)
+ if (errno || *endptr || l > UINT_MAX)
return 0;
*max_timeouts = (unsigned) l;
diff --git a/daemons/lvmpolld/lvmpolld-data-utils.c b/daemons/lvmpolld/lvmpolld-data-utils.c
index 2520becbe..8a4c6f8b0 100644
--- a/daemons/lvmpolld/lvmpolld-data-utils.c
+++ b/daemons/lvmpolld/lvmpolld-data-utils.c
@@ -124,7 +124,7 @@ struct lvmpolld_lv *pdlv_create(struct lvmpolld_state *ls, const char *id,
if (!pdlv || !tmp.lvmpolld_id || !tmp.lvname || !tmp.lvm_system_dir_env || !tmp.sinterval)
goto err;
- tmp.lvid = _get_lvid(tmp.lvmpolld_id, sysdir),
+ tmp.lvid = _get_lvid(tmp.lvmpolld_id, sysdir);
*pdlv = tmp;
--
2.54.0

View File

@ -0,0 +1,33 @@
From 8c0234432df5df88076b487a249ccc8c067171c1 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Sat, 21 Mar 2026 23:46:44 +0100
Subject: [PATCH 101/211] pvmove_poll: fix off-by-one in mirror image bounds
check
_is_pvmove_image_removable uses > instead of >= to validate
mimage_to_remove against area_count. Since area indices are
0-based, index == area_count is out of bounds and would cause
seg_lv to access past the areas array.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 9dd36a05c11f9fec8f5896a77b626a4a19fac9d0)
---
tools/pvmove_poll.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/pvmove_poll.c b/tools/pvmove_poll.c
index 58687ebdc..65e242ae9 100644
--- a/tools/pvmove_poll.c
+++ b/tools/pvmove_poll.c
@@ -35,7 +35,7 @@ static int _is_pvmove_image_removable(struct logical_volume *mimage_lv,
return 0;
}
- if (mimage_to_remove > mirror_seg->area_count) {
+ if (mimage_to_remove >= mirror_seg->area_count) {
log_error(INTERNAL_ERROR "Mirror image %" PRIu32 " not found in segment",
mimage_to_remove);
return 0;
--
2.54.0

View File

@ -0,0 +1,31 @@
From 142ce4f7bb4e0d2d1f62aa24fdfb27c7d0153ecf Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 11:06:50 +0200
Subject: [PATCH 102/211] pvck: fix copy-paste error checking mda2_size_set
The condition was checking set->mda_size_set instead of
set->mda2_size_set in the mda2 settings block, making
the mda2 size setting ignored.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit b3e913dcecfe9fe2d745546464efb1a88123a217)
---
tools/pvck.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/pvck.c b/tools/pvck.c
index 8c7bbc92f..3be469562 100644
--- a/tools/pvck.c
+++ b/tools/pvck.c
@@ -1631,7 +1631,7 @@ static int _dump_search(struct cmd_context *cmd, const char *dump, struct settin
mda_offset = set->mda2_offset;
set_vals++;
}
- if (set->mda_size_set) {
+ if (set->mda2_size_set) {
mda_size = set->mda2_size;
set_vals++;
}
--
2.54.0

View File

@ -0,0 +1,49 @@
From 8a05f2db20f70a91822dbbdc79169f39d6fc368c Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 13:27:24 +0200
Subject: [PATCH 103/211] online: fix buffer pointer in write loop
The buffer is small enough that partial writes are unlikely, but
use a separate char pointer to correctly advance the write position
since buf is a stack array and cannot use pointer arithmetic.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit bb429948514e9cfd18791afbb934fa04020634c2)
---
lib/device/online.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/lib/device/online.c b/lib/device/online.c
index 302b3d2d7..b5dfa29cb 100644
--- a/lib/device/online.c
+++ b/lib/device/online.c
@@ -247,6 +247,7 @@ int online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const c
{
char path[PATH_MAX];
char buf[MAX_PVID_FILE_SIZE] = { 0 };
+ char *bufp;
char file_vgname[NAME_LEN];
char file_devname[NAME_LEN];
char devname[NAME_LEN];
@@ -302,8 +303,9 @@ int online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const c
return 0;
}
+ bufp = buf;
while (len > 0) {
- rv = write(fd, buf, len);
+ rv = write(fd, bufp, len);
if (rv < 0) {
/* file exists so it still works in part */
log_warn("Cannot write online file for %s to %s error %d",
@@ -312,6 +314,7 @@ int online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const c
log_sys_debug("close", path);
return 1;
}
+ bufp += rv;
len -= rv;
}
--
2.54.0

View File

@ -0,0 +1,28 @@
From e68693f02780259d8d15fa2aeb07c70260263068 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Sat, 21 Mar 2026 18:42:01 +0100
Subject: [PATCH 104/211] libdm: fix cut and past bug
set_read_ahead is using BLKRASET ioctl.
(cherry picked from commit 2bb10cf33ee95f06fa4f10555fec433458c8b83f)
---
libdm/libdm-common.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libdm/libdm-common.c b/libdm/libdm-common.c
index 21bdd826e..3ee5a1070 100644
--- a/libdm/libdm-common.c
+++ b/libdm/libdm-common.c
@@ -1334,7 +1334,7 @@ static int _set_read_ahead(const char *dev_name, uint32_t major, uint32_t minor,
}
if (!*dev_name) {
- log_error("Empty device name passed to BLKRAGET");
+ log_error("Empty device name passed to BLKRASET.");
return 0;
}
--
2.54.0

View File

@ -0,0 +1,31 @@
From fc9e8b150de592d14cc6e9777e099a7d2f71b5c8 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 14:05:59 +0200
Subject: [PATCH 105/211] lvmpolld: fix STDOUT/STDERR copy-paste error in poll
warning
The fds[1] block handles STDERR but the error message said "STDOUT",
copy-pasted from the fds[0] block above.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 56a6ff90b8701564170b79153be1870fc3f90670)
---
daemons/lvmpolld/lvmpolld-core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/daemons/lvmpolld/lvmpolld-core.c b/daemons/lvmpolld/lvmpolld-core.c
index ff47e1fbc..65ac18ab5 100644
--- a/daemons/lvmpolld/lvmpolld-core.c
+++ b/daemons/lvmpolld/lvmpolld-core.c
@@ -294,7 +294,7 @@ static int poll_for_output(struct lvmpolld_lv *pdlv, struct lvmpolld_thread_data
if (fds[1].revents & POLLHUP)
DEBUGLOG(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "caught err POLLHUP");
else
- WARN(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "poll for command's STDOUT failed");
+ WARN(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "poll for command's STDERR failed");
fds[1].fd = -1;
fds_count--;
--
2.54.0

View File

@ -0,0 +1,31 @@
From cad7f79a1ec5523d36cc55332105e28d69903cd4 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 10:29:51 +0200
Subject: [PATCH 106/211] lvmcache: use label_destroy instead of free for label
cleanup
Use label_destroy to properly call the labeller's destroy_label
callback, matching the pattern in _lvmcache_destroy_info.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 50a6700e44bc018cb925818e2d57eeec07937f23)
---
lib/cache/lvmcache.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index da9a55d09..1effc254c 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -2596,7 +2596,7 @@ update_vginfo:
if (created) {
dm_hash_remove(_pvid_hash, pvid);
info->dev->pvid[0] = 0;
- free(info->label);
+ label_destroy(info->label);
free(info);
}
return NULL;
--
2.54.0

View File

@ -0,0 +1,30 @@
From 9ed020b671833bc27368b5de220aaf992991dcb4 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 11:29:14 +0200
Subject: [PATCH 107/211] lvm: null correct field after freeing log_rh
After dm_report_free(log_rh), the code was nulling report_group
(already handled above) instead of log_rh.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 9e13c72a8d8273195b27fe9c54dc643e51a5a383)
---
tools/lvm.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/lvm.c b/tools/lvm.c
index b0bbdd8f2..47c03170a 100644
--- a/tools/lvm.c
+++ b/tools/lvm.c
@@ -363,7 +363,7 @@ report_log:
if (cmd->cmd_report.log_rh) {
dm_report_free(cmd->cmd_report.log_rh);
- cmd->cmd_report.report_group = NULL;
+ cmd->cmd_report.log_rh = NULL;
}
return 0;
--
2.54.0

View File

@ -0,0 +1,33 @@
From 71cf345a9ece2419c8e9625f9bb99d41a11d3c50 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 12:43:38 +0200
Subject: [PATCH 108/211] dmeventd: fix msg.data memory leak in
_restart_dmeventd
msg.data from GET_STATUS was not freed before the second daemon_talk()
for GET_PARAMETERS, which zeroes the struct via memset, leaking the
previous allocation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 9124508d1f692a7bd143e7b28a3ab89b4c8c270f)
---
daemons/dmeventd/dmeventd.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/daemons/dmeventd/dmeventd.c b/daemons/dmeventd/dmeventd.c
index 22a713b9a..0c3880787 100644
--- a/daemons/dmeventd/dmeventd.c
+++ b/daemons/dmeventd/dmeventd.c
@@ -2203,6 +2203,9 @@ static int _restart_dmeventd(struct dm_event_fifos *fifos)
message += strlen(message) + 1;
}
+ free(msg.data);
+ msg.data = NULL;
+
if (version >= 2) {
if (daemon_talk(fifos, &msg, DM_EVENT_CMD_GET_PARAMETERS, "-", "-", 0, 0)) {
fprintf(stderr, "Failed to acquire parameters from old dmeventd.\n");
--
2.54.0

View File

@ -0,0 +1,32 @@
From ddb889f750e64b8f368493a373bf5fa7e7fe88a1 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Sat, 21 Mar 2026 23:33:53 +0100
Subject: [PATCH 109/211] polldaemon: check finish_copy result on abort path
When update_metadata fails during segment progression, finish_copy
is called for cleanup but its return value was silently ignored.
Add stack trace on failure for diagnostics.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit f8c2e6054226f8ff8bfcfd25791640490b378b1b)
---
tools/polldaemon.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tools/polldaemon.c b/tools/polldaemon.c
index 1a3c8fefc..605c90779 100644
--- a/tools/polldaemon.c
+++ b/tools/polldaemon.c
@@ -104,7 +104,8 @@ static int _check_lv_status(struct cmd_context *cmd,
if (parms->poll_fns->update_metadata &&
!parms->poll_fns->update_metadata(cmd, vg, lv, lvs_changed, 0)) {
log_error("ABORTING: Segment progression failed.");
- parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed);
+ if (!parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed))
+ stack;
return 0;
}
*finished = 0; /* Another segment */
--
2.54.0

View File

@ -0,0 +1,214 @@
From 6c03e5fd5c875aa29ad761aa1cd4aa764849b026 Mon Sep 17 00:00:00 2001
From: Peter Rajnoha <prajnoha@redhat.com>
Date: Thu, 5 Feb 2026 17:09:43 +0100
Subject: [PATCH 110/211] metadata: fix lv_raid_healthy to detect metadata
alrady updated to match device state
Consider a RAID LV where one of the legs goes missing.
There is a difference between these two:
A) transient device failure
1) A RAID LV has underlying PV representing a leg missing.
2) The missing leg is replaced by error target.
3) LVM metadata are not updated yet, just transient error (vgreduce not called).
4) The missing PV is present again.
5) A message is reported when getting LV RAID health status (for e.g. lvs):
"WARNING: RaidLV vg/lv needs to be refreshed!"
6) After using lvchange --refresh vg/lv, the RAID LV is refreshed
and the error targets for previously missing leg is mapped onto
the actual PV again.
B) non-transient device failure
1) A RAID LV has underlying PV representing a leg missing.
2) The missing PV is reduced (vgreduce called).
3) LVM metadata are updated.
4) The missing leg is present again.
5) Inconsistent metadata is detected, the VG is made consistent first,
removes the actual leg from the old PV (vgck --updatemetadata vg).
6) The message "WARNING: RaidLV vg/lv needs to be repaired!" is reported.
7) After using lvconvert --repair vg/lv, the RAID LV is repaired and
the error targets for previously missing leg is mapped onto the
free PV.
Before, lv_raid_healthy could not differentiate between A) and B).
Now, lv_raid_healthy sets 'raid_need_t *need' output arguments where the value is one of:
- RAID_NEED_ERROR (an error occurred while trying to acquire the healthy status)
- RAID_NEED_REFRESH (refresh is needed for transient failure)
- RAID_NEED_REPAIR (repair is needed for non-transient failure)
- RAID_NEED_REFRESH_OR_REPAIR (either refresh or repair, can't determine)
(cherry picked from commit 6104a78a87bbdc2d4a84791ea45c3df18422a28c)
---
lib/metadata/lv.c | 87 ++++++++++++++++++++++++++++++++++++++++-------
lib/metadata/lv.h | 9 ++++-
2 files changed, 83 insertions(+), 13 deletions(-)
diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c
index c7b213067..d201244a7 100644
--- a/lib/metadata/lv.c
+++ b/lib/metadata/lv.c
@@ -1230,13 +1230,17 @@ static int lv_raid_integrity_image_in_sync(const struct logical_volume *lv_iorig
* _lv_raid_healthy
* @lv: A RAID_IMAGE, RAID_META, or RAID logical volume.
*
- * Returns: 1 if healthy, 0 if device is not health
+ * Returns:
+ * 1 if LV of RAID type healthy
+ * 0 if LV of RAID type not healthy ('need' set appropriately if not NULL)
*/
-int lv_raid_healthy(const struct logical_volume *lv)
+int lv_raid_healthy(const struct logical_volume *lv, raid_need_t *need)
{
unsigned s;
char *raid_health;
+ size_t raid_health_len;
struct lv_segment *seg, *raid_seg = NULL;
+ raid_need_t r_need = RAID_NEED_ERROR;
/*
* If the LV is not active locally,
@@ -1247,7 +1251,7 @@ int lv_raid_healthy(const struct logical_volume *lv)
if (!lv_is_raid_type(lv)) {
log_error(INTERNAL_ERROR "%s is not of RAID type", lv->name);
- return 0;
+ goto out_need;
}
if (lv_is_raid(lv))
@@ -1257,20 +1261,55 @@ int lv_raid_healthy(const struct logical_volume *lv)
if (!raid_seg) {
log_error("Failed to find RAID segment for %s", lv->name);
- return 0;
+ goto out_need;
}
if (!seg_is_raid(raid_seg)) {
log_error(INTERNAL_ERROR "%s on %s is not a RAID segment.",
raid_seg->lv->name, lv->name);
- return 0;
+ goto out_need;
}
- if (!lv_raid_dev_health(raid_seg->lv, &raid_health))
- return_0;
+ if (!lv_raid_dev_health(raid_seg->lv, &raid_health)) {
+ stack;
+ goto out_need;
+ }
- if (lv_is_raid(lv))
- return (strchr(raid_health, 'D')) ? 0 : 1;
+ raid_health_len = strlen(raid_health);
+
+ if (lv_is_raid(lv)) {
+ if (raid_seg->area_count == raid_health_len) {
+ /*
+ * Counts match - positions correspond.
+ * Check each leg - only report unhealthy if a device is down
+ * but the metadata expects it to be on a real device (not error segment).
+ * If a device shows 'D' but the corresponding image LV is virtual
+ * (error segment from vgreduce --removemissing), that's expected and
+ * doesn't need a refresh.
+ */
+ for (s = 0; s < raid_seg->area_count; s++) {
+ if (raid_health[s] == 'D') {
+ if (lv_is_virtual(seg_lv(raid_seg, s)))
+ r_need = RAID_NEED_REPAIR;
+ else
+ r_need = RAID_NEED_REFRESH;
+ goto out_need;
+ }
+ }
+ return 1;
+ }
+
+ /*
+ * Counts don't match (reshape in progress) - can't reliably
+ * correlate health positions with metadata, fall back to
+ * behavior where any 'D' means unhealthy.
+ */
+ if (strchr(raid_health, 'D')) {
+ r_need = RAID_NEED_REFRESH_OR_REPAIR;
+ goto out_need;
+ }
+ return 1;
+ }
/* Find out which sub-LV this is. */
for (s = 0; s < raid_seg->area_count; s++)
@@ -1281,13 +1320,37 @@ int lv_raid_healthy(const struct logical_volume *lv)
log_error(INTERNAL_ERROR
"sub-LV %s was not found in raid segment",
lv->name);
- return 0;
+ goto out_need;
}
- if (raid_health[s] == 'D')
- return 0;
+ if (raid_seg->area_count == raid_health_len) {
+ /* Counts match - positions correspond */
+ /* If sub-LV is virtual (error segment), 'D' is expected - not unhealthy */
+ if (raid_health[s] == 'D') {
+ if (lv_is_virtual(lv))
+ r_need = RAID_NEED_REPAIR;
+ else
+ r_need = RAID_NEED_REFRESH;
+ goto out_need;
+ }
+ return 1;
+ }
+ /*
+ * Counts don't match (reshape in progress) - can't reliably
+ * correlate health position with sub-LV, fall back to
+ * behavior where any 'D' means unhealthy.
+ */
+ if (strchr(raid_health, 'D')) {
+ r_need = RAID_NEED_REFRESH_OR_REPAIR;
+ goto out_need;
+ }
return 1;
+
+out_need:
+ if (need)
+ *need = r_need;
+ return 0;
}
/* Helper: check for any sub LVs after a disk removing reshape */
diff --git a/lib/metadata/lv.h b/lib/metadata/lv.h
index 0596ed86b..3bf9d4211 100644
--- a/lib/metadata/lv.h
+++ b/lib/metadata/lv.h
@@ -139,7 +139,6 @@ const struct logical_volume *lv_lock_holder(const struct logical_volume *lv);
const struct logical_volume *lv_committed(const struct logical_volume *lv);
int lv_mirror_image_in_sync(const struct logical_volume *lv);
int lv_raid_image_in_sync(const struct logical_volume *lv);
-int lv_raid_healthy(const struct logical_volume *lv);
const char *lvseg_name(const struct lv_segment *seg);
uint64_t lvseg_start(const struct lv_segment *seg);
struct dm_list *lvseg_devices(struct dm_pool *mem, const struct lv_segment *seg);
@@ -153,6 +152,14 @@ char *lvseg_seg_le_ranges_str(struct dm_pool *mem, const struct lv_segment *seg)
struct dm_list *lvseg_seg_metadata_le_ranges(struct dm_pool *mem, const struct lv_segment *seg);
char *lvseg_seg_metadata_le_ranges_str(struct dm_pool *mem, const struct lv_segment *seg);
+typedef enum {
+ RAID_NEED_ERROR,
+ RAID_NEED_REFRESH,
+ RAID_NEED_REPAIR,
+ RAID_NEED_REFRESH_OR_REPAIR, /* can't determine */
+} raid_need_t;
+int lv_raid_healthy(const struct logical_volume *lv, raid_need_t *need);
+
/* LV kernel properties */
int lv_kernel_major(const struct logical_volume *lv);
int lv_kernel_minor(const struct logical_volume *lv);
--
2.54.0

View File

@ -0,0 +1,61 @@
From af3088f447aace9e6e87de388aa2da5dd6273c40 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Wed, 22 Oct 2025 16:44:09 +0200
Subject: [PATCH 111/211] lv_manip: detect insufficient space for RAID with
split metadata
When approx_alloc reduces allocation and we have RAID/mirror with
split metadata (alloc_and_split_meta), verify that we allocated at
least 1 extent per data area.
If total_area_len < area_count, the integer division during parallel
area allocation (area_len = max_to_allocate / ah->area_multiple) results
in area_len=0 for individual data areas. This causes those areas to be
skipped (empty allocation), creating RAID image LVs with no segments.
This create invalid metadata with LVs without segments.
Example:
- Need 52 extents total (4 data areas x 13 extents)
- Metadata allocated: 4 extents (4 rmeta x 1 extent each)
- Only 1 extent remains for data
- Result: area_len = 1/4 = 0, all data areas get empty allocation
Fix by failing allocation early with clear error message instead of
creating corrupted RAID LVs that trigger internal errors.
Resolves: https://issues.redhat.com/browse/RHEL-116884
Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit d2715e01f585c55f76215002db470c752ad208e3)
---
lib/metadata/lv_manip.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index 61816bd8c..42e3f2489 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -3494,6 +3494,19 @@ static int _allocate(struct alloc_handle *ah,
lv ? lv->name : "");
goto out;
}
+ /*
+ * For RAID/mirror with split metadata, verify we allocated
+ * at least 1 extent per data area. If total_area_len is less
+ * than area_count, integer division during allocation would
+ * result in some areas getting 0 extents, creating LVs with
+ * no segments which triggers internal errors during vg_write.
+ */
+ if (ah->alloc_and_split_meta && ah->total_area_len < ah->area_count) {
+ log_error("Insufficient suitable allocatable extents for logical volume %s: "
+ "%u extents found but at least %u required (1 extent per data image).",
+ lv ? lv->name : "", ah->total_area_len, ah->area_count);
+ goto out;
+ }
log_verbose("Found fewer %sallocatable extents "
"for logical volume %s than requested: using %" PRIu32 " extents (reduced by %u).",
can_split ? "" : "contiguous ",
--
2.54.0

View File

@ -0,0 +1,103 @@
From f64c7195de1d8a111b8f5a7054c5f4af2f5eb483 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Wed, 15 Oct 2025 18:07:00 +0200
Subject: [PATCH 112/211] libdm: deptree: cancel delay_resume for new children
when parent is inactive
When preloading children of an inactive parent node (other than root),
cancel the delay_resume_if_new flag to allow immediate resume of new devices.
This fixes a specific issue with pvmove operations on RAID devices where:
1. The RAID logical volume has not been instantiated yet (inactive parent)
2. A pvmove device needs to be created for one of the RAID legs
3. The RAID table requires the leg LV to be active before loading
4. Therefore, the pvmove device must be resumed immediately, not delayed
Background:
When both RAID and pvmove devices are being created from scratch,
the pvmove device can be started immediately without leaking direct
access to the PV - all I/O goes through appropriate targets from the start.
However, if the RAID device is already active, we can only preload the
new pvmove table. In that case, a full suspend must occur before resuming
the new table, ensuring all I/O continues through the existing table
until the resume point.
(cherry picked from commit 48015ecb3c674b0f7326f94a44af4d93c77f0fbe)
---
device_mapper/libdm-deptree.c | 23 ++++++++++++++++++++---
libdm/libdm-deptree.c | 23 ++++++++++++++++++++---
2 files changed, 40 insertions(+), 6 deletions(-)
diff --git a/device_mapper/libdm-deptree.c b/device_mapper/libdm-deptree.c
index 2d5829c9c..98c0fa814 100644
--- a/device_mapper/libdm-deptree.c
+++ b/device_mapper/libdm-deptree.c
@@ -3363,9 +3363,26 @@ int dm_tree_preload_children(struct dm_tree_node *dnode,
/* Preload children first */
while ((child = dm_tree_next_child(&handle, dnode, 0))) {
- /* Propagate delay of resume from parent node */
- if (dnode->props.delay_resume_if_new > 1)
- child->props.delay_resume_if_new = dnode->props.delay_resume_if_new;
+ /* When a parent node (other than root) is inactive, we cannot delay
+ * the resume of a new device.
+ * For example, preloading a RAID table with a pvmoved leg requires the
+ * leg LV to be active before loading the RAID LV, so the pvmove device must
+ * be resumed immediately.
+ * This scenario only occurs when neither the RAID nor pvmove device has
+ * been instantiated yet. In this case, starting the pvmove device does
+ * not leak access to the PV device without going through the mirror target.
+ * However, if the RAID is already active and running, we can only preload
+ * the new pvmove device, and a full suspend must occur before resuming
+ * the new table with the running pvmove. So until the resume point
+ * any IO is going through the existing table line and not via pvmove target.
+ */
+ if ((child->props.delay_resume_if_new > 1) &&
+ !dnode->info.exists &&
+ (dnode != &dnode->dtree->root)) { /* Ignore when parent is root node */
+ log_debug("%s with inactive parent cancels delay_resume_if_new.",
+ _node_name(child));
+ child->props.delay_resume_if_new = 0;
+ }
/* Skip existing non-device-mapper devices */
if (!child->info.exists && child->info.major)
diff --git a/libdm/libdm-deptree.c b/libdm/libdm-deptree.c
index b0f476b3b..318b23ecc 100644
--- a/libdm/libdm-deptree.c
+++ b/libdm/libdm-deptree.c
@@ -2906,9 +2906,26 @@ int dm_tree_preload_children(struct dm_tree_node *dnode,
/* Preload children first */
while ((child = dm_tree_next_child(&handle, dnode, 0))) {
- /* Propagate delay of resume from parent node */
- if (dnode->props.delay_resume_if_new > 1)
- child->props.delay_resume_if_new = dnode->props.delay_resume_if_new;
+ /* When a parent node (other than root) is inactive, we cannot delay
+ * the resume of a new device.
+ * For example, preloading a RAID table with a pvmoved leg requires the
+ * leg LV to be active before loading the RAID LV, so the pvmove device must
+ * be resumed immediately.
+ * This scenario only occurs when neither the RAID nor pvmove device has
+ * been instantiated yet. In this case, starting the pvmove device does
+ * not leak access to the PV device without going through the mirror target.
+ * However, if the RAID is already active and running, we can only preload
+ * the new pvmove device, and a full suspend must occur before resuming
+ * the new table with the running pvmove. So until the resume point
+ * any IO is going through the existing table line and not via pvmove target.
+ */
+ if ((child->props.delay_resume_if_new > 1) &&
+ !dnode->info.exists &&
+ (dnode != &dnode->dtree->root)) { /* Ignore when parent is root node */
+ log_debug("%s with inactive parent cancels delay_resume_if_new.",
+ _node_name(child));
+ child->props.delay_resume_if_new = 0;
+ }
/* Skip existing non-device-mapper devices */
if (!child->info.exists && child->info.major)
--
2.54.0

View File

@ -0,0 +1,139 @@
From ec6b806b893bedda0356b825f9d775f546debbae Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Mon, 29 Sep 2025 14:03:42 +0200
Subject: [PATCH 113/211] integrity: fix adding integrity to RAID
Fix several issues in integrity addition to RAID logical volumes and
improve error handling and cleanup:
Synchronization and activation fixes:
- Add sync_local_dev_names() before LV activation to handle potential
udev deactivation in-flight from metadata device wiping
- Consolidate deactivate_lv() calls to avoid code duplication in
_set_integrity_block_size() error path
Missing segment relationship:
- Add missing add_seg_to_segs_using_this_lv() call for imeta LV to
properly establish segment relationships
Enhanced error recovery:
- Improve error path cleanup in case integrity addition fails
(cherry picked from commit c33233cb88961a51e0effad02510f5f01bb930d4)
---
lib/metadata/integrity_manip.c | 59 ++++++++++++++++++++++++++--------
1 file changed, 45 insertions(+), 14 deletions(-)
diff --git a/lib/metadata/integrity_manip.c b/lib/metadata/integrity_manip.c
index be2d458b3..e03d422b2 100644
--- a/lib/metadata/integrity_manip.c
+++ b/lib/metadata/integrity_manip.c
@@ -515,7 +515,7 @@ int lv_add_integrity_to_raid(struct logical_volume *lv, struct integrity_setting
struct logical_volume *imeta_lvs[DEFAULT_RAID_MAX_IMAGES];
struct cmd_context *cmd = lv->vg->cmd;
struct volume_group *vg = lv->vg;
- struct logical_volume *lv_image, *lv_imeta;
+ struct logical_volume *lv_image, *lv_imeta, *lv_iorig;
struct lv_segment *seg_top, *seg_image;
struct pv_list *pvl;
const struct segment_type *segtype;
@@ -526,6 +526,7 @@ int lv_add_integrity_to_raid(struct logical_volume *lv, struct integrity_setting
int lbs_4k = 0, lbs_512 = 0, lbs_unknown = 0;
int pbs_4k = 0, pbs_512 = 0, pbs_unknown = 0;
int is_active;
+ int r;
memset(imeta_lvs, 0, sizeof(imeta_lvs));
@@ -659,6 +660,8 @@ int lv_add_integrity_to_raid(struct logical_volume *lv, struct integrity_setting
}
if (!is_active) {
+ if (!sync_local_dev_names(cmd))
+ stack;
/* checking block size of fs on the lv requires the lv to be active */
if (!activate_lv(cmd, lv)) {
log_error("Failed to activate LV to check block size %s", display_lvname(lv));
@@ -673,19 +676,18 @@ int lv_add_integrity_to_raid(struct logical_volume *lv, struct integrity_setting
* integrity block size chosen based on device logical block size and
* file system block size.
*/
- if (!_set_integrity_block_size(cmd, lv, is_active, settings, lbs_4k, lbs_512, pbs_4k, pbs_512)) {
- if (!is_active && !deactivate_lv(cmd, lv))
- stack;
- goto_bad;
- }
- if (!is_active) {
- if (!deactivate_lv(cmd, lv)) {
- log_error("Failed to deactivate LV after checking block size %s", display_lvname(lv));
- goto bad;
- }
+ if (!(r = _set_integrity_block_size(cmd, lv, is_active, settings, lbs_4k, lbs_512, pbs_4k, pbs_512)))
+ stack;
+
+ if (!is_active && !deactivate_lv(cmd, lv)) {
+ log_error("Failed to deactivate LV %s after checking block size.", display_lvname(lv));
+ goto bad;
}
+ if (!r)
+ goto bad;
+
/*
* For each rimage, move its segments to a new rimage_iorig and give
* the rimage a new integrity segment.
@@ -727,6 +729,8 @@ int lv_add_integrity_to_raid(struct logical_volume *lv, struct integrity_setting
seg_image->integrity_data_sectors = lv_image->size;
seg_image->integrity_meta_dev = lv_imeta;
seg_image->integrity_recalculate = 1;
+ if (!add_seg_to_segs_using_this_lv(lv_imeta, seg_image))
+ goto_bad;
memcpy(&seg_image->integrity_settings, settings, sizeof(struct integrity_settings));
set = &seg_image->integrity_settings;
@@ -801,11 +805,38 @@ bad:
for (s = 0; s < DEFAULT_RAID_MAX_IMAGES; s++) {
if (!imeta_lvs[s])
continue;
- if (!lv_remove(imeta_lvs[s]))
- log_error("New integrity metadata LV may require manual removal.");
+
+ lv_image = seg_lv(seg_top, s);
+ seg_image = first_seg(lv_image);
+ lv_iorig = seg_lv(seg_image, 0);
+ if (lv_image->status & INTEGRITY) {
+ if (!remove_layer_from_lv(lv_image, lv_iorig) ||
+ !remove_seg_from_segs_using_this_lv(seg_image->integrity_meta_dev,
+ seg_image)) {
+ log_error("Aborting. Cannot remove integrity layer from LV %s.", display_lvname(lv_image));
+ return 0;
+ }
+
+ lv_image->status &= ~INTEGRITY;
+ imeta_lvs[s]->status &= ~INTEGRITY_METADATA;
+
+ lv_set_visible(imeta_lvs[s]);
+ lv_set_visible(lv_iorig);
+
+ if (!lv_remove(lv_iorig)) {
+ log_error("Aborting. Cannot remove new integrity origin LV %s.",
+ display_lvname(lv_iorig));
+ return 0;
+ }
+ }
+ if (!lv_remove(imeta_lvs[s])) {
+ log_error("Aborting. Cannot remove new integrity metadata LV %s.",
+ display_lvname(imeta_lvs[s]));
+ return 0;
+ }
}
}
-
+
if (!vg_write(vg) || !vg_commit(vg))
log_error("New integrity metadata LV may require manual removal.");
--
2.54.0

View File

@ -0,0 +1,36 @@
From a61fbb2bb54a29d03a00175c7e124c6ebe2e536a Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Thu, 2 Oct 2025 15:54:12 +0200
Subject: [PATCH 114/211] integrity: fix activation race during setup
Add synchronization after deactivate_lv() to prevent races between
deactivation and subsequent activation using the same udev cookie.
This ensures proper ordering when wiping prepared integrity metadata
devices during RAID integrity setup, affecting both active and inactive
code paths.
(cherry picked from commit 42e3a8705ec2911e256e14e541a2c97f5b404e15)
---
lib/metadata/integrity_manip.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/lib/metadata/integrity_manip.c b/lib/metadata/integrity_manip.c
index e03d422b2..26b644266 100644
--- a/lib/metadata/integrity_manip.c
+++ b/lib/metadata/integrity_manip.c
@@ -685,7 +685,10 @@ int lv_add_integrity_to_raid(struct logical_volume *lv, struct integrity_setting
goto bad;
}
- if (!r)
+ if (!sync_local_dev_names(cmd))
+ stack;
+
+ if (!r)
goto bad;
/*
--
2.54.0

View File

@ -0,0 +1,295 @@
From abaf179f7ecb2945b6a5f5c795e60c80445c4f97 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Thu, 26 Feb 2026 20:24:46 +0100
Subject: [PATCH 115/211] snapshot: reject lvreduce that would truncate COW
exception data
Add a safety check in _lv_resize_check_type() that blocks lvreduce on
an active CoW snapshot LV when the requested new size would truncate
already-allocated exception blocks.
The check reads the exact used_sectors / total_sectors counts from the
DM snapshot target (via new lv_snapshot_status() / dev_manager_snapshot_status())
with a DM flush so that in-flight writes are accounted for before the
comparison.
Fixes: https://github.com/lvmteam/lvm2/issues/164
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
(cherry picked from commit 21d5277eee3822c64b26f1a98b1d84764d612bd8)
---
lib/activate/activate.c | 34 ++++++++++++++
lib/activate/activate.h | 3 ++
lib/activate/dev_manager.c | 77 ++++++++++++++++++++++++++++++++
lib/activate/dev_manager.h | 6 +++
lib/metadata/lv_manip.c | 52 +++++++++++++++++++++
lib/metadata/metadata-exported.h | 5 +++
6 files changed, 177 insertions(+)
diff --git a/lib/activate/activate.c b/lib/activate/activate.c
index 50b8523ed..efeeaf26e 100644
--- a/lib/activate/activate.c
+++ b/lib/activate/activate.c
@@ -258,6 +258,12 @@ int lv_snapshot_percent(const struct logical_volume *lv, dm_percent_t *percent)
{
return 0;
}
+int lv_snapshot_status(const struct logical_volume *lv,
+ int flush,
+ struct lv_status_snapshot **status)
+{
+ return 0;
+}
int lv_mirror_percent(struct cmd_context *cmd, const struct logical_volume *lv,
int wait, dm_percent_t *percent, uint32_t *event_nr)
{
@@ -999,6 +1005,34 @@ int lv_snapshot_percent(const struct logical_volume *lv, dm_percent_t *percent)
return r;
}
+/*
+ * Get COW snapshot status for an active COW snapshot LV.
+ * If flush is non-zero a DM flush is issued so in-flight writes are
+ * reflected in the counts before the status is read.
+ * On success returns 1 with *status populated.
+ * Caller must call dm_pool_destroy((*status)->mem) when done.
+ * Returns 0 if the LV is inactive or the query fails.
+ */
+int lv_snapshot_status(const struct logical_volume *lv,
+ int flush,
+ struct lv_status_snapshot **status)
+{
+ struct dev_manager *dm;
+ int exists;
+
+ if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name, 1)))
+ return_0;
+
+ if (!dev_manager_snapshot_status(dm, lv, flush, status, &exists)) {
+ dev_manager_destroy(dm);
+ if (exists)
+ stack;
+ return 0;
+ }
+
+ return 1;
+}
+
/* FIXME Merge with snapshot_percent */
int lv_mirror_percent(struct cmd_context *cmd, const struct logical_volume *lv,
int wait, dm_percent_t *percent, uint32_t *event_nr)
diff --git a/lib/activate/activate.h b/lib/activate/activate.h
index 155936135..34ca7adb8 100644
--- a/lib/activate/activate.h
+++ b/lib/activate/activate.h
@@ -187,6 +187,9 @@ int lv_check_transient(struct logical_volume *lv);
* Returns 1 if percent has been set, else 0.
*/
int lv_snapshot_percent(const struct logical_volume *lv, dm_percent_t *percent);
+int lv_snapshot_status(const struct logical_volume *lv,
+ int flush,
+ struct lv_status_snapshot **status);
int lv_mirror_percent(struct cmd_context *cmd, const struct logical_volume *lv,
int wait, dm_percent_t *percent, uint32_t *event_nr);
int lv_raid_percent(const struct logical_volume *lv, dm_percent_t *percent);
diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c
index 46c626de5..7a7c6a475 100644
--- a/lib/activate/dev_manager.c
+++ b/lib/activate/dev_manager.c
@@ -1620,6 +1620,83 @@ int dev_manager_snapshot_percent(struct dev_manager *dm,
return 1;
}
+/*
+ * Get COW snapshot status for an active COW snapshot LV.
+ * If flush is non-zero a DM flush is issued so that in-flight writes are
+ * accounted for before the status is read.
+ * On success *status is populated and (*status)->mem must be destroyed by
+ * the caller. Returns 1 on success, 0 on failure. *exists is set to 0 if
+ * the DM device does not exist (inactive LV), 1 if it does.
+ */
+int dev_manager_snapshot_status(struct dev_manager *dm,
+ const struct logical_volume *lv,
+ int flush,
+ struct lv_status_snapshot **status,
+ int *exists)
+{
+ int r = 0;
+ const struct logical_volume *snap_lv;
+ const char *dlid;
+ struct dm_task *dmt;
+ struct dm_info info;
+ uint64_t start, length;
+ char *type = NULL;
+ char *params = NULL;
+
+ *exists = -1;
+ if (!(*status = dm_pool_zalloc(dm->mem, sizeof(struct lv_status_snapshot))))
+ return_0;
+
+ if (lv_is_merging_cow(lv))
+ /* must check status of origin for a merging snapshot */
+ snap_lv = origin_from_cow(lv);
+ else
+ snap_lv = lv;
+
+ if (!(dlid = build_dm_uuid(dm->mem, snap_lv, NULL)))
+ return_0;
+
+ if (!(dmt = _setup_task_run(DM_DEVICE_STATUS, &info, NULL, dlid, 0, 0, 0, 0, flush, 0)))
+ return_0;
+
+ if (!(*exists = info.exists))
+ goto out;
+
+ log_debug_activation("Checking snapshot status for LV %s.",
+ display_lvname(snap_lv));
+
+ dm_get_next_target(dmt, NULL, &start, &length, &type, &params);
+
+ if (!type || strcmp(type, TARGET_NAME_SNAPSHOT) || !params) {
+ log_error("Expected snapshot segment type for %s but got %s.",
+ display_lvname(snap_lv), type ? type : "NULL");
+ goto out;
+ }
+
+ if (!dm_get_status_snapshot(dm->mem, params, &(*status)->snap))
+ goto_out;
+
+ (*status)->mem = dm->mem;
+
+ if ((*status)->snap->invalid)
+ (*status)->usage = DM_PERCENT_INVALID;
+ else if ((*status)->snap->merge_failed)
+ (*status)->usage = LVM_PERCENT_MERGE_FAILED;
+ else if ((*status)->snap->has_metadata_sectors &&
+ (*status)->snap->used_sectors == (*status)->snap->metadata_sectors)
+ (*status)->usage = DM_PERCENT_0;
+ else if ((*status)->snap->used_sectors == (*status)->snap->total_sectors)
+ (*status)->usage = DM_PERCENT_100;
+ else
+ (*status)->usage = dm_make_percent((*status)->snap->used_sectors,
+ (*status)->snap->total_sectors);
+ r = 1;
+out:
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
/* FIXME Merge with snapshot_percent, auto-detecting target type */
/* FIXME Cope with more than one target */
int dev_manager_mirror_percent(struct dev_manager *dm,
diff --git a/lib/activate/dev_manager.h b/lib/activate/dev_manager.h
index 353c3c7d2..92c374a28 100644
--- a/lib/activate/dev_manager.h
+++ b/lib/activate/dev_manager.h
@@ -20,6 +20,7 @@
struct logical_volume;
struct lv_activate_opts;
+struct lv_status_snapshot;
struct volume_group;
struct cmd_context;
struct dev_manager;
@@ -56,6 +57,11 @@ int dev_manager_info(struct cmd_context *cmd, const struct logical_volume *lv,
int dev_manager_snapshot_percent(struct dev_manager *dm,
const struct logical_volume *lv,
dm_percent_t *percent);
+int dev_manager_snapshot_status(struct dev_manager *dm,
+ const struct logical_volume *lv,
+ int flush,
+ struct lv_status_snapshot **status,
+ int *exists);
int dev_manager_mirror_percent(struct dev_manager *dm,
const struct logical_volume *lv, int wait,
dm_percent_t *percent, uint32_t *event_nr);
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index 42e3f2489..4397f0bc2 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -5816,6 +5816,55 @@ static int _lv_reduce_vdo_discard(struct cmd_context *cmd,
return 1;
}
+static int _lv_resize_check_cow_reduce(struct logical_volume *lv,
+ uint64_t new_size)
+{
+ struct cmd_context *cmd = lv->vg->cmd;
+ struct lv_status_snapshot *snap_status;
+ int r;
+
+ /*
+ * COW snapshot must be active to check how many exception blocks are
+ * in use. Reject when inactive to avoid silently truncating data.
+ * If the snapshot content is not needed, remove it with lvremove.
+ */
+ if (!lv_is_active(lv_lock_holder(lv))) {
+ log_error("Inactive snapshot %s cannot be reduced "
+ "(activate or remove it).",
+ display_lvname(lv));
+ return 0;
+ }
+
+ /*
+ * Reject reduce if it would truncate exception data already written
+ * to the COW store. Flush so that in-flight writes are reflected
+ * in the exception counts before we compare.
+ */
+ if ((r = lv_snapshot_status(lv, 1, &snap_status))) {
+ /* merge_failed cannot occur here: merging snapshots are
+ * rejected earlier in _lv_resize_check_type(); kept as
+ * a defensive check.
+ * overflow mode is currently not supported by lvm2
+ * kept as a defensive check. */
+ if (snap_status->snap->invalid ||
+ snap_status->snap->merge_failed ||
+ snap_status->snap->overflow) {
+ log_error("Invalid snapshot %s cannot be reduced, only removed.",
+ display_lvname(lv));
+ r = 0;
+ } else if (new_size < snap_status->snap->used_sectors) {
+ log_error("Cannot reduce snapshot %s to below %s (%.2f%% full, would lose exception data).",
+ display_lvname(lv),
+ display_size(cmd, snap_status->snap->used_sectors),
+ dm_percent_to_round_float(snap_status->usage, 2));
+ r = 0;
+ }
+ dm_pool_destroy(snap_status->mem);
+ }
+
+ return r;
+}
+
static int _lv_resize_check_type(struct logical_volume *lv,
struct lvresize_params *lp)
{
@@ -5888,6 +5937,9 @@ static int _lv_resize_check_type(struct logical_volume *lv,
log_error("Cannot reduce LV with integrity.");
return 0;
}
+ if (lv_is_cow(lv) &&
+ !_lv_resize_check_cow_reduce(lv, (uint64_t)lp->extents * lv->vg->extent_size))
+ return_0;
} else if (lp->resize == LV_EXTEND) {
if (lv_is_thin_pool_metadata(lv) &&
(!(seg = find_pool_seg(first_seg(lv))) ||
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index 7ec81f27d..a89d77284 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -1201,6 +1201,11 @@ int vg_remove_snapshot(struct logical_volume *cow);
int validate_snapshot_origin(const struct logical_volume *origin_lv);
+struct lv_status_snapshot {
+ struct dm_pool *mem;
+ struct dm_status_snapshot *snap;
+ dm_percent_t usage;
+};
int vg_check_status(const struct volume_group *vg, uint64_t status);
--
2.54.0

View File

@ -0,0 +1,36 @@
From c0bd6195c00ef6d0e620c61ab05464424329a2e1 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Thu, 26 Feb 2026 21:57:52 +0100
Subject: [PATCH 116/211] snapshot: reject resize of merging CoW snapshot
A snapshot that is in the process of merging back into its origin
cannot be resized - the kernel runs a snapshot-merge target during
the merge and resizing the COW store while the merge is in progress
is not supported and could corrupt the operation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
(cherry picked from commit 152b0fe2ca6c5ac5482332b9352098a6f78d7d61)
---
lib/metadata/lv_manip.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index 4397f0bc2..783cd2c66 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -5870,6 +5870,12 @@ static int _lv_resize_check_type(struct logical_volume *lv,
{
struct lv_segment *seg;
+ if (lv_is_merging_cow(lv)) {
+ log_error("Cannot resize merging snapshot %s.",
+ display_lvname(lv));
+ return 0;
+ }
+
if (lv_is_origin(lv)) {
if (lp->resize == LV_REDUCE) {
log_error("Snapshot origin volumes cannot be reduced in size yet.");
--
2.54.0

View File

@ -0,0 +1,57 @@
From 00b13dac560f293fa2a64b1e7e9dc0248bc88b45 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Thu, 26 Feb 2026 16:31:43 +0100
Subject: [PATCH 117/211] lvresize: skip fs handling for CoW snapshot COW store
LVs
The COW store LV has linear segments internally (it stores exception
blocks), so lv_is_linear() returned true for it. This caused the
fsopt disable check to be skipped, and LVM incorrectly ran fs_get_info()
on the snapshot device which presents the origin filesystem data.
The result was a misleading error:
File system xfs found on fedora/root-snap.
File system size (9.00 GiB) is larger than the requested size (5.00 GiB).
File system reduce is required and not supported (xfs).
Fix by explicitly excluding CoW snapshot LVs (lv_is_cow) from filesystem
handling regardless of their underlying segment type.
Fixes: https://gitlab.com/lvmteam/lvm2/-/issues/27
Fixes: https://github.com/lvmteam/lvm2/issues/163
TODO: might be worth to implemement -V options to allow resize
of snapshot volume itself.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
(cherry picked from commit 99639a37fb73b4a1e044ec822a562fb4608dda0e)
---
lib/metadata/lv_manip.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index 783cd2c66..0fce14f21 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -7050,11 +7050,15 @@ int lv_resize(struct cmd_context *cmd, struct logical_volume *lv,
/*
* Disable fsopt if LV type cannot hold a file system.
+ * CoW snapshot LVs (the COW store) do not hold a filesystem even
+ * though their segments are linear; the filesystem lives on the
+ * origin LV and the COW store only holds exception blocks.
*/
if (lp->fsopt[0] &&
- !(lv_is_linear(lv) || lv_is_striped(lv) || lv_is_raid(lv) ||
- lv_is_mirror(lv) || lv_is_thin_volume(lv) || lv_is_vdo(lv) ||
- lv_is_cache(lv) || lv_is_writecache(lv))) {
+ (lv_is_cow(lv) ||
+ !(lv_is_linear(lv) || lv_is_striped(lv) || lv_is_raid(lv) ||
+ lv_is_mirror(lv) || lv_is_thin_volume(lv) || lv_is_vdo(lv) ||
+ lv_is_cache(lv) || lv_is_writecache(lv)))) {
log_print_unless_silent("Ignoring fs resizing options for LV type %s.",
seg ? seg->segtype->name : "unknown");
lp->fsopt[0] = '\0';
--
2.54.0

View File

@ -0,0 +1,35 @@
From 5c95e79a235f01c50f4d44a5a31b6afe6711958d Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 08:36:06 +0200
Subject: [PATCH 118/211] metadata: fix vg_split_mdas losing first _move_mdas
result
The second assignment to common_mda was overwriting the result from
the first _move_mdas() call. If a common MDA was found in the in_use
list but not in the ignored list, the information was lost. Use |=
to accumulate results from both calls.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 14ba69b358c3c25be7fc3cc461677430ebd35d09)
---
lib/metadata/metadata.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index d15682fdb..ab037fd06 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -1402,8 +1402,8 @@ int vg_split_mdas(struct cmd_context *cmd __attribute__((unused)),
common_mda = _move_mdas(vg_from, vg_to,
mdas_from_in_use, mdas_to_in_use);
- common_mda = _move_mdas(vg_from, vg_to,
- mdas_from_ignored, mdas_to_ignored);
+ common_mda |= _move_mdas(vg_from, vg_to,
+ mdas_from_ignored, mdas_to_ignored);
if ((dm_list_empty(mdas_from_in_use) &&
dm_list_empty(mdas_from_ignored)) ||
--
2.54.0

View File

@ -0,0 +1,43 @@
From c51e71383068fc61ebfc8a241509454cda48c3fd Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 12:04:54 +0200
Subject: [PATCH 119/211] libdm: stats: fix bitwise AND and use escaped
aux_data
Fix two bugs:
- Histogram parsing used bitwise & instead of logical && to check
for unexpected characters, causing some chars to be missed.
- stats_create message used unescaped aux_data instead of
aux_data_escaped, defeating the escaping done just above.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 55f9f953c663a3f3545c6afe73e9d2d43ce7dcf1)
---
libdm/libdm-stats.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libdm/libdm-stats.c b/libdm/libdm-stats.c
index 53c3c5480..6e6d63a8b 100644
--- a/libdm/libdm-stats.c
+++ b/libdm/libdm-stats.c
@@ -1248,7 +1248,7 @@ static int _stats_parse_histogram(struct dm_pool *mem, char *hist_str,
if (*c == ':')
c++;
- else if (*c & (*c != '\n'))
+ else if (*c && (*c != '\n'))
/* Expected ':', '\n', or NULL. */
goto badchar;
@@ -2060,7 +2060,7 @@ static int _stats_create_region(struct dm_stats *dms, uint64_t *region_id,
" %s %s %s", (start || len) ? range : "-",
(step < 0) ? "/" : "",
(uint64_t)llabs(step),
- opt_args, program_id, aux_data) < 0) {
+ opt_args, program_id, aux_data_escaped) < 0) {
err = "message";
goto_bad;
}
--
2.54.0

View File

@ -0,0 +1,32 @@
From 103e03c497d82b7338780b25549852c9345d0fb8 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 12:06:47 +0200
Subject: [PATCH 120/211] libdm: report: fix missing negation in time range
not-equal
The NOT-EQUAL case for time comparison was returning the same
expression as EQUAL when a range was specified, missing the
negation. The int and double comparison functions have it correct.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit f3ffd13dd1d91559812579b63779d91559467968)
---
libdm/libdm-report.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libdm/libdm-report.c b/libdm/libdm-report.c
index 87c588c3c..f2f0774cd 100644
--- a/libdm/libdm-report.c
+++ b/libdm/libdm-report.c
@@ -1731,7 +1731,7 @@ static int _cmp_field_time(struct dm_report *rh,
case FLD_CMP_EQUAL:
return range ? ((val >= sel1) && (val <= sel2)) : val == sel1;
case FLD_CMP_NOT|FLD_CMP_EQUAL:
- return range ? ((val >= sel1) && (val <= sel2)) : val != sel1;
+ return range ? !((val >= sel1) && (val <= sel2)) : val != sel1;
case FLD_CMP_TIME|FLD_CMP_GT:
if (_check_value_is_strictly_reserved(rh, field_num, DM_REPORT_FIELD_TYPE_TIME, &val, fs))
return 0;
--
2.54.0

View File

@ -0,0 +1,62 @@
From 9af697082ac096eb6eaff7acf7ebaef81a4a2940 Mon Sep 17 00:00:00 2001
From: Peter Rajnoha <prajnoha@redhat.com>
Date: Thu, 26 Mar 2026 16:34:04 +0100
Subject: [PATCH 121/211] libdm: report: fix row sorting of percent fields
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
_row_compare was treating the sort_value of DM_REPORT_FIELD_TYPE_PERCENT
fields as plain char pointer, but it is actually an integer. This
caused incorrect sorting of these fields.
For example, before this patch:
lvs -o lv_name,data_percent -O data_percent vg
lv_name data_percent
pool2 20.00
pool 100.00
lvol3 0.00
lvol4 0.00
lvol7 0.00
lvol6 1.95
lvol2 9.77
With this patch applied:
lvs -o lv_name,data_percent -O data_percent vg
lv_name data_percent
lvol3 0.00
lvol4 0.00
lvol7 0.00
lvol6 1.95
lvol2 9.77
pool2 20.00
pool 100.00
(cherry picked from commit 1e80cdad1e7e20825040e9a80ffeff39719d5ca8)
---
libdm/libdm-report.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/libdm/libdm-report.c b/libdm/libdm-report.c
index f2f0774cd..6022fef8f 100644
--- a/libdm/libdm-report.c
+++ b/libdm/libdm-report.c
@@ -4623,9 +4623,10 @@ static int _row_compare(const void *a, const void *b)
sfa = (*rowa->sort_fields)[cnt];
sfb = (*rowb->sort_fields)[cnt];
if (sfa->props->flags &
- ((DM_REPORT_FIELD_TYPE_NUMBER) |
- (DM_REPORT_FIELD_TYPE_SIZE) |
- (DM_REPORT_FIELD_TYPE_TIME))) {
+ (DM_REPORT_FIELD_TYPE_NUMBER |
+ DM_REPORT_FIELD_TYPE_SIZE |
+ DM_REPORT_FIELD_TYPE_PERCENT |
+ DM_REPORT_FIELD_TYPE_TIME)) {
const uint64_t numa =
*(const uint64_t *) sfa->sort_value;
const uint64_t numb =
--
2.54.0

View File

@ -0,0 +1,80 @@
From f62fa4b34736978650523e2ffed0d6d0acb249a1 Mon Sep 17 00:00:00 2001
From: Peter Rajnoha <prajnoha@redhat.com>
Date: Thu, 26 Mar 2026 12:42:32 +0100
Subject: [PATCH 122/211] libdm: fix row sorting for string list fields
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
_row_compare was treating the sort_value of DM_REPORT_FIELD_TYPE_STRING_LIST
fields as a plain char pointer, but it is actually a pointer to
struct str_list_sort_value. This caused strcmp to compare the raw bytes
of the struct (starting with a pointer) instead of the actual string
content, producing effectively random sort order for string list fields.
Fix by extracting the display string (sortval->value) from
str_list_sort_value before comparing with strcmp.
For example, before this patch:
lvs -o lv_name,lv_layout -O lv_layout vg
lv_name lv_layout
lvol5 raid,raid1
lvol4 thin,sparse
lvol3 thin,sparse
lvol2 thin,sparse
lvol0 raid,raid1
pool thin,pool
With this patch applied:
lvs -o lv_name,lv_layout -O lv_layout vg
lv_name lv_layout
lvol0 raid,raid1
lvol5 raid,raid1
pool thin,pool
lvol2 thin,sparse
lvol3 thin,sparse
lvol4 thin,sparse
Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit 424e995f6daeb56eeccec4b3d54d151d4e259040)
---
libdm/libdm-report.c | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/libdm/libdm-report.c b/libdm/libdm-report.c
index 6022fef8f..13bc9a1e5 100644
--- a/libdm/libdm-report.c
+++ b/libdm/libdm-report.c
@@ -4640,12 +4640,22 @@ static int _row_compare(const void *a, const void *b)
} else { /* FLD_DESCENDING */
return (numa < numb) ? 1 : -1;
}
+ } else if (sfa->props->flags & DM_REPORT_FIELD_TYPE_STRING_LIST) {
+ int cmp = strcmp(((const struct str_list_sort_value *) sfa->sort_value)->value,
+ ((const struct str_list_sort_value *) sfb->sort_value)->value);
+
+ if (!cmp)
+ continue;
+
+ if (sfa->props->flags & FLD_ASCENDING) {
+ return (cmp > 0) ? 1 : -1;
+ } else { /* FLD_DESCENDING */
+ return (cmp < 0) ? 1 : -1;
+ }
} else {
- /* DM_REPORT_FIELD_TYPE_STRING
- * DM_REPORT_FIELD_TYPE_STRING_LIST */
- const char *stra = (const char *) sfa->sort_value;
- const char *strb = (const char *) sfb->sort_value;
- int cmp = strcmp(stra, strb);
+ /* DM_REPORT_FIELD_TYPE_STRING and others */
+ int cmp = strcmp((const char *) sfa->sort_value,
+ (const char *) sfb->sort_value);
if (!cmp)
continue;
--
2.54.0

View File

@ -0,0 +1,130 @@
From 78dc9788be1a9296340f66b3168a6fd48dbf31b1 Mon Sep 17 00:00:00 2001
From: Peter Rajnoha <prajnoha@redhat.com>
Date: Thu, 26 Mar 2026 12:42:32 +0100
Subject: [PATCH 123/211] libdm: report: fix row sorting for string list fields
(continued)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
(extending commit 424e995f6daeb56eeccec4b3d54d151d4e259040)
_row_compare was treating the sort_value of DM_REPORT_FIELD_TYPE_STRING_LIST
fields as plain char pointer, but it was actually a pointer to
struct str_list_sort_value. This caused incorrect sorting of these fields.
Also add an internal error report if a field type is not handled
for sorting, so we notice if a new type is added without a sorting
handler.
For example, before this patch:
lvs -o lv_name,lv_layout -O lv_layout vg
lv_name lv_layout
lvol5 raid,raid1
lvol4 thin,sparse
lvol3 thin,sparse
lvol2 thin,sparse
lvol0 raid,raid1
pool thin,pool
With this patch applied:
lvs -o lv_name,lv_layout -O lv_layout vg
lv_name lv_layout
lvol0 raid,raid1
lvol5 raid,raid1
pool thin,pool
lvol2 thin,sparse
lvol3 thin,sparse
lvol4 thin,sparse
Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit fabee343f79327bcfd9ca653bfcd4af4c03ad693)
---
libdm/libdm-report.c | 52 ++++++++++++++++++++------------------------
1 file changed, 24 insertions(+), 28 deletions(-)
diff --git a/libdm/libdm-report.c b/libdm/libdm-report.c
index 13bc9a1e5..a92f96720 100644
--- a/libdm/libdm-report.c
+++ b/libdm/libdm-report.c
@@ -4618,10 +4618,12 @@ static int _row_compare(const void *a, const void *b)
const struct row *rowb = *(const struct row * const *) b;
const struct dm_report_field *sfa, *sfb;
uint32_t cnt;
+ int cmp;
for (cnt = 0; cnt < rowa->rh->keys_count; cnt++) {
sfa = (*rowa->sort_fields)[cnt];
sfb = (*rowb->sort_fields)[cnt];
+
if (sfa->props->flags &
(DM_REPORT_FIELD_TYPE_NUMBER |
DM_REPORT_FIELD_TYPE_SIZE |
@@ -4632,40 +4634,34 @@ static int _row_compare(const void *a, const void *b)
const uint64_t numb =
*(const uint64_t *) sfb->sort_value;
- if (numa == numb)
- continue;
+ cmp = (numa == numb) ? 0 : (numa > numb) ? 1 : -1;
+ } else if (sfa->props->flags &
+ (DM_REPORT_FIELD_TYPE_STRING |
+ DM_REPORT_FIELD_TYPE_STRING_LIST)) {
+ const char *stra, *strb;
- if (sfa->props->flags & FLD_ASCENDING) {
- return (numa > numb) ? 1 : -1;
- } else { /* FLD_DESCENDING */
- return (numa < numb) ? 1 : -1;
+ if (sfa->props->flags & DM_REPORT_FIELD_TYPE_STRING_LIST) {
+ stra = ((const struct str_list_sort_value *) sfa->sort_value)->value;
+ strb = ((const struct str_list_sort_value *) sfb->sort_value)->value;
+ } else {
+ stra = (const char *) sfa->sort_value;
+ strb = (const char *) sfb->sort_value;
}
- } else if (sfa->props->flags & DM_REPORT_FIELD_TYPE_STRING_LIST) {
- int cmp = strcmp(((const struct str_list_sort_value *) sfa->sort_value)->value,
- ((const struct str_list_sort_value *) sfb->sort_value)->value);
- if (!cmp)
- continue;
-
- if (sfa->props->flags & FLD_ASCENDING) {
- return (cmp > 0) ? 1 : -1;
- } else { /* FLD_DESCENDING */
- return (cmp < 0) ? 1 : -1;
- }
+ cmp = strcmp(stra, strb);
} else {
- /* DM_REPORT_FIELD_TYPE_STRING and others */
- int cmp = strcmp((const char *) sfa->sort_value,
- (const char *) sfb->sort_value);
+ log_err_once(INTERNAL_ERROR "_row_compare: unhandled field type: %#x",
+ sfa->props->flags & DM_REPORT_FIELD_TYPE_MASK);
+ return 0;
+ }
- if (!cmp)
- continue;
+ if (!cmp)
+ continue;
- if (sfa->props->flags & FLD_ASCENDING) {
- return (cmp > 0) ? 1 : -1;
- } else { /* FLD_DESCENDING */
- return (cmp < 0) ? 1 : -1;
- }
- }
+ if (sfa->props->flags & FLD_ASCENDING)
+ return (cmp > 0) ? 1 : -1;
+ else /* FLD_DESCENDING */
+ return (cmp < 0) ? 1 : -1;
}
return 0; /* Identical */
--
2.54.0

View File

@ -0,0 +1,31 @@
From e106d5239e4687f27cefff9812b3028111df6838 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 08:59:00 +0200
Subject: [PATCH 124/211] device_id: fix checking wrong buffer after
format_general_id
Check outbuf (the output buffer) instead of buf (the input buffer)
after format_general_id, matching the correct pattern at line 805.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 4116375f61cad895d0bce2f2ff0ee03ef5188e85)
---
lib/device/device_id.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/device/device_id.c b/lib/device/device_id.c
index 6ec8f43ba..a55074c3c 100644
--- a/lib/device/device_id.c
+++ b/lib/device/device_id.c
@@ -821,7 +821,7 @@ static int _dev_read_sys_serial(struct cmd_context *cmd, struct device *dev,
ret = 0;
if (ret) {
format_general_id((const char *)buf, sizeof(buf), (unsigned char *)outbuf, outbufsize);
- if (buf[0])
+ if (outbuf[0])
return 1;
}
}
--
2.54.0

View File

@ -0,0 +1,111 @@
From 56b263eb9f539e7bc07b0666780048addbfcaede Mon Sep 17 00:00:00 2001
From: David Teigland <teigland@redhat.com>
Date: Thu, 6 Nov 2025 16:45:28 -0600
Subject: [PATCH 125/211] device_id: prevent incorrect dm uuid idtype
If a dm-uuid-based device was added to system.devices, and
the wrong dm-based type was specified, then the system.devices
entry would have the correct idname but the wrong idtype. e.g.
lvmdevices --adddev /dev/mapper/mpatha --deviceidtype crypt_uuid
would add an entry with: IDTYPE=crypt_uuid IDNAME=mpath-<uuid>
rather than the correct: IDTYPE=mpath_uuid IDNAME=mpath-<uuid>
This mistaken type would not affect lvm, and the device would
be entirely usable. The dm-based types (mpath, crypt, lvmlv)
were interchangable because they all read the device id from
the same dm/uuid sysfs file.
Fix this by adding a missing check that the dm uuid prefix is
correct for the specified idtype. Also, accept an existing
system.devices file which contains this mistake.
(cherry picked from commit f0b991a9ef9b0e232cf38df44693a1b935ed93d3)
---
lib/device/device_id.c | 54 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 53 insertions(+), 1 deletion(-)
diff --git a/lib/device/device_id.c b/lib/device/device_id.c
index a55074c3c..91c998fe2 100644
--- a/lib/device/device_id.c
+++ b/lib/device/device_id.c
@@ -845,9 +845,22 @@ char *device_id_system_read(struct cmd_context *cmd, struct device *dev, uint16_
_dev_read_sys_serial(cmd, dev, sysbuf, sizeof(sysbuf));
break;
case DEV_ID_TYPE_MPATH_UUID:
+ if (!dev_dm_uuid(cmd, dev, sysbuf, sizeof(sysbuf)))
+ stack;
+ if (sysbuf[0] && !dm_uuid_has_prefix(sysbuf, "mpath-"))
+ sysbuf[0] = '\0';
+ break;
case DEV_ID_TYPE_CRYPT_UUID:
+ if (!dev_dm_uuid(cmd, dev, sysbuf, sizeof(sysbuf)))
+ stack;
+ if (sysbuf[0] && !dm_uuid_has_prefix(sysbuf, "CRYPT-"))
+ sysbuf[0] = '\0';
+ break;
case DEV_ID_TYPE_LVMLV_UUID:
- (void)dev_dm_uuid(cmd, dev, sysbuf, sizeof(sysbuf));
+ if (!dev_dm_uuid(cmd, dev, sysbuf, sizeof(sysbuf)))
+ stack;
+ if (sysbuf[0] && !dm_uuid_has_prefix(sysbuf, "LVM-"))
+ sysbuf[0] = '\0';
break;
case DEV_ID_TYPE_MD_UUID:
read_sys_block(cmd, dev, "md/uuid", sysbuf, sizeof(sysbuf));
@@ -2524,6 +2537,33 @@ static int _match_dm_names(struct cmd_context *cmd, char *idname, struct device
return 0;
}
+static void _replace_incorrect_dm_idtype(struct dev_use *du)
+{
+ /* Previous bug let one of these types be swapped for another
+ because they all read their value from sysfs file dm/uuid. */
+ if (du->idtype != DEV_ID_TYPE_MPATH_UUID &&
+ du->idtype != DEV_ID_TYPE_CRYPT_UUID &&
+ du->idtype != DEV_ID_TYPE_LVMLV_UUID)
+ return;
+
+ /* Use the IDNAME value to determine the correct IDTYPE. */
+ if (dm_uuid_has_prefix(du->idname, "mpath-") && (du->idtype != DEV_ID_TYPE_MPATH_UUID)) {
+ log_debug("replace incorrect mpath device id type for %u %s", du->idtype, du->idname);
+ du->idtype = DEV_ID_TYPE_MPATH_UUID;
+ return;
+ }
+ if (dm_uuid_has_prefix(du->idname, "CRYPT-") && (du->idtype != DEV_ID_TYPE_CRYPT_UUID)) {
+ log_debug("replace incorrect crypt device id type for %u %s", du->idtype, du->idname);
+ du->idtype = DEV_ID_TYPE_CRYPT_UUID;
+ return;
+ }
+ if (dm_uuid_has_prefix(du->idname, "LVM-") && (du->idtype != DEV_ID_TYPE_LVMLV_UUID)) {
+ log_debug("replace incorrect lvmlv device id type for %u %s", du->idtype, du->idname);
+ du->idtype = DEV_ID_TYPE_LVMLV_UUID;
+ return;
+ }
+}
+
/*
* du is a devices file entry. dev is any device on the system.
* check if du is for dev by comparing the device's ids to du->idname.
@@ -2634,6 +2674,18 @@ static int _match_du_to_dev(struct cmd_context *cmd, struct dev_use *du, struct
_reduce_repeating_underscores(du_idname, sizeof(du_idname));
}
+ /*
+ * A past bug allowed mpath, crypt, or lvm devices to be added to
+ * system.devices using any of the dm id types, and they could be
+ * used normally. Accept an old system.devices with that mistake
+ * by updating du->idtype to have the correct idtype based on the
+ * idname dm prefix.
+ */
+ if (((du->idtype == DEV_ID_TYPE_MPATH_UUID) && !dm_uuid_has_prefix(du->idname, "mpath-")) ||
+ ((du->idtype == DEV_ID_TYPE_CRYPT_UUID) && !dm_uuid_has_prefix(du->idname, "CRYPT-")) ||
+ ((du->idtype == DEV_ID_TYPE_LVMLV_UUID) && !dm_uuid_has_prefix(du->idname, "LVM")))
+ _replace_incorrect_dm_idtype(du);
+
/*
* Try to match du with ids that have already been read for the dev
* (and saved on dev->ids to avoid rereading.)
--
2.54.0

View File

@ -0,0 +1,32 @@
From 0ecb57749f1bb0971510bf4c1a1692d0a4a51525 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 11:12:02 +0200
Subject: [PATCH 126/211] lvmcmdline: fix line-too-long check using wrong
buffer index
The expression buffer[sizeof(buffer) - 1] - 2 performed arithmetic
on the char value at the last position instead of indexing position
sizeof(buffer) - 2 to check for newline.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 79eca4f6a5061c9ec4f544122edd341799d38ec6)
---
tools/lvmcmdline.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c
index 2ecdbd164..df11f57ab 100644
--- a/tools/lvmcmdline.c
+++ b/tools/lvmcmdline.c
@@ -3526,7 +3526,7 @@ static int _run_script(struct cmd_context *cmd, int argc, char **argv)
}
}
if ((strlen(buffer) == sizeof(buffer) - 1)
- && (buffer[sizeof(buffer) - 1] - 2 != '\n')) {
+ && (buffer[sizeof(buffer) - 2] != '\n')) {
buffer[50] = '\0';
log_error("Line too long (max 255) beginning: %s",
buffer);
--
2.54.0

View File

@ -0,0 +1,32 @@
From 007326d4b11a22bc27e3e96ebd45d63f009ac82a Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 08:33:48 +0200
Subject: [PATCH 127/211] report: fix lvseg_metadata_devices_str calling wrong
function
lvseg_metadata_devices_str() was calling lvseg_devices() instead
of lvseg_metadata_devices(), causing the metadata_devices report
field to return data devices instead of metadata devices.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit aba1e6f1aed9cf2cf22ba53b54f06462107426b7)
---
lib/metadata/lv.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c
index d201244a7..164ad7aba 100644
--- a/lib/metadata/lv.c
+++ b/lib/metadata/lv.c
@@ -158,7 +158,7 @@ char *lvseg_metadata_devices_str(struct dm_pool *mem, const struct lv_segment *s
{
struct dm_list *list;
- if (!(list = lvseg_devices(mem, seg)))
+ if (!(list = lvseg_metadata_devices(mem, seg)))
return_NULL;
return str_list_to_str(mem, list, ",");
--
2.54.0

View File

@ -0,0 +1,31 @@
From 9e70f14abecee521f3036d0a8bca90646f8f83c2 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 08:34:15 +0200
Subject: [PATCH 128/211] report: fix lvseg_seg_le_ranges_str calling wrong
function
lvseg_seg_le_ranges_str() was calling lvseg_seg_pe_ranges() instead
of lvseg_seg_le_ranges(), missing the report_mark_hidden_devices flag.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 826632ee8890fd9305f270f6c3ee09ad4fd8422c)
---
lib/metadata/lv.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c
index 164ad7aba..7476cef8d 100644
--- a/lib/metadata/lv.c
+++ b/lib/metadata/lv.c
@@ -188,7 +188,7 @@ char *lvseg_seg_le_ranges_str(struct dm_pool *mem, const struct lv_segment *seg)
{
struct dm_list *list;
- if (!(list = lvseg_seg_pe_ranges(mem, seg)))
+ if (!(list = lvseg_seg_le_ranges(mem, seg)))
return_NULL;
return str_list_to_str(mem, list, seg->lv->vg->cmd->report_list_item_separator);
--
2.54.0

View File

@ -0,0 +1,31 @@
From 84f108796ba80141465c035de75d36ac52a08b23 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 11:42:41 +0200
Subject: [PATCH 129/211] vgchange: add NULL check for vg->system_id before
strcmp
vg->system_id can be NULL (set in vg.c and metadata.c), so
strcmp without a NULL check may cause undefined behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 0e95d6933e2901e765a34d849f70ad718af66cf5)
---
tools/vgchange.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/vgchange.c b/tools/vgchange.c
index deefbfe6d..2ffd73ca1 100644
--- a/tools/vgchange.c
+++ b/tools/vgchange.c
@@ -559,7 +559,7 @@ static int _vgchange_system_id(struct cmd_context *cmd, struct volume_group *vg)
return 0;
}
- if (!strcmp(vg->system_id, system_id)) {
+ if (vg->system_id && !strcmp(vg->system_id, system_id)) {
log_error("Volume Group system ID is already \"%s\".", vg->system_id);
return 0;
}
--
2.54.0

View File

@ -0,0 +1,34 @@
From 6adbfc16d1b8ba3d15bb7dc5d0e1998bb75349a5 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 11:59:23 +0200
Subject: [PATCH 130/211] libdm: iface: use st_rdev instead of st_mode for
MAJOR/MINOR
The log message was extracting major/minor from buf.st_mode
(file type/permissions) instead of buf.st_rdev (device number).
The comparison on line above correctly uses buf.st_rdev.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 42fb34bcf836c3356bf8f23d24bac2fe34d35346)
---
libdm/ioctl/libdm-iface.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libdm/ioctl/libdm-iface.c b/libdm/ioctl/libdm-iface.c
index 3a3060475..4bbb40a7d 100644
--- a/libdm/ioctl/libdm-iface.c
+++ b/libdm/ioctl/libdm-iface.c
@@ -278,8 +278,8 @@ static int _control_exists(const char *control, uint32_t major, uint32_t minor)
if (major && buf.st_rdev != MKDEV(major, minor)) {
log_verbose("%s: Wrong device number: (%u, %u) instead of "
- "(%u, %u)", control,
- MAJOR(buf.st_mode), MINOR(buf.st_mode),
+ "(%u, %u).", control,
+ MAJOR(buf.st_rdev), MINOR(buf.st_rdev),
major, minor);
return _control_unlink(control);
}
--
2.54.0

View File

@ -0,0 +1,31 @@
From c238b6ccc110030a272b1d07e229edc2e0db28e6 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 01:25:37 +0200
Subject: [PATCH 131/211] lvmcache: fix unnecessary address-of on array in
memcmp
Remove extra '&' before 'pvid' array in _get_pvsummary_device_id
memcmp call, matching the style of the other two pvsummary functions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 27afb397ff88c514cf21a4b02a1e04f5c04a8332)
---
lib/cache/lvmcache.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index 1effc254c..4c95db7db 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -524,7 +524,7 @@ static const char *_get_pvsummary_device_id(const char *pvid_arg, const char **d
dm_list_iterate_items(vginfo, &_vginfos) {
dm_list_iterate_items(pvl, &vginfo->pvsummaries) {
- if (!memcmp(&pvid, &pvl->pv->id.uuid, ID_LEN)) {
+ if (!memcmp(pvid, &pvl->pv->id.uuid, ID_LEN)) {
*device_id_type = pvl->pv->device_id_type;
return pvl->pv->device_id;
}
--
2.54.0

View File

@ -0,0 +1,115 @@
From 444317ac2c2c6d40271c1e4ced0c95726c925de5 Mon Sep 17 00:00:00 2001
From: Marian Csontos <mcsontos@redhat.com>
Date: Fri, 15 May 2026 10:34:34 +0200
Subject: [PATCH 132/211] RHEL9: fix compilation issues
---
lib/device/device_id.c | 18 +++++++++---------
lib/metadata/lv.c | 2 +-
lib/report/report.c | 2 +-
tools/reporter.c | 2 +-
4 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/lib/device/device_id.c b/lib/device/device_id.c
index 91c998fe2..a83257e50 100644
--- a/lib/device/device_id.c
+++ b/lib/device/device_id.c
@@ -847,19 +847,19 @@ char *device_id_system_read(struct cmd_context *cmd, struct device *dev, uint16_
case DEV_ID_TYPE_MPATH_UUID:
if (!dev_dm_uuid(cmd, dev, sysbuf, sizeof(sysbuf)))
stack;
- if (sysbuf[0] && !dm_uuid_has_prefix(sysbuf, "mpath-"))
+ if (sysbuf[0] && !_dm_uuid_has_prefix(sysbuf, "mpath-"))
sysbuf[0] = '\0';
break;
case DEV_ID_TYPE_CRYPT_UUID:
if (!dev_dm_uuid(cmd, dev, sysbuf, sizeof(sysbuf)))
stack;
- if (sysbuf[0] && !dm_uuid_has_prefix(sysbuf, "CRYPT-"))
+ if (sysbuf[0] && !_dm_uuid_has_prefix(sysbuf, "CRYPT-"))
sysbuf[0] = '\0';
break;
case DEV_ID_TYPE_LVMLV_UUID:
if (!dev_dm_uuid(cmd, dev, sysbuf, sizeof(sysbuf)))
stack;
- if (sysbuf[0] && !dm_uuid_has_prefix(sysbuf, "LVM-"))
+ if (sysbuf[0] && !_dm_uuid_has_prefix(sysbuf, "LVM-"))
sysbuf[0] = '\0';
break;
case DEV_ID_TYPE_MD_UUID:
@@ -2547,17 +2547,17 @@ static void _replace_incorrect_dm_idtype(struct dev_use *du)
return;
/* Use the IDNAME value to determine the correct IDTYPE. */
- if (dm_uuid_has_prefix(du->idname, "mpath-") && (du->idtype != DEV_ID_TYPE_MPATH_UUID)) {
+ if (_dm_uuid_has_prefix(du->idname, "mpath-") && (du->idtype != DEV_ID_TYPE_MPATH_UUID)) {
log_debug("replace incorrect mpath device id type for %u %s", du->idtype, du->idname);
du->idtype = DEV_ID_TYPE_MPATH_UUID;
return;
}
- if (dm_uuid_has_prefix(du->idname, "CRYPT-") && (du->idtype != DEV_ID_TYPE_CRYPT_UUID)) {
+ if (_dm_uuid_has_prefix(du->idname, "CRYPT-") && (du->idtype != DEV_ID_TYPE_CRYPT_UUID)) {
log_debug("replace incorrect crypt device id type for %u %s", du->idtype, du->idname);
du->idtype = DEV_ID_TYPE_CRYPT_UUID;
return;
}
- if (dm_uuid_has_prefix(du->idname, "LVM-") && (du->idtype != DEV_ID_TYPE_LVMLV_UUID)) {
+ if (_dm_uuid_has_prefix(du->idname, "LVM-") && (du->idtype != DEV_ID_TYPE_LVMLV_UUID)) {
log_debug("replace incorrect lvmlv device id type for %u %s", du->idtype, du->idname);
du->idtype = DEV_ID_TYPE_LVMLV_UUID;
return;
@@ -2681,9 +2681,9 @@ static int _match_du_to_dev(struct cmd_context *cmd, struct dev_use *du, struct
* by updating du->idtype to have the correct idtype based on the
* idname dm prefix.
*/
- if (((du->idtype == DEV_ID_TYPE_MPATH_UUID) && !dm_uuid_has_prefix(du->idname, "mpath-")) ||
- ((du->idtype == DEV_ID_TYPE_CRYPT_UUID) && !dm_uuid_has_prefix(du->idname, "CRYPT-")) ||
- ((du->idtype == DEV_ID_TYPE_LVMLV_UUID) && !dm_uuid_has_prefix(du->idname, "LVM")))
+ if (((du->idtype == DEV_ID_TYPE_MPATH_UUID) && !_dm_uuid_has_prefix(du->idname, "mpath-")) ||
+ ((du->idtype == DEV_ID_TYPE_CRYPT_UUID) && !_dm_uuid_has_prefix(du->idname, "CRYPT-")) ||
+ ((du->idtype == DEV_ID_TYPE_LVMLV_UUID) && !_dm_uuid_has_prefix(du->idname, "LVM")))
_replace_incorrect_dm_idtype(du);
/*
diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c
index 7476cef8d..0917cdd4e 100644
--- a/lib/metadata/lv.c
+++ b/lib/metadata/lv.c
@@ -1552,7 +1552,7 @@ char *lv_attr_dup_with_info_and_seg_status(struct dm_pool *mem, const struct lv_
if (!activation())
repstr[8] = 'X'; /* Unknown */
- else if (!lv_raid_healthy(lv))
+ else if (!lv_raid_healthy(lv, NULL))
repstr[8] = 'r'; /* RAID needs 'r'efresh */
else if (lv_is_raid(lv)) {
if (lv_raid_mismatch_count(lv, &n) && n)
diff --git a/lib/report/report.c b/lib/report/report.c
index 049accf41..ab0675f69 100644
--- a/lib/report/report.c
+++ b/lib/report/report.c
@@ -4005,7 +4005,7 @@ static int _lvhealthstatus_disp(struct dm_report *rh, struct dm_pool *mem,
else if (lv_is_raid_type(lv)) {
if (!activation())
health = "unknown";
- else if (!lv_raid_healthy(lv))
+ else if (!lv_raid_healthy(lv, NULL))
health = "refresh needed";
else if (lv_is_raid(lv)) {
if (lv_raid_mismatch_count(lv, &n) && n)
diff --git a/tools/reporter.c b/tools/reporter.c
index e88b8fa72..f4255680c 100644
--- a/tools/reporter.c
+++ b/tools/reporter.c
@@ -155,7 +155,7 @@ static int _check_merging_origin(const struct logical_volume *lv,
static void _cond_warn_raid_volume_health(struct cmd_context *cmd, const struct logical_volume *lv)
{
- if (lv_is_raid(lv) && !lv_raid_healthy(lv) && !lv_is_partial(lv))
+ if (lv_is_raid(lv) && !lv_raid_healthy(lv, NULL) && !lv_is_partial(lv))
log_warn("WARNING: RaidLV %s needs to be refreshed! See character 'r' at position 9 in the RaidLV's attributes%s.", display_lvname(lv),
arg_is_set(cmd, all_ARG) ? " and its SubLV(s)" : " and also its SubLV(s) with option '-a'");
}
--
2.54.0

View File

@ -0,0 +1,37 @@
From 4cd7e5b0d9d00995153702a068a02c643b079b5f Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Wed, 1 Apr 2026 22:37:32 +0200
Subject: [PATCH 133/211] cov: command: fix NULL dereference in
_add_oo_definition_line
Check strchr return value before adding offset. If the line
has no colon, strchr returns NULL and the +2 produces an
invalid pointer passed to strdup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit a794299bb03fbd88f35780ad9005060a2b558804)
---
tools/command.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/tools/command.c b/tools/command.c
index db1058bd7..519bb7eb3 100644
--- a/tools/command.c
+++ b/tools/command.c
@@ -667,7 +667,12 @@ static void _add_oo_definition_line(const char *name, const char *line)
return;
}
- start = strchr(line, ':') + 2;
+ if (!(start = strchr(line, ':'))) {
+ log_error("Parsing command defs: invalid OO line.");
+ return;
+ }
+ start += 2;
+
if (!(oo->line = strdup(start))) {
log_error("Failed to duplicate line %s.", start);
return;
--
2.54.0

View File

@ -0,0 +1,52 @@
From 2742b532fbe93dc2d19725031ebc85cdbcac36f5 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 10:16:17 +0200
Subject: [PATCH 134/211] bcache: fix flush orphaning locked dirty blocks
bcache_flush used _list_pop to remove blocks from the dirty list
before checking ref_count. Blocks still locked (e.g. superblock)
were popped but never re-added to any list, orphaning them.
Switch to dm_list_iterate_items_gen_safe matching the pattern
used by _writeback(). _issue_write() internally moves written
blocks from dirty to io_pending via dm_list_move(), while locked
blocks are skipped and remain on the dirty list.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit b81b48881d4af2bc28dff257ccbf583b129b9204)
---
lib/device/bcache.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/lib/device/bcache.c b/lib/device/bcache.c
index c61f7a5d5..afa0315fd 100644
--- a/lib/device/bcache.c
+++ b/lib/device/bcache.c
@@ -1294,15 +1294,15 @@ bool bcache_flush(struct bcache *cache)
// try and rewrite everything.
dm_list_splice(&cache->dirty, &cache->errored);
- while (!dm_list_empty(&cache->dirty)) {
- struct block *b = dm_list_item(_list_pop(&cache->dirty), struct block);
- if (b->ref_count || _test_flags(b, BF_IO_PENDING)) {
- // The superblock may well be still locked.
- continue;
- }
-
- _issue_write(b);
- }
+ /*
+ * _writeback() uses safe iteration and skips locked blocks
+ * (ref_count > 0). _issue_write() moves written blocks from
+ * dirty to io_pending via dm_list_move() in _issue_low_level().
+ * BF_IO_PENDING cannot occur here - _issue_low_level() moves
+ * the block off dirty when setting that flag, and _complete_io()
+ * clears it before moving to errored/clean.
+ */
+ _writeback(cache, cache->nr_cache_blocks);
_wait_all(cache);
--
2.54.0

View File

@ -0,0 +1,32 @@
From 12de2eed0e6bcf34f3f019b53fd7a412299ca429 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 12:41:41 +0200
Subject: [PATCH 135/211] dmeventd: fix inverted systemd activation env var
check
strcmp(e, "1") returns 0 (false) when strings match, so the condition
was setting _systemd_activation when env var was NOT "1" and skipping
it when it WAS "1" -- the exact opposite of the intended behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 6c0aad9e0f8e844eeae4350d79ba2674c7c4342d)
---
daemons/dmeventd/dmeventd.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/daemons/dmeventd/dmeventd.c b/daemons/dmeventd/dmeventd.c
index 0c3880787..1ae5ef629 100644
--- a/daemons/dmeventd/dmeventd.c
+++ b/daemons/dmeventd/dmeventd.c
@@ -2233,7 +2233,7 @@ static int _restart_dmeventd(struct dm_event_fifos *fifos)
}
if (!_systemd_activation &&
- ((e = getenv(SD_ACTIVATION_ENV_VAR_NAME)) && strcmp(e, "1")))
+ ((e = getenv(SD_ACTIVATION_ENV_VAR_NAME)) && !strcmp(e, "1")))
_systemd_activation = 1;
fini_fifos(fifos);
--
2.54.0

View File

@ -0,0 +1,32 @@
From b0a6b406e5ca1eec0727192193f6d6c2566e15f3 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 11:27:21 +0200
Subject: [PATCH 136/211] lvrename: fix historical LV prefix stripping using
wrong variable
When both old and new names have the historical prefix, the prefix
was stripped from lv_name_old instead of lv_name_new, leaving
lv_name_new with the prefix still attached.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 067fa3eb938bbb808978dcf21ac62236a0dff45b)
---
tools/lvrename.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/lvrename.c b/tools/lvrename.c
index 74248ee91..6fcf1db53 100644
--- a/tools/lvrename.c
+++ b/tools/lvrename.c
@@ -165,7 +165,7 @@ int lvrename(struct cmd_context *cmd, int argc, char **argv)
if (!strncmp(lv_name_new, HISTORICAL_LV_PREFIX, strlen(HISTORICAL_LV_PREFIX))) {
if (historical)
- lv_name_new = lv_name_old + strlen(HISTORICAL_LV_PREFIX);
+ lv_name_new = lv_name_new + strlen(HISTORICAL_LV_PREFIX);
else {
log_error("Old name references live LV while "
"new name is for historical LV.");
--
2.54.0

View File

@ -0,0 +1,30 @@
From 2a3765160281eda24e7a1aaa5089f86bc49fc688 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 11:35:25 +0200
Subject: [PATCH 137/211] lvmdevices: fix resource leak on delpvid duplicate
error path
When a duplicate PVID was found, du was already removed from the
list but never freed before goto bad, leaking the structure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit d0f9bf1c9f4438a369087edf6e51147b844ae336)
---
tools/lvmdevices.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/tools/lvmdevices.c b/tools/lvmdevices.c
index 65399c96c..78ff70455 100644
--- a/tools/lvmdevices.c
+++ b/tools/lvmdevices.c
@@ -1010,6 +1010,7 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
if ((du2 = get_du_for_pvid(cmd, pvid))) {
log_error("Multiple devices file entries for PVID %s (%s %s), remove by device name.",
pvid, du->devname, du2->devname);
+ free_du(du);
goto bad;
}
--
2.54.0

View File

@ -0,0 +1,37 @@
From 8ae8f80d5234875e22a753b664f8ffa77754fb20 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 09:04:35 +0200
Subject: [PATCH 138/211] dev-cache: fix missing dev_iter_destroy in
iterate_devs_for_index
Add missing dev_iter_destroy() call before return in
_dev_cache_iterate_devs_for_index().
The leak only occurs on the udev scanning path - the non-udev sysfs
fallback (_dev_cache_iterate_sysfs_for_index) reads sysfs directly
without using an iterator. Since the function is called once per
command, the leak is small (iterator struct + values array), which
explains why it was not caught in normal testing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 9d98bf8a448a58d56417daa127b8cd5002184c00)
---
lib/device/dev-cache.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lib/device/dev-cache.c b/lib/device/dev-cache.c
index 24a4d91c5..8f9ef1bd7 100644
--- a/lib/device/dev-cache.c
+++ b/lib/device/dev-cache.c
@@ -912,6 +912,8 @@ static int _dev_cache_iterate_devs_for_index(struct cmd_context *cmd)
if (!_index_dev_by_vgid_and_lvid(cmd, dev))
r = 0;
+ dev_iter_destroy(iter);
+
return r;
}
--
2.54.0

View File

@ -0,0 +1,30 @@
From 1b21775c5b6d64ecc7e2acde34eac00abe4a5bb7 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 10:07:28 +0200
Subject: [PATCH 139/211] import_vsn1: fix resource leak on historical LV id
read failure
Use goto bad instead of return 0 to properly free allocations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit dd4fd687ca3042d0c35b7a134cdb3eaaefaeec79)
---
lib/format_text/import_vsn1.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/format_text/import_vsn1.c b/lib/format_text/import_vsn1.c
index 4f76625a5..20b1739e9 100644
--- a/lib/format_text/import_vsn1.c
+++ b/lib/format_text/import_vsn1.c
@@ -792,7 +792,7 @@ static int _read_historical_lvnames(struct cmd_context *cmd,
if (!_read_id(&glv->historical->lvid.id[1], hlvn, "id")) {
log_error("Couldn't read uuid for removed logical volume %s in vg %s.",
glv->historical->name, vg->name);
- return 0;
+ goto bad;
}
memcpy(&glv->historical->lvid.id[0], &glv->historical->vg->id, sizeof(glv->historical->lvid.id[0]));
--
2.54.0

View File

@ -0,0 +1,36 @@
From 8dd9169a9128aca196b2b53bd7ede956bc24cd70 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 13:07:54 +0200
Subject: [PATCH 140/211] lvm-exec: fix close error msg and null_fd == fd case
1. Fix copy-paste error in log_sys_error: reported "dup2" instead of
"close" for the close(null_fd) call.
2. Guard close(null_fd) with null_fd != fd check. When the target fd
was already closed, open("/dev/null") reuses the same fd number,
dup2 is a no-op, and unconditional close would undo the setup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 0139987d8d200ae2688fba2029af6c52f441f632)
---
lib/misc/lvm-exec.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/misc/lvm-exec.c b/lib/misc/lvm-exec.c
index 37b392545..5ae33516c 100644
--- a/lib/misc/lvm-exec.c
+++ b/lib/misc/lvm-exec.c
@@ -136,8 +136,8 @@ static int _reopen_fd_to_null(int fd)
r = 1;
out:
- if (close(null_fd)) {
- log_sys_error("dup2", "");
+ if ((null_fd != fd) && close(null_fd)) {
+ log_sys_error("close", "/dev/null");
return 0;
}
--
2.54.0

View File

@ -0,0 +1,43 @@
From 159c89d0d33161e18dd75a53891528747313c62e Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Apr 2026 18:09:34 +0200
Subject: [PATCH 141/211] lvmlockctl: fix close error msg and null_fd == fd
case
Add missing null_fd != fd guards in _reopen_fd_to_null to match
lib/misc/lvm-exec.c implementation. Without these guards, if
open("/dev/null") returns the same fd number as the target fd
(e.g., fd 0 is already closed so open returns 0), the function
would incorrectly close the just-opened fd before dup2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit f5ca64812df94d7816f06cfa383a0b06086105e1)
---
daemons/lvmlockd/lvmlockctl.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/daemons/lvmlockd/lvmlockctl.c b/daemons/lvmlockd/lvmlockctl.c
index e36293260..63b7f77c6 100644
--- a/daemons/lvmlockd/lvmlockctl.c
+++ b/daemons/lvmlockd/lvmlockctl.c
@@ -726,7 +726,7 @@ static int _reopen_fd_to_null(int fd)
return 0;
}
- if (close(fd)) {
+ if ((null_fd != fd) && close(fd)) {
log_error("close error fd %d %d", fd, errno);
goto out;
}
@@ -738,7 +738,7 @@ static int _reopen_fd_to_null(int fd)
r = 1;
out:
- if (close(null_fd)) {
+ if ((null_fd != fd) && close(null_fd)) {
log_error("close error fd %d %d", null_fd, errno);
return 0;
}
--
2.54.0

View File

@ -0,0 +1,60 @@
From 82ab9fbd220d8f1461397dafbbbbc59d6fc15ba5 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 3 Oct 2025 20:14:31 +0200
Subject: [PATCH 142/211] bcache: do not call io_destroy in forked process
(cherry picked from commit 2b632d5f3a3fad50e3e39a89af58bd62d3a2d7e3)
---
lib/device/bcache.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/lib/device/bcache.c b/lib/device/bcache.c
index afa0315fd..b66c5fa7c 100644
--- a/lib/device/bcache.c
+++ b/lib/device/bcache.c
@@ -131,6 +131,7 @@ struct async_engine {
io_context_t aio_context;
struct cb_set *cbs;
unsigned page_mask;
+ pid_t aio_context_pid; /* PID that created this AIO context */
};
static struct async_engine *_to_async(struct io_engine *e)
@@ -140,15 +141,21 @@ static struct async_engine *_to_async(struct io_engine *e)
static void _async_destroy(struct io_engine *ioe)
{
- int r;
struct async_engine *e = _to_async(ioe);
_cb_set_destroy(e->cbs);
- // io_destroy is really slow
- r = io_destroy(e->aio_context);
- if (r)
- log_sys_warn("io_destroy");
+ /*
+ * Only call io_destroy() if we're in the same process that created
+ * the AIO context. After fork(), the child inherits the parent's
+ * aio_context value but must not call io_destroy() on it.
+ */
+ if (e->aio_context) {
+ if (e->aio_context_pid != getpid())
+ log_debug("Skipping io_destroy() for different pid.");
+ else if (io_destroy(e->aio_context)) // really slow
+ log_sys_warn("io_destroy");
+ }
free(e);
}
@@ -376,6 +383,7 @@ struct io_engine *create_async_io_engine(void)
e->e.max_io = _async_max_io;
e->aio_context = 0;
+ e->aio_context_pid = getpid();
r = io_setup(MAX_IO, &e->aio_context);
if (r < 0) {
log_debug("io_setup failed %d", r);
--
2.54.0

View File

@ -0,0 +1,36 @@
From f4cbf26376cea75db8d1ed8cfeebc11a94693c9f Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Mon, 8 Dec 2025 14:44:49 +0100
Subject: [PATCH 143/211] debug: add tracing for context destroy
Add trace note where the time is being lost.
Context destroy may take ~40ms in kernel.
(cherry picked from commit f1374c5876783301b26812b144d2d016bb5f519f)
---
lib/device/bcache.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/lib/device/bcache.c b/lib/device/bcache.c
index b66c5fa7c..915db250c 100644
--- a/lib/device/bcache.c
+++ b/lib/device/bcache.c
@@ -152,9 +152,12 @@ static void _async_destroy(struct io_engine *ioe)
*/
if (e->aio_context) {
if (e->aio_context_pid != getpid())
- log_debug("Skipping io_destroy() for different pid.");
- else if (io_destroy(e->aio_context)) // really slow
- log_sys_warn("io_destroy");
+ log_debug_devs("Skipping AIO context destroy for different pid.");
+ else {
+ log_debug_devs("Destroy AIO context.");
+ if (io_destroy(e->aio_context)) // really slow (~40ms)
+ log_sys_warn("io_destroy");
+ }
}
free(e);
--
2.54.0

View File

@ -0,0 +1,62 @@
From 884d5e0dd0afca46d2f72b9448a6fa9e1cddf1c1 Mon Sep 17 00:00:00 2001
From: Mike Gilbert <floppym@gentoo.org>
Date: Fri, 13 Feb 2026 21:47:12 -0500
Subject: [PATCH 144/211] bcache: report libaio errors properly
io_getevents and io_destroy return a negative error value instead of
setting errno.
Bug: https://bugs.gentoo.org/970017
(cherry picked from commit 512a3944893af8c1d4ce69c69d2ae587aebf2b08)
---
lib/device/bcache.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/lib/device/bcache.c b/lib/device/bcache.c
index 915db250c..2ecad6376 100644
--- a/lib/device/bcache.c
+++ b/lib/device/bcache.c
@@ -40,9 +40,9 @@ static int *_fd_table = NULL;
//----------------------------------------------------------------
-static void log_sys_warn(const char *call)
+static void log_sys_warn(const char *call, int err)
{
- log_warn("WARNING: %s failed: %s.", call, strerror(errno));
+ log_warn("WARNING: %s failed: %s.", call, strerror(err));
}
// Assumes the list is not empty.
@@ -141,6 +141,7 @@ static struct async_engine *_to_async(struct io_engine *e)
static void _async_destroy(struct io_engine *ioe)
{
+ int r;
struct async_engine *e = _to_async(ioe);
_cb_set_destroy(e->cbs);
@@ -155,8 +156,9 @@ static void _async_destroy(struct io_engine *ioe)
log_debug_devs("Skipping AIO context destroy for different pid.");
else {
log_debug_devs("Destroy AIO context.");
- if (io_destroy(e->aio_context)) // really slow (~40ms)
- log_sys_warn("io_destroy");
+ r = io_destroy(e->aio_context); // really slow (~40ms)
+ if (r < 0)
+ log_sys_warn("io_destroy", -r);
}
}
@@ -331,7 +333,7 @@ static bool _async_wait(struct io_engine *ioe, io_complete_fn fn)
r = io_getevents(e->aio_context, 1, MAX_EVENT, event, NULL);
if (r < 0) {
- log_sys_warn("io_getevents");
+ log_sys_warn("io_getevents", -r);
return false;
}
--
2.54.0

View File

@ -0,0 +1,30 @@
From 895b49acb8fd5a10d0bb7ae243fe430386aee0a2 Mon Sep 17 00:00:00 2001
From: Mike Gilbert <floppym@gentoo.org>
Date: Fri, 13 Feb 2026 22:03:32 -0500
Subject: [PATCH 145/211] bcache: retry io_getevents on EINTR
Bug: https://bugs.gentoo.org/970017
(cherry picked from commit 791a842dea11808d647c1a605ca244d0fc1cdf5d)
---
lib/device/bcache.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/lib/device/bcache.c b/lib/device/bcache.c
index 2ecad6376..4c91402c4 100644
--- a/lib/device/bcache.c
+++ b/lib/device/bcache.c
@@ -330,7 +330,10 @@ static bool _async_wait(struct io_engine *ioe, io_complete_fn fn)
struct async_engine *e = _to_async(ioe);
memset(&event, 0, sizeof(event));
- r = io_getevents(e->aio_context, 1, MAX_EVENT, event, NULL);
+
+ do {
+ r = io_getevents(e->aio_context, 1, MAX_EVENT, event, NULL);
+ } while (r == -EINTR);
if (r < 0) {
log_sys_warn("io_getevents", -r);
--
2.54.0

View File

@ -0,0 +1,56 @@
From 424b80266e954f6d612c23121325a891a2a8eec3 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Sat, 21 Feb 2026 10:31:45 +0100
Subject: [PATCH 146/211] bcache: retry io_getevents() only if sigint not
caught
Stray signals (SIGALRM, SIGCHLD, SIGWINCH, etc.) cause EINTR
but should not abort I/O - the loop retries transparently.
SIGINT/SIGTERM delivered inside a sigint_allow()/sigint_restore()
window set sigint_caught(), which stops the loop and lets the
caller detect and handle the cancellation.
LVM supports io_getevents() interruptible in commit b3c7a2b3f0bc.
However when read-only command do not use signal handlers,
this sigint_caught() check is effectively ignored. But in this case
command will be interrupted with the default signal handler anyway.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
diff --git a/lib/device/bcache.c b/lib/device/bcache.c
(cherry picked from commit 0a55dd6dcdcad64a103f1d5ce29a0f1909548b7a)
---
lib/device/bcache.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/lib/device/bcache.c b/lib/device/bcache.c
index 4c91402c4..38da044b1 100644
--- a/lib/device/bcache.c
+++ b/lib/device/bcache.c
@@ -17,6 +17,7 @@
#include "base/data-struct/radix-tree.h"
#include "lib/log/lvm-logging.h"
#include "lib/log/log.h"
+#include "lib/misc/lvm-signal.h"
#include <errno.h>
#include <fcntl.h>
@@ -331,9 +332,13 @@ static bool _async_wait(struct io_engine *ioe, io_complete_fn fn)
memset(&event, 0, sizeof(event));
+ /*
+ * Retry on EINTR from stray signals, but stop if an LVM interrupt
+ * signal (SIGINT/SIGTERM via sigint_allow()) has been caught.
+ */
do {
r = io_getevents(e->aio_context, 1, MAX_EVENT, event, NULL);
- } while (r == -EINTR);
+ } while (r == -EINTR && !sigint_caught());
if (r < 0) {
log_sys_warn("io_getevents", -r);
--
2.54.0

View File

@ -0,0 +1,136 @@
From 6e47af32fc2eb4d7d4a110c213b7eba308be0985 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Tue, 24 Feb 2026 00:47:20 +0100
Subject: [PATCH 147/211] bcache: fix _wait_io failure ignored by _wait_all and
_wait_specific
1. Use stack backtrace for EINTR - sigint_caught() already emits
log_error("Interrupted..."), so only a backtrace marker is needed
here; the previous log_sys_warn was incorrect since io_getevents
returns -errno directly.
2. Break _wait_all/_wait_specific loops when _wait_io fails -
previously the return value was discarded causing infinite
busy-spin since BF_IO_PENDING is only cleared by _complete_io
which is never called when io_getevents returns EINTR.
3. Propagate _wait_specific failure through _lookup_or_read_block
returning NULL so bcache_get and callers can detect the
interrupted I/O and abort the operation.
4. Make _wait_all() return bool and update all three callers to check
the return value:
- _new_block: return NULL on failure (same path as I/O errors)
- bcache_flush: return false immediately without checking errored list
(writes never completed so the list state is unreliable)
- bcache_invalidate_di: return false on failure
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
diff --git a/lib/device/bcache.c b/lib/device/bcache.c
index 770d7d8ff..4dc5f9a2f 100644
--- a/lib/device/bcache.c
+++ b/lib/device/bcache.c
(cherry picked from commit 4e82d987df02a0f2e71b56519196ab4b5ad7403b)
---
lib/device/bcache.c | 34 ++++++++++++++++++++++++----------
1 file changed, 24 insertions(+), 10 deletions(-)
diff --git a/lib/device/bcache.c b/lib/device/bcache.c
index 38da044b1..bf8d873f6 100644
--- a/lib/device/bcache.c
+++ b/lib/device/bcache.c
@@ -341,7 +341,10 @@ static bool _async_wait(struct io_engine *ioe, io_complete_fn fn)
} while (r == -EINTR && !sigint_caught());
if (r < 0) {
- log_sys_warn("io_getevents", -r);
+ if (r == -EINTR)
+ stack;
+ else
+ log_sys_warn("io_getevents", -r);
return false;
}
@@ -922,16 +925,20 @@ static bool _wait_io(struct bcache *cache)
* High level IO handling
*--------------------------------------------------------------*/
-static void _wait_all(struct bcache *cache)
+static bool _wait_all(struct bcache *cache)
{
while (!dm_list_empty(&cache->io_pending))
- _wait_io(cache);
+ if (!_wait_io(cache))
+ return false;
+ return true;
}
-static void _wait_specific(struct block *b)
+static bool _wait_specific(struct block *b)
{
while (_test_flags(b, BF_IO_PENDING))
- _wait_io(b->cache);
+ if (!_wait_io(b->cache))
+ return false;
+ return true;
}
static unsigned _writeback(struct bcache *cache, unsigned count)
@@ -983,7 +990,8 @@ static struct block *_new_block(struct bcache *cache, int di, block_address i, b
if (can_wait) {
if (dm_list_empty(&cache->io_pending))
_writeback(cache, 16); // FIXME: magic number
- _wait_all(cache);
+ if (!_wait_all(cache))
+ return NULL;
if (dm_list_size(&cache->errored) >= cache->max_io) {
log_debug("bcache no new blocks for di %d index %u with >%d errors.",
di, (uint32_t) i, cache->max_io);
@@ -1061,7 +1069,8 @@ static struct block *_lookup_or_read_block(struct bcache *cache,
if (_test_flags(b, BF_IO_PENDING)) {
_miss(cache, flags);
- _wait_specific(b);
+ if (!_wait_specific(b))
+ return NULL;
} else
_hit(b, flags);
@@ -1081,7 +1090,10 @@ static struct block *_lookup_or_read_block(struct bcache *cache,
else {
_issue_read(b);
- _wait_specific(b);
+ if (!_wait_specific(b)) {
+ _unlink_block(b);
+ return NULL;
+ }
// we know the block is clean and unerrored.
_unlink_block(b);
@@ -1325,7 +1337,8 @@ bool bcache_flush(struct bcache *cache)
*/
_writeback(cache, cache->nr_cache_blocks);
- _wait_all(cache);
+ if (!_wait_all(cache))
+ return false;
return dm_list_empty(&cache->errored);
}
@@ -1422,7 +1435,8 @@ bool bcache_invalidate_di(struct bcache *cache, int di)
it.it.visit = _writeback_v;
radix_tree_iterate(cache->rtree, k.bytes, sizeof(k.parts.di), &it.it);
- _wait_all(cache);
+ if (!_wait_all(cache))
+ return false;
it.success = true;
it.it.visit = _invalidate_v;
--
2.54.0

View File

@ -0,0 +1,32 @@
From 8239bbf3cad6fba2db70bcf62ff24eb275ded214 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Tue, 10 Feb 2026 13:11:24 +0100
Subject: [PATCH 148/211] libdm: fix cache feature_flags cleaner policy to
preserve non-mode bits
When enforcing writethrough mode for cleaner policy, the code was
assigning ~modemask to feature_flags instead of masking with &=.
This discarded non-mode feature flags like DM_CACHE_FEATURE_METADATA2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 8b955d13ea0d4eeb14427c68ff2939679766bbfa)
---
libdm/libdm-deptree.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libdm/libdm-deptree.c b/libdm/libdm-deptree.c
index 318b23ecc..cfb57854d 100644
--- a/libdm/libdm-deptree.c
+++ b/libdm/libdm-deptree.c
@@ -3442,7 +3442,7 @@ DM_EXPORT_NEW_SYMBOL(int, dm_tree_node_add_cache_target, 1_02_138)
case DM_CACHE_FEATURE_WRITEBACK:
if (strcmp(policy_name, "cleaner") == 0) {
/* Enforce writethrough mode for cleaner policy */
- feature_flags = ~_modemask;
+ feature_flags &= ~_modemask;
feature_flags |= DM_CACHE_FEATURE_WRITETHROUGH;
}
/* Fall through */
--
2.54.0

View File

@ -0,0 +1,31 @@
From aff798f2816e323163da9878031ba8f3f0385a26 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Tue, 10 Feb 2026 13:11:43 +0100
Subject: [PATCH 149/211] libdm: fix cache origin uuid error message printing
wrong variable
The error message for missing cache origin was printing metadata_uuid
instead of origin_uuid.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 045f941798f06f88121aac44a4e9a19f97de1a30)
---
libdm/libdm-deptree.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libdm/libdm-deptree.c b/libdm/libdm-deptree.c
index cfb57854d..2fdd9fa07 100644
--- a/libdm/libdm-deptree.c
+++ b/libdm/libdm-deptree.c
@@ -3490,7 +3490,7 @@ DM_EXPORT_NEW_SYMBOL(int, dm_tree_node_add_cache_target, 1_02_138)
if (!(seg->origin = dm_tree_find_node_by_uuid(node->dtree,
origin_uuid))) {
log_error("Missing cache's origin uuid %s.",
- metadata_uuid);
+ origin_uuid);
return 0;
}
if (!_link_tree_nodes(node, seg->origin))
--
2.54.0

View File

@ -0,0 +1,40 @@
From 338c45f3128680ca511c4c92402999bcbe8df6b1 Mon Sep 17 00:00:00 2001
From: Peter Rajnoha <prajnoha@redhat.com>
Date: Tue, 10 Feb 2026 11:21:51 +0100
Subject: [PATCH 150/211] lvmlockd: check if sanlock lv is not partial before
trying to activate
If the sanlock lv is partial, we can't execute lockd operations, like 'lock start'.
The activate_lv would check if the lv is partial or not, but it would provide
a misleading hint in this case:
"Refusing activation of partial LV <vg/lv>. Use '--activationmode partial' to override."
Instead, check for partial sanlock lv early and provide this message instead:
"Cannot use sanlock lv <vg>/<lv> with missing PVs."
(cherry picked from commit 365b9768dfc542689424a1840a208229cf43b25d)
---
lib/locking/lvmlockd.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/lib/locking/lvmlockd.c b/lib/locking/lvmlockd.c
index 68dea52e0..af827e49e 100644
--- a/lib/locking/lvmlockd.c
+++ b/lib/locking/lvmlockd.c
@@ -782,6 +782,11 @@ static int _handle_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg)
static int _activate_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg)
{
+ if (lv_is_partial(vg->sanlock_lv)) {
+ log_error("Cannot use sanlock lv %s/%s with missing PVs.", vg->name, vg->sanlock_lv->name);
+ return 0;
+ }
+
if (!activate_lv(cmd, vg->sanlock_lv)) {
log_error("Failed to activate sanlock lv %s/%s", vg->name, vg->sanlock_lv->name);
return 0;
--
2.54.0

View File

@ -0,0 +1,57 @@
From 83676b371fd99ca2c4ab9bc8932661539eec90b9 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Thu, 23 Oct 2025 19:18:16 +0200
Subject: [PATCH 151/211] pool: initialize chunk_size_calc_method in update
functions
Fix uninitialized variable bug causing valgrind warnings when
converting LVs to pools with user-specified --chunksize.
The update_thin_pool_params() and update_cache_pool_params()
functions only initialized chunk_size_calc_method in the code
path that calculates default chunk size from profile settings.
When users specified --chunksize, this output parameter remained
uninitialized and was later used in a conditional check in
recalculate_pool_chunk_size_with_dev_hints(), triggering valgrind:
"Conditional jump or move depends on uninitialised value(s)"
Fix by initializing chunk_size_calc_method to 0 at the start of
both functions. A value of 0 indicates chunk size was user-specified
or should not be recalculated from device hints.
Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit 620cc808d1a47e007f40d0666f10048577eaa4d7)
---
lib/metadata/cache_manip.c | 2 ++
lib/metadata/thin_manip.c | 2 ++
2 files changed, 4 insertions(+)
diff --git a/lib/metadata/cache_manip.c b/lib/metadata/cache_manip.c
index 3e7b34a66..fecf56919 100644
--- a/lib/metadata/cache_manip.c
+++ b/lib/metadata/cache_manip.c
@@ -212,6 +212,8 @@ int update_cache_pool_params(struct cmd_context *cmd,
DM_CACHE_MIN_DATA_BLOCK_SIZE - 1) /
DM_CACHE_MIN_DATA_BLOCK_SIZE) * DM_CACHE_MIN_DATA_BLOCK_SIZE;
+ *chunk_size_calc_method = 0;
+
if (!*chunk_size) {
if (!(*chunk_size = find_config_tree_int(cmd, allocation_cache_pool_chunk_size_CFG,
profile) * 2)) {
diff --git a/lib/metadata/thin_manip.c b/lib/metadata/thin_manip.c
index b741ba0f5..b9db5d4b0 100644
--- a/lib/metadata/thin_manip.c
+++ b/lib/metadata/thin_manip.c
@@ -832,6 +832,8 @@ int update_thin_pool_params(struct cmd_context *cmd,
uint64_t max_pool_data_size;
const char *str;
+ *chunk_size_calc_method = 0;
+
if (!*chunk_size &&
find_config_tree_node(cmd, allocation_thin_pool_chunk_size_CFG, profile))
*chunk_size = find_config_tree_int(cmd, allocation_thin_pool_chunk_size_CFG, profile) * 2;
--
2.54.0

View File

@ -0,0 +1,59 @@
From 23e65201eee7460e6a40ddce520271924229d46c Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Sun, 14 Dec 2025 15:11:00 +0100
Subject: [PATCH 152/211] pool: drop LV_ACTIVATION_SKIP from converted LVs
When converting LVs to use them as pool data & metadata LVs,
drop LV_ACTIVATION_SKIP from their status.
This flag is not meaningful for internal LVs, and for thin-pools
it has a side-effect selecting whether the thin-pool should be
left active standalone.
(cherry picked from commit 0d0b42e418aac36c88aecb19aabc0b8d7b81cab3)
---
lib/metadata/pool_manip.c | 3 +++
tools/lvconvert.c | 6 ++++++
2 files changed, 9 insertions(+)
diff --git a/lib/metadata/pool_manip.c b/lib/metadata/pool_manip.c
index cbe1b3ff0..eb081d978 100644
--- a/lib/metadata/pool_manip.c
+++ b/lib/metadata/pool_manip.c
@@ -38,6 +38,7 @@ int attach_pool_metadata_lv(struct lv_segment *pool_seg,
return 0;
}
pool_seg->metadata_lv = metadata_lv;
+ metadata_lv->status &= ~LV_ACTIVATION_SKIP; /* Internal volume does not use skip */
metadata_lv->status |= seg_is_thin_pool(pool_seg) ?
THIN_POOL_METADATA : CACHE_POOL_METADATA;
lv_set_hidden(metadata_lv);
@@ -80,6 +81,8 @@ int attach_pool_data_lv(struct lv_segment *pool_seg,
THIN_POOL_DATA : CACHE_POOL_DATA))
return_0;
+ pool_data_lv->status &= ~LV_ACTIVATION_SKIP;
+ pool_seg->lv->status &= ~LV_ACTIVATION_SKIP;
pool_seg->lv->status |= seg_is_thin_pool(pool_seg) ?
THIN_POOL : CACHE_POOL;
lv_set_hidden(pool_data_lv);
diff --git a/tools/lvconvert.c b/tools/lvconvert.c
index 7a17028e5..7dc8a3736 100644
--- a/tools/lvconvert.c
+++ b/tools/lvconvert.c
@@ -3441,6 +3441,12 @@ static int _lvconvert_to_pool(struct cmd_context *cmd,
pool_lv = lv;
}
+ /* For pool skipping activation has slightly different meaning
+ * so by default we create a regular pool with standard activation
+ * User may use 'lvchange --setactivationskip' to use pool
+ * with skipped activation */
+ pool_lv->status &= ~LV_ACTIVATION_SKIP;
+
/*
* starts with pool_lv foo (not a pool yet)
* creates new data_lv foo_tdata
--
2.54.0

View File

@ -0,0 +1,66 @@
From 80d6b94863e6f49f38e16090ca2067c8f640c0cc Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Thu, 16 Apr 2026 11:56:00 +0200
Subject: [PATCH 153/211] lib: fix buffer overflows in device_id and GPT
parsing
device_id: use dm_snprintf in device_ids_write to handle
truncation safely.
dev-type: validate GPT partition entry size before iterating.
A crafted disk with sz_entry=0 causes ~4 billion loop iterations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 8f39af43dd8d78ad99c1801cb0a2f0d1f5d2a19d)
---
lib/device/dev-type.c | 6 ++++++
lib/device/device_id.c | 13 ++++++-------
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/lib/device/dev-type.c b/lib/device/dev-type.c
index 4c1040c9f..5c6717f01 100644
--- a/lib/device/dev-type.c
+++ b/lib/device/dev-type.c
@@ -612,6 +612,12 @@ static int _has_gpt_partition_table(struct device *dev)
nr_entries = le32_to_cpu(gpt_header.nr_part_entries);
sz_entry = le32_to_cpu(gpt_header.sz_part_entry);
+ if (sz_entry < sizeof(gpt_part_entry)) {
+ log_debug("GPT partition entry size %u too small on %s.",
+ sz_entry, dev_name(dev));
+ return 0;
+ }
+
for (i = 0; i < nr_entries; i++) {
if (!dev_read_bytes(dev, entries_start + (uint64_t)i * sz_entry,
sizeof(gpt_part_entry), &gpt_part_entry))
diff --git a/lib/device/device_id.c b/lib/device/device_id.c
index a83257e50..73306558b 100644
--- a/lib/device/device_id.c
+++ b/lib/device/device_id.c
@@ -1830,16 +1830,15 @@ int device_ids_write(struct cmd_context *cmd)
t = time(NULL);
- if ((fc_bytes = snprintf(fc, sizeof(fc),
- "# LVM uses devices listed in this file.\n" \
- "# Created by LVM command %s%s pid %d at %s" \
- "# HASH=%u\n",
- cmd->name, cmd->device_ids_auto_import ? " (auto)" : "",
- getpid(), ctime(&t), hash)) < 0) {
+ if ((fc_bytes = dm_snprintf(fc, sizeof(fc),
+ "# LVM uses devices listed in this file.\n"
+ "# Created by LVM command %s%s pid %d at %s"
+ "# HASH=%u\n",
+ cmd->name, cmd->device_ids_auto_import ? " (auto)" : "",
+ getpid(), ctime(&t), hash)) < 0) {
log_warn("Failed to write buffer for devices file content.");
goto out;
}
- fc[fc_bytes] = '\0';
if (fputs(fc, fp) < 0) {
log_warn("Failed to write devices file header.");
--
2.54.0

View File

@ -0,0 +1,64 @@
From 483caa0b663c8b1b180967856f495bae5fe978a2 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Thu, 16 Apr 2026 11:55:40 +0200
Subject: [PATCH 154/211] lib: add missing NULL checks to prevent dereference
crashes
cache_manip: check cn->v before dereferencing in cache_set_policy
and cache_vol_set_params - a config section node has NULL value.
label: check scan_bcache in dev_invalidate_bytes and dev_invalidate,
matching the guard already present in peer functions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 36fe3bfb26dced3bd33018f4d820dd895f5a4074)
---
lib/label/label.c | 4 ++++
lib/metadata/cache_manip.c | 4 ++--
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/lib/label/label.c b/lib/label/label.c
index 12a825c62..d114c8e42 100644
--- a/lib/label/label.c
+++ b/lib/label/label.c
@@ -1940,11 +1940,15 @@ bool dev_write_bytes(struct device *dev, uint64_t start, size_t len, void *data)
bool dev_invalidate_bytes(struct device *dev, uint64_t start, size_t len)
{
+ if (!scan_bcache)
+ return true;
return bcache_invalidate_bytes(scan_bcache, dev->bcache_di, start, len);
}
void dev_invalidate(struct device *dev)
{
+ if (!scan_bcache)
+ return;
bcache_invalidate_di(scan_bcache, dev->bcache_di);
}
diff --git a/lib/metadata/cache_manip.c b/lib/metadata/cache_manip.c
index fecf56919..7bf0a2255 100644
--- a/lib/metadata/cache_manip.c
+++ b/lib/metadata/cache_manip.c
@@ -858,7 +858,7 @@ int cache_set_policy(struct lv_segment *lvseg, const char *name,
restart: /* remove any 'default" nodes */
cn = seg->policy_settings ? seg->policy_settings->child : NULL;
while (cn) {
- if (cn->v->type == DM_CFG_STRING && !strcmp(cn->v->v.str, "default")) {
+ if (cn->v && cn->v->type == DM_CFG_STRING && !strcmp(cn->v->v.str, "default")) {
dm_config_remove_node(seg->policy_settings, cn);
goto restart;
}
@@ -1157,7 +1157,7 @@ int cache_vol_set_params(struct cmd_context *cmd,
restart: /* remove any 'default" nodes */
cn = policy_settings ? policy_settings->child : NULL;
while (cn) {
- if (cn->v->type == DM_CFG_STRING && !strcmp(cn->v->v.str, "default")) {
+ if (cn->v && cn->v->type == DM_CFG_STRING && !strcmp(cn->v->v.str, "default")) {
dm_config_remove_node(policy_settings, cn);
goto restart;
}
--
2.54.0

View File

@ -0,0 +1,85 @@
From d075514407ffda578ac763491857f2abcab56667 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Thu, 16 Apr 2026 01:59:10 +0200
Subject: [PATCH 155/211] pvck: fix buffer overflow, integer truncation, and
type mismatches
- _chars_to_hexstr: memcpy used hardcoded 256 instead of max parameter
- _backup_file_to_raw_metadata: back_size * 2 truncated uint64_t to uint32_t
- _dump_backup_to_raw, _read_metadata_file: read() rv was int, compared
via (int) cast against uint64_t sizes losing high bits
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 60fa392c72d7aa299a1488506608c619cac3a362)
---
tools/pvck.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/tools/pvck.c b/tools/pvck.c
index 3be469562..325590f11 100644
--- a/tools/pvck.c
+++ b/tools/pvck.c
@@ -123,7 +123,7 @@ static char *_chars_to_hexstr(const void *in, void *out, int num, int max, const
i++;
}
- memcpy(out, tmp, 256);
+ memcpy(out, tmp, max);
free(tmp);
@@ -2696,7 +2696,12 @@ static int _backup_file_to_raw_metadata(char *back_buf, uint64_t back_size,
uint32_t text_pos, pre_len = 0, back_pos, text_max;
int len, len2, vgnamelen;
- text_max = back_size * 2;
+ if (back_size > UINT32_MAX / 2) {
+ log_error("Backup file too large.");
+ return 0;
+ }
+
+ text_max = (uint32_t)(back_size * 2);
if (!(text_buf = zalloc(text_max)))
return_0;
@@ -2799,7 +2804,8 @@ static int _dump_backup_to_raw(struct cmd_context *cmd, struct settings *set)
struct stat sb;
char *back_buf, *text_buf;
uint64_t back_size, text_size;
- int fd, rv, ret;
+ ssize_t rv;
+ int fd, ret;
if (arg_is_set(cmd, file_ARG)) {
if (!(tofile = arg_str_value(cmd, file_ARG, NULL)))
@@ -2830,7 +2836,7 @@ static int _dump_backup_to_raw(struct cmd_context *cmd, struct settings *set)
goto fail_close;
rv = read(fd, back_buf, back_size);
- if (rv != (int)back_size) {
+ if (rv != (ssize_t)back_size) {
log_error("Cannot read file: %s", input);
free(back_buf);
goto fail_close;
@@ -2946,7 +2952,8 @@ static int _read_metadata_file(struct cmd_context *cmd, struct metadata_file *mf
char *text_buf;
uint64_t text_size;
uint32_t text_crc;
- int fd, rv;
+ ssize_t rv;
+ int fd;
if ((fd = open(mf->filename, O_RDONLY)) < 0) {
log_error("Cannot open file: %s", mf->filename);
@@ -2967,7 +2974,7 @@ static int _read_metadata_file(struct cmd_context *cmd, struct metadata_file *mf
goto_out;
rv = read(fd, text_buf, text_size);
- if (rv != (int)text_size) {
+ if (rv != (ssize_t)text_size) {
log_error("Cannot read file: %s", mf->filename);
free(text_buf);
goto out;
--
2.54.0

View File

@ -0,0 +1,74 @@
From 247ca0605f7d0602666abe0cb3d45fb02ebac4a7 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Thu, 16 Apr 2026 00:52:57 +0200
Subject: [PATCH 156/211] lvmpolld: replace asserts with proper error handling
Replace assert(read_single_line()) with if-continue - assert with
side effects compiles out with NDEBUG, silently skipping pipe reads
and leaving data->line stale for subsequent log calls.
Replace assert(type < POLL_TYPE_MAX) with proper bounds check
returning EINVAL - assert vanishes with NDEBUG allowing
out-of-bounds array access.
Check devicesfile strdup failure in pdlv_create() - the allocation
was not validated unlike all other fields in the struct.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit d0c1a22ce823e54eec42e6d2888b20dbcb8306a2)
---
daemons/lvmpolld/lvmpolld-core.c | 9 ++++++---
daemons/lvmpolld/lvmpolld-data-utils.c | 3 ++-
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/daemons/lvmpolld/lvmpolld-core.c b/daemons/lvmpolld/lvmpolld-core.c
index 65ac18ab5..8ede78597 100644
--- a/daemons/lvmpolld/lvmpolld-core.c
+++ b/daemons/lvmpolld/lvmpolld-core.c
@@ -269,7 +269,8 @@ static int poll_for_output(struct lvmpolld_lv *pdlv, struct lvmpolld_thread_data
if (fds[0].revents & POLLIN) {
DEBUGLOG(pdlv->ls, "%s: %s", PD_LOG_PREFIX, "caught input data in STDOUT");
- assert(read_single_line(data, 0)); /* may block indef. anyway */
+ if (!read_single_line(data, 0))
+ continue; /* may block indef. anyway */
INFO(pdlv->ls, "%s: PID %d: %s: '%s'", LVM2_LOG_PREFIX,
pdlv->cmd_pid, "STDOUT", data->line);
} else if (fds[0].revents) {
@@ -287,7 +288,8 @@ static int poll_for_output(struct lvmpolld_lv *pdlv, struct lvmpolld_thread_data
DEBUGLOG(pdlv->ls, "%s: %s", PD_LOG_PREFIX,
"caught input data in STDERR");
- assert(read_single_line(data, 1)); /* may block indef. anyway */
+ if (!read_single_line(data, 1))
+ continue; /* may block indef. anyway */
WARN(pdlv->ls, "%s: PID %d: %s: '%s'", LVM2_LOG_PREFIX,
pdlv->cmd_pid, "STDERR", data->line);
} else if (fds[1].revents) {
@@ -628,7 +630,8 @@ static response poll_init(client_handle h, struct lvmpolld_state *ls, request re
const char *devicesfile = daemon_request_str(req, LVMPD_PARM_DEVICESFILE, NULL);
unsigned abort_polling = daemon_request_int(req, LVMPD_PARM_ABORT, 0);
- assert(type < POLL_TYPE_MAX);
+ if (type >= POLL_TYPE_MAX)
+ return reply(LVMPD_RESP_EINVAL, REASON_ILLEGAL_ABORT_REQUEST);
if (abort_polling && type != PVMOVE)
return reply(LVMPD_RESP_EINVAL, REASON_ILLEGAL_ABORT_REQUEST);
diff --git a/daemons/lvmpolld/lvmpolld-data-utils.c b/daemons/lvmpolld/lvmpolld-data-utils.c
index 8a4c6f8b0..111a0f2b9 100644
--- a/daemons/lvmpolld/lvmpolld-data-utils.c
+++ b/daemons/lvmpolld/lvmpolld-data-utils.c
@@ -121,7 +121,8 @@ struct lvmpolld_lv *pdlv_create(struct lvmpolld_state *ls, const char *id,
.init_rq_count = 1
}, *pdlv = (struct lvmpolld_lv *) malloc(sizeof(struct lvmpolld_lv));
- if (!pdlv || !tmp.lvmpolld_id || !tmp.lvname || !tmp.lvm_system_dir_env || !tmp.sinterval)
+ if (!pdlv || !tmp.lvmpolld_id || !tmp.lvname || !tmp.lvm_system_dir_env || !tmp.sinterval ||
+ (devicesfile && !tmp.devicesfile))
goto err;
tmp.lvid = _get_lvid(tmp.lvmpolld_id, sysdir);
--
2.54.0

View File

@ -0,0 +1,46 @@
From 77967846b66b69eacca94a88c092d31515648410 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 10 Apr 2026 10:52:31 +0200
Subject: [PATCH 157/211] import_vsn1: validate area_count before subtracting
parity_devs
The RAID extent calculation subtracts parity_devs from area_count
without checking that area_count is large enough:
area_count - segtype->parity_devs
Both are uint32_t. If an attacker crafts RAID6 metadata with
device_count = 1, the subtraction underflows (1 - 2 = 0xFFFFFFFF),
producing an invalid stripes value passed to raid_rimage_extents().
Add validation that area_count > parity_devs before the subtraction
to reject semantically invalid metadata (fewer devices than parity
requires).
Reported-by: Tony Asleson <tasleson@redhat.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
(cherry picked from commit 73d24094985d7e27dc6e0f02f898747609b3635b)
---
lib/format_text/import_vsn1.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/lib/format_text/import_vsn1.c b/lib/format_text/import_vsn1.c
index 20b1739e9..5b84d875f 100644
--- a/lib/format_text/import_vsn1.c
+++ b/lib/format_text/import_vsn1.c
@@ -440,6 +440,12 @@ static int _read_segment(struct cmd_context *cmd,
!segtype->ops->text_import_area_count(sn_child, &area_count))
return_0;
+ if (segtype->parity_devs && area_count <= segtype->parity_devs) {
+ log_error("area_count %u must exceed parity_devs %u for segment in %s.",
+ area_count, segtype->parity_devs, display_lvname(lv));
+ return 0;
+ }
+
area_extents = segtype->parity_devs ?
raid_rimage_extents(segtype, extent_count, area_count - segtype->parity_devs, data_copies) : extent_count;
if (!(seg = alloc_lv_segment(segtype, lv, start_extent,
--
2.54.0

View File

@ -0,0 +1,75 @@
From 8903bd4c1c5393a45dcf3273d947ce125bf1c672 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Fri, 10 Apr 2026 10:52:21 +0200
Subject: [PATCH 158/211] format-text: validate mda size and rlocn size limits
Two related issues in metadata area validation:
1. Integer underflow in mdah->size validation: The rlocn bounds
checks subtract MDA_HEADER_SIZE (512) from mdah->size without
verifying mdah->size >= MDA_HEADER_SIZE. If mdah->size = 0,
the subtraction underflows to 0xFFFFFFFFFFFFFE00, bypassing
all bounds checks.
Fix: Add minimum size validation in _raw_read_mda_header() to
reject mdah->size < MDA_HEADER_SIZE.
2. Unsafe uint64_t to uint32_t truncation: rlocn->size is a 64-bit
value from disk but passed as uint32_t to text_read_metadata().
If rlocn->size exceeds UINT32_MAX, the cast silently discards
high bits.
Fix: Add rlocn->size > UINT32_MAX check in the existing bounds
validation. Since wrap < rlocn->size (because rlocn->offset <
mdah->size), this guarantees wrap also fits in uint32_t.
Both issues affect _vg_read_raw_area() and read_metadata_location_summary().
Reported-by: Tony Asleson <tasleson@redhat.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
(cherry picked from commit d214bc8b158deab09c799c6e3a585332f633d983)
---
lib/format_text/format-text.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/lib/format_text/format-text.c b/lib/format_text/format-text.c
index 3165312d7..39f1afcad 100644
--- a/lib/format_text/format-text.c
+++ b/lib/format_text/format-text.c
@@ -234,6 +234,13 @@ static int _raw_read_mda_header(struct mda_header *mdah, struct device_area *dev
*bad_fields |= BAD_MDA_START;
}
+ if (mdah->size < MDA_HEADER_SIZE) {
+ log_warn("WARNING: Metadata area size %llu too small in mda header on %s at %llu.",
+ (unsigned long long)mdah->size,
+ dev_name(dev_area->dev), (unsigned long long)dev_area->start);
+ *bad_fields |= BAD_MDA_HEADER;
+ }
+
*bad_fields &= ~ignore_bad_fields;
if (*bad_fields)
@@ -418,7 +425,8 @@ static struct volume_group *_vg_read_raw_area(struct cmd_context *cmd,
/* Validate rlocn fields fit within mda bounds before uint32_t cast */
if (rlocn->offset >= mdah->size ||
- rlocn->size > mdah->size - MDA_HEADER_SIZE) {
+ rlocn->size > mdah->size - MDA_HEADER_SIZE ||
+ rlocn->size > UINT32_MAX) {
log_error("Metadata location out of bounds (offset %llu size %llu mda %llu) on %s.",
(unsigned long long)rlocn->offset,
(unsigned long long)rlocn->size,
@@ -1515,7 +1523,8 @@ int read_metadata_location_summary(const struct format_type *fmt,
/* Validate rlocn fields fit within mda bounds before uint32_t cast */
if (rlocn->offset >= mdah->size ||
- rlocn->size > mdah->size - MDA_HEADER_SIZE) {
+ rlocn->size > mdah->size - MDA_HEADER_SIZE ||
+ rlocn->size > UINT32_MAX) {
log_warn("WARNING: Metadata location out of bounds (offset %llu size %llu mda %llu) on %s.",
(unsigned long long)rlocn->offset,
(unsigned long long)rlocn->size,
--
2.54.0

View File

@ -0,0 +1,72 @@
From 56e7c266bc2b6667481b589e230870c5086fb8cb Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Thu, 9 Apr 2026 21:34:07 +0200
Subject: [PATCH 159/211] lv_manip: validate area_count against MAX_STRIPES
Add _validate_area_count helper to prevent integer overflow in
multiplication before allocating segment areas.
Reported-by: Tony Asleson <tasleson@redhat.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
(cherry picked from commit 30c19ac8d2b86895332bef2c1892403c7ceab0c4)
---
lib/metadata/lv_manip.c | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index 0fce14f21..72ab89211 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -1024,6 +1024,15 @@ static uint32_t _round_to_stripe_boundary(struct volume_group *vg, uint32_t exte
return new_extents;
}
+static int _validate_area_count(uint32_t area_count)
+{
+ if (area_count > MAX_STRIPES) {
+ log_error(INTERNAL_ERROR "area_count %u exceeds maximum %u.", area_count, MAX_STRIPES);
+ return_0;
+ }
+ return 1;
+}
+
/*
* All lv_segments get created here.
*/
@@ -1044,13 +1053,18 @@ struct lv_segment *alloc_lv_segment(const struct segment_type *segtype,
{
struct lv_segment *seg;
struct dm_pool *mem = lv->vg->vgmem;
- uint32_t areas_sz = area_count * sizeof(*seg->areas);
+ uint32_t areas_sz;
if (!segtype) {
log_error(INTERNAL_ERROR "alloc_lv_segment: Missing segtype.");
return NULL;
}
+ if (!_validate_area_count(area_count))
+ return_NULL;
+
+ areas_sz = area_count * sizeof(*seg->areas);
+
if (!(seg = dm_pool_zalloc(mem, sizeof(*seg))))
return_NULL;
@@ -1354,7 +1368,12 @@ int set_lv_segment_area_lv(struct lv_segment *seg, uint32_t area_num,
int add_lv_segment_areas(struct lv_segment *seg, uint32_t new_area_count)
{
struct lv_segment_area *newareas;
- uint32_t areas_sz = new_area_count * sizeof(*newareas);
+ uint32_t areas_sz;
+
+ if (!_validate_area_count(new_area_count))
+ return_0;
+
+ areas_sz = new_area_count * sizeof(*newareas);
if (!(newareas = dm_pool_zalloc(seg->lv->vg->vgmem, areas_sz))) {
log_error("Failed to allocate widened LV segment for %s.",
--
2.54.0

View File

@ -0,0 +1,30 @@
From af71204b76908cdc13ccfd36e8171a440f492df8 Mon Sep 17 00:00:00 2001
From: Zdenek Kabelac <zkabelac@redhat.com>
Date: Thu, 16 Apr 2026 11:56:10 +0200
Subject: [PATCH 160/211] lvmcache: fix use-after-free on vgid update failure
When _lvmcache_update_vgid fails after vginfo was already inserted
into _vgname_hash, the error path frees vginfo without removing it
from the hash, leaving a dangling pointer.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit 58ab3ef97891ab334137e271e9bc5f46acf7265a)
---
lib/cache/lvmcache.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index 4c95db7db..2da7c81ce 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -1876,6 +1876,7 @@ static int _lvmcache_update_vgname(struct cmd_context *cmd,
}
if (!_lvmcache_update_vgid(NULL, vginfo, vgid)) {
+ dm_hash_remove(_vgname_hash, vgname);
free(vginfo->vgname);
free(vginfo);
return_0;
--
2.54.0

Some files were not shown because too many files have changed in this diff Show More