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

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


  • To: xen-devel@xxxxxxxxxxxxxxxxxxxx
  • From: Kevin Lampis <kevin.lampis@xxxxxxxxxx>
  • Date: Mon, 22 Jun 2026 16:18:27 +0100
  • 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=ZhBWTpnyw2XmT+IZFDCCw+YPmi5DDkc8GEqwHJ16MNI=; b=dwWYUpDN7blaHrrZRXTBSDIzPup7zOEoFQPWDFwshep5y2WuDAPdv0LbQJEk8YDMjxFZTH7fd1AT6+T5/1RTXeqSWKDEHfNehjcczzbxab7b1S/4UofeA8HcyZtqJpPzrgHBPB7Nu/btdr7BroPaprLXn1efybJ9XZta/KjOkNgw+NIFVQnCSTFyUzrnwAD9jmhMibza3nSbd6aCHLo9Q9+YPNlbstyTOXm8C5UCl8kG5JUBP7xDpX4nbUTHBlvNJMbanrmBsvq4P1oZG3fPdISOsnKEfKwu2c2TALW/TLKq3U1ImYZNVoUpQG2jkjjhGOHwZWAgR9hm5l6lyIdbyw==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=hPr0TAusxP7cvig3ycixIk8AJyYmILi06vEjoDJwbp8qryY02oVTht8bWrxhy64VbycKPWto9+7oZTSqrLLId0tmKlAJW37UvzzFJT7+61ezaFwKHo48KnSSZTFk/wAjRIg8wU0oK6xzEXHN5iQYEe965W2b7Y/uJWTDGjJBKpLE61pBq2c5asVWBEiGl0Xdr6kib35TI2XWQKmi7fwGfhV4BSczXmr3wr0bWVBRM1xMirOmvjD6cAsSJDtUJXV81bxn+MgSOxG9IlRVjMMPvbesMU8YCUGTT/+Jpc/MDmDVWiki1dQkCM43HHZ13g4iE63mtsLOrV/P+k43U3so/A==
  • 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: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=citrix.com;
  • Cc: jbeulich@xxxxxxxx, andrew.cooper3@xxxxxxxxxx, roger.pau@xxxxxxxxxx, ross.lagerwall@xxxxxxxxxx, Kevin Lampis <kevin.lampis@xxxxxxxxxx>
  • Delivery-date: Mon, 22 Jun 2026 15:17:24 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

From: Ross Lagerwall <ross.lagerwall@xxxxxxxxxx>

To support UEFI Secure Boot we must check that the kexec data has not
changed between signature verification and actual execution.
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.

For now, only kexec crash images are supported.

Signed-off-by: Ross Lagerwall <ross.lagerwall@xxxxxxxxxx>
Signed-off-by: Kevin Lampis <kevin.lampis@xxxxxxxxxx>
---
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 7535d269e8..f31ab3fa07 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 )
@@ -943,6 +949,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 a0e4e67df7..6e009529ae 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 fccba1d88d..8ed89d4fa3 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®.