[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v3 1/7] x86/kexec: add digest checks


  • To: xen-devel@xxxxxxxxxxxxxxxxxxxx
  • From: Kevin Lampis <kevin.lampis@xxxxxxxxxx>
  • Date: Mon, 26 Oct 2026 09:20:11 +0000
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=citrix.com; dmarc=pass action=none header.from=citrix.com; dkim=pass header.d=citrix.com; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=pQKpg0FcY1WqH3GQcUzAyEh301CnfBHGqKg1GHGRaso=; b=l276Z00JOMyY/9Kslas8U0/UW+kby5kyc5F/8/2DHyznsSVuy5/oAlQddfJfOY61XYHiB5L28a/oPiSk28j13deAKOfhgQs5fezE0UtsCnEciuNcl5P+d+3leQw+lUfcsRDCWe0HPSy2K8EIgu65NJNK3sacgBCS703/Yq947T8ninCZskUgmVd5Oj6Gj0K4a4r+GiD0DF2HyD3rH1vHwx2OlbV9RH7KZ7BL6STk9m94LaU2VsMi9UiW2TqTw3jqKI77QpzOQeWM/O9syGCZ4hX++rz05U9YPiMO26UvXedsaZD9nogIycTjtgEW+OK+ug4oJKX54CWDDEGac3ih5Q==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=Ftd6RKBWpPbdcPEQji4cvv6NTbjMvdzMSXL20m+4/Yb5k3tjnOlvASIxdJHHoUYi4HyNQN3Uuu2AdqQi8w35ENxNoMlgSnuQxHu1RdleKNoJ03Y488+J+iCA7E3uibA1OO+obbs6nacD56XrXFQCAl/d2oFe17hVUcXeAZtJFhs53yWiYcBW7HRhHlbuh4cdofScqSuVdphTRZ4OTSuNVn2nBHBLmYSRB8kyKFRYiHF+HJNN3SB0UaZBcGZDCI4qIKHY3C0wgFRrd0Qg6xov1RVqrA50mYnPhJgxtYia2QFd5efRzMdP6FzLDOFSkD20pVTA4ILdqD9emKNN8bbspg==
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=selector1 header.d=citrix.com header.i="@citrix.com" header.h="From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck"
  • Authentication-results: mx.microsoft.com 1; dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=citrix.com;
  • Cc: jbeulich@xxxxxxxx, andrew.cooper3@xxxxxxxxxx, roger@xxxxxxxxxxxxxx, teddy.astie@xxxxxxxxxx, ross.lagerwall@xxxxxxxxxx, Kevin Lampis <kevin.lampis@xxxxxxxxxx>
  • Delivery-date: Mon, 26 Oct 2026 09:17:52 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

From: Ross Lagerwall <ross.lagerwall@xxxxxxxxxx>

As part of the work to support UEFI Secure Boot we must ensure that the
kexec image data has changed between loading and executing the image.
However, this is also a good check to perform generally.

During kexec load, calculate a digest over all the kexec segments. This
digest is stored and verified again later prior to entering the image.

Signed-off-by: Ross Lagerwall <ross.lagerwall@xxxxxxxxxx>
Signed-off-by: Kevin Lampis <kevin.lampis@xxxxxxxxxx>
---
Changes in v3:
- Reword the commit message to make it clear that digest checks are only
  part of the kexec strategy.

Changes in v2:
- Reword the commit message
- Change the "kexec digest failed" error message
- Remove the `!= 0` from the call to kimage_verify_digest()
- Remove include sha2.h from kimage.h
- Remove error checking from kimage_calc_one_digest()
  because map_domain_page() doesn't fail
- Change kimage_calc_digest() to a void return too
- Remove references to KIMAGE_SHA256_REGIONS
  It was a purgatory thing which is not used anymore
- Fix style on `for ( s = 0; s < image->nr_segments; s++ ) {`
- Add newlines and indentation to digest check fail message
- Make kimage_verify_digest() a bool return value
---
 xen/common/kexec.c       |  8 +++++
 xen/common/kimage.c      | 73 ++++++++++++++++++++++++++++++++++++++++
 xen/include/xen/kimage.h |  6 ++++
 3 files changed, 87 insertions(+)

diff --git a/xen/common/kexec.c b/xen/common/kexec.c
index 8ccaef1e0258..68f4c2cb2671 100644
--- a/xen/common/kexec.c
+++ b/xen/common/kexec.c
@@ -389,6 +389,12 @@ void kexec_crash(enum crash_reason reason)
     if ( !test_bit(KEXEC_IMAGE_CRASH_BASE + pos, &kexec_flags) )
         return;
 
+    if ( !kimage_verify_digest(kexec_image[KEXEC_IMAGE_CRASH_BASE + pos]) )
+    {
+        printk(XENLOG_ERR "kexec digest failed, aborting kexec crash 
transfer\n");
+        return;
+    }
+
     kexecing = true;
 
     if ( kexec_common_shutdown() != 0 )
@@ -940,6 +946,8 @@ static int kexec_load(XEN_GUEST_HANDLE_PARAM(void) uarg)
     if ( ret < 0 )
         goto error;
 
+    kimage_calc_digest(kimage, kimage->digest);
+
     ret = kexec_load_slot(kimage);
     if ( ret < 0 )
         goto error;
diff --git a/xen/common/kimage.c b/xen/common/kimage.c
index a0e4e67df7ee..6e009529ae01 100644
--- a/xen/common/kimage.c
+++ b/xen/common/kimage.c
@@ -818,6 +818,79 @@ int kimage_alloc(struct kexec_image **rimage, uint8_t 
type, uint16_t arch,
     return result;
 }
 
+static void kimage_calc_one_digest(struct sha2_256_state *ctx,
+                                   xen_kexec_segment_t *segment)
+{
+    paddr_t dest;
+    unsigned long sbytes;
+
+    sbytes = segment->buf_size;
+    dest = segment->dest_maddr;
+
+    while ( sbytes )
+    {
+        unsigned long dest_mfn;
+        void *dest_va;
+        size_t schunk, dchunk;
+
+        dest_mfn = dest >> PAGE_SHIFT;
+
+        dchunk = PAGE_SIZE;
+        schunk = min(dchunk, sbytes);
+
+        dest_va = map_domain_page(_mfn(dest_mfn));
+        sha2_256_update(ctx, dest_va, schunk);
+        unmap_domain_page(dest_va);
+
+        sbytes -= schunk;
+        dest += dchunk;
+    }
+}
+
+void kimage_calc_digest(const struct kexec_image *image,
+                        uint8_t digest[SHA2_256_DIGEST_SIZE])
+{
+    struct sha2_256_state ctx;
+    unsigned int s;
+
+    if ( image->type == KEXEC_TYPE_DEFAULT )
+    {
+        /* TODO implement digest calculation for normal kexec */
+        return;
+    }
+
+    sha2_256_init(&ctx);
+
+    for ( s = 0; s < image->nr_segments; s++ )
+        kimage_calc_one_digest(&ctx, &image->segments[s]);
+
+    sha2_256_final(&ctx, digest);
+}
+
+bool kimage_verify_digest(const struct kexec_image *image)
+{
+    uint8_t digest[SHA2_256_DIGEST_SIZE];
+
+    if ( image->type == KEXEC_TYPE_DEFAULT )
+    {
+        /* TODO implement digest check for normal kexec */
+        return true;
+    }
+
+    kimage_calc_digest(image, digest);
+
+    if ( memcmp(digest, image->digest, sizeof(digest)) != 0 )
+    {
+        printk(XENLOG_WARNING "kexec digest mismatch:\n"
+               "  expected %" STR(SHA2_256_DIGEST_SIZE) "phN\n"
+               "       got %" STR(SHA2_256_DIGEST_SIZE) "phN\n",
+               image->digest, digest);
+        return false;
+    }
+
+    return true;
+}
+
 int kimage_load_segments(struct kexec_image *image)
 {
     int s;
diff --git a/xen/include/xen/kimage.h b/xen/include/xen/kimage.h
index fccba1d88d5c..8ed89d4fa306 100644
--- a/xen/include/xen/kimage.h
+++ b/xen/include/xen/kimage.h
@@ -11,6 +11,7 @@
 
 #include <xen/list.h>
 #include <xen/mm.h>
+#include <xen/sha2.h>
 #include <public/kexec.h>
 
 #define KEXEC_SEGMENT_MAX 16
@@ -37,6 +38,8 @@ struct kexec_image {
 
     /* Address of next control page to allocate for crash kernels. */
     paddr_t next_crash_page;
+
+    uint8_t digest[SHA2_256_DIGEST_SIZE];
 };
 
 int kimage_alloc(struct kexec_image **rimage, uint8_t type, uint16_t arch,
@@ -52,6 +55,9 @@ mfn_t kimage_entry_mfn(kimage_entry_t *entry, bool compat);
 unsigned long kimage_entry_ind(kimage_entry_t *entry, bool compat);
 int kimage_build_ind(struct kexec_image *image, mfn_t ind_mfn,
                      bool compat);
+bool kimage_verify_digest(const struct kexec_image *image);
+void kimage_calc_digest(const struct kexec_image *image,
+                        uint8_t digest[SHA2_256_DIGEST_SIZE]);
 
 #endif /* __ASSEMBLER__ */
 
-- 
2.52.0




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.