mbox series

[0/1] use crc32 instead of md5 for hibernation e820 integrity check

Message ID 20210401122458.12663-1-crecklin@redhat.com
Headers show
Series use crc32 instead of md5 for hibernation e820 integrity check | expand

Message

Chris von Recklinghausen April 1, 2021, 12:24 p.m. UTC
Currently, suspend on x86_64 fails when FIPS mode is enabled because it uses md5
to generate a digest of the e820 region. MD5 is not FIPS compliant so an error
is reported and the suspend fails.

MD5 is used only to create a digest to ensure integrity of the region, no actual
encryption is done. This patch set changes the integrity check to use crc32
instead of md5 since crc32 is available in both FIPS and non-FIPS modes.

Chris von Recklinghausen (1):
  use crc32 instead of md5 for hibernation image integrity check

 arch/x86/power/hibernate.c | 31 +++++++++++++++++--------------
 1 file changed, 17 insertions(+), 14 deletions(-)

Comments

Rafael J. Wysocki April 1, 2021, 1:34 p.m. UTC | #1
On Thu, Apr 1, 2021 at 2:25 PM Chris von Recklinghausen
<crecklin@redhat.com> wrote:
>
> Suspend fails on a system in fips mode because md5 is used for the e820
> integrity check and is not available. Use crc32 instead.
>
> Fixes: 62a03defeabd ("PM / hibernate: Verify the consistent of e820 memory map
>        by md5 digest")
> Signed-off-by: Chris von Recklinghausen <crecklin@redhat.com>
> ---
>  arch/x86/power/hibernate.c | 31 +++++++++++++++++--------------
>  1 file changed, 17 insertions(+), 14 deletions(-)
>
> diff --git a/arch/x86/power/hibernate.c b/arch/x86/power/hibernate.c
> index cd3914fc9f3d..6a3f4e32e49c 100644
> --- a/arch/x86/power/hibernate.c
> +++ b/arch/x86/power/hibernate.c
> @@ -55,31 +55,31 @@ int pfn_is_nosave(unsigned long pfn)
>  }
>
>
> -#define MD5_DIGEST_SIZE 16
> +#define CRC32_DIGEST_SIZE 16
>
>  struct restore_data_record {
>         unsigned long jump_address;
>         unsigned long jump_address_phys;
>         unsigned long cr3;
>         unsigned long magic;
> -       u8 e820_digest[MD5_DIGEST_SIZE];
> +       u8 e820_digest[CRC32_DIGEST_SIZE];
>  };

No.

CRC32 was used here before and it was deemed insufficient.

Please find a different way to address this issue.

> -#if IS_BUILTIN(CONFIG_CRYPTO_MD5)
> +#if IS_BUILTIN(CONFIG_CRYPTO_CRC32)
>  /**
> - * get_e820_md5 - calculate md5 according to given e820 table
> + * get_e820_crc32 - calculate crc32 according to given e820 table
>   *
>   * @table: the e820 table to be calculated
> - * @buf: the md5 result to be stored to
> + * @buf: the crc32 result to be stored to
>   */
> -static int get_e820_md5(struct e820_table *table, void *buf)
> +static int get_e820_crc32(struct e820_table *table, void *buf)
>  {
>         struct crypto_shash *tfm;
>         struct shash_desc *desc;
>         int size;
>         int ret = 0;
>
> -       tfm = crypto_alloc_shash("md5", 0, 0);
> +       tfm = crypto_alloc_shash("crc32", 0, 0);
>         if (IS_ERR(tfm))
>                 return -ENOMEM;
>
> @@ -107,24 +107,24 @@ static int get_e820_md5(struct e820_table *table, void *buf)
>
>  static int hibernation_e820_save(void *buf)
>  {
> -       return get_e820_md5(e820_table_firmware, buf);
> +       return get_e820_crc32(e820_table_firmware, buf);
>  }
>
>  static bool hibernation_e820_mismatch(void *buf)
>  {
>         int ret;
> -       u8 result[MD5_DIGEST_SIZE];
> +       u8 result[CRC32_DIGEST_SIZE];
>
> -       memset(result, 0, MD5_DIGEST_SIZE);
> +       memset(result, 0, CRC32_DIGEST_SIZE);
>         /* If there is no digest in suspend kernel, let it go. */
> -       if (!memcmp(result, buf, MD5_DIGEST_SIZE))
> +       if (!memcmp(result, buf, CRC32_DIGEST_SIZE))
>                 return false;
>
> -       ret = get_e820_md5(e820_table_firmware, result);
> +       ret = get_e820_crc32(e820_table_firmware, result);
>         if (ret)
>                 return true;
>
> -       return memcmp(result, buf, MD5_DIGEST_SIZE) ? true : false;
> +       return memcmp(result, buf, CRC32_DIGEST_SIZE) ? true : false;
>  }
>  #else
>  static int hibernation_e820_save(void *buf)
> @@ -134,7 +134,7 @@ static int hibernation_e820_save(void *buf)
>
>  static bool hibernation_e820_mismatch(void *buf)
>  {
> -       /* If md5 is not builtin for restore kernel, let it go. */
> +       /* If crc32 is not builtin for restore kernel, let it go. */
>         return false;
>  }
>  #endif
> @@ -160,6 +160,9 @@ int arch_hibernation_header_save(void *addr, unsigned int max_size)
>         rdr->jump_address = (unsigned long)restore_registers;
>         rdr->jump_address_phys = __pa_symbol(restore_registers);
>
> +       /* crc32 digest size is 4 but digest buffer size is 16 so zero it all */
> +       memset(rdr->e820_digest, 0, CRC32_DIGEST_SIZE);
> +
>         /*
>          * The restore code fixes up CR3 and CR4 in the following sequence:
>          *
> --
> 2.18.1
>
Ard Biesheuvel April 1, 2021, 1:59 p.m. UTC | #2
On Thu, 1 Apr 2021 at 15:34, Rafael J. Wysocki <rafael@kernel.org> wrote:
>
> On Thu, Apr 1, 2021 at 2:25 PM Chris von Recklinghausen
> <crecklin@redhat.com> wrote:
> >
> > Suspend fails on a system in fips mode because md5 is used for the e820
> > integrity check and is not available. Use crc32 instead.
> >
> > Fixes: 62a03defeabd ("PM / hibernate: Verify the consistent of e820 memory map
> >        by md5 digest")
> > Signed-off-by: Chris von Recklinghausen <crecklin@redhat.com>
> > ---
> >  arch/x86/power/hibernate.c | 31 +++++++++++++++++--------------
> >  1 file changed, 17 insertions(+), 14 deletions(-)
> >
> > diff --git a/arch/x86/power/hibernate.c b/arch/x86/power/hibernate.c
> > index cd3914fc9f3d..6a3f4e32e49c 100644
> > --- a/arch/x86/power/hibernate.c
> > +++ b/arch/x86/power/hibernate.c
> > @@ -55,31 +55,31 @@ int pfn_is_nosave(unsigned long pfn)
> >  }
> >
> >
> > -#define MD5_DIGEST_SIZE 16
> > +#define CRC32_DIGEST_SIZE 16
> >
> >  struct restore_data_record {
> >         unsigned long jump_address;
> >         unsigned long jump_address_phys;
> >         unsigned long cr3;
> >         unsigned long magic;
> > -       u8 e820_digest[MD5_DIGEST_SIZE];
> > +       u8 e820_digest[CRC32_DIGEST_SIZE];
> >  };
>
> No.
>
> CRC32 was used here before and it was deemed insufficient.
>

Why? The git commit log does not have an explanation of this.



> Please find a different way to address this issue.
>
> > -#if IS_BUILTIN(CONFIG_CRYPTO_MD5)
> > +#if IS_BUILTIN(CONFIG_CRYPTO_CRC32)
> >  /**
> > - * get_e820_md5 - calculate md5 according to given e820 table
> > + * get_e820_crc32 - calculate crc32 according to given e820 table
> >   *
> >   * @table: the e820 table to be calculated
> > - * @buf: the md5 result to be stored to
> > + * @buf: the crc32 result to be stored to
> >   */
> > -static int get_e820_md5(struct e820_table *table, void *buf)
> > +static int get_e820_crc32(struct e820_table *table, void *buf)
> >  {
> >         struct crypto_shash *tfm;
> >         struct shash_desc *desc;
> >         int size;
> >         int ret = 0;
> >
> > -       tfm = crypto_alloc_shash("md5", 0, 0);
> > +       tfm = crypto_alloc_shash("crc32", 0, 0);
> >         if (IS_ERR(tfm))
> >                 return -ENOMEM;
> >
> > @@ -107,24 +107,24 @@ static int get_e820_md5(struct e820_table *table, void *buf)
> >
> >  static int hibernation_e820_save(void *buf)
> >  {
> > -       return get_e820_md5(e820_table_firmware, buf);
> > +       return get_e820_crc32(e820_table_firmware, buf);
> >  }
> >
> >  static bool hibernation_e820_mismatch(void *buf)
> >  {
> >         int ret;
> > -       u8 result[MD5_DIGEST_SIZE];
> > +       u8 result[CRC32_DIGEST_SIZE];
> >
> > -       memset(result, 0, MD5_DIGEST_SIZE);
> > +       memset(result, 0, CRC32_DIGEST_SIZE);
> >         /* If there is no digest in suspend kernel, let it go. */
> > -       if (!memcmp(result, buf, MD5_DIGEST_SIZE))
> > +       if (!memcmp(result, buf, CRC32_DIGEST_SIZE))
> >                 return false;
> >
> > -       ret = get_e820_md5(e820_table_firmware, result);
> > +       ret = get_e820_crc32(e820_table_firmware, result);
> >         if (ret)
> >                 return true;
> >
> > -       return memcmp(result, buf, MD5_DIGEST_SIZE) ? true : false;
> > +       return memcmp(result, buf, CRC32_DIGEST_SIZE) ? true : false;
> >  }
> >  #else
> >  static int hibernation_e820_save(void *buf)
> > @@ -134,7 +134,7 @@ static int hibernation_e820_save(void *buf)
> >
> >  static bool hibernation_e820_mismatch(void *buf)
> >  {
> > -       /* If md5 is not builtin for restore kernel, let it go. */
> > +       /* If crc32 is not builtin for restore kernel, let it go. */
> >         return false;
> >  }
> >  #endif
> > @@ -160,6 +160,9 @@ int arch_hibernation_header_save(void *addr, unsigned int max_size)
> >         rdr->jump_address = (unsigned long)restore_registers;
> >         rdr->jump_address_phys = __pa_symbol(restore_registers);
> >
> > +       /* crc32 digest size is 4 but digest buffer size is 16 so zero it all */
> > +       memset(rdr->e820_digest, 0, CRC32_DIGEST_SIZE);
> > +
> >         /*
> >          * The restore code fixes up CR3 and CR4 in the following sequence:
> >          *
> > --
> > 2.18.1
> >
Eric Biggers April 1, 2021, 6:43 p.m. UTC | #3
On Thu, Apr 01, 2021 at 06:19:57PM +0200, Rafael J. Wysocki wrote:
> On Thu, Apr 1, 2021 at 3:59 PM Ard Biesheuvel <ardb@kernel.org> wrote:
> >
> > On Thu, 1 Apr 2021 at 15:34, Rafael J. Wysocki <rafael@kernel.org> wrote:
> > >
> > > On Thu, Apr 1, 2021 at 2:25 PM Chris von Recklinghausen
> > > <crecklin@redhat.com> wrote:
> > > >
> > > > Suspend fails on a system in fips mode because md5 is used for the e820
> > > > integrity check and is not available. Use crc32 instead.
> > > >
> > > > Fixes: 62a03defeabd ("PM / hibernate: Verify the consistent of e820 memory map
> > > >        by md5 digest")
> > > > Signed-off-by: Chris von Recklinghausen <crecklin@redhat.com>
> > > > ---
> > > >  arch/x86/power/hibernate.c | 31 +++++++++++++++++--------------
> > > >  1 file changed, 17 insertions(+), 14 deletions(-)
> > > >
> > > > diff --git a/arch/x86/power/hibernate.c b/arch/x86/power/hibernate.c
> > > > index cd3914fc9f3d..6a3f4e32e49c 100644
> > > > --- a/arch/x86/power/hibernate.c
> > > > +++ b/arch/x86/power/hibernate.c
> > > > @@ -55,31 +55,31 @@ int pfn_is_nosave(unsigned long pfn)
> > > >  }
> > > >
> > > >
> > > > -#define MD5_DIGEST_SIZE 16
> > > > +#define CRC32_DIGEST_SIZE 16
> > > >
> > > >  struct restore_data_record {
> > > >         unsigned long jump_address;
> > > >         unsigned long jump_address_phys;
> > > >         unsigned long cr3;
> > > >         unsigned long magic;
> > > > -       u8 e820_digest[MD5_DIGEST_SIZE];
> > > > +       u8 e820_digest[CRC32_DIGEST_SIZE];
> > > >  };
> > >
> > > No.
> > >
> > > CRC32 was used here before and it was deemed insufficient.
> > >
> >
> > Why? The git commit log does not have an explanation of this.
> 
> IIRC there was an example of a memory map that would produce the same
> CRC32 value as the original or something like that.

Collisions can easily be found for MD5 as well, as it is heavily broken.

Either you need a cryptographic hash function, *or* a (non-cryptographic)
checksum would be sufficient.  There isn't really any in-between.

And if a checksum suffices, MD5 is a bad choice because it was designed as a
cryptographic hash function, so it is much slower than a checksum.

> 
> But that said this code is all about failing more gracefully, so I
> guess it isn't a big deal if the failure is more graceful in fewer
> cases ...

If the 1 in 2^32 chance of a CRC-32 collision is too high, then use CRC-64 or
xxHash64 for a 1 in 2^64 chance of a collision.

- Eric