]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-135234: improve `_hashlib` exceptions when reporting an OpenSSL function failure...
authorBénédikt Tran <10796600+picnixz@users.noreply.github.com>
Mon, 9 Jun 2025 07:13:29 +0000 (09:13 +0200)
committerGitHub <noreply@github.com>
Mon, 9 Jun 2025 07:13:29 +0000 (09:13 +0200)
- Refactor `get_openssl_evp_md_by_utf8name` error branches.
- Refactor `HASH.{digest,hexdigest}` computations.
- Refactor `_hashlib_HASH_copy_locked` and `locked_HMAC_CTX_copy`.

Misc/NEWS.d/next/Library/2025-06-08-11-11-07.gh-issue-135234.wJCdh0.rst [new file with mode: 0644]
Modules/_hashopenssl.c

diff --git a/Misc/NEWS.d/next/Library/2025-06-08-11-11-07.gh-issue-135234.wJCdh0.rst b/Misc/NEWS.d/next/Library/2025-06-08-11-11-07.gh-issue-135234.wJCdh0.rst
new file mode 100644 (file)
index 0000000..e1c11e4
--- /dev/null
@@ -0,0 +1,3 @@
+:mod:`hashlib`: improve exception messages when an OpenSSL function failed.
+When memory allocation fails on OpenSSL's side, a :exc:`MemoryError` is
+raised instead of a :exc:`ValueError`. Patch by Bénédikt Tran.
index 50cf3c574910498321aec651da94763a5d28963c..ce9603d5db841f5acc353da0f923b4d18bab3e21 100644 (file)
@@ -311,8 +311,9 @@ class _hashlib.HMAC "HMACobject *" "((_hashlibstate *)PyModule_GetState(module))
 
 /* Set an exception of given type using the given OpenSSL error code. */
 static void
-set_ssl_exception_from_errcode(PyObject *exc, unsigned long errcode)
+set_ssl_exception_from_errcode(PyObject *exc_type, unsigned long errcode)
 {
+    assert(exc_type != NULL);
     assert(errcode != 0);
 
     /* ERR_ERROR_STRING(3) ensures that the messages below are ASCII */
@@ -321,13 +322,29 @@ set_ssl_exception_from_errcode(PyObject *exc, unsigned long errcode)
     const char *reason = ERR_reason_error_string(errcode);
 
     if (lib && func) {
-        PyErr_Format(exc, "[%s: %s] %s", lib, func, reason);
+        PyErr_Format(exc_type, "[%s: %s] %s", lib, func, reason);
     }
     else if (lib) {
-        PyErr_Format(exc, "[%s] %s", lib, reason);
+        PyErr_Format(exc_type, "[%s] %s", lib, reason);
     }
     else {
-        PyErr_SetString(exc, reason);
+        PyErr_SetString(exc_type, reason);
+    }
+}
+
+/*
+ * Get an appropriate exception type for the given OpenSSL error code.
+ *
+ * The exception type depends on the error code reason.
+ */
+static PyObject *
+get_smart_ssl_exception_type(unsigned long errcode, PyObject *default_exc_type)
+{
+    switch (ERR_GET_REASON(errcode)) {
+        case ERR_R_MALLOC_FAILURE:
+            return PyExc_MemoryError;
+        default:
+            return default_exc_type;
     }
 }
 
@@ -335,36 +352,83 @@ set_ssl_exception_from_errcode(PyObject *exc, unsigned long errcode)
  * Set an exception of given type.
  *
  * By default, the exception's message is constructed by using the last SSL
- * error that occurred. If no error occurred, the 'fallback_format' is used
- * to create a C-style formatted fallback message.
+ * error that occurred. If no error occurred, the 'fallback_message' is used
+ * to create an exception message.
  */
 static void
-raise_ssl_error(PyObject *exc, const char *fallback_format, ...)
+raise_ssl_error(PyObject *exc_type, const char *fallback_message)
+{
+    assert(fallback_message != NULL);
+    unsigned long errcode = ERR_peek_last_error();
+    if (errcode) {
+        ERR_clear_error();
+        set_ssl_exception_from_errcode(exc_type, errcode);
+    }
+    else {
+        PyErr_SetString(exc_type, fallback_message);
+    }
+}
+
+/* Same as raise_ssl_error() but with a C-style formatted message. */
+static void
+raise_ssl_error_f(PyObject *exc_type, const char *fallback_format, ...)
 {
     assert(fallback_format != NULL);
     unsigned long errcode = ERR_peek_last_error();
     if (errcode) {
         ERR_clear_error();
-        set_ssl_exception_from_errcode(exc, errcode);
+        set_ssl_exception_from_errcode(exc_type, errcode);
     }
     else {
         va_list vargs;
         va_start(vargs, fallback_format);
-        PyErr_FormatV(exc, fallback_format, vargs);
+        PyErr_FormatV(exc_type, fallback_format, vargs);
+        va_end(vargs);
+    }
+}
+
+/* Same as raise_ssl_error_f() with smart exception types. */
+static void
+raise_smart_ssl_error_f(PyObject *exc_type, const char *fallback_format, ...)
+{
+    unsigned long errcode = ERR_peek_last_error();
+    if (errcode) {
+        ERR_clear_error();
+        exc_type = get_smart_ssl_exception_type(errcode, exc_type);
+        set_ssl_exception_from_errcode(exc_type, errcode);
+    }
+    else {
+        va_list vargs;
+        va_start(vargs, fallback_format);
+        PyErr_FormatV(exc_type, fallback_format, vargs);
         va_end(vargs);
     }
 }
 
 /*
- * Set an exception with a generic default message after an error occurred.
- *
- * It can also be used without previous calls to SSL built-in functions,
- * in which case a generic error message is provided.
+ * Raise a ValueError with a default message after an error occurred.
+ * It can also be used without previous calls to SSL built-in functions.
  */
 static inline void
-notify_ssl_error_occurred(void)
+notify_ssl_error_occurred(const char *message)
+{
+    raise_ssl_error(PyExc_ValueError, message);
+}
+
+/* Same as notify_ssl_error_occurred() for failed OpenSSL functions. */
+static inline void
+notify_ssl_error_occurred_in(const char *funcname)
 {
-    raise_ssl_error(PyExc_ValueError, "no reason supplied");
+    raise_ssl_error_f(PyExc_ValueError,
+                      "error in OpenSSL function %s()", funcname);
+}
+
+/* Same as notify_ssl_error_occurred_in() with smart exception types. */
+static inline void
+notify_smart_ssl_error_occurred_in(const char *funcname)
+{
+    raise_smart_ssl_error_f(PyExc_ValueError,
+                            "error in OpenSSL function %s()", funcname);
 }
 /* LCOV_EXCL_STOP */
 
@@ -408,16 +472,19 @@ get_asn1_utf8name_by_nid(int nid)
         // In OpenSSL 3.0 and later, OBJ_nid*() are thread-safe and may raise.
         assert(ERR_peek_last_error() != 0);
         if (ERR_GET_REASON(ERR_peek_last_error()) != OBJ_R_UNKNOWN_NID) {
-            notify_ssl_error_occurred();
-            return NULL;
+            goto error;
         }
         // fallback to short name and unconditionally propagate errors
         name = OBJ_nid2sn(nid);
         if (name == NULL) {
-            raise_ssl_error(PyExc_ValueError, "cannot resolve NID %d", nid);
+            goto error;
         }
     }
     return name;
+
+error:
+    raise_ssl_error_f(PyExc_ValueError, "cannot resolve NID %d", nid);
+    return NULL;
 }
 
 /*
@@ -449,8 +516,7 @@ static PY_EVP_MD *
 get_openssl_evp_md_by_utf8name(PyObject *module, const char *name,
                                Py_hash_type py_ht)
 {
-    PY_EVP_MD *digest = NULL;
-    PY_EVP_MD *other_digest = NULL;
+    PY_EVP_MD *digest = NULL, *other_digest = NULL;
     _hashlibstate *state = get_hashlib_state(module);
     py_hashentry_t *entry = (py_hashentry_t *)_Py_hashtable_get(
         state->hashtable, (const void*)name
@@ -484,15 +550,16 @@ get_openssl_evp_md_by_utf8name(PyObject *module, const char *name,
 #endif
             }
             break;
+        default:
+            goto invalid_hash_type;
         }
         // if another thread same thing at same time make sure we got same ptr
         assert(other_digest == NULL || other_digest == digest);
-        if (digest != NULL) {
-            if (other_digest == NULL) {
-                PY_EVP_MD_up_ref(digest);
-            }
+        if (digest != NULL && other_digest == NULL) {
+            PY_EVP_MD_up_ref(digest);
         }
-    } else {
+    }
+    else {
         // Fall back for looking up an unindexed OpenSSL specific name.
         switch (py_ht) {
         case Py_ht_evp:
@@ -503,14 +570,21 @@ get_openssl_evp_md_by_utf8name(PyObject *module, const char *name,
         case Py_ht_evp_nosecurity:
             digest = PY_EVP_MD_fetch(name, "-fips");
             break;
+        default:
+            goto invalid_hash_type;
         }
     }
     if (digest == NULL) {
-        raise_ssl_error(state->unsupported_digestmod_error,
-                        "unsupported hash type %s", name);
+        raise_ssl_error_f(state->unsupported_digestmod_error,
+                          "unsupported digest name: %s", name);
         return NULL;
     }
     return digest;
+
+invalid_hash_type:
+    assert(digest == NULL);
+    PyErr_Format(PyExc_SystemError, "unsupported hash type %d", py_ht);
+    return NULL;
 }
 
 /*
@@ -556,6 +630,22 @@ get_openssl_evp_md(PyObject *module, PyObject *digestmod, Py_hash_type py_ht)
     return get_openssl_evp_md_by_utf8name(module, name, py_ht);
 }
 
+// --- OpenSSL HASH wrappers --------------------------------------------------
+
+/* Thin wrapper around EVP_MD_CTX_new() which sets an exception on failure. */
+static EVP_MD_CTX *
+py_wrapper_EVP_MD_CTX_new(void)
+{
+    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
+    if (ctx == NULL) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+    return ctx;
+}
+
+// --- HASH interface ---------------------------------------------------------
+
 static HASHobject *
 new_hash_object(PyTypeObject *type)
 {
@@ -565,10 +655,9 @@ new_hash_object(PyTypeObject *type)
     }
     HASHLIB_INIT_MUTEX(retval);
 
-    retval->ctx = EVP_MD_CTX_new();
+    retval->ctx = py_wrapper_EVP_MD_CTX_new();
     if (retval->ctx == NULL) {
         Py_DECREF(retval);
-        PyErr_NoMemory();
         return NULL;
     }
 
@@ -586,7 +675,7 @@ _hashlib_HASH_hash(HASHobject *self, const void *vp, Py_ssize_t len)
         else
             process = Py_SAFE_DOWNCAST(len, Py_ssize_t, unsigned int);
         if (!EVP_DigestUpdate(self->ctx, (const void*)cp, process)) {
-            notify_ssl_error_occurred();
+            notify_ssl_error_occurred_in(Py_STRINGIFY(EVP_DigestUpdate));
             return -1;
         }
         len -= process;
@@ -614,7 +703,11 @@ _hashlib_HASH_copy_locked(HASHobject *self, EVP_MD_CTX *new_ctx_p)
     ENTER_HASHLIB(self);
     result = EVP_MD_CTX_copy(new_ctx_p, self->ctx);
     LEAVE_HASHLIB(self);
-    return result;
+    if (result == 0) {
+        notify_smart_ssl_error_occurred_in(Py_STRINGIFY(EVP_MD_CTX_copy));
+        return -1;
+    }
+    return 0;
 }
 
 /* External methods for a hash object */
@@ -634,14 +727,36 @@ _hashlib_HASH_copy_impl(HASHobject *self)
     if ((newobj = new_hash_object(Py_TYPE(self))) == NULL)
         return NULL;
 
-    if (!_hashlib_HASH_copy_locked(self, newobj->ctx)) {
+    if (_hashlib_HASH_copy_locked(self, newobj->ctx) < 0) {
         Py_DECREF(newobj);
-        notify_ssl_error_occurred();
         return NULL;
     }
     return (PyObject *)newobj;
 }
 
+static Py_ssize_t
+_hashlib_HASH_digest_compute(HASHobject *self, unsigned char *digest)
+{
+    EVP_MD_CTX *ctx = py_wrapper_EVP_MD_CTX_new();
+    if (ctx == NULL) {
+        return -1;
+    }
+    if (_hashlib_HASH_copy_locked(self, ctx) < 0) {
+        goto error;
+    }
+    Py_ssize_t digest_size = EVP_MD_CTX_size(ctx);
+    if (!EVP_DigestFinal(ctx, digest, NULL)) {
+        notify_ssl_error_occurred_in(Py_STRINGIFY(EVP_DigestFinal));
+        goto error;
+    }
+    EVP_MD_CTX_free(ctx);
+    return digest_size;
+
+error:
+    EVP_MD_CTX_free(ctx);
+    return -1;
+}
+
 /*[clinic input]
 _hashlib.HASH.digest
 
@@ -653,32 +768,8 @@ _hashlib_HASH_digest_impl(HASHobject *self)
 /*[clinic end generated code: output=3fc6f9671d712850 input=d8d528d6e50af0de]*/
 {
     unsigned char digest[EVP_MAX_MD_SIZE];
-    EVP_MD_CTX *temp_ctx;
-    PyObject *retval;
-    unsigned int digest_size;
-
-    temp_ctx = EVP_MD_CTX_new();
-    if (temp_ctx == NULL) {
-        PyErr_NoMemory();
-        return NULL;
-    }
-
-    if (!_hashlib_HASH_copy_locked(self, temp_ctx)) {
-        goto error;
-    }
-    digest_size = EVP_MD_CTX_size(temp_ctx);
-    if (!EVP_DigestFinal(temp_ctx, digest, NULL)) {
-        goto error;
-    }
-
-    retval = PyBytes_FromStringAndSize((const char *)digest, digest_size);
-    EVP_MD_CTX_free(temp_ctx);
-    return retval;
-
-error:
-    EVP_MD_CTX_free(temp_ctx);
-    notify_ssl_error_occurred();
-    return NULL;
+    Py_ssize_t n = _hashlib_HASH_digest_compute(self, digest);
+    return n < 0 ? NULL : PyBytes_FromStringAndSize((const char *)digest, n);
 }
 
 /*[clinic input]
@@ -692,32 +783,8 @@ _hashlib_HASH_hexdigest_impl(HASHobject *self)
 /*[clinic end generated code: output=1b8e60d9711e7f4d input=ae7553f78f8372d8]*/
 {
     unsigned char digest[EVP_MAX_MD_SIZE];
-    EVP_MD_CTX *temp_ctx;
-    unsigned int digest_size;
-
-    temp_ctx = EVP_MD_CTX_new();
-    if (temp_ctx == NULL) {
-        PyErr_NoMemory();
-        return NULL;
-    }
-
-    /* Get the raw (binary) digest value */
-    if (!_hashlib_HASH_copy_locked(self, temp_ctx)) {
-        goto error;
-    }
-    digest_size = EVP_MD_CTX_size(temp_ctx);
-    if (!EVP_DigestFinal(temp_ctx, digest, NULL)) {
-        goto error;
-    }
-
-    EVP_MD_CTX_free(temp_ctx);
-
-    return _Py_strhex((const char *)digest, (Py_ssize_t)digest_size);
-
-error:
-    EVP_MD_CTX_free(temp_ctx);
-    notify_ssl_error_occurred();
-    return NULL;
+    Py_ssize_t n = _hashlib_HASH_digest_compute(self, digest);
+    return n < 0 ? NULL : _Py_strhex((const char *)digest, n);
 }
 
 /*[clinic input]
@@ -788,7 +855,7 @@ _hashlib_HASH_get_name(PyObject *op, void *Py_UNUSED(closure))
     HASHobject *self = HASHobject_CAST(op);
     const EVP_MD *md = EVP_MD_CTX_md(self->ctx);
     if (md == NULL) {
-        notify_ssl_error_occurred();
+        notify_ssl_error_occurred("missing EVP_MD for HASH context");
         return NULL;
     }
     const char *name = get_hashlib_utf8name_by_evp_md(md);
@@ -877,20 +944,20 @@ _hashlib_HASHXOF_digest_impl(HASHobject *self, Py_ssize_t length)
         return NULL;
     }
 
-    temp_ctx = EVP_MD_CTX_new();
+    temp_ctx = py_wrapper_EVP_MD_CTX_new();
     if (temp_ctx == NULL) {
         Py_DECREF(retval);
-        PyErr_NoMemory();
         return NULL;
     }
 
-    if (!_hashlib_HASH_copy_locked(self, temp_ctx)) {
+    if (_hashlib_HASH_copy_locked(self, temp_ctx) < 0) {
         goto error;
     }
     if (!EVP_DigestFinalXOF(temp_ctx,
                             (unsigned char*)PyBytes_AS_STRING(retval),
                             length))
     {
+        notify_ssl_error_occurred_in(Py_STRINGIFY(EVP_DigestFinalXOF));
         goto error;
     }
 
@@ -900,7 +967,6 @@ _hashlib_HASHXOF_digest_impl(HASHobject *self, Py_ssize_t length)
 error:
     Py_DECREF(retval);
     EVP_MD_CTX_free(temp_ctx);
-    notify_ssl_error_occurred();
     return NULL;
 }
 
@@ -926,18 +992,18 @@ _hashlib_HASHXOF_hexdigest_impl(HASHobject *self, Py_ssize_t length)
         return NULL;
     }
 
-    temp_ctx = EVP_MD_CTX_new();
+    temp_ctx = py_wrapper_EVP_MD_CTX_new();
     if (temp_ctx == NULL) {
         PyMem_Free(digest);
-        PyErr_NoMemory();
         return NULL;
     }
 
     /* Get the raw (binary) digest value */
-    if (!_hashlib_HASH_copy_locked(self, temp_ctx)) {
+    if (_hashlib_HASH_copy_locked(self, temp_ctx) < 0) {
         goto error;
     }
     if (!EVP_DigestFinalXOF(temp_ctx, digest, length)) {
+        notify_ssl_error_occurred_in(Py_STRINGIFY(EVP_DigestFinalXOF));
         goto error;
     }
 
@@ -950,7 +1016,6 @@ _hashlib_HASHXOF_hexdigest_impl(HASHobject *self, Py_ssize_t length)
 error:
     PyMem_Free(digest);
     EVP_MD_CTX_free(temp_ctx);
-    notify_ssl_error_occurred();
     return NULL;
 }
 
@@ -1054,7 +1119,7 @@ _hashlib_HASH(PyObject *module, const char *digestname, PyObject *data_obj,
 
     int result = EVP_DigestInit_ex(self->ctx, digest, NULL);
     if (!result) {
-        notify_ssl_error_occurred();
+        notify_ssl_error_occurred_in(Py_STRINGIFY(EVP_DigestInit_ex));
         Py_CLEAR(self);
         goto exit;
     }
@@ -1463,7 +1528,7 @@ pbkdf2_hmac_impl(PyObject *module, const char *hash_name,
 
     if (!retval) {
         Py_CLEAR(key_obj);
-        notify_ssl_error_occurred();
+        notify_ssl_error_occurred_in(Py_STRINGIFY(PKCS5_PBKDF2_HMAC));
         goto end;
     }
 
@@ -1539,8 +1604,8 @@ _hashlib_scrypt_impl(PyObject *module, Py_buffer *password, Py_buffer *salt,
     /* let OpenSSL validate the rest */
     retval = EVP_PBE_scrypt(NULL, 0, NULL, 0, n, r, p, maxmem, NULL, 0);
     if (!retval) {
-        raise_ssl_error(PyExc_ValueError,
-                        "Invalid parameter combination for n, r, p, maxmem.");
+        notify_ssl_error_occurred(
+            "Invalid parameter combination for n, r, p, maxmem.");
         return NULL;
    }
 
@@ -1561,7 +1626,7 @@ _hashlib_scrypt_impl(PyObject *module, Py_buffer *password, Py_buffer *salt,
 
     if (!retval) {
         Py_CLEAR(key_obj);
-        notify_ssl_error_occurred();
+        notify_ssl_error_occurred_in(Py_STRINGIFY(EVP_PBE_scrypt));
         return NULL;
     }
     return key_obj;
@@ -1618,7 +1683,7 @@ _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key,
     PY_EVP_MD_free(evp);
 
     if (result == NULL) {
-        notify_ssl_error_occurred();
+        notify_ssl_error_occurred_in(Py_STRINGIFY(HMAC));
         return NULL;
     }
     return PyBytes_FromStringAndSize((const char*)md, md_len);
@@ -1627,6 +1692,18 @@ _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key,
 /* OpenSSL-based HMAC implementation
  */
 
+/* Thin wrapper around HMAC_CTX_new() which sets an exception on failure. */
+static HMAC_CTX *
+py_openssl_wrapper_HMAC_CTX_new(void)
+{
+    HMAC_CTX *ctx = HMAC_CTX_new();
+    if (ctx == NULL) {
+        PyErr_NoMemory();
+        return NULL;
+    }
+    return ctx;
+}
+
 static int _hmac_update(HMACobject*, PyObject*);
 
 static const EVP_MD *
@@ -1634,7 +1711,7 @@ _hashlib_hmac_get_md(HMACobject *self)
 {
     const EVP_MD *md = HMAC_CTX_get_md(self->ctx);
     if (md == NULL) {
-        raise_ssl_error(PyExc_ValueError, "missing EVP_MD for HMAC context");
+        notify_ssl_error_occurred("missing EVP_MD for HMAC context");
     }
     return md;
 }
@@ -1676,17 +1753,16 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj,
         return NULL;
     }
 
-    ctx = HMAC_CTX_new();
+    ctx = py_openssl_wrapper_HMAC_CTX_new();
     if (ctx == NULL) {
         PY_EVP_MD_free(digest);
-        PyErr_NoMemory();
         goto error;
     }
 
     r = HMAC_Init_ex(ctx, key->buf, (int)key->len, digest, NULL /* impl */);
     PY_EVP_MD_free(digest);
     if (r == 0) {
-        notify_ssl_error_occurred();
+        notify_ssl_error_occurred_in(Py_STRINGIFY(HMAC_Init_ex));
         goto error;
     }
 
@@ -1721,7 +1797,11 @@ locked_HMAC_CTX_copy(HMAC_CTX *new_ctx_p, HMACobject *self)
     ENTER_HASHLIB(self);
     result = HMAC_CTX_copy(new_ctx_p, self->ctx);
     LEAVE_HASHLIB(self);
-    return result;
+    if (result == 0) {
+        notify_smart_ssl_error_occurred_in(Py_STRINGIFY(HMAC_CTX_copy));
+        return -1;
+    }
+    return 0;
 }
 
 /* returning 0 means that an error occurred and an exception is set */
@@ -1735,7 +1815,7 @@ _hashlib_hmac_digest_size(HMACobject *self)
     unsigned int digest_size = EVP_MD_size(md);
     assert(digest_size <= EVP_MAX_MD_SIZE);
     if (digest_size == 0) {
-        raise_ssl_error(PyExc_ValueError, "invalid digest size");
+        notify_ssl_error_occurred("invalid digest size");
     }
     return digest_size;
 }
@@ -1768,7 +1848,7 @@ _hmac_update(HMACobject *self, PyObject *obj)
     PyBuffer_Release(&view);
 
     if (r == 0) {
-        notify_ssl_error_occurred();
+        notify_ssl_error_occurred_in(Py_STRINGIFY(HMAC_Update));
         return 0;
     }
     return 1;
@@ -1786,13 +1866,12 @@ _hashlib_HMAC_copy_impl(HMACobject *self)
 {
     HMACobject *retval;
 
-    HMAC_CTX *ctx = HMAC_CTX_new();
+    HMAC_CTX *ctx = py_openssl_wrapper_HMAC_CTX_new();
     if (ctx == NULL) {
-        return PyErr_NoMemory();
+        return NULL;
     }
-    if (!locked_HMAC_CTX_copy(ctx, self)) {
+    if (locked_HMAC_CTX_copy(ctx, self) < 0) {
         HMAC_CTX_free(ctx);
-        notify_ssl_error_occurred();
         return NULL;
     }
 
@@ -1854,20 +1933,18 @@ _hashlib_HMAC_update_impl(HMACobject *self, PyObject *msg)
 static int
 _hmac_digest(HMACobject *self, unsigned char *buf, unsigned int len)
 {
-    HMAC_CTX *temp_ctx = HMAC_CTX_new();
+    HMAC_CTX *temp_ctx = py_openssl_wrapper_HMAC_CTX_new();
     if (temp_ctx == NULL) {
-        (void)PyErr_NoMemory();
         return 0;
     }
-    if (!locked_HMAC_CTX_copy(temp_ctx, self)) {
+    if (locked_HMAC_CTX_copy(temp_ctx, self) < 0) {
         HMAC_CTX_free(temp_ctx);
-        notify_ssl_error_occurred();
         return 0;
     }
     int r = HMAC_Final(temp_ctx, buf, &len);
     HMAC_CTX_free(temp_ctx);
     if (r == 0) {
-        notify_ssl_error_occurred();
+        notify_ssl_error_occurred_in(Py_STRINGIFY(HMAC_Final));
         return 0;
     }
     return 1;
@@ -2088,16 +2165,13 @@ _hashlib_get_fips_mode_impl(PyObject *module)
 #else
     ERR_clear_error();
     int result = FIPS_mode();
-    if (result == 0) {
+    if (result == 0 && ERR_peek_last_error()) {
         // "If the library was built without support of the FIPS Object Module,
         // then the function will return 0 with an error code of
         // CRYPTO_R_FIPS_MODE_NOT_SUPPORTED (0x0f06d065)."
         // But 0 is also a valid result value.
-        unsigned long errcode = ERR_peek_last_error();
-        if (errcode) {
-            notify_ssl_error_occurred();
-            return -1;
-        }
+        notify_ssl_error_occurred_in(Py_STRINGIFY(FIPS_mode));
+        return -1;
     }
     return result;
 #endif