NULL pointer dereference in tls_mosq.c
Version: 28f9147
Description: The function X509_STORE_CTX_get_ex_data() can return NULL, so a NULL check is needed at line 57 in mqtt_protocol/mosquitto/lib/tls_mosq.c.
When X509_STORE_CTX_get_ex_data() returns NULL, the runtime ASAN Log shows:
./08-ssl-bridge.py
1760012847: The 'port' option is now deprecated and will be removed in a future version. Please use 'listener' instead.
1760012847: Warning: Bridge bridge_test using insecure mode.
1760012847: Warning: Unable to drop privileges to 'mosquitto' because this user does not exist. Trying 'nobody' instead.
1760012847: mosquitto version 2.0.22 starting
1760012847: Config loaded from 08-ssl-bridge.conf.
1760012847: Opening ipv4 listen socket on port 1889.
1760012847: Opening ipv6 listen socket on port 1889.
1760012847: Bridge local.5634ed8d3141.bridge_test doing local SUBSCRIBE on topic bridge/#
1760012847: Connecting bridge bridge_test (127.0.0.1:1888)
1760012847: Bridge 5634ed8d3141.bridge_test sending CONNECT
1760012847: mosquitto version 2.0.22 running
1760012847: New connection from ::1:49066 on port 1889.
1760012847: Client <unknown> closed its connection.
AddressSanitizer:DEADLYSIGNAL
=================================================================
==1660457==ERROR: AddressSanitizer: SEGV on unknown address 0x0000000005b8 (pc 0x7fe6c8330bdd bp 0x7ffed89ae290 sp 0x7ffed89ae180 T0)
==1660457==The signal is caused by a READ memory access.
==1660457==Hint: address points to the zero page.
==1660457==WARNING: invalid path to external symbolizer!
==1660457==WARNING: Failed to use and restart external symbolizer!
#0 0x7fe6c8330bdd (openssl/libcrypto.so.1.1+0x180bdd)
#1 0x757b2d (mosquitto/src/mosquitto+0x757b2d)
#2 0x7fe6c83be6c9 (openssl/libcrypto.so.1.1+0x20e6c9)
#3 0x7fe6c83c05cf (openssl/libcrypto.so.1.1+0x2105cf)
#4 0x7fe6c83c10e7 (openssl/libcrypto.so.1.1+0x2110e7)
#5 0x7fe6c814ade7 (openssl/libssl.so.1.1+0x32de7)
#6 0x7fe6c816f7a8 (openssl/libssl.so.1.1+0x577a8)
#7 0x7fe6c8171ed4 (openssl/libssl.so.1.1+0x59ed4)
#8 0x7fe6c816b7ac (openssl/libssl.so.1.1+0x537ac)
#9 0x7fe6c813f185 (openssl/libssl.so.1.1+0x27185)
#10 0x7fe6c8147143 (openssl/libssl.so.1.1+0x2f143)
#11 0x7fe6c8153d4e (openssl/libssl.so.1.1+0x3bd4e)
#12 0x7fe6c8153e56 (openssl/libssl.so.1.1+0x3be56)
#13 0x6067f6 (mosquitto/src/mosquitto+0x6067f6)
#14 0x612aec (mosquitto/src/mosquitto+0x612aec)
#15 0x5e8e0f (mosquitto/src/mosquitto+0x5e8e0f)
#16 0x5e7f0c (mosquitto/src/mosquitto+0x5e7f0c)
#17 0x5e4cf3 (mosquitto/src/mosquitto+0x5e4cf3)
#18 0x5d9ecb (mosquitto/src/mosquitto+0x5d9ecb)
#19 0x4ce9c3 (mosquitto/src/mosquitto+0x4ce9c3)
#20 0x7fe6c7d9d0b2 (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
#21 0x41fd8d (mosquitto/src/mosquitto+0x41fd8d)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (openssl/libcrypto.so.1.1+0x180bdd)
==1660457==ABORTING
Can you please provide an example of how to reproduce this please?
This bug was discovered through fault injection testing on the OpenSSL 1.1 library. I injected faults to force the X509_STORE_CTX_get_ex_data() function to return NULL, which exposed this vulnerability.
Reproduction Steps:
- Apply fault injection to make X509_STORE_CTX_get_ex_data() return NULL
- Use the existing test case: ./mosquitto/test/broker/08-ssl-bridge.py
- The crash will occur.
Root Cause:
At line 57-58 in /mqtt_protocol/mosquitto/lib/tls_mosq.c:
ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
mosq = SSL_get_ex_data(ssl, tls_ex_index_mosq); // Crashes here if ssl is NULL
The function X509_STORE_CTX_get_ex_data() can return NULL in the following scenarios:
- Invalid index: If SSL_get_ex_data_X509_STORE_CTX_idx() returns an invalid index
- Memory allocation failures: In low-memory conditions or when OpenSSL internal structures fail to initialize
- Extra data not set: If the SSL connection context was not properly initialized with the required extra data
Thank you for the detailed explanation.
From my analysis of the openssl code, it is impossible for SSL_get_ex_data_X509_STORE_CTX_idx() to return an invalid index in a certificate verify callback - ssl_verify_cert_chain() would already have exited were that the case. For the out of memory case (which also covers case 3), I haven't checked extensively but from what I've seen so far, the same is true - the verify callback would not be called because an earlier error would have prevented it.
It is important to fix the check, but from what I can see this is almost impossible to trigger in practice.
Thank you for the quick fix and analysis.