CVE-2019-25049: Out-of-bounds Read (CWE-125)
value: LibreSSL 2.9.1 through 3.2.1 has an out-of-bounds read in asn1_item_print_ctx (called from asn1_template_print_ctx).
CWE description: The product reads data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 7.1
Published: 2021-07-01T03:15Z
Repository: https://github.com/libressl-portable/portable
Language: C
Files changed: include/compat/pthread.h
Commit date: 2020-09-20 02:09:35 +1000
Commit message: Make pthread_mutex static initialisation work on Windows.

This takes the dynamic initialisation code added to CRYPTO_lock() in e5081719
and applies it to the Window's pthread_mutex implementation. This allows for
PTHREAD_MUTEX_INITIALIZER to be used on Windows.

bcook has agreed to place this code in the public domain (as per the rest of
the code in pthread.h).

Diff: diff --git a/include/compat/pthread.h b/include/compat/pthread.h
old mode 100644
new mode 100755
index b2a42a6..1527d3c
--- a/include/compat/pthread.h
+++ b/include/compat/pthread.h
@@ -6,21 +6,23 @@
 #ifndef LIBCRYPTOCOMPAT_PTHREAD_H
 #define LIBCRYPTOCOMPAT_PTHREAD_H
 
 #ifdef _WIN32
 
+#include <malloc.h>
+#include <stdlib.h>
 #include <windows.h>
 
 /*
  * Static once initialization values.
  */
 #define PTHREAD_ONCE_INIT   { INIT_ONCE_STATIC_INIT }
 
 /*
  * Static mutex initialization values.
  */
-#define PTHREAD_MUTEX_INITIALIZER	{ 0, 0, 0, 0, 0, 0 }
+#define PTHREAD_MUTEX_INITIALIZER	{ .lock = NULL }
 
 /*
  * Once definitions.
  */
 struct pthread_once {
@@ -58,31 +60,47 @@ static inline int
 pthread_equal(pthread_t t1, pthread_t t2)
 {
 	return t1 == t2;
 }
 
-typedef CRITICAL_SECTION pthread_mutex_t;
+struct pthread_mutex {
+	volatile LPCRITICAL_SECTION lock;
+};
+typedef struct pthread_mutex pthread_mutex_t;
 typedef void pthread_mutexattr_t;
 
 static inline int
 pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
 {
-	InitializeCriticalSection(mutex);
+	if ((mutex->lock = malloc(sizeof(CRITICAL_SECTION))) == NULL)
+		exit(ENOMEM);
+	InitializeCriticalSection(mutex->lock);
 	return 0;
 }
 
 static inline int
 pthread_mutex_lock(pthread_mutex_t *mutex)
 {
-	EnterCriticalSection(mutex);
+	if (mutex->lock == NULL) {
+		LPCRITICAL_SECTION lcs;
+
+		if ((lcs = malloc(sizeof(CRITICAL_SECTION))) == NULL)
+			exit(ENOMEM);
+		InitializeCriticalSection(lcs);
+		if (InterlockedCompareExchangePointer((PVOID*)&mutex->lock, (PVOID)lcs, NULL) != NULL) {
+			DeleteCriticalSection(lcs);
+			free(lcs);
+		}
+	}
+	EnterCriticalSection(mutex->lock);
 	return 0;
 }
 
 static inline int
 pthread_mutex_unlock(pthread_mutex_t *mutex)
 {
-	LeaveCriticalSection(mutex);
+	LeaveCriticalSection(mutex->lock);
 	return 0;
 }
 
 #else
 #include_next <pthread.h>

Vulnerable code: #define PTHREAD_MUTEX_INITIALIZER	{ 0, 0, 0, 0, 0, 0 }
typedef CRITICAL_SECTION pthread_mutex_t;
	InitializeCriticalSection(mutex);
	EnterCriticalSection(mutex);
	LeaveCriticalSection(mutex);
Fixed code: #include <malloc.h>
#include <stdlib.h>
#define PTHREAD_MUTEX_INITIALIZER	{ .lock = NULL }
struct pthread_mutex {
	volatile LPCRITICAL_SECTION lock;
};
typedef struct pthread_mutex pthread_mutex_t;
	if ((mutex->lock = malloc(sizeof(CRITICAL_SECTION))) == NULL)
		exit(ENOMEM);
	InitializeCriticalSection(mutex->lock);
	if (mutex->lock == NULL) {
		LPCRITICAL_SECTION lcs;

		if ((lcs = malloc(sizeof(CRITICAL_SECTION))) == NULL)
			exit(ENOMEM);
		InitializeCriticalSection(lcs);
		if (InterlockedCompareExchangePointer((PVOID*)&mutex->lock, (PVOID)lcs, NULL) != NULL) {
			DeleteCriticalSection(lcs);
			free(lcs);
		}
	}
	EnterCriticalSection(mutex->lock);
	LeaveCriticalSection(mutex->lock);