Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crash in md_rand.c when MD_Init fails #2087

Closed
bernd-edlinger opened this issue Dec 15, 2016 · 13 comments
Closed

crash in md_rand.c when MD_Init fails #2087

bernd-edlinger opened this issue Dec 15, 2016 · 13 comments

Comments

@bernd-edlinger
Copy link
Member

bernd-edlinger commented Dec 15, 2016

Hi,

I tried to test out of memory handling with openssl_1.0.2j.

There is a crash in RAND_bytes, when MD_Init fails,
a later MD_Update crashes with null pointer.

Not sure if return code of MD_Update and MD_Final need to be
handled as well, at least with SHA1 there is no crash if
that is ignored.

possible, but probably incomplete fix follows.

@bernd-edlinger
Copy link
Member Author

Hrmm..

attached a readable patch file (hopefully)

rand-patch.diff.txt

@richsalz
Copy link
Contributor

@bernd-edlinger , Is this a trivial change? If not, we'll need a CLA from you.

1 similar comment
@richsalz
Copy link
Contributor

@bernd-edlinger , Is this a trivial change? If not, we'll need a CLA from you.

@bernd-edlinger
Copy link
Member Author

It is probably trivial, only checking return codes.
But it is not meant to be checked in as is.
It is only a hint, what might be done to fix it.
I have a copyright assignment with the FSF already, (I contribute to gcc occasionally).

@levitte
Copy link
Member

levitte commented Dec 19, 2016

FYI, we are not the FSF.

@bernd-edlinger
Copy link
Member Author

OK understood.
Maybe I should do the paperwork now.
Please give me instructions how to file a CLA.

@levitte
Copy link
Member

levitte commented Dec 19, 2016

Certainly! Please go here: https://www.openssl.org/policies/cla.html

@bernd-edlinger
Copy link
Member Author

OK this will take a while, because it needs to be signed by my company.

in the meantime, here is a somewhat better patch:

--- crypto/rand/md_rand.c.orig  2016-09-26 11:49:07.000000000 +0200
+++ crypto/rand/md_rand.c       2016-12-15 18:53:04.159408650 +0100
@@ -266,17 +266,21 @@ static void ssleay_rand_add(const void *
         j = (num - i);
         j = (j > MD_DIGEST_LENGTH) ? MD_DIGEST_LENGTH : j;

-        MD_Init(&m);
-        MD_Update(&m, local_md, MD_DIGEST_LENGTH);
+        if (!MD_Init(&m) ||
+            !MD_Update(&m, local_md, MD_DIGEST_LENGTH))
+           goto err;
         k = (st_idx + j) - STATE_SIZE;
         if (k > 0) {
-            MD_Update(&m, &(state[st_idx]), j - k);
-            MD_Update(&m, &(state[0]), k);
+            if (!MD_Update(&m, &(state[st_idx]), j - k) ||
+                !MD_Update(&m, &(state[0]), k))
+                goto err;
         } else
-            MD_Update(&m, &(state[st_idx]), j);
+            if (!MD_Update(&m, &(state[st_idx]), j))
+                goto err;

         /* DO NOT REMOVE THE FOLLOWING CALL TO MD_Update()! */
-        MD_Update(&m, buf, j);
+        if (!MD_Update(&m, buf, j))
+            goto err;
         /*
          * We know that line may cause programs such as purify and valgrind
          * to complain about use of uninitialized data.  The problem is not,
@@ -285,8 +289,9 @@ static void ssleay_rand_add(const void *
          * insecure keys.
          */

-        MD_Update(&m, (unsigned char *)&(md_c[0]), sizeof(md_c));
-        MD_Final(&m, local_md);
+        if (!MD_Update(&m, (unsigned char *)&(md_c[0]), sizeof(md_c)) ||
+            !MD_Final(&m, local_md))
+            goto err;
         md_c[1]++;

         buf = (const char *)buf + j;
@@ -305,7 +310,6 @@ static void ssleay_rand_add(const void *
                 st_idx = 0;
         }
     }
-    EVP_MD_CTX_cleanup(&m);

     if (!do_not_lock)
         CRYPTO_w_lock(CRYPTO_LOCK_RAND);
@@ -326,6 +330,9 @@ static void ssleay_rand_add(const void *
 #if !defined(OPENSSL_THREADS) && !defined(OPENSSL_SYS_WIN32)
     assert(md_c[1] == md_count[1]);
 #endif
+
+ err:
+    EVP_MD_CTX_cleanup(&m);
 }

 static void ssleay_rand_seed(const void *buf, int num)
@@ -469,15 +476,18 @@ int ssleay_rand_bytes(unsigned char *buf
         /* num_ceil -= MD_DIGEST_LENGTH/2 */
         j = (num >= MD_DIGEST_LENGTH / 2) ? MD_DIGEST_LENGTH / 2 : num;
         num -= j;
-        MD_Init(&m);
+        if (!MD_Init(&m))
+           goto err;
 #ifndef GETPID_IS_MEANINGLESS
         if (curr_pid) {         /* just in the first iteration to save time */
-            MD_Update(&m, (unsigned char *)&curr_pid, sizeof curr_pid);
+            if (!MD_Update(&m, (unsigned char *)&curr_pid, sizeof curr_pid))
+                goto err;
             curr_pid = 0;
         }
 #endif
-        MD_Update(&m, local_md, MD_DIGEST_LENGTH);
-        MD_Update(&m, (unsigned char *)&(md_c[0]), sizeof(md_c));
+        if (!MD_Update(&m, local_md, MD_DIGEST_LENGTH) ||
+            !MD_Update(&m, (unsigned char *)&(md_c[0]), sizeof(md_c)))
+            goto err;

 #ifndef PURIFY                  /* purify complains */
         /*
@@ -487,16 +497,20 @@ int ssleay_rand_bytes(unsigned char *buf
          * builds it is not used: the removal of such a small source of
          * entropy has negligible impact on security.
          */
-        MD_Update(&m, buf, j);
+        if (!MD_Update(&m, buf, j))
+            goto err;
 #endif

         k = (st_idx + MD_DIGEST_LENGTH / 2) - st_num;
         if (k > 0) {
-            MD_Update(&m, &(state[st_idx]), MD_DIGEST_LENGTH / 2 - k);
-            MD_Update(&m, &(state[0]), k);
+            if (!MD_Update(&m, &(state[st_idx]), MD_DIGEST_LENGTH / 2 - k) ||
+                !MD_Update(&m, &(state[0]), k))
+                goto err;
         } else
-            MD_Update(&m, &(state[st_idx]), MD_DIGEST_LENGTH / 2);
-        MD_Final(&m, local_md);
+            if (!MD_Update(&m, &(state[st_idx]), MD_DIGEST_LENGTH / 2))
+                goto err;
+        if (!MD_Final(&m, local_md))
+            goto err;

         for (i = 0; i < MD_DIGEST_LENGTH / 2; i++) {
             /* may compete with other threads */
@@ -508,13 +522,18 @@ int ssleay_rand_bytes(unsigned char *buf
         }
     }

-    MD_Init(&m);
-    MD_Update(&m, (unsigned char *)&(md_c[0]), sizeof(md_c));
-    MD_Update(&m, local_md, MD_DIGEST_LENGTH);
+    if (!MD_Init(&m) ||
+        !MD_Update(&m, (unsigned char *)&(md_c[0]), sizeof(md_c)) ||
+        !MD_Update(&m, local_md, MD_DIGEST_LENGTH))
+        goto err;
     if (lock)
         CRYPTO_w_lock(CRYPTO_LOCK_RAND);
-    MD_Update(&m, md, MD_DIGEST_LENGTH);
-    MD_Final(&m, md);
+    if (!MD_Update(&m, md, MD_DIGEST_LENGTH) ||
+        !MD_Final(&m, md)) {
+        if (lock)
+            CRYPTO_w_unlock(CRYPTO_LOCK_RAND);
+        goto err;
+    }
     if (lock)
         CRYPTO_w_unlock(CRYPTO_LOCK_RAND);

@@ -529,6 +548,10 @@ int ssleay_rand_bytes(unsigned char *buf
                            "http://www.openssl.org/support/faq.html");
         return (0);
     }
+
+ err:
+    EVP_MD_CTX_cleanup(&m);
+    return (0);
 }

 static int ssleay_rand_nopseudo_bytes(unsigned char *buf, int num)

@bernd-edlinger
Copy link
Member Author

Note I see more places where EVP_DigestInit_ex is used without error checking,
but these are probably not used in my test application.

For instance crypto/srp/srp_lib.c, ./crypto/srp/srp_vfy.c
and ssl/s3_enc.c.

And also here:
./crypto/pem/pem_sign.c:

void PEM_SignInit(EVP_MD_CTX *ctx, EVP_MD *type)
{
EVP_DigestInit_ex(ctx, type, NULL);
}

how is that supposed to work?

@richsalz
Copy link
Contributor

it's a bug and we're stuck with it. some places errors just cannot be returned because they are "swallowed" by the existing functions :( We could add a new function if needed.

@bernd-edlinger
Copy link
Member Author

No, fortunatley I don't need it.
BTW, in 1.1.0 it is "int PEM_SignInit(EVP_MD_CTX *ctx, EVP_MD *type)"

bernd-edlinger added a commit to bernd-edlinger/openssl that referenced this issue Feb 1, 2017
Fixed a memory leak in ASN1_digest and ASN1_item_digest.

asn1_template_noexp_d2i call ASN1_item_ex_free(&skfield,...) on error.

Reworked error handling in asn1_item_ex_combine_new:
- call ASN1_item_ex_free and return the correct error code if ASN1_template_new failed.
- dont call ASN1_item_ex_free if ASN1_OP_NEW_PRE failed.

Reworked error handing in x509_name_ex_d2i and x509_name_encode.

Fixed error handling in int_ctx_new and EVP_PKEY_CTX_dup.

Fixed a memory leak in def_get_class if lh_EX_CLASS_ITEM_insert fails due to OOM:
- to figure out if the insertion succeeded, use lh_EX_CLASS_ITEM_retrieve again.
- on error, p will be NULL, and gen needs to be cleaned up again.

int_free_ex_data needs to have a fallback solution if unable to allocate "storage":
- if free_func is non-zero this must be called to clean up all memory.

Fixed error handling in pkey_hmac_copy.

Fixed error handling in ssleay_rand_add and ssleay_rand_bytes.

Fixed error handling in X509_STORE_new.

Fixed a memory leak in ssl3_get_key_exchange.

Check for null pointer in ssl3_write_bytes.

Check for null pointer in ssl3_get_cert_verify.

Fixed a memory leak in ssl_cert_dup.

Fixes openssl#2087 openssl#2094 openssl#2103 openssl#2104 openssl#2105 openssl#2106 openssl#2107 openssl#2108 openssl#2110 openssl#2111 openssl#2112 openssl#2115
levitte pushed a commit that referenced this issue Feb 6, 2017
Fixed a memory leak in ASN1_digest and ASN1_item_digest.

asn1_template_noexp_d2i call ASN1_item_ex_free(&skfield,...) on error.

Reworked error handling in asn1_item_ex_combine_new:
- call ASN1_item_ex_free and return the correct error code if ASN1_template_new failed.
- dont call ASN1_item_ex_free if ASN1_OP_NEW_PRE failed.

Reworked error handing in x509_name_ex_d2i and x509_name_encode.

Fixed error handling in int_ctx_new and EVP_PKEY_CTX_dup.

Fixed a memory leak in def_get_class if lh_EX_CLASS_ITEM_insert fails due to OOM:
- to figure out if the insertion succeeded, use lh_EX_CLASS_ITEM_retrieve again.
- on error, p will be NULL, and gen needs to be cleaned up again.

int_free_ex_data needs to have a fallback solution if unable to allocate "storage":
- if free_func is non-zero this must be called to clean up all memory.

Fixed error handling in pkey_hmac_copy.

Fixed error handling in ssleay_rand_add and ssleay_rand_bytes.

Fixed error handling in X509_STORE_new.

Fixed a memory leak in ssl3_get_key_exchange.

Check for null pointer in ssl3_write_bytes.

Check for null pointer in ssl3_get_cert_verify.

Fixed a memory leak in ssl_cert_dup.

Fixes #2087 #2094 #2103 #2104 #2105 #2106 #2107 #2108 #2110 #2111 #2112 #2115

Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Rich Salz <rsalz@openssl.org>
(Merged from #2127)
@richsalz richsalz closed this as completed Feb 7, 2017
@ltdragonfly
Copy link

Which file defines the functions MD_Init and MD_Update?

@richsalz
Copy link
Contributor

git grep MD_Init shows rand_lcl.h

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants