* [PATCH v2 01/10] dm-ima: remove dm_ima_reset_data()
2026-04-29 20:20 [PATCH v2 00/10] Fix dm-ima bugs Benjamin Marzinski
@ 2026-04-29 20:20 ` Benjamin Marzinski
2026-04-29 20:21 ` [PATCH v2 02/10] dm-ima: remove broken last_target_measured logic Benjamin Marzinski
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Benjamin Marzinski @ 2026-04-29 20:20 UTC (permalink / raw)
To: Mikulas Patocka, Mike Snitzer
Cc: dm-devel, linux-integrity, Alasdair Kergon, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Lakshmi Ramasubramanian,
steven chen
There's no point in saving the string length of DM_IMA_VERSION_STR. It's
a constant, so the compiler will precompute it. dm_create() will already
zero out the rest of dm->ima.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
drivers/md/dm-ima.c | 32 ++++++++++++--------------------
drivers/md/dm-ima.h | 3 ---
drivers/md/dm.c | 2 --
3 files changed, 12 insertions(+), 25 deletions(-)
diff --git a/drivers/md/dm-ima.c b/drivers/md/dm-ima.c
index 9495ca035056..a639bb0fe6c3 100644
--- a/drivers/md/dm-ima.c
+++ b/drivers/md/dm-ima.c
@@ -159,15 +159,6 @@ static int dm_ima_alloc_and_copy_capacity_str(struct mapped_device *md, char **c
capacity);
}
-/*
- * Initialize/reset the dm ima related data structure variables.
- */
-void dm_ima_reset_data(struct mapped_device *md)
-{
- memset(&(md->ima), 0, sizeof(md->ima));
- md->ima.dm_version_str_len = strlen(DM_IMA_VERSION_STR);
-}
-
/*
* Build up the IMA data for each target, and finally measure.
*/
@@ -204,8 +195,8 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
sha256_init(&hash_ctx);
- memcpy(ima_buf + l, DM_IMA_VERSION_STR, table->md->ima.dm_version_str_len);
- l += table->md->ima.dm_version_str_len;
+ memcpy(ima_buf + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
+ l += strlen(DM_IMA_VERSION_STR);
device_data_buf_len = strlen(device_data_buf);
memcpy(ima_buf + l, device_data_buf, device_data_buf_len);
@@ -260,8 +251,8 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
* prefix, so that multiple records from the same "dm_table_load" for
* a given device can be linked together.
*/
- memcpy(ima_buf + l, DM_IMA_VERSION_STR, table->md->ima.dm_version_str_len);
- l += table->md->ima.dm_version_str_len;
+ memcpy(ima_buf + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
+ l += strlen(DM_IMA_VERSION_STR);
memcpy(ima_buf + l, device_data_buf, device_data_buf_len);
l += device_data_buf_len;
@@ -349,8 +340,8 @@ void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap)
if (capacity_len < 0)
goto error;
- memcpy(device_table_data + l, DM_IMA_VERSION_STR, md->ima.dm_version_str_len);
- l += md->ima.dm_version_str_len;
+ memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
+ l += strlen(DM_IMA_VERSION_STR);
if (swap) {
if (md->ima.active_table.hash != md->ima.inactive_table.hash)
@@ -460,8 +451,8 @@ void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all)
goto exit;
}
- memcpy(device_table_data + l, DM_IMA_VERSION_STR, md->ima.dm_version_str_len);
- l += md->ima.dm_version_str_len;
+ memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
+ l += strlen(DM_IMA_VERSION_STR);
if (md->ima.active_table.device_metadata) {
memcpy(device_table_data + l, device_active_str, device_active_len);
@@ -551,7 +542,8 @@ void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all)
if (md->ima.active_table.hash != md->ima.inactive_table.hash)
kfree(md->ima.inactive_table.hash);
- dm_ima_reset_data(md);
+ memset(&md->ima.active_table, 0, sizeof(md->ima.active_table));
+ memset(&md->ima.inactive_table, 0, sizeof(md->ima.inactive_table));
kfree(dev_name);
kfree(dev_uuid);
@@ -578,8 +570,8 @@ void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map)
if (capacity_len < 0)
goto error1;
- memcpy(device_table_data + l, DM_IMA_VERSION_STR, md->ima.dm_version_str_len);
- l += md->ima.dm_version_str_len;
+ memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
+ l += strlen(DM_IMA_VERSION_STR);
if (md->ima.inactive_table.device_metadata_len &&
md->ima.inactive_table.hash_len) {
diff --git a/drivers/md/dm-ima.h b/drivers/md/dm-ima.h
index a403deca6093..b0b166aa2283 100644
--- a/drivers/md/dm-ima.h
+++ b/drivers/md/dm-ima.h
@@ -52,10 +52,8 @@ struct dm_ima_device_table_metadata {
struct dm_ima_measurements {
struct dm_ima_device_table_metadata active_table;
struct dm_ima_device_table_metadata inactive_table;
- unsigned int dm_version_str_len;
};
-void dm_ima_reset_data(struct mapped_device *md);
void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags);
void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap);
void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all);
@@ -64,7 +62,6 @@ void dm_ima_measure_on_device_rename(struct mapped_device *md);
#else
-static inline void dm_ima_reset_data(struct mapped_device *md) {}
static inline void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags) {}
static inline void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap) {}
static inline void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all) {}
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index e178fe19973e..8b60c9804f5b 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -2546,8 +2546,6 @@ int dm_create(int minor, struct mapped_device **result)
if (!md)
return -ENXIO;
- dm_ima_reset_data(md);
-
*result = md;
return 0;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 02/10] dm-ima: remove broken last_target_measured logic
2026-04-29 20:20 [PATCH v2 00/10] Fix dm-ima bugs Benjamin Marzinski
2026-04-29 20:20 ` [PATCH v2 01/10] dm-ima: remove dm_ima_reset_data() Benjamin Marzinski
@ 2026-04-29 20:21 ` Benjamin Marzinski
2026-04-29 20:21 ` [PATCH v2 03/10] dm-ima: Remove status_flags from dm_ima_measure_on_table_load() Benjamin Marzinski
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Benjamin Marzinski @ 2026-04-29 20:21 UTC (permalink / raw)
To: Mikulas Patocka, Mike Snitzer
Cc: dm-devel, linux-integrity, Alasdair Kergon, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Lakshmi Ramasubramanian,
steven chen
When it ran out of space for adding more targets to the ima_buf,
dm_ima_measure_on_table_load() would measure the dm device early, and
then add the rest of the targets and measure it again.
last_target_measured was intended to flag the last target measured so
that the device wouldn't get remeasured, if no new targets were added
after the early measurement. But the way to code works, the dm device
will never be measured early unless there is another target to add to
the ima_buf. Instead, if there is only one more target to add, that
target was getting added to the ima_buf, but it wasn't getting
remeasured, because last_target_measured was set. Since
dm_ima_measure_on_table_load() only measures a device early when there
are more targets to add, the final measurement must always happen, and
last_target_measured is unneeded.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
drivers/md/dm-ima.c | 18 ++----------------
1 file changed, 2 insertions(+), 16 deletions(-)
diff --git a/drivers/md/dm-ima.c b/drivers/md/dm-ima.c
index a639bb0fe6c3..209221fa8bc5 100644
--- a/drivers/md/dm-ima.c
+++ b/drivers/md/dm-ima.c
@@ -167,7 +167,6 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
size_t device_data_buf_len, target_metadata_buf_len, target_data_buf_len, l = 0;
char *target_metadata_buf = NULL, *target_data_buf = NULL, *digest_buf = NULL;
char *ima_buf = NULL, *device_data_buf = NULL;
- int last_target_measured = -1;
status_type_t type = STATUSTYPE_IMA;
size_t cur_total_buf_len = 0;
unsigned int num_targets, i;
@@ -205,8 +204,6 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
for (i = 0; i < num_targets; i++) {
struct dm_target *ti = dm_table_get_target(table, i);
- last_target_measured = 0;
-
/*
* First retrieve the target metadata.
*/
@@ -256,14 +253,6 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
memcpy(ima_buf + l, device_data_buf, device_data_buf_len);
l += device_data_buf_len;
-
- /*
- * If this iteration of the for loop turns out to be the last target
- * in the table, dm_ima_measure_data("dm_table_load", ...) doesn't need
- * to be called again, just the hash needs to be finalized.
- * "last_target_measured" tracks this state.
- */
- last_target_measured = 1;
}
/*
@@ -277,11 +266,8 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
l += target_data_buf_len;
}
- if (!last_target_measured) {
- dm_ima_measure_data(table_load_event_name, ima_buf, l, noio);
-
- sha256_update(&hash_ctx, (const u8 *)ima_buf, l);
- }
+ dm_ima_measure_data(table_load_event_name, ima_buf, l, noio);
+ sha256_update(&hash_ctx, (const u8 *)ima_buf, l);
/*
* Finalize the table hash, and store it in table->md->ima.inactive_table.hash,
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 03/10] dm-ima: Remove status_flags from dm_ima_measure_on_table_load()
2026-04-29 20:20 [PATCH v2 00/10] Fix dm-ima bugs Benjamin Marzinski
2026-04-29 20:20 ` [PATCH v2 01/10] dm-ima: remove dm_ima_reset_data() Benjamin Marzinski
2026-04-29 20:21 ` [PATCH v2 02/10] dm-ima: remove broken last_target_measured logic Benjamin Marzinski
@ 2026-04-29 20:21 ` Benjamin Marzinski
2026-04-29 20:21 ` [PATCH v2 04/10] dm-ima: don't copy the active table to the inactive table Benjamin Marzinski
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Benjamin Marzinski @ 2026-04-29 20:21 UTC (permalink / raw)
To: Mikulas Patocka, Mike Snitzer
Cc: dm-devel, linux-integrity, Alasdair Kergon, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Lakshmi Ramasubramanian,
steven chen
There is no status flag that is is used for STATUSTYPE_IMA type
status() calls, and STATUSTYPE_IMA itself is not a valid
status flag.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
drivers/md/dm-ima.c | 4 ++--
drivers/md/dm-ima.h | 4 ++--
drivers/md/dm-ioctl.c | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/md/dm-ima.c b/drivers/md/dm-ima.c
index 209221fa8bc5..8b84b676cad4 100644
--- a/drivers/md/dm-ima.c
+++ b/drivers/md/dm-ima.c
@@ -162,7 +162,7 @@ static int dm_ima_alloc_and_copy_capacity_str(struct mapped_device *md, char **c
/*
* Build up the IMA data for each target, and finally measure.
*/
-void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags)
+void dm_ima_measure_on_table_load(struct dm_table *table)
{
size_t device_data_buf_len, target_metadata_buf_len, target_data_buf_len, l = 0;
char *target_metadata_buf = NULL, *target_data_buf = NULL, *digest_buf = NULL;
@@ -217,7 +217,7 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
* Then retrieve the actual target data.
*/
if (ti->type->status)
- ti->type->status(ti, type, status_flags, target_data_buf,
+ ti->type->status(ti, type, 0, target_data_buf,
DM_IMA_TARGET_DATA_BUF_LEN);
else
target_data_buf[0] = '\0';
diff --git a/drivers/md/dm-ima.h b/drivers/md/dm-ima.h
index b0b166aa2283..c0548492bef0 100644
--- a/drivers/md/dm-ima.h
+++ b/drivers/md/dm-ima.h
@@ -54,7 +54,7 @@ struct dm_ima_measurements {
struct dm_ima_device_table_metadata inactive_table;
};
-void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags);
+void dm_ima_measure_on_table_load(struct dm_table *table);
void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap);
void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all);
void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map);
@@ -62,7 +62,7 @@ void dm_ima_measure_on_device_rename(struct mapped_device *md);
#else
-static inline void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags) {}
+static inline void dm_ima_measure_on_table_load(struct dm_table *table) {}
static inline void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap) {}
static inline void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all) {}
static inline void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map) {}
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index a529174c94cf..074a3c71297e 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -1552,7 +1552,7 @@ static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_si
if (r)
goto err_unlock_md_type;
- dm_ima_measure_on_table_load(t, STATUSTYPE_IMA);
+ dm_ima_measure_on_table_load(t);
immutable_target_type = dm_get_immutable_target_type(md);
if (immutable_target_type &&
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 04/10] dm-ima: don't copy the active table to the inactive table
2026-04-29 20:20 [PATCH v2 00/10] Fix dm-ima bugs Benjamin Marzinski
` (2 preceding siblings ...)
2026-04-29 20:21 ` [PATCH v2 03/10] dm-ima: Remove status_flags from dm_ima_measure_on_table_load() Benjamin Marzinski
@ 2026-04-29 20:21 ` Benjamin Marzinski
2026-04-29 20:21 ` [PATCH v2 05/10] dm-ima: Fix UAF errors and measuring incorrect context Benjamin Marzinski
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Benjamin Marzinski @ 2026-04-29 20:21 UTC (permalink / raw)
To: Mikulas Patocka, Mike Snitzer
Cc: dm-devel, linux-integrity, Alasdair Kergon, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Lakshmi Ramasubramanian,
steven chen
If an inactive table was cleared, dm_ima_measure_on_table_clear() was
copying the ima.active_table to ima.inactive_table. This is not what
device-mapper does, and it makes the IMA measurements show an inactive
table when there isn't one. Also, once this is removed, the code no
longer needs to keep checking if the active and the inactive table point
to the same memory.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
drivers/md/dm-ima.c | 64 +++++++--------------------------------------
1 file changed, 10 insertions(+), 54 deletions(-)
diff --git a/drivers/md/dm-ima.c b/drivers/md/dm-ima.c
index 8b84b676cad4..c141068bc6b4 100644
--- a/drivers/md/dm-ima.c
+++ b/drivers/md/dm-ima.c
@@ -281,17 +281,13 @@ void dm_ima_measure_on_table_load(struct dm_table *table)
if (!digest_buf)
goto error;
- if (table->md->ima.active_table.hash != table->md->ima.inactive_table.hash)
- kfree(table->md->ima.inactive_table.hash);
-
+ kfree(table->md->ima.inactive_table.hash);
table->md->ima.inactive_table.hash = digest_buf;
table->md->ima.inactive_table.hash_len = strlen(digest_buf);
table->md->ima.inactive_table.num_targets = num_targets;
- if (table->md->ima.active_table.device_metadata !=
- table->md->ima.inactive_table.device_metadata)
- kfree(table->md->ima.inactive_table.device_metadata);
+ kfree(table->md->ima.inactive_table.device_metadata);
table->md->ima.inactive_table.device_metadata = device_data_buf;
table->md->ima.inactive_table.device_metadata_len = device_data_buf_len;
@@ -330,19 +326,9 @@ void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap)
l += strlen(DM_IMA_VERSION_STR);
if (swap) {
- if (md->ima.active_table.hash != md->ima.inactive_table.hash)
- kfree(md->ima.active_table.hash);
-
- md->ima.active_table.hash = NULL;
- md->ima.active_table.hash_len = 0;
-
- if (md->ima.active_table.device_metadata !=
- md->ima.inactive_table.device_metadata)
- kfree(md->ima.active_table.device_metadata);
-
- md->ima.active_table.device_metadata = NULL;
- md->ima.active_table.device_metadata_len = 0;
- md->ima.active_table.num_targets = 0;
+ kfree(md->ima.active_table.hash);
+ kfree(md->ima.active_table.device_metadata);
+ memset(&md->ima.active_table, 0, sizeof(md->ima.active_table));
if (md->ima.inactive_table.hash) {
md->ima.active_table.hash = md->ima.inactive_table.hash;
@@ -518,15 +504,10 @@ void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all)
kfree(capacity_str);
exit:
kfree(md->ima.active_table.device_metadata);
-
- if (md->ima.active_table.device_metadata !=
- md->ima.inactive_table.device_metadata)
- kfree(md->ima.inactive_table.device_metadata);
+ kfree(md->ima.inactive_table.device_metadata);
kfree(md->ima.active_table.hash);
-
- if (md->ima.active_table.hash != md->ima.inactive_table.hash)
- kfree(md->ima.inactive_table.hash);
+ kfree(md->ima.inactive_table.hash);
memset(&md->ima.active_table, 0, sizeof(md->ima.active_table));
memset(&md->ima.inactive_table, 0, sizeof(md->ima.inactive_table));
@@ -594,34 +575,9 @@ void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map)
dm_ima_measure_data("dm_table_clear", device_table_data, l, noio);
if (new_map) {
- if (md->ima.inactive_table.hash &&
- md->ima.inactive_table.hash != md->ima.active_table.hash)
- kfree(md->ima.inactive_table.hash);
-
- md->ima.inactive_table.hash = NULL;
- md->ima.inactive_table.hash_len = 0;
-
- if (md->ima.inactive_table.device_metadata &&
- md->ima.inactive_table.device_metadata != md->ima.active_table.device_metadata)
- kfree(md->ima.inactive_table.device_metadata);
-
- md->ima.inactive_table.device_metadata = NULL;
- md->ima.inactive_table.device_metadata_len = 0;
- md->ima.inactive_table.num_targets = 0;
-
- if (md->ima.active_table.hash) {
- md->ima.inactive_table.hash = md->ima.active_table.hash;
- md->ima.inactive_table.hash_len = md->ima.active_table.hash_len;
- }
-
- if (md->ima.active_table.device_metadata) {
- md->ima.inactive_table.device_metadata =
- md->ima.active_table.device_metadata;
- md->ima.inactive_table.device_metadata_len =
- md->ima.active_table.device_metadata_len;
- md->ima.inactive_table.num_targets =
- md->ima.active_table.num_targets;
- }
+ kfree(md->ima.inactive_table.hash);
+ kfree(md->ima.inactive_table.device_metadata);
+ memset(&md->ima.inactive_table, 0, sizeof(md->ima.inactive_table));
}
kfree(dev_name);
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 05/10] dm-ima: Fix UAF errors and measuring incorrect context
2026-04-29 20:20 [PATCH v2 00/10] Fix dm-ima bugs Benjamin Marzinski
` (3 preceding siblings ...)
2026-04-29 20:21 ` [PATCH v2 04/10] dm-ima: don't copy the active table to the inactive table Benjamin Marzinski
@ 2026-04-29 20:21 ` Benjamin Marzinski
2026-04-29 20:21 ` [PATCH v2 06/10] dm-ima: remove new_map from dm_ima_measure_on_device_clear Benjamin Marzinski
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Benjamin Marzinski @ 2026-04-29 20:21 UTC (permalink / raw)
To: Mikulas Patocka, Mike Snitzer
Cc: dm-devel, linux-integrity, Alasdair Kergon, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Lakshmi Ramasubramanian,
steven chen
the dm-ima code did not keep the dm_ima_measure_on_* functions from
running at the same time. This could lead to various errors. If two
processes were updating the device state, one could update the state
first, but the other could measure the state first, causing the the
current device state to appear incorrect. If a table load happened while
a device was resuming, the IMA measurement could report the wrong table
being active. And if two dm_ima_measure_on_* functions ran at the same
time, one of them could free data that the other was accessing, causing
a crash.
All the core dm functions that call a dm_ima_measure_on_* function
update the device state they want to measure under the _hash_lock,
except for do_resume(). But holding the _hash_lock is not a good way to
synchronize these functions. It's a global mutex, that is needed in many
dm operations, and the dm_ima_measure_* functions can sleep, blocking
any dm operation on any device that needs the _hash_lock.
To serialize and order the IMA measurement functions, the
dm_ima_measurements now has two counters, update_idx and measure_idx.
update_idx is incremented while holding the _hash_lock and saved, along
with the device name and uuid, in a dm_ima_context struct. Once the
_hash_lock is dropped, the dm_ima_measure_* function is called. It waits
until measure_idx matches the saved value of update_idx, ensuring that
the updates and measurements happen in the same order if there are
multiple processes changing the device at the same time. Then it
measures the device, updates measure_idx, and wakes up any other
process waiting to do a measurement. This makes sure that the
measurements are serialized and done in the order that the _hash_lock
was acquired in. But they only block other measurements for the same
device, which are unlikely to happen at the same time.
do_resume() is trickier, because it removes the inactive table while
holding the _hash_lock, but doesn't hold it while updating md->map. To
make sure it is also ordered, the IMA code grabs the _hash_lock after
md->map is updated. Then it makes sure that the device isn't being
removed and that another do_resume() hasn't already changed the active
table again, and serializes like the other functions do.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
drivers/md/dm-ima.c | 259 ++++++++++++++++++++++++------------------
drivers/md/dm-ima.h | 65 +++++++++--
drivers/md/dm-ioctl.c | 144 +++++++++++++++++++++--
drivers/md/dm.c | 2 +
4 files changed, 340 insertions(+), 130 deletions(-)
diff --git a/drivers/md/dm-ima.c b/drivers/md/dm-ima.c
index c141068bc6b4..096c664d855c 100644
--- a/drivers/md/dm-ima.c
+++ b/drivers/md/dm-ima.c
@@ -21,25 +21,32 @@
* character, so that they don't interfere with the construction of key-value pairs,
* and clients can split the key1=val1,key2=val2,key3=val3; pairs properly.
*/
-static void fix_separator_chars(char **buf)
+static void fix_separator_chars(char *buf)
{
- int l = strlen(*buf);
+ int l = strlen(buf);
int i, j, sp = 0;
for (i = 0; i < l; i++)
- if ((*buf)[i] == '\\' || (*buf)[i] == ';' || (*buf)[i] == '=' || (*buf)[i] == ',')
+ if (buf[i] == '\\' || buf[i] == ';' || buf[i] == '=' || buf[i] == ',')
sp++;
if (!sp)
return;
+ buf[l + sp] = '\0';
for (i = l-1, j = i+sp; i >= 0; i--) {
- (*buf)[j--] = (*buf)[i];
- if ((*buf)[i] == '\\' || (*buf)[i] == ';' || (*buf)[i] == '=' || (*buf)[i] == ',')
- (*buf)[j--] = '\\';
+ buf[j--] = buf[i];
+ if (buf[i] == '\\' || buf[i] == ';' || buf[i] == '=' || buf[i] == ',')
+ buf[j--] = '\\';
}
}
+static void fix_context_strings(struct dm_ima_context *context)
+{
+ fix_separator_chars(context->dev_name);
+ fix_separator_chars(context->dev_uuid);
+}
+
/*
* Internal function to allocate memory for IMA measurements.
*/
@@ -59,68 +66,89 @@ static void *dm_ima_alloc(size_t len, bool noio)
return ptr;
}
+void dm_ima_init(struct mapped_device *md)
+{
+ md->ima.update_idx = 0;
+ md->ima.measure_idx = 0;
+ init_waitqueue_head(&md->ima.ima_wq);
+ spin_lock_init(&md->ima.ima_lock);
+}
+
+void dm_ima_alloc_context(struct dm_ima_context **context, bool noio)
+{
+ *context = dm_ima_alloc(sizeof(struct dm_ima_context), noio);
+}
+
+void dm_ima_free_context(struct dm_ima_context *context)
+{
+ if (likely(context)) {
+ kfree(context->table.device_metadata);
+ kfree(context->table.hash);
+ kfree(context);
+ }
+}
+
+static void wait_to_measure(struct dm_ima_measurements *ima,
+ unsigned int update_idx)
+{
+ spin_lock_irq(&ima->ima_lock);
+ wait_event_lock_irq(ima->ima_wq,
+ ima->measure_idx == update_idx,
+ ima->ima_lock);
+ spin_unlock_irq(&ima->ima_lock);
+}
+
+static void wake_next_measure(struct dm_ima_measurements *ima)
+{
+ spin_lock_irq(&ima->ima_lock);
+ ima->measure_idx++;
+ spin_unlock_irq(&ima->ima_lock);
+ wake_up_all(&ima->ima_wq);
+}
+
/*
- * Internal function to allocate and copy name and uuid for IMA measurements.
+ * Helper function for swapping the table, to make sure that the
+ * correct table metadata is saved and restored.
*/
-static int dm_ima_alloc_and_copy_name_uuid(struct mapped_device *md, char **dev_name,
- char **dev_uuid, bool noio)
+void dm_ima_context_table_op(struct mapped_device *md,
+ struct dm_ima_context *context,
+ enum dm_ima_table_op op)
{
- int r;
- *dev_name = dm_ima_alloc(DM_NAME_LEN*2, noio);
- if (!(*dev_name)) {
- r = -ENOMEM;
- goto error;
- }
+ struct dm_ima_measurements *ima = &md->ima;
- *dev_uuid = dm_ima_alloc(DM_UUID_LEN*2, noio);
- if (!(*dev_uuid)) {
- r = -ENOMEM;
- goto error;
- }
+ if (unlikely(!context))
+ return;
- r = dm_copy_name_and_uuid(md, *dev_name, *dev_uuid);
- if (r)
- goto error;
+ wait_to_measure(ima, context->update_idx);
- fix_separator_chars(dev_name);
- fix_separator_chars(dev_uuid);
+ if (op == DM_IMA_TABLE_SAVE) {
+ context->table = ima->inactive_table;
+ memset(&ima->inactive_table, 0, sizeof(ima->inactive_table));
+ } else {
+ ima->inactive_table = context->table;
+ memset(&context->table, 0, sizeof(context->table));
+ }
- return 0;
-error:
- kfree(*dev_name);
- kfree(*dev_uuid);
- *dev_name = NULL;
- *dev_uuid = NULL;
- return r;
+ wake_next_measure(ima);
}
/*
* Internal function to allocate and copy device data for IMA measurements.
*/
static int dm_ima_alloc_and_copy_device_data(struct mapped_device *md, char **device_data,
+ struct dm_ima_context *context,
unsigned int num_targets, bool noio)
{
- char *dev_name = NULL, *dev_uuid = NULL;
- int r;
-
- r = dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio);
- if (r)
- return r;
-
*device_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
- if (!(*device_data)) {
- r = -ENOMEM;
- goto error;
- }
+ if (!(*device_data))
+ return -ENOMEM;
scnprintf(*device_data, DM_IMA_DEVICE_BUF_LEN,
"name=%s,uuid=%s,major=%d,minor=%d,minor_count=%d,num_targets=%u;",
- dev_name, dev_uuid, md->disk->major, md->disk->first_minor,
- md->disk->minors, num_targets);
-error:
- kfree(dev_name);
- kfree(dev_uuid);
- return r;
+ context->dev_name, context->dev_uuid, md->disk->major,
+ md->disk->first_minor, md->disk->minors, num_targets);
+
+ return 0;
}
/*
@@ -162,7 +190,8 @@ static int dm_ima_alloc_and_copy_capacity_str(struct mapped_device *md, char **c
/*
* Build up the IMA data for each target, and finally measure.
*/
-void dm_ima_measure_on_table_load(struct dm_table *table)
+void dm_ima_measure_on_table_load(struct dm_table *table,
+ struct dm_ima_context *context)
{
size_t device_data_buf_len, target_metadata_buf_len, target_data_buf_len, l = 0;
char *target_metadata_buf = NULL, *target_data_buf = NULL, *digest_buf = NULL;
@@ -175,9 +204,14 @@ void dm_ima_measure_on_table_load(struct dm_table *table)
bool noio = false;
char table_load_event_name[] = "dm_table_load";
+ if (unlikely(!context))
+ return;
+
+ wait_to_measure(&table->md->ima, context->update_idx);
+
ima_buf = dm_ima_alloc(DM_IMA_MEASUREMENT_BUF_LEN, noio);
if (!ima_buf)
- return;
+ goto error;
target_metadata_buf = dm_ima_alloc(DM_IMA_TARGET_METADATA_BUF_LEN, noio);
if (!target_metadata_buf)
@@ -189,7 +223,9 @@ void dm_ima_measure_on_table_load(struct dm_table *table)
num_targets = table->num_targets;
- if (dm_ima_alloc_and_copy_device_data(table->md, &device_data_buf, num_targets, noio))
+ fix_context_strings(context);
+ if (dm_ima_alloc_and_copy_device_data(table->md, &device_data_buf,
+ context, num_targets, noio))
goto error;
sha256_init(&hash_ctx);
@@ -299,14 +335,17 @@ void dm_ima_measure_on_table_load(struct dm_table *table)
kfree(ima_buf);
kfree(target_metadata_buf);
kfree(target_data_buf);
+
+ wake_next_measure(&table->md->ima);
}
/*
* Measure IMA data on device resume.
*/
-void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap)
+void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap,
+ struct dm_ima_context *context)
{
- char *device_table_data, *dev_name = NULL, *dev_uuid = NULL, *capacity_str = NULL;
+ char *device_table_data = NULL, *capacity_str = NULL;
char active[] = "active_table_hash=";
unsigned int active_len = strlen(active);
unsigned int l = 0;
@@ -314,9 +353,14 @@ void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap)
bool nodata = true;
int capacity_len;
+ if (unlikely(!context))
+ return;
+
+ wait_to_measure(&md->ima, context->update_idx);
+
device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
if (!device_table_data)
- return;
+ goto error;
capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio);
if (capacity_len < 0)
@@ -328,25 +372,8 @@ void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap)
if (swap) {
kfree(md->ima.active_table.hash);
kfree(md->ima.active_table.device_metadata);
- memset(&md->ima.active_table, 0, sizeof(md->ima.active_table));
-
- if (md->ima.inactive_table.hash) {
- md->ima.active_table.hash = md->ima.inactive_table.hash;
- md->ima.active_table.hash_len = md->ima.inactive_table.hash_len;
- md->ima.inactive_table.hash = NULL;
- md->ima.inactive_table.hash_len = 0;
- }
-
- if (md->ima.inactive_table.device_metadata) {
- md->ima.active_table.device_metadata =
- md->ima.inactive_table.device_metadata;
- md->ima.active_table.device_metadata_len =
- md->ima.inactive_table.device_metadata_len;
- md->ima.active_table.num_targets = md->ima.inactive_table.num_targets;
- md->ima.inactive_table.device_metadata = NULL;
- md->ima.inactive_table.device_metadata_len = 0;
- md->ima.inactive_table.num_targets = 0;
- }
+ md->ima.active_table = context->table;
+ memset(&context->table, 0, sizeof(context->table));
}
if (md->ima.active_table.device_metadata) {
@@ -372,12 +399,11 @@ void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap)
}
if (nodata) {
- if (dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio))
- goto error;
-
+ fix_context_strings(context);
l = scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN,
"%sname=%s,uuid=%s;device_resume=no_data;",
- DM_IMA_VERSION_STR, dev_name, dev_uuid);
+ DM_IMA_VERSION_STR, context->dev_name,
+ context->dev_uuid);
}
memcpy(device_table_data + l, capacity_str, capacity_len);
@@ -385,19 +411,21 @@ void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap)
dm_ima_measure_data("dm_device_resume", device_table_data, l, noio);
- kfree(dev_name);
- kfree(dev_uuid);
error:
kfree(capacity_str);
kfree(device_table_data);
+
+ wake_next_measure(&md->ima);
}
/*
* Measure IMA data on remove.
*/
-void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all)
+void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all,
+ struct dm_ima_context *context,
+ unsigned int idx)
{
- char *device_table_data, *dev_name = NULL, *dev_uuid = NULL, *capacity_str = NULL;
+ char *device_table_data, *capacity_str = NULL;
char active_table_str[] = "active_table_hash=";
char inactive_table_str[] = "inactive_table_hash=";
char device_active_str[] = "device_active_metadata=";
@@ -413,6 +441,11 @@ void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all)
bool nodata = true;
int capacity_len;
+ wait_to_measure(&md->ima, idx);
+
+ if (unlikely(!context))
+ goto exit;
+
device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN*2, noio);
if (!device_table_data)
goto exit;
@@ -481,12 +514,11 @@ void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all)
* in IMA measurements.
*/
if (nodata) {
- if (dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio))
- goto error;
-
+ fix_context_strings(context);
l = scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN,
"%sname=%s,uuid=%s;device_remove=no_data;",
- DM_IMA_VERSION_STR, dev_name, dev_uuid);
+ DM_IMA_VERSION_STR, context->dev_name,
+ context->dev_uuid);
}
memcpy(device_table_data + l, remove_all_str, remove_all_len);
@@ -499,7 +531,6 @@ void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all)
dm_ima_measure_data("dm_device_remove", device_table_data, l, noio);
-error:
kfree(device_table_data);
kfree(capacity_str);
exit:
@@ -512,30 +543,35 @@ void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all)
memset(&md->ima.active_table, 0, sizeof(md->ima.active_table));
memset(&md->ima.inactive_table, 0, sizeof(md->ima.inactive_table));
- kfree(dev_name);
- kfree(dev_uuid);
+ wake_next_measure(&md->ima);
}
/*
* Measure ima data on table clear.
*/
-void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map)
+void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map,
+ struct dm_ima_context *context)
{
unsigned int l = 0;
- char *device_table_data = NULL, *dev_name = NULL, *dev_uuid = NULL, *capacity_str = NULL;
+ char *device_table_data = NULL, *capacity_str = NULL;
char inactive_str[] = "inactive_table_hash=";
unsigned int inactive_len = strlen(inactive_str);
bool noio = true;
bool nodata = true;
int capacity_len;
+ if (unlikely(!context))
+ return;
+
+ wait_to_measure(&md->ima, context->update_idx);
+
device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
if (!device_table_data)
- return;
+ goto error;
capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio);
if (capacity_len < 0)
- goto error1;
+ goto error;
memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
l += strlen(DM_IMA_VERSION_STR);
@@ -561,12 +597,11 @@ void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map)
}
if (nodata) {
- if (dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio))
- goto error2;
-
+ fix_context_strings(context);
l = scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN,
"%sname=%s,uuid=%s;table_clear=no_data;",
- DM_IMA_VERSION_STR, dev_name, dev_uuid);
+ DM_IMA_VERSION_STR, context->dev_name,
+ context->dev_uuid);
}
memcpy(device_table_data + l, capacity_str, capacity_len);
@@ -580,29 +615,33 @@ void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map)
memset(&md->ima.inactive_table, 0, sizeof(md->ima.inactive_table));
}
- kfree(dev_name);
- kfree(dev_uuid);
-error2:
+error:
kfree(capacity_str);
-error1:
kfree(device_table_data);
+
+ wake_next_measure(&md->ima);
}
/*
* Measure IMA data on device rename.
*/
-void dm_ima_measure_on_device_rename(struct mapped_device *md)
+void dm_ima_measure_on_device_rename(struct mapped_device *md,
+ struct dm_ima_context *context)
{
- char *old_device_data = NULL, *new_device_data = NULL, *combined_device_data = NULL;
- char *new_dev_name = NULL, *new_dev_uuid = NULL, *capacity_str = NULL;
+ char *old_device_data = NULL, *new_device_data = NULL;
+ char *combined_device_data = NULL, *capacity_str = NULL;
bool noio = true;
int len;
- if (dm_ima_alloc_and_copy_device_data(md, &new_device_data,
- md->ima.active_table.num_targets, noio))
+ if (unlikely(!context))
return;
- if (dm_ima_alloc_and_copy_name_uuid(md, &new_dev_name, &new_dev_uuid, noio))
+ wait_to_measure(&md->ima, context->update_idx);
+
+ fix_context_strings(context);
+ if (dm_ima_alloc_and_copy_device_data(md, &new_device_data, context,
+ md->ima.active_table.num_targets,
+ noio))
goto error;
combined_device_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN * 2, noio);
@@ -619,7 +658,7 @@ void dm_ima_measure_on_device_rename(struct mapped_device *md)
len = scnprintf(combined_device_data, DM_IMA_DEVICE_BUF_LEN * 2,
"%s%snew_name=%s,new_uuid=%s;%s", DM_IMA_VERSION_STR, old_device_data,
- new_dev_name, new_dev_uuid, capacity_str);
+ context->dev_name, context->dev_uuid, capacity_str);
dm_ima_measure_data("dm_device_rename", combined_device_data, len, noio);
@@ -631,6 +670,6 @@ void dm_ima_measure_on_device_rename(struct mapped_device *md)
kfree(capacity_str);
kfree(combined_device_data);
kfree(old_device_data);
- kfree(new_dev_name);
- kfree(new_dev_uuid);
+
+ wake_next_measure(&md->ima);
}
diff --git a/drivers/md/dm-ima.h b/drivers/md/dm-ima.h
index c0548492bef0..01fa0b89a385 100644
--- a/drivers/md/dm-ima.h
+++ b/drivers/md/dm-ima.h
@@ -24,6 +24,11 @@
__dm_ima_str(DM_VERSION_MINOR) "." \
__dm_ima_str(DM_VERSION_PATCHLEVEL) ";"
+enum dm_ima_table_op {
+ DM_IMA_TABLE_SAVE,
+ DM_IMA_TABLE_RESTORE,
+};
+
#ifdef CONFIG_IMA
struct dm_ima_device_table_metadata {
@@ -45,28 +50,68 @@ struct dm_ima_device_table_metadata {
unsigned int hash_len;
};
+struct dm_ima_context {
+ struct dm_ima_device_table_metadata table;
+ unsigned int update_idx;
+ char dev_name[DM_NAME_LEN*2];
+ char dev_uuid[DM_UUID_LEN*2];
+};
+
/*
* This structure contains device metadata, and table hash for
* active and inactive tables for ima measurements.
*/
struct dm_ima_measurements {
+ unsigned int update_idx;
+ unsigned int measure_idx;
+ struct wait_queue_head ima_wq;
+ spinlock_t ima_lock;
struct dm_ima_device_table_metadata active_table;
struct dm_ima_device_table_metadata inactive_table;
};
-void dm_ima_measure_on_table_load(struct dm_table *table);
-void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap);
-void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all);
-void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map);
-void dm_ima_measure_on_device_rename(struct mapped_device *md);
+void dm_ima_init(struct mapped_device *md);
+void dm_ima_alloc_context(struct dm_ima_context **context, bool noio);
+void dm_ima_free_context(struct dm_ima_context *context);
+void dm_ima_context_table_op(struct mapped_device *md,
+ struct dm_ima_context *context,
+ enum dm_ima_table_op op);
+void dm_ima_measure_on_table_load(struct dm_table *table,
+ struct dm_ima_context *context);
+void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap,
+ struct dm_ima_context *context);
+void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all,
+ struct dm_ima_context *context,
+ unsigned int idx);
+void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map,
+ struct dm_ima_context *context);
+void dm_ima_measure_on_device_rename(struct mapped_device *md,
+ struct dm_ima_context *context);
#else
-static inline void dm_ima_measure_on_table_load(struct dm_table *table) {}
-static inline void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap) {}
-static inline void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all) {}
-static inline void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map) {}
-static inline void dm_ima_measure_on_device_rename(struct mapped_device *md) {}
+struct dm_ima_context;
+
+static inline void dm_ima_init(struct mapped_device *md) {}
+static inline void dm_ima_alloc_context(struct dm_ima_context **context, bool noio) {}
+static inline void dm_ima_free_context(struct dm_ima_context *context) {}
+static inline void dm_ima_context_table_op(struct mapped_device *md,
+ struct dm_ima_context *context,
+ enum dm_ima_table_op op) {}
+static inline void dm_ima_measure_on_table_load(struct dm_table *table,
+ struct dm_ima_context *context) {}
+static inline void dm_ima_measure_on_device_resume(struct mapped_device *md,
+ bool swap,
+ struct dm_ima_context *context) {}
+static inline void dm_ima_measure_on_device_remove(struct mapped_device *md,
+ bool remove_all,
+ struct dm_ima_context *context,
+ unsigned int idx) {}
+static inline void dm_ima_measure_on_table_clear(struct mapped_device *md,
+ bool new_map,
+ struct dm_ima_context *context) {}
+static inline void dm_ima_measure_on_device_rename(struct mapped_device *md,
+ struct dm_ima_context *context) {}
#endif /* CONFIG_IMA */
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index 074a3c71297e..3da8b33cdc54 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -259,6 +259,80 @@ static void free_cell(struct hash_cell *hc)
}
}
+#ifdef CONFIG_IMA
+
+/*
+ * Called while holding to _hash_lock, to guarantee the ordering of the
+ * following dm_ima_measure_on_* functions, which should be called
+ * right after dropping the _hash_lock
+ */
+static unsigned int dm_ima_init_context(struct hash_cell *hc,
+ struct dm_ima_context *context,
+ bool need_idx)
+{
+ lockdep_assert_held(&_hash_lock);
+
+ if (unlikely(!context))
+ return need_idx ? hc->md->ima.update_idx++ : 0;
+
+ context->update_idx = hc->md->ima.update_idx++;
+ strcpy(context->dev_name, hc->name);
+ strcpy(context->dev_uuid, hc->uuid ? : "");
+
+ return context->update_idx;
+}
+
+/*
+ * Called by do_resume() to guarantee correct ordering, since do_resume()
+ * does not grab the _hash_lock when the table is not getting swapped or
+ * when actually swapping the active table
+ */
+static bool dm_ima_need_measure(struct mapped_device *md,
+ struct dm_table *table,
+ struct dm_ima_context *context)
+{
+ int srcu_idx;
+ struct hash_cell *hc;
+ bool need_measure = false;
+
+ if (unlikely(!context))
+ return false;
+
+ down_write(&_hash_lock);
+ /* Check if the device has been removed */
+ hc = dm_get_mdptr(md);
+ if (hc) {
+ /*
+ * If we have a table, we need to make sure that it's the
+ * active table. Otherwise we raced with another process
+ * setting the active table and it will do the measurement
+ */
+ if (!table || dm_get_live_table(md, &srcu_idx) == table) {
+ dm_ima_init_context(hc, context, false);
+ need_measure = true;
+ }
+ if (table)
+ dm_put_live_table(md, srcu_idx);
+ }
+ up_write(&_hash_lock);
+
+ return need_measure;
+}
+#else
+static inline unsigned int dm_ima_init_context(struct hash_cell *hc,
+ struct dm_ima_context *context,
+ bool neex_idx)
+{
+ return 0;
+}
+static inline bool dm_ima_need_measure(struct mapped_device *md,
+ struct dm_table *table,
+ struct dm_ima_context *context)
+{
+ return false;
+}
+#endif
+
/*
* The kdev_t and uuid of a device can never change once it is
* initially inserted.
@@ -344,7 +418,10 @@ static int dm_hash_remove_all(unsigned flags)
struct hash_cell *hc;
struct mapped_device *md;
struct dm_table *t;
+ struct dm_ima_context *ima_context = NULL;
+ unsigned int ima_idx;
+ dm_ima_alloc_context(&ima_context, true);
retry:
dev_skipped = 0;
@@ -353,6 +430,7 @@ static int dm_hash_remove_all(unsigned flags)
for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) {
if (flags & DM_REMOVE_INTERRUPTIBLE && fatal_signal_pending(current)) {
up_write(&_hash_lock);
+ dm_ima_free_context(ima_context);
return -EINTR;
}
@@ -367,6 +445,7 @@ static int dm_hash_remove_all(unsigned flags)
continue;
}
+ ima_idx = dm_ima_init_context(hc, ima_context, true);
t = __hash_remove(hc);
up_write(&_hash_lock);
@@ -375,7 +454,7 @@ static int dm_hash_remove_all(unsigned flags)
dm_sync_table(md);
dm_table_destroy(t);
}
- dm_ima_measure_on_device_remove(md, true);
+ dm_ima_measure_on_device_remove(md, true, ima_context, ima_idx);
dm_put(md);
if (likely(flags & DM_REMOVE_KEEP_OPEN_DEVICES))
dm_destroy(md);
@@ -396,6 +475,7 @@ static int dm_hash_remove_all(unsigned flags)
if (dev_skipped && !(flags & DM_REMOVE_ONLY_DEFERRED))
DMWARN("remove_all left %d open device(s)", dev_skipped);
+ dm_ima_free_context(ima_context);
return 0;
}
@@ -443,6 +523,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
struct mapped_device *md;
unsigned int change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0;
int srcu_idx;
+ struct dm_ima_context *ima_context = NULL;
/*
* duplicate new.
@@ -451,6 +532,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
if (!new_data)
return ERR_PTR(-ENOMEM);
+ dm_ima_alloc_context(&ima_context, true);
down_write(&_hash_lock);
/*
@@ -467,6 +549,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
param->name, new);
dm_put(hc->md);
up_write(&_hash_lock);
+ dm_ima_free_context(ima_context);
kfree(new_data);
return ERR_PTR(-EBUSY);
}
@@ -479,6 +562,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
DMERR("Unable to rename non-existent device, %s to %s%s",
param->name, change_uuid ? "uuid " : "", new);
up_write(&_hash_lock);
+ dm_ima_free_context(ima_context);
kfree(new_data);
return ERR_PTR(-ENXIO);
}
@@ -492,6 +576,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
param->name, new, hc->uuid);
dm_put(hc->md);
up_write(&_hash_lock);
+ dm_ima_free_context(ima_context);
kfree(new_data);
return ERR_PTR(-EINVAL);
}
@@ -514,9 +599,11 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
md = hc->md;
- dm_ima_measure_on_device_rename(md);
+ dm_ima_init_context(hc, ima_context, false);
up_write(&_hash_lock);
+ dm_ima_measure_on_device_rename(md, ima_context);
+ dm_ima_free_context(ima_context);
kfree(old_name);
return md;
@@ -995,13 +1082,17 @@ static int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_si
struct mapped_device *md;
int r;
struct dm_table *t;
+ struct dm_ima_context *ima_context = NULL;
+ unsigned int ima_idx;
+ dm_ima_alloc_context(&ima_context, true);
down_write(&_hash_lock);
hc = __find_device_hash_cell(param);
if (!hc) {
DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
up_write(&_hash_lock);
+ dm_ima_free_context(ima_context);
return -ENXIO;
}
@@ -1015,14 +1106,17 @@ static int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_si
if (r == -EBUSY && param->flags & DM_DEFERRED_REMOVE) {
up_write(&_hash_lock);
dm_put(md);
+ dm_ima_free_context(ima_context);
return 0;
}
DMDEBUG_LIMIT("unable to remove open device %s", hc->name);
up_write(&_hash_lock);
dm_put(md);
+ dm_ima_free_context(ima_context);
return r;
}
+ ima_idx = dm_ima_init_context(hc, ima_context, true);
t = __hash_remove(hc);
up_write(&_hash_lock);
@@ -1033,7 +1127,8 @@ static int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_si
param->flags &= ~DM_DEFERRED_REMOVE;
- dm_ima_measure_on_device_remove(md, false);
+ dm_ima_measure_on_device_remove(md, false, ima_context, ima_idx);
+ dm_ima_free_context(ima_context);
if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr, false))
param->flags |= DM_UEVENT_GENERATED_FLAG;
@@ -1169,13 +1264,16 @@ static int do_resume(struct dm_ioctl *param)
struct mapped_device *md;
struct dm_table *new_map, *old_map = NULL;
bool need_resize_uevent = false;
+ struct dm_ima_context *ima_context = NULL;
+ dm_ima_alloc_context(&ima_context, true);
down_write(&_hash_lock);
hc = __find_device_hash_cell(param);
if (!hc) {
DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
up_write(&_hash_lock);
+ dm_ima_free_context(ima_context);
return -ENXIO;
}
@@ -1184,13 +1282,15 @@ static int do_resume(struct dm_ioctl *param)
new_map = hc->new_map;
hc->new_map = NULL;
param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
-
+ if (new_map)
+ dm_ima_init_context(hc, ima_context, false);
up_write(&_hash_lock);
/* Do we need to load a new map ? */
if (new_map) {
sector_t old_size, new_size;
+ dm_ima_context_table_op(md, ima_context, DM_IMA_TABLE_SAVE);
/* Suspend if it isn't already suspended */
if (param->flags & DM_SKIP_LOCKFS_FLAG)
suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
@@ -1204,6 +1304,8 @@ static int do_resume(struct dm_ioctl *param)
if (hc && !hc->new_map) {
hc->new_map = new_map;
new_map = NULL;
+ dm_ima_init_context(hc, ima_context,
+ false);
} else {
r = -ENXIO;
}
@@ -1211,7 +1313,9 @@ static int do_resume(struct dm_ioctl *param)
if (new_map) {
dm_sync_table(md);
dm_table_destroy(new_map);
- }
+ } else
+ dm_ima_context_table_op(md, ima_context, DM_IMA_TABLE_RESTORE);
+ dm_ima_free_context(ima_context);
dm_put(md);
return r;
}
@@ -1222,9 +1326,12 @@ static int do_resume(struct dm_ioctl *param)
if (IS_ERR(old_map)) {
dm_sync_table(md);
dm_table_destroy(new_map);
+ dm_ima_free_context(ima_context);
dm_put(md);
return PTR_ERR(old_map);
}
+ if (dm_ima_need_measure(md, new_map, ima_context))
+ dm_ima_measure_on_device_resume(md, true, ima_context);
new_size = dm_get_size(md);
if (old_size && new_size && old_size != new_size)
need_resize_uevent = true;
@@ -1238,7 +1345,10 @@ static int do_resume(struct dm_ioctl *param)
if (dm_suspended_md(md)) {
r = dm_resume(md);
if (!r) {
- dm_ima_measure_on_device_resume(md, new_map ? true : false);
+ if (!new_map && dm_ima_need_measure(md, NULL,
+ ima_context))
+ dm_ima_measure_on_device_resume(md, false,
+ ima_context);
if (!dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr, need_resize_uevent))
param->flags |= DM_UEVENT_GENERATED_FLAG;
@@ -1255,6 +1365,7 @@ static int do_resume(struct dm_ioctl *param)
if (!r)
__dev_status(md, param);
+ dm_ima_free_context(ima_context);
dm_put(md);
return r;
}
@@ -1532,11 +1643,12 @@ static bool is_valid_type(enum dm_queue_mode cur, enum dm_queue_mode new)
static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_size)
{
- int r;
+ int r, srcu_idx;
struct hash_cell *hc;
struct dm_table *t, *old_map = NULL;
struct mapped_device *md;
struct target_type *immutable_target_type;
+ struct dm_ima_context *ima_context = NULL;
md = find_device(param);
if (!md)
@@ -1552,8 +1664,6 @@ static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_si
if (r)
goto err_unlock_md_type;
- dm_ima_measure_on_table_load(t);
-
immutable_target_type = dm_get_immutable_target_type(md);
if (immutable_target_type &&
(immutable_target_type != dm_table_get_immutable_target_type(t)) &&
@@ -1580,12 +1690,14 @@ static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_si
dm_unlock_md_type(md);
+ dm_ima_alloc_context(&ima_context, false);
/* stage inactive table */
down_write(&_hash_lock);
hc = dm_get_mdptr(md);
if (!hc) {
DMERR("device has been removed from the dev hash table.");
up_write(&_hash_lock);
+ dm_ima_free_context(ima_context);
r = -ENXIO;
goto err_destroy_table;
}
@@ -1593,8 +1705,15 @@ static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_si
if (hc->new_map)
old_map = hc->new_map;
hc->new_map = t;
+ dm_ima_init_context(hc, ima_context, false);
+ /* Make sure new_map doesn't get freed before we measure it*/
+ dm_get_live_table(md, &srcu_idx);
up_write(&_hash_lock);
+ dm_ima_measure_on_table_load(t, ima_context);
+ dm_ima_free_context(ima_context);
+ dm_put_live_table(md, srcu_idx);
+
param->flags |= DM_INACTIVE_PRESENT_FLAG;
__dev_status(md, param);
@@ -1623,13 +1742,16 @@ static int table_clear(struct file *filp, struct dm_ioctl *param, size_t param_s
struct mapped_device *md;
struct dm_table *old_map = NULL;
bool has_new_map = false;
+ struct dm_ima_context *ima_context = NULL;
+ dm_ima_alloc_context(&ima_context, true);
down_write(&_hash_lock);
hc = __find_device_hash_cell(param);
if (!hc) {
DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
up_write(&_hash_lock);
+ dm_ima_free_context(ima_context);
return -ENXIO;
}
@@ -1639,8 +1761,11 @@ static int table_clear(struct file *filp, struct dm_ioctl *param, size_t param_s
has_new_map = true;
}
+ dm_ima_init_context(hc, ima_context, false);
md = hc->md;
up_write(&_hash_lock);
+ dm_ima_measure_on_table_clear(md, has_new_map, ima_context);
+ dm_ima_free_context(ima_context);
param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
__dev_status(md, param);
@@ -1649,7 +1774,6 @@ static int table_clear(struct file *filp, struct dm_ioctl *param, size_t param_s
dm_sync_table(md);
dm_table_destroy(old_map);
}
- dm_ima_measure_on_table_clear(md, has_new_map);
dm_put(md);
return 0;
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 8b60c9804f5b..87011c41ef7b 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -2546,6 +2546,8 @@ int dm_create(int minor, struct mapped_device **result)
if (!md)
return -ENXIO;
+ dm_ima_init(md);
+
*result = md;
return 0;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 06/10] dm-ima: remove new_map from dm_ima_measure_on_device_clear
2026-04-29 20:20 [PATCH v2 00/10] Fix dm-ima bugs Benjamin Marzinski
` (4 preceding siblings ...)
2026-04-29 20:21 ` [PATCH v2 05/10] dm-ima: Fix UAF errors and measuring incorrect context Benjamin Marzinski
@ 2026-04-29 20:21 ` Benjamin Marzinski
2026-04-29 20:21 ` [PATCH v2 07/10] dm-ima: Fix issues with dm_ima_measure_on_device_rename Benjamin Marzinski
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Benjamin Marzinski @ 2026-04-29 20:21 UTC (permalink / raw)
To: Mikulas Patocka, Mike Snitzer
Cc: dm-devel, linux-integrity, Alasdair Kergon, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Lakshmi Ramasubramanian,
steven chen
Now that two processes can't modify md->ima in
dm_ima_measure_on_device_clear() at the same time, there's no need to
track if an inactive table was actually removed. We might as well
clean it up unconditionally, on the off chance that a previous
ima measurement failed and left md->ima.inactive_table behind.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
drivers/md/dm-ima.c | 10 ++++------
drivers/md/dm-ima.h | 3 +--
drivers/md/dm-ioctl.c | 4 +---
3 files changed, 6 insertions(+), 11 deletions(-)
diff --git a/drivers/md/dm-ima.c b/drivers/md/dm-ima.c
index 096c664d855c..75c46b5af3f7 100644
--- a/drivers/md/dm-ima.c
+++ b/drivers/md/dm-ima.c
@@ -549,7 +549,7 @@ void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all,
/*
* Measure ima data on table clear.
*/
-void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map,
+void dm_ima_measure_on_table_clear(struct mapped_device *md,
struct dm_ima_context *context)
{
unsigned int l = 0;
@@ -609,11 +609,9 @@ void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map,
dm_ima_measure_data("dm_table_clear", device_table_data, l, noio);
- if (new_map) {
- kfree(md->ima.inactive_table.hash);
- kfree(md->ima.inactive_table.device_metadata);
- memset(&md->ima.inactive_table, 0, sizeof(md->ima.inactive_table));
- }
+ kfree(md->ima.inactive_table.hash);
+ kfree(md->ima.inactive_table.device_metadata);
+ memset(&md->ima.inactive_table, 0, sizeof(md->ima.inactive_table));
error:
kfree(capacity_str);
diff --git a/drivers/md/dm-ima.h b/drivers/md/dm-ima.h
index 01fa0b89a385..b240e0e4b6a1 100644
--- a/drivers/md/dm-ima.h
+++ b/drivers/md/dm-ima.h
@@ -83,7 +83,7 @@ void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap,
void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all,
struct dm_ima_context *context,
unsigned int idx);
-void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map,
+void dm_ima_measure_on_table_clear(struct mapped_device *md,
struct dm_ima_context *context);
void dm_ima_measure_on_device_rename(struct mapped_device *md,
struct dm_ima_context *context);
@@ -108,7 +108,6 @@ static inline void dm_ima_measure_on_device_remove(struct mapped_device *md,
struct dm_ima_context *context,
unsigned int idx) {}
static inline void dm_ima_measure_on_table_clear(struct mapped_device *md,
- bool new_map,
struct dm_ima_context *context) {}
static inline void dm_ima_measure_on_device_rename(struct mapped_device *md,
struct dm_ima_context *context) {}
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index 3da8b33cdc54..b92ec3efff01 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -1741,7 +1741,6 @@ static int table_clear(struct file *filp, struct dm_ioctl *param, size_t param_s
struct hash_cell *hc;
struct mapped_device *md;
struct dm_table *old_map = NULL;
- bool has_new_map = false;
struct dm_ima_context *ima_context = NULL;
dm_ima_alloc_context(&ima_context, true);
@@ -1758,13 +1757,12 @@ static int table_clear(struct file *filp, struct dm_ioctl *param, size_t param_s
if (hc->new_map) {
old_map = hc->new_map;
hc->new_map = NULL;
- has_new_map = true;
}
dm_ima_init_context(hc, ima_context, false);
md = hc->md;
up_write(&_hash_lock);
- dm_ima_measure_on_table_clear(md, has_new_map, ima_context);
+ dm_ima_measure_on_table_clear(md, ima_context);
dm_ima_free_context(ima_context);
param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 07/10] dm-ima: Fix issues with dm_ima_measure_on_device_rename
2026-04-29 20:20 [PATCH v2 00/10] Fix dm-ima bugs Benjamin Marzinski
` (5 preceding siblings ...)
2026-04-29 20:21 ` [PATCH v2 06/10] dm-ima: remove new_map from dm_ima_measure_on_device_clear Benjamin Marzinski
@ 2026-04-29 20:21 ` Benjamin Marzinski
2026-04-29 20:21 ` [PATCH v2 08/10] dm-ima: Handle race between rename and table swap Benjamin Marzinski
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Benjamin Marzinski @ 2026-04-29 20:21 UTC (permalink / raw)
To: Mikulas Patocka, Mike Snitzer
Cc: dm-devel, linux-integrity, Alasdair Kergon, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Lakshmi Ramasubramanian,
steven chen
dm_ima_measure_on_device_rename() can be called on a device before it
ever loads a table, so it needs to handle the case where there is no
table metadata. Also, it was only updating the table_metadata on the
active table. If there was an inactive table when the device was renamed
and that table was later swapped in as the active table, it would
still have the old name. dm_ima_measure_on_device_rename() was also
needlessly allocating new memory for the updated table metadata, instead
of just reusing the existing memory.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
drivers/md/dm-ima.c | 69 ++++++++++++++++++++++++---------------------
1 file changed, 37 insertions(+), 32 deletions(-)
diff --git a/drivers/md/dm-ima.c b/drivers/md/dm-ima.c
index 75c46b5af3f7..f563c4381489 100644
--- a/drivers/md/dm-ima.c
+++ b/drivers/md/dm-ima.c
@@ -133,22 +133,18 @@ void dm_ima_context_table_op(struct mapped_device *md,
}
/*
- * Internal function to allocate and copy device data for IMA measurements.
+ * Internal function to copy device data for IMA measurements.
*/
-static int dm_ima_alloc_and_copy_device_data(struct mapped_device *md, char **device_data,
- struct dm_ima_context *context,
- unsigned int num_targets, bool noio)
+static void dm_ima_copy_device_data(struct mapped_device *md, char *device_data,
+ struct dm_ima_context *context,
+ unsigned int num_targets)
{
- *device_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
- if (!(*device_data))
- return -ENOMEM;
-
- scnprintf(*device_data, DM_IMA_DEVICE_BUF_LEN,
+ memset(device_data, 0, DM_IMA_DEVICE_BUF_LEN);
+ scnprintf(device_data, DM_IMA_DEVICE_BUF_LEN,
"name=%s,uuid=%s,major=%d,minor=%d,minor_count=%d,num_targets=%u;",
context->dev_name, context->dev_uuid, md->disk->major,
md->disk->first_minor, md->disk->minors, num_targets);
- return 0;
}
/*
@@ -223,11 +219,14 @@ void dm_ima_measure_on_table_load(struct dm_table *table,
num_targets = table->num_targets;
- fix_context_strings(context);
- if (dm_ima_alloc_and_copy_device_data(table->md, &device_data_buf,
- context, num_targets, noio))
+ device_data_buf = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
+ if (!device_data_buf)
goto error;
+ fix_context_strings(context);
+ dm_ima_copy_device_data(table->md, device_data_buf, context,
+ num_targets);
+
sha256_init(&hash_ctx);
memcpy(ima_buf + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
@@ -626,48 +625,54 @@ void dm_ima_measure_on_table_clear(struct mapped_device *md,
void dm_ima_measure_on_device_rename(struct mapped_device *md,
struct dm_ima_context *context)
{
- char *old_device_data = NULL, *new_device_data = NULL;
+ char *old_device_data = NULL;
char *combined_device_data = NULL, *capacity_str = NULL;
bool noio = true;
int len;
+ struct dm_ima_device_table_metadata *table;
if (unlikely(!context))
return;
wait_to_measure(&md->ima, context->update_idx);
- fix_context_strings(context);
- if (dm_ima_alloc_and_copy_device_data(md, &new_device_data, context,
- md->ima.active_table.num_targets,
- noio))
- goto error;
-
combined_device_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN * 2, noio);
if (!combined_device_data)
- goto error;
+ goto exit;
if (dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio) < 0)
- goto error;
-
- old_device_data = md->ima.active_table.device_metadata;
-
- md->ima.active_table.device_metadata = new_device_data;
- md->ima.active_table.device_metadata_len = strlen(new_device_data);
+ goto exit;
+ if (md->ima.active_table.device_metadata)
+ old_device_data = md->ima.active_table.device_metadata;
+ else if (md->ima.inactive_table.device_metadata)
+ old_device_data = md->ima.inactive_table.device_metadata;
+ else
+ old_device_data = "device_rename=no_data;";
+ fix_context_strings(context);
len = scnprintf(combined_device_data, DM_IMA_DEVICE_BUF_LEN * 2,
"%s%snew_name=%s,new_uuid=%s;%s", DM_IMA_VERSION_STR, old_device_data,
context->dev_name, context->dev_uuid, capacity_str);
- dm_ima_measure_data("dm_device_rename", combined_device_data, len, noio);
+ if (md->ima.active_table.device_metadata) {
+ table = &md->ima.active_table;
+ dm_ima_copy_device_data(md, table->device_metadata, context,
+ table->num_targets);
+ table->device_metadata_len = strlen(table->device_metadata);
+ }
- goto exit;
+ if (md->ima.inactive_table.device_metadata) {
+ table = &md->ima.inactive_table;
+ dm_ima_copy_device_data(md, table->device_metadata, context,
+ table->num_targets);
+ table->device_metadata_len = strlen(table->device_metadata);
+ }
+
+ dm_ima_measure_data("dm_device_rename", combined_device_data, len, noio);
-error:
- kfree(new_device_data);
exit:
kfree(capacity_str);
kfree(combined_device_data);
- kfree(old_device_data);
wake_next_measure(&md->ima);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 08/10] dm-ima: Handle race between rename and table swap
2026-04-29 20:20 [PATCH v2 00/10] Fix dm-ima bugs Benjamin Marzinski
` (6 preceding siblings ...)
2026-04-29 20:21 ` [PATCH v2 07/10] dm-ima: Fix issues with dm_ima_measure_on_device_rename Benjamin Marzinski
@ 2026-04-29 20:21 ` Benjamin Marzinski
2026-04-29 20:21 ` [PATCH v2 09/10] dm-ima: Fail more gracefully in dm_ima_measure_on_* Benjamin Marzinski
2026-04-29 20:21 ` [PATCH v2 10/10] dm-ima: use active table's size if available Benjamin Marzinski
9 siblings, 0 replies; 11+ messages in thread
From: Benjamin Marzinski @ 2026-04-29 20:21 UTC (permalink / raw)
To: Mikulas Patocka, Mike Snitzer
Cc: dm-devel, linux-integrity, Alasdair Kergon, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Lakshmi Ramasubramanian,
steven chen
a device rename could happen after do_resume() removed the inactive
table that it was swapping to out of the hash cell, but before it was
made the active table. In this case, the table metadata would still
have the old name. Update the swapped table's metadata to avoid this.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
drivers/md/dm-ima.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/drivers/md/dm-ima.c b/drivers/md/dm-ima.c
index f563c4381489..47af99c9b79c 100644
--- a/drivers/md/dm-ima.c
+++ b/drivers/md/dm-ima.c
@@ -373,6 +373,19 @@ void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap,
kfree(md->ima.active_table.device_metadata);
md->ima.active_table = context->table;
memset(&context->table, 0, sizeof(context->table));
+ if (md->ima.active_table.device_metadata) {
+ /*
+ * A rename could have happened while the swap was
+ * going on. In that case, the saved table info would
+ * still have the old name. Update the metadata to be
+ * sure that it has the current name
+ */
+ struct dm_ima_device_table_metadata *table = &md->ima.active_table;
+ fix_context_strings(context);
+ dm_ima_copy_device_data(md, table->device_metadata,
+ context, table->num_targets);
+ table->device_metadata_len = strlen(table->device_metadata);
+ }
}
if (md->ima.active_table.device_metadata) {
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 09/10] dm-ima: Fail more gracefully in dm_ima_measure_on_*
2026-04-29 20:20 [PATCH v2 00/10] Fix dm-ima bugs Benjamin Marzinski
` (7 preceding siblings ...)
2026-04-29 20:21 ` [PATCH v2 08/10] dm-ima: Handle race between rename and table swap Benjamin Marzinski
@ 2026-04-29 20:21 ` Benjamin Marzinski
2026-04-29 20:21 ` [PATCH v2 10/10] dm-ima: use active table's size if available Benjamin Marzinski
9 siblings, 0 replies; 11+ messages in thread
From: Benjamin Marzinski @ 2026-04-29 20:21 UTC (permalink / raw)
To: Mikulas Patocka, Mike Snitzer
Cc: dm-devel, linux-integrity, Alasdair Kergon, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Lakshmi Ramasubramanian,
steven chen
In all the dm_ima_measure_on_* functions besides
dm_ima_measure_on_table_load(), even if measuring the event fails, it's
still possible to update dm->ima, so that it continues to correctly
track the device state. This means that one measurement failure won't
cause future measurements to record the wrong data.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
drivers/md/dm-ima.c | 38 +++++++++++++++++++-------------------
1 file changed, 19 insertions(+), 19 deletions(-)
diff --git a/drivers/md/dm-ima.c b/drivers/md/dm-ima.c
index 47af99c9b79c..5e2efcd1de33 100644
--- a/drivers/md/dm-ima.c
+++ b/drivers/md/dm-ima.c
@@ -357,17 +357,6 @@ void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap,
wait_to_measure(&md->ima, context->update_idx);
- device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
- if (!device_table_data)
- goto error;
-
- capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio);
- if (capacity_len < 0)
- goto error;
-
- memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
- l += strlen(DM_IMA_VERSION_STR);
-
if (swap) {
kfree(md->ima.active_table.hash);
kfree(md->ima.active_table.device_metadata);
@@ -388,6 +377,17 @@ void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap,
}
}
+ device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
+ if (!device_table_data)
+ goto error;
+
+ capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio);
+ if (capacity_len < 0)
+ goto error;
+
+ memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
+ l += strlen(DM_IMA_VERSION_STR);
+
if (md->ima.active_table.device_metadata) {
memcpy(device_table_data + l, md->ima.active_table.device_metadata,
md->ima.active_table.device_metadata_len);
@@ -621,11 +621,11 @@ void dm_ima_measure_on_table_clear(struct mapped_device *md,
dm_ima_measure_data("dm_table_clear", device_table_data, l, noio);
+error:
kfree(md->ima.inactive_table.hash);
kfree(md->ima.inactive_table.device_metadata);
memset(&md->ima.inactive_table, 0, sizeof(md->ima.inactive_table));
-error:
kfree(capacity_str);
kfree(device_table_data);
@@ -649,6 +649,8 @@ void dm_ima_measure_on_device_rename(struct mapped_device *md,
wait_to_measure(&md->ima, context->update_idx);
+ fix_context_strings(context);
+
combined_device_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN * 2, noio);
if (!combined_device_data)
goto exit;
@@ -662,11 +664,15 @@ void dm_ima_measure_on_device_rename(struct mapped_device *md,
old_device_data = md->ima.inactive_table.device_metadata;
else
old_device_data = "device_rename=no_data;";
- fix_context_strings(context);
len = scnprintf(combined_device_data, DM_IMA_DEVICE_BUF_LEN * 2,
"%s%snew_name=%s,new_uuid=%s;%s", DM_IMA_VERSION_STR, old_device_data,
context->dev_name, context->dev_uuid, capacity_str);
+ dm_ima_measure_data("dm_device_rename", combined_device_data, len, noio);
+exit:
+ kfree(capacity_str);
+ kfree(combined_device_data);
+
if (md->ima.active_table.device_metadata) {
table = &md->ima.active_table;
dm_ima_copy_device_data(md, table->device_metadata, context,
@@ -681,11 +687,5 @@ void dm_ima_measure_on_device_rename(struct mapped_device *md,
table->device_metadata_len = strlen(table->device_metadata);
}
- dm_ima_measure_data("dm_device_rename", combined_device_data, len, noio);
-
-exit:
- kfree(capacity_str);
- kfree(combined_device_data);
-
wake_next_measure(&md->ima);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 10/10] dm-ima: use active table's size if available
2026-04-29 20:20 [PATCH v2 00/10] Fix dm-ima bugs Benjamin Marzinski
` (8 preceding siblings ...)
2026-04-29 20:21 ` [PATCH v2 09/10] dm-ima: Fail more gracefully in dm_ima_measure_on_* Benjamin Marzinski
@ 2026-04-29 20:21 ` Benjamin Marzinski
9 siblings, 0 replies; 11+ messages in thread
From: Benjamin Marzinski @ 2026-04-29 20:21 UTC (permalink / raw)
To: Mikulas Patocka, Mike Snitzer
Cc: dm-devel, linux-integrity, Alasdair Kergon, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Lakshmi Ramasubramanian,
steven chen
It is possible that the dm_ima_measure_on_* functions run at the same
time as a table is getting swapped, but before the md->ima.active_table
is updated by dm_ima_measure_on_device_resume(). Instead of using the
current device size, use the size of the active table that is being
measured (assuming there is one), so the information is consistent.
Also, don't allocate a separate string to hold the capactiy. Just
print it directly to the measurement buffer.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
drivers/md/dm-ima.c | 79 +++++++++++----------------------------------
drivers/md/dm-ima.h | 1 +
2 files changed, 20 insertions(+), 60 deletions(-)
diff --git a/drivers/md/dm-ima.c b/drivers/md/dm-ima.c
index 5e2efcd1de33..5e7232582feb 100644
--- a/drivers/md/dm-ima.c
+++ b/drivers/md/dm-ima.c
@@ -165,22 +165,10 @@ static void dm_ima_measure_data(const char *event_name, const void *buf, size_t
memalloc_noio_restore(noio_flag);
}
-/*
- * Internal function to allocate and copy current device capacity for IMA measurements.
- */
-static int dm_ima_alloc_and_copy_capacity_str(struct mapped_device *md, char **capacity_str,
- bool noio)
+static sector_t dm_ima_capacity(struct mapped_device *md)
{
- sector_t capacity;
-
- capacity = get_capacity(md->disk);
-
- *capacity_str = dm_ima_alloc(DM_IMA_DEVICE_CAPACITY_BUF_LEN, noio);
- if (!(*capacity_str))
- return -ENOMEM;
-
- return scnprintf(*capacity_str, DM_IMA_DEVICE_BUF_LEN, "current_device_capacity=%llu;",
- capacity);
+ return (md->ima.active_table.device_metadata) ?
+ md->ima.active_table.capacity : get_capacity(md->disk);
}
/*
@@ -320,6 +308,7 @@ void dm_ima_measure_on_table_load(struct dm_table *table,
table->md->ima.inactive_table.hash = digest_buf;
table->md->ima.inactive_table.hash_len = strlen(digest_buf);
table->md->ima.inactive_table.num_targets = num_targets;
+ table->md->ima.inactive_table.capacity = dm_table_get_size(table);
kfree(table->md->ima.inactive_table.device_metadata);
@@ -344,13 +333,12 @@ void dm_ima_measure_on_table_load(struct dm_table *table,
void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap,
struct dm_ima_context *context)
{
- char *device_table_data = NULL, *capacity_str = NULL;
+ char *device_table_data = NULL;
char active[] = "active_table_hash=";
unsigned int active_len = strlen(active);
unsigned int l = 0;
bool noio = true;
bool nodata = true;
- int capacity_len;
if (unlikely(!context))
return;
@@ -381,10 +369,6 @@ void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap,
if (!device_table_data)
goto error;
- capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio);
- if (capacity_len < 0)
- goto error;
-
memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
l += strlen(DM_IMA_VERSION_STR);
@@ -417,14 +401,12 @@ void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap,
DM_IMA_VERSION_STR, context->dev_name,
context->dev_uuid);
}
-
- memcpy(device_table_data + l, capacity_str, capacity_len);
- l += capacity_len;
+ l += scnprintf(device_table_data + l, DM_IMA_DEVICE_BUF_LEN - l,
+ "current_device_capacity=%llu;", dm_ima_capacity(md));
dm_ima_measure_data("dm_device_resume", device_table_data, l, noio);
error:
- kfree(capacity_str);
kfree(device_table_data);
wake_next_measure(&md->ima);
@@ -437,21 +419,18 @@ void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all,
struct dm_ima_context *context,
unsigned int idx)
{
- char *device_table_data, *capacity_str = NULL;
+ char *device_table_data;
char active_table_str[] = "active_table_hash=";
char inactive_table_str[] = "inactive_table_hash=";
char device_active_str[] = "device_active_metadata=";
char device_inactive_str[] = "device_inactive_metadata=";
- char remove_all_str[] = "remove_all=";
unsigned int active_table_len = strlen(active_table_str);
unsigned int inactive_table_len = strlen(inactive_table_str);
unsigned int device_active_len = strlen(device_active_str);
unsigned int device_inactive_len = strlen(device_inactive_str);
- unsigned int remove_all_len = strlen(remove_all_str);
unsigned int l = 0;
bool noio = true;
bool nodata = true;
- int capacity_len;
wait_to_measure(&md->ima, idx);
@@ -462,12 +441,6 @@ void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all,
if (!device_table_data)
goto exit;
- capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio);
- if (capacity_len < 0) {
- kfree(device_table_data);
- goto exit;
- }
-
memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
l += strlen(DM_IMA_VERSION_STR);
@@ -533,18 +506,13 @@ void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all,
context->dev_uuid);
}
- memcpy(device_table_data + l, remove_all_str, remove_all_len);
- l += remove_all_len;
- memcpy(device_table_data + l, remove_all ? "y;" : "n;", 2);
- l += 2;
-
- memcpy(device_table_data + l, capacity_str, capacity_len);
- l += capacity_len;
+ l += scnprintf(device_table_data + l, (DM_IMA_DEVICE_BUF_LEN * 2) - l,
+ "remove_all=%c;current_device_capacity=%llu;",
+ remove_all ? 'y' : 'n', dm_ima_capacity(md));
dm_ima_measure_data("dm_device_remove", device_table_data, l, noio);
kfree(device_table_data);
- kfree(capacity_str);
exit:
kfree(md->ima.active_table.device_metadata);
kfree(md->ima.inactive_table.device_metadata);
@@ -565,12 +533,11 @@ void dm_ima_measure_on_table_clear(struct mapped_device *md,
struct dm_ima_context *context)
{
unsigned int l = 0;
- char *device_table_data = NULL, *capacity_str = NULL;
+ char *device_table_data = NULL;
char inactive_str[] = "inactive_table_hash=";
unsigned int inactive_len = strlen(inactive_str);
bool noio = true;
bool nodata = true;
- int capacity_len;
if (unlikely(!context))
return;
@@ -581,10 +548,6 @@ void dm_ima_measure_on_table_clear(struct mapped_device *md,
if (!device_table_data)
goto error;
- capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio);
- if (capacity_len < 0)
- goto error;
-
memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
l += strlen(DM_IMA_VERSION_STR);
@@ -616,8 +579,8 @@ void dm_ima_measure_on_table_clear(struct mapped_device *md,
context->dev_uuid);
}
- memcpy(device_table_data + l, capacity_str, capacity_len);
- l += capacity_len;
+ l += scnprintf(device_table_data + l, DM_IMA_DEVICE_BUF_LEN - l,
+ "current_device_capacity=%llu;", dm_ima_capacity(md));
dm_ima_measure_data("dm_table_clear", device_table_data, l, noio);
@@ -626,7 +589,6 @@ void dm_ima_measure_on_table_clear(struct mapped_device *md,
kfree(md->ima.inactive_table.device_metadata);
memset(&md->ima.inactive_table, 0, sizeof(md->ima.inactive_table));
- kfree(capacity_str);
kfree(device_table_data);
wake_next_measure(&md->ima);
@@ -639,7 +601,7 @@ void dm_ima_measure_on_device_rename(struct mapped_device *md,
struct dm_ima_context *context)
{
char *old_device_data = NULL;
- char *combined_device_data = NULL, *capacity_str = NULL;
+ char *combined_device_data = NULL;
bool noio = true;
int len;
struct dm_ima_device_table_metadata *table;
@@ -655,9 +617,6 @@ void dm_ima_measure_on_device_rename(struct mapped_device *md,
if (!combined_device_data)
goto exit;
- if (dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio) < 0)
- goto exit;
-
if (md->ima.active_table.device_metadata)
old_device_data = md->ima.active_table.device_metadata;
else if (md->ima.inactive_table.device_metadata)
@@ -665,14 +624,14 @@ void dm_ima_measure_on_device_rename(struct mapped_device *md,
else
old_device_data = "device_rename=no_data;";
len = scnprintf(combined_device_data, DM_IMA_DEVICE_BUF_LEN * 2,
- "%s%snew_name=%s,new_uuid=%s;%s", DM_IMA_VERSION_STR, old_device_data,
- context->dev_name, context->dev_uuid, capacity_str);
+ "%s%snew_name=%s,new_uuid=%s;current_device_capacity=%llu;",
+ DM_IMA_VERSION_STR, old_device_data, context->dev_name,
+ context->dev_uuid, dm_ima_capacity(md));
dm_ima_measure_data("dm_device_rename", combined_device_data, len, noio);
-exit:
- kfree(capacity_str);
kfree(combined_device_data);
+exit:
if (md->ima.active_table.device_metadata) {
table = &md->ima.active_table;
dm_ima_copy_device_data(md, table->device_metadata, context,
diff --git a/drivers/md/dm-ima.h b/drivers/md/dm-ima.h
index b240e0e4b6a1..0ec013d1545c 100644
--- a/drivers/md/dm-ima.h
+++ b/drivers/md/dm-ima.h
@@ -41,6 +41,7 @@ struct dm_ima_device_table_metadata {
char *device_metadata;
unsigned int device_metadata_len;
unsigned int num_targets;
+ sector_t capacity;
/*
* Contains the sha256 hashes of the IMA measurements of the target
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread