]> git.ipfire.org Git - thirdparty/gnulib.git/commitdiff
crypto/sha3, crypto/sha3-buffer: Don't leak memory when using OpenSSL.
authorCollin Funk <collin.funk1@gmail.com>
Sat, 6 Sep 2025 17:55:40 +0000 (10:55 -0700)
committerCollin Funk <collin.funk1@gmail.com>
Sat, 6 Sep 2025 17:55:40 +0000 (10:55 -0700)
Reported by Pádraig Brady in:
<https://lists.gnu.org/archive/html/bug-gnulib/2025-09/msg00058.html>.

* lib/sha3.c (DEFINE_SHA3_INIT_CTX) [!HAVE_OPENSSL_SHA3]: Always return
true.
[HAVE_OPENSSL_SHA3]: Call EVP_MD_CTX_create to allocate an EVP_MD_CTX.
(DEFINE_SHA3_BUFFER, sha3_process_bytes, sha3_process_block)
[!HAVE_OPENSSL_SHA3]: Always return true.
[HAVE_OPENSSL_SHA3]: Return NULL if any function calls fail.
(sha3_finish_ctx) [HAVE_OPENSSL_SHA3]: Free memory allocated by
EV_MD_CTX_create.
(sha3_free_ctx): New function.
* lib/sha3.h (struct sha3_ctx): Use a heap allocated EVP_MD_CTX.
(sha3_224_init_ctx, sha3_256_init_ctx, sha3_384_init_ctx)
(sha3_512_init_ctx, sha3_process_block, sha3_process_bytes): Change
prototype to return a bool. Mention that they return false if an OpenSSL
function fails.
(sha3_finish_ctx, sha3_read_ctx, sha3_224_buffer, sha3_256_buffer)
(sha3_384_buffer, sha3_512_buffer): Mention that these functions return
NULL if an OpenSSL function fails.
(sha3_free_ctx): New function.
* lib/sha3-stream.c (sha3_xxx_stream): Expect a function parameter that
returns bool. Check the return values of the sha3 functions, cleaning up
memory on failure.
* modules/crypto/sha3-buffer: Add bool.

ChangeLog
lib/sha3-stream.c
lib/sha3.c
lib/sha3.h
modules/crypto/sha3-buffer

index 423cdd5be93f1447ad2054b41d8f455e8e41363e..b8a4ced9282b721f3a3f0c58ae843e8d50a78720 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,31 @@
+2025-09-06  Collin Funk  <collin.funk1@gmail.com>
+
+       crypto/sha3, crypto/sha3-buffer: Don't leak memory when using OpenSSL.
+       Reported by Pádraig Brady in:
+       <https://lists.gnu.org/archive/html/bug-gnulib/2025-09/msg00058.html>.
+       * lib/sha3.c (DEFINE_SHA3_INIT_CTX) [!HAVE_OPENSSL_SHA3]: Always return
+       true.
+       [HAVE_OPENSSL_SHA3]: Call EVP_MD_CTX_create to allocate an EVP_MD_CTX.
+       (DEFINE_SHA3_BUFFER, sha3_process_bytes, sha3_process_block)
+       [!HAVE_OPENSSL_SHA3]: Always return true.
+       [HAVE_OPENSSL_SHA3]: Return NULL if any function calls fail.
+       (sha3_finish_ctx) [HAVE_OPENSSL_SHA3]: Free memory allocated by
+       EV_MD_CTX_create.
+       (sha3_free_ctx): New function.
+       * lib/sha3.h (struct sha3_ctx): Use a heap allocated EVP_MD_CTX.
+       (sha3_224_init_ctx, sha3_256_init_ctx, sha3_384_init_ctx)
+       (sha3_512_init_ctx, sha3_process_block, sha3_process_bytes): Change
+       prototype to return a bool. Mention that they return false if an OpenSSL
+       function fails.
+       (sha3_finish_ctx, sha3_read_ctx, sha3_224_buffer, sha3_256_buffer)
+       (sha3_384_buffer, sha3_512_buffer): Mention that these functions return
+       NULL if an OpenSSL function fails.
+       (sha3_free_ctx): New function.
+       * lib/sha3-stream.c (sha3_xxx_stream): Expect a function parameter that
+       returns bool. Check the return values of the sha3 functions, cleaning up
+       memory on failure.
+       * modules/crypto/sha3-buffer: Add bool.
+
 2025-09-06  Bruno Haible  <bruno@clisp.org>
 
        gitsub.sh: Support URL redirections also for subcheckouts.
index 708dafa7dc7c7b8bb8d95fa73ea9c61ac35f26d2..43c056e22a3f2bb8bba73f13ae7a9c6b174db3a3 100644 (file)
@@ -41,7 +41,7 @@
    successful.  */
 static int
 sha3_xxx_stream (FILE *stream, char const *alg, void *resblock,
-                 ssize_t hashlen, void (*init_ctx) (struct sha3_ctx *))
+                 ssize_t hashlen, bool (*init_ctx) (struct sha3_ctx *))
 {
   switch (afalg_stream (stream, alg, resblock, hashlen))
     {
@@ -54,7 +54,11 @@ sha3_xxx_stream (FILE *stream, char const *alg, void *resblock,
     return 1;
 
   struct sha3_ctx ctx;
-  init_ctx (&ctx);
+  if (! init_ctx (&ctx))
+    {
+      free (buffer);
+      return 1;
+    }
   size_t sum;
 
   /* Iterate over full file contents.  */
@@ -92,6 +96,7 @@ sha3_xxx_stream (FILE *stream, char const *alg, void *resblock,
               if (ferror (stream))
                 {
                   free (buffer);
+                  sha3_free_ctx (&ctx);
                   return 1;
                 }
               goto process_partial_block;
@@ -101,18 +106,35 @@ sha3_xxx_stream (FILE *stream, char const *alg, void *resblock,
       /* Process buffer with BLOCKSIZE bytes.  Note that
                         BLOCKSIZE % ctx.blocklen == 0
        */
-      sha3_process_block (buffer, BLOCKSIZE, &ctx);
+      if (! sha3_process_block (buffer, BLOCKSIZE, &ctx))
+        {
+          free (buffer);
+          sha3_free_ctx (&ctx);
+          return 1;
+        }
     }
 
  process_partial_block:;
 
   /* Process any remaining bytes.  */
   if (sum > 0)
-    sha3_process_bytes (buffer, sum, &ctx);
+    {
+      if (! sha3_process_bytes (buffer, sum, &ctx))
+        {
+          free (buffer);
+          sha3_free_ctx (&ctx);
+          return 1;
+        }
+    }
 
-  /* Construct result in desired memory.  */
-  sha3_finish_ctx (&ctx, resblock);
   free (buffer);
+
+  /* Construct result in desired memory.  */
+  if (sha3_finish_ctx (&ctx, resblock) == NULL)
+    {
+      sha3_free_ctx (&ctx);
+      return 1;
+    }
   return 0;
 }
 
index e30210fb3eab4f64b2073c3ae27199734280c0d5..e7b3b9c7c6aabaf1427f2c8a35d3366581acb18e 100644 (file)
@@ -50,13 +50,14 @@ static const u64 rc[] = {
 };
 
 #define DEFINE_SHA3_INIT_CTX(SIZE)                                      \
-  void                                                                  \
+  bool                                                                  \
   sha3_##SIZE##_init_ctx (struct sha3_ctx *ctx)                         \
   {                                                                     \
     memset (&ctx->state, '\0', sizeof ctx->state);                      \
     ctx->buflen = 0;                                                    \
     ctx->digestlen = SHA3_##SIZE##_DIGEST_SIZE;                         \
     ctx->blocklen = SHA3_##SIZE##_BLOCK_SIZE;                           \
+    return true;                                                        \
   }
 
 DEFINE_SHA3_INIT_CTX (224)
@@ -64,6 +65,12 @@ DEFINE_SHA3_INIT_CTX (256)
 DEFINE_SHA3_INIT_CTX (384)
 DEFINE_SHA3_INIT_CTX (512)
 
+void
+sha3_free_ctx (_GL_UNUSED struct sha3_ctx *ctx)
+{
+  /* Do nothing.  */
+}
+
 /* Copy the value from V into the memory location pointed to by *CP,
    If your architecture allows unaligned access, this is equivalent to
    * (__typeof__ (v) *) cp = v  */
@@ -127,7 +134,7 @@ DEFINE_SHA3_BUFFER (256)
 DEFINE_SHA3_BUFFER (384)
 DEFINE_SHA3_BUFFER (512)
 
-void
+bool
 sha3_process_bytes (const void *buffer, size_t len, struct sha3_ctx *ctx)
 {
   if (0 < ctx->buflen)
@@ -138,7 +145,7 @@ sha3_process_bytes (const void *buffer, size_t len, struct sha3_ctx *ctx)
           /* Not enough to fill a full block.  */
           memcpy (ctx->buffer + ctx->buflen, buffer, len);
           ctx->buflen += len;
-          return;
+          return true;
         }
       /* Process the block that already had bytes buffered.  */
       memcpy (ctx->buffer + ctx->buflen, buffer, left);
@@ -156,9 +163,10 @@ sha3_process_bytes (const void *buffer, size_t len, struct sha3_ctx *ctx)
       memcpy (ctx->buffer, buffer, len);
       ctx->buflen = len;
     }
+  return true;
 }
 
-void
+bool
 sha3_process_block (const void *buffer, size_t len, struct sha3_ctx *ctx)
 {
   u64 *a = ctx->state;
@@ -315,21 +323,27 @@ sha3_process_block (const void *buffer, size_t len, struct sha3_ctx *ctx)
           a[0] = u64xor (a[0], rc[i]);
         }
     }
+  return true;
 }
 
 #else /* OpenSSL implementation.  */
 
 #define DEFINE_SHA3_INIT_CTX(SIZE)                                      \
-  void                                                                  \
+  bool                                                                  \
   sha3_##SIZE##_init_ctx (struct sha3_ctx *ctx)                         \
   {                                                                     \
-    /* EVP_DigestInit_ex expects all bytes to be zero.  */              \
-    memset (ctx, 0, sizeof *ctx);                                       \
-    EVP_MD_CTX *evp_ctx = (EVP_MD_CTX *) ctx->evp_ctx_buffer;           \
-    int rc = EVP_DigestInit_ex (evp_ctx, EVP_sha3_##SIZE (), NULL);     \
-    /* This should never fail.  */                                      \
-    if (rc == 0)                                                        \
-      abort ();                                                         \
+    int result;                                                         \
+    ctx->evp_ctx = EVP_MD_CTX_create ();                                \
+    if (ctx->evp_ctx == NULL)                                           \
+      return false;                                                     \
+    result = EVP_DigestInit_ex (ctx->evp_ctx, EVP_sha3_##SIZE (),       \
+                                NULL);                                  \
+    if (result == 0)                                                    \
+      {                                                                 \
+        sha3_free_ctx (ctx);                                            \
+        return false;                                                   \
+      }                                                                 \
+    return true;                                                        \
   }
 
 DEFINE_SHA3_INIT_CTX (224)
@@ -337,6 +351,12 @@ DEFINE_SHA3_INIT_CTX (256)
 DEFINE_SHA3_INIT_CTX (384)
 DEFINE_SHA3_INIT_CTX (512)
 
+void
+sha3_free_ctx (struct sha3_ctx *ctx)
+{
+  EVP_MD_CTX_free (ctx->evp_ctx);
+}
+
 void *
 sha3_read_ctx (const struct sha3_ctx *ctx, void *resbuf)
 {
@@ -347,11 +367,10 @@ sha3_read_ctx (const struct sha3_ctx *ctx, void *resbuf)
 void *
 sha3_finish_ctx (struct sha3_ctx *ctx, void *resbuf)
 {
-  EVP_MD_CTX *evp_ctx = (EVP_MD_CTX *) ctx->evp_ctx_buffer;
-  /* This should never fail.  */
-  int result = EVP_DigestFinal_ex (evp_ctx, resbuf, NULL);
+  int result = EVP_DigestFinal_ex (ctx->evp_ctx, resbuf, NULL);
+  sha3_free_ctx (ctx);
   if (result == 0)
-    abort ();
+    return NULL;
   return resbuf;
 }
 
@@ -360,8 +379,10 @@ sha3_finish_ctx (struct sha3_ctx *ctx, void *resbuf)
   sha3_##SIZE##_buffer (const char *buffer, size_t len, void *resblock) \
   {                                                                     \
     struct sha3_ctx ctx;                                                \
-    sha3_##SIZE##_init_ctx (&ctx);                                      \
-    sha3_process_bytes (buffer, len, &ctx);                             \
+    if (! sha3_##SIZE##_init_ctx (&ctx))                                \
+      return NULL;                                                      \
+    if (! sha3_process_bytes (buffer, len, &ctx))                       \
+      return NULL;                                                      \
     return sha3_finish_ctx (&ctx, resblock);                            \
   }
 
@@ -370,19 +391,22 @@ DEFINE_SHA3_BUFFER (256)
 DEFINE_SHA3_BUFFER (384)
 DEFINE_SHA3_BUFFER (512)
 
-void
+bool
 sha3_process_bytes (const void *buffer, size_t len, struct sha3_ctx *ctx)
 {
-  EVP_MD_CTX *evp_ctx = (EVP_MD_CTX *) ctx->evp_ctx_buffer;
-  int result = EVP_DigestUpdate (evp_ctx, buffer, len);
+  int result = EVP_DigestUpdate (ctx->evp_ctx, buffer, len);
   if (result == 0)
-    abort ();
+    {
+      sha3_free_ctx (ctx);
+      return false;
+    }
+  return true;
 }
 
-void
+bool
 sha3_process_block (const void *buffer, size_t len, struct sha3_ctx *ctx)
 {
-  sha3_process_bytes (buffer, len, ctx);
+  return sha3_process_bytes (buffer, len, ctx);
 }
 
 #endif
index adce43eac72ea119a1770ee7c25f47f5f8e551ef..259f0d1f56b95e6febc924baf3bc6c86fa5f26c6 100644 (file)
@@ -51,9 +51,8 @@ enum { SHA3_512_BLOCK_SIZE = 576 / 8 };
 struct sha3_ctx
 {
 # if HAVE_OPENSSL_SHA3
-  /* EVP_MD_CTX is an incomplete type.  EVP_MD_CTX_create allocates 72 bytes of
-     memory as of 2025-09-02.  */
-  max_align_t evp_ctx_buffer[256 / sizeof (max_align_t)];
+  /* EVP_MD_CTX is an incomplete type.  It cannot be placed on the stack.  */
+  EVP_MD_CTX *evp_ctx;
 # else
   u64 state[25];
   uint8_t buffer[144]; /* Up to BLOCKLEN in use.  */
@@ -64,40 +63,49 @@ struct sha3_ctx
 };
 
 /* Initialize structure containing state of computation.  */
-extern void sha3_224_init_ctx (struct sha3_ctx *ctx);
-extern void sha3_256_init_ctx (struct sha3_ctx *ctx);
-extern void sha3_384_init_ctx (struct sha3_ctx *ctx);
-extern void sha3_512_init_ctx (struct sha3_ctx *ctx);
+extern bool sha3_224_init_ctx (struct sha3_ctx *ctx);
+extern bool sha3_256_init_ctx (struct sha3_ctx *ctx);
+extern bool sha3_384_init_ctx (struct sha3_ctx *ctx);
+extern bool sha3_512_init_ctx (struct sha3_ctx *ctx);
+
+/* Free memory allocated by the init_structure.  This is only required if
+   OpenSSL is used.  */
+extern void sha3_free_ctx (struct sha3_ctx *ctx);
 
 /* Starting with the result of former calls of this function (or the
    initialization function update the context for the next LEN bytes
    starting at BUFFER.
-   It is necessary that LEN is a multiple of the BLOCKLEN member of CTX!!!  */
-extern void sha3_process_block (const void *buffer, size_t len,
+   It is necessary that LEN is a multiple of the BLOCKLEN member of CTX!!!
+   Return false if an OpenSSL function fails.  */
+extern bool sha3_process_block (const void *buffer, size_t len,
                                 struct sha3_ctx *ctx);
 
 /* Starting with the result of former calls of this function (or the
    initialization function update the context for the next LEN bytes
    starting at BUFFER.
-   It is NOT required that LEN is a multiple of the BLOCKLEN member of CTX.  */
-extern void sha3_process_bytes (const void *buffer, size_t len,
+   It is NOT required that LEN is a multiple of the BLOCKLEN member of CTX.
+   Return false if an OpenSSL function fails.  */
+extern bool sha3_process_bytes (const void *buffer, size_t len,
                                 struct sha3_ctx *ctx);
 
 /* Process the remaining bytes in the buffer and put result from CTX in RESBUF.
    The result is always in little endian byte order, so that a byte-wise output
-   yields to the wanted ASCII representation of the message digest.  */
+   yields to the wanted ASCII representation of the message digest.
+   Return NULL if an OpenSSL function fails.  */
 extern void *sha3_finish_ctx (struct sha3_ctx *ctx, void *restrict resbuf);
 
 /* Put result from CTX in RESBUF.  The result is always in little endian byte
    order, so that a byte-wise output yields to the wanted ASCII representation
-   of the message digest.  */
+   of the message digest.
+   Return NULL if an OpenSSL function fails.  */
 extern void *sha3_read_ctx (const struct sha3_ctx *ctx,
                             void *restrict resbuf);
 
 /* Compute a SHA-3 message digest for LEN bytes beginning at BUFFER.
    The result is always in little endian byte order, so that a byte-wise
    output yields to the wanted ASCII representation of the message
-   digest.  */
+   digest.
+   Return NULL if an OpenSSL function fails.  */
 extern void *sha3_224_buffer (const char *buffer, size_t len,
                               void *restrict resblock);
 extern void *sha3_256_buffer (const char *buffer, size_t len,
index e0e654e56377000e57e7d7211f40a7a6ac6d8aeb..7ab61dea30c2d263123e734fb99bb2e062890aaa 100644 (file)
@@ -8,6 +8,7 @@ m4/gl-openssl.m4
 m4/sha3.m4
 
 Depends-on:
+bool
 byteswap
 c99
 stddef-h