diff mbox series

[4.19,287/346] crypto: ecdh - avoid unaligned accesses in ecdh_set_secret()

Message ID 20201228124933.649086790@linuxfoundation.org
State New
Headers show
Series None | expand

Commit Message

Greg KH Dec. 28, 2020, 12:50 p.m. UTC
From: Ard Biesheuvel <ardb@kernel.org>

commit 17858b140bf49961b71d4e73f1c3ea9bc8e7dda0 upstream.

ecdh_set_secret() casts a void* pointer to a const u64* in order to
feed it into ecc_is_key_valid(). This is not generally permitted by
the C standard, and leads to actual misalignment faults on ARMv6
cores. In some cases, these are fixed up in software, but this still
leads to performance hits that are entirely avoidable.

So let's copy the key into the ctx buffer first, which we will do
anyway in the common case, and which guarantees correct alignment.

Cc: <stable@vger.kernel.org>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

---
 crypto/ecdh.c |    9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

Comments

Pavel Machek Dec. 31, 2020, 8:09 p.m. UTC | #1
Hi!

> ecdh_set_secret() casts a void* pointer to a const u64* in order to

> feed it into ecc_is_key_valid(). This is not generally permitted by

> the C standard, and leads to actual misalignment faults on ARMv6

> cores. In some cases, these are fixed up in software, but this still

> leads to performance hits that are entirely avoidable.

> 

> So let's copy the key into the ctx buffer first, which we will do

> anyway in the common case, and which guarantees correct alignment.


Fair enough... but: params.key_size is validated in
ecc_is_key_valid(), and that now happens _after_ memcpy.

How is it guaranteed that we don't overflow the buffer during memcpy?

> +++ b/crypto/ecdh.c

> @@ -57,12 +57,13 @@ static int ecdh_set_secret(struct crypto

>  		return ecc_gen_privkey(ctx->curve_id, ctx->ndigits,

>  				       ctx->private_key);

>  

> -	if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits,

> -			     (const u64 *)params.key, params.key_size) < 0)

> -		return -EINVAL;

> -

>  	memcpy(ctx->private_key, params.key, params.key_size);

>  

> +	if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits,

> +			     ctx->private_key, params.key_size) < 0) {

> +		memzero_explicit(ctx->private_key, params.key_size);

> +		return -EINVAL;

> +	}

>  	return 0;


Best regards,
								Pavel
-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Ard Biesheuvel Jan. 2, 2021, 11:29 a.m. UTC | #2
On Thu, 31 Dec 2020 at 21:09, Pavel Machek <pavel@denx.de> wrote:
>

> Hi!

>

> > ecdh_set_secret() casts a void* pointer to a const u64* in order to

> > feed it into ecc_is_key_valid(). This is not generally permitted by

> > the C standard, and leads to actual misalignment faults on ARMv6

> > cores. In some cases, these are fixed up in software, but this still

> > leads to performance hits that are entirely avoidable.

> >

> > So let's copy the key into the ctx buffer first, which we will do

> > anyway in the common case, and which guarantees correct alignment.

>

> Fair enough... but: params.key_size is validated in

> ecc_is_key_valid(), and that now happens _after_ memcpy.

>

> How is it guaranteed that we don't overflow the buffer during memcpy?

>


It is not, thanks for pointing that out. There are some redundant
checks being performed, so you won't trigger it easily with fuzzing,
but afaict, an intentionally crafted invalid input could indeed
overflow the buffer.

I'll send a fix shortly.

> > +++ b/crypto/ecdh.c

> > @@ -57,12 +57,13 @@ static int ecdh_set_secret(struct crypto

> >               return ecc_gen_privkey(ctx->curve_id, ctx->ndigits,

> >                                      ctx->private_key);

> >

> > -     if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits,

> > -                          (const u64 *)params.key, params.key_size) < 0)

> > -             return -EINVAL;

> > -

> >       memcpy(ctx->private_key, params.key, params.key_size);

> >

> > +     if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits,

> > +                          ctx->private_key, params.key_size) < 0) {

> > +             memzero_explicit(ctx->private_key, params.key_size);

> > +             return -EINVAL;

> > +     }

> >       return 0;

>

> Best regards,

>                                                                 Pavel

> --

> DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk

> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
diff mbox series

Patch

--- a/crypto/ecdh.c
+++ b/crypto/ecdh.c
@@ -57,12 +57,13 @@  static int ecdh_set_secret(struct crypto
 		return ecc_gen_privkey(ctx->curve_id, ctx->ndigits,
 				       ctx->private_key);
 
-	if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits,
-			     (const u64 *)params.key, params.key_size) < 0)
-		return -EINVAL;
-
 	memcpy(ctx->private_key, params.key, params.key_size);
 
+	if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits,
+			     ctx->private_key, params.key_size) < 0) {
+		memzero_explicit(ctx->private_key, params.key_size);
+		return -EINVAL;
+	}
 	return 0;
 }