Re: [PATCH 3/5] crypto: ecdsa - Avoid signed integer overflow on signature decoding

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

 





On 7/29/24 9:49 AM, Lukas Wunner wrote:
When extracting a signature component r or s from an ASN.1-encoded
integer, ecdsa_get_signature_rs() subtracts the expected length
"bufsize" from the ASN.1 length "vlen" (both of unsigned type size_t)
and stores the result in "diff" (of signed type ssize_t).

This results in a signed integer overflow if vlen > SSIZE_MAX + bufsize.

The kernel is compiled with -fno-strict-overflow, which implies -fwrapv,
meaning signed integer overflow is not undefined behavior.  And the
function does check for overflow:

        if (-diff >= bufsize)
                return -EINVAL;

So the code is fine in principle but not very obvious.  In the future it
might trigger a false-positive with CONFIG_UBSAN_SIGNED_WRAP=y.

Avoid by comparing the two unsigned variables directly and erroring out
if "vlen" is too large.

Signed-off-by: Lukas Wunner <lukas@xxxxxxxxx>

---
  crypto/ecdsa.c | 17 ++++-------------
  1 file changed, 4 insertions(+), 13 deletions(-)

diff --git a/crypto/ecdsa.c b/crypto/ecdsa.c
index f63731fb7535..03f608132242 100644
--- a/crypto/ecdsa.c
+++ b/crypto/ecdsa.c
@@ -35,29 +35,20 @@ static int ecdsa_get_signature_rs(u64 *dest, size_t hdrlen, unsigned char tag,
  				  const void *value, size_t vlen, unsigned int ndigits)
  {
  	size_t bufsize = ndigits * sizeof(u64);
-	ssize_t diff = vlen - bufsize;
  	const char *d = value;
- if (!value || !vlen)
+	if (!value || !vlen || vlen > bufsize + 1)
  		return -EINVAL; >
-	/* diff = 0: 'value' has exacly the right size
-	 * diff > 0: 'value' has too many bytes; one leading zero is allowed that
-	 *           makes the value a positive integer; error on more
-	 * diff < 0: 'value' is missing leading zeros
-	 */
-	if (diff > 0) {
+	if (vlen > bufsize) {

At this point vlen could be 1 larger then bufsize in the worst case and there must be a leading 0.

  		/* skip over leading zeros that make 'value' a positive int */
  		if (*d == 0) {
  			vlen -= 1;
-			diff--;
  			d++;
-		 > -		if (diff)
+		} else { >   			return -EINVAL;
+		}
  	}
-	if (-diff >= bufsize)
-		return -EINVAL;
ecc_digits_from_bytes(d, vlen, dest, ndigits);

Reviewed-by: Stefan Berger <stefanb@xxxxxxxxxxxxx>




[Index of Archives]     [Kernel]     [Gnu Classpath]     [Gnu Crypto]     [DM Crypt]     [Netfilter]     [Bugtraq]
  Powered by Linux