From: Roberto Sassu <[email protected]>

Refuse to delete staged measurements (both with prompt and without) if
a kexec racing with the deletion already prepended staged measurements in
the kexec buffer. In this way, user space becomes aware that staged
measurements are going to appear in the secondary kernel, and thus they
don't have to be saved twice.

Link: https://github.com/linux-integrity/linux/issues/1
Signed-off-by: Roberto Sassu <[email protected]>
---
 security/integrity/ima/ima.h       |  1 +
 security/integrity/ima/ima_kexec.c |  3 +++
 security/integrity/ima/ima_queue.c | 17 +++++++++++++++++
 3 files changed, 21 insertions(+)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index de0693fce53c..0816b69f7c39 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -342,6 +342,7 @@ extern atomic_long_t ima_num_violations;
 extern struct hlist_head __rcu *ima_htable;
 extern struct mutex ima_extend_list_mutex;
 extern bool ima_flush_htable;
+extern bool ima_staged_measurements_prepended;
 
 static inline unsigned int ima_hash_key(u8 *digest)
 {
diff --git a/security/integrity/ima/ima_kexec.c 
b/security/integrity/ima/ima_kexec.c
index d5503dd5cc9b..25c8356be22a 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -116,6 +116,9 @@ static int ima_dump_measurement_list(unsigned long 
*buffer_size, void **buffer,
                        break;
        }
 
+       if (!list_empty(&ima_measurements_staged))
+               ima_staged_measurements_prepended = true;
+
        list_for_each_entry_rcu(qe, &ima_measurements, later,
                                lockdep_is_held(&ima_extend_list_mutex)) {
                if (!ret)
diff --git a/security/integrity/ima/ima_queue.c 
b/security/integrity/ima/ima_queue.c
index 4fb557d61a88..1918c8ea9abb 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -72,6 +72,13 @@ DEFINE_MUTEX(ima_extend_list_mutex);
  */
 static bool ima_measurements_suspended;
 
+/*
+ * Used to determine if staged measurements were prepended during kexec,
+ * so that an error can be sent to user space during deletion, and they don't
+ * store staged measurements twice.
+ */
+bool ima_staged_measurements_prepended;
+
 /* Callers must call synchronize_rcu() and free the hash table. */
 static struct hlist_head *ima_alloc_replace_htable(void)
 {
@@ -344,6 +351,11 @@ int ima_queue_staged_delete_all(void)
                return -ENOENT;
        }
 
+       if (ima_staged_measurements_prepended) {
+               mutex_unlock(&ima_extend_list_mutex);
+               return -ESTALE;
+       }
+
        list_replace(&ima_measurements_staged, &ima_measurements_trim);
        INIT_LIST_HEAD(&ima_measurements_staged);
 
@@ -408,6 +420,11 @@ int ima_queue_staged_delete_partial(unsigned long 
req_value)
                return -ENOENT;
        }
 
+       if (ima_staged_measurements_prepended) {
+               mutex_unlock(&ima_extend_list_mutex);
+               return -ESTALE;
+       }
+
        if (cut_pos != NULL)
                /*
                 * ima_dump_measurement_list() does not modify the list,
-- 
2.43.0


Reply via email to