Commit 4f17865ba4 for openssl.org
commit 4f17865ba44b904c99d8e10ac1e460b811b7842e
Author: Daniel Kubec <kubec@openssl.foundation>
Date: Tue Jul 7 09:16:19 2026 +0000
NULL-pointer subtraction UB in tls_collect_extensions()
Fixed invalid-pointer-pair in the existing branch by ensuring thisex != NULL
before subtraction.
Fixes #31689
Reviewed-by: Tom Cosgrove <tom.cosgrove@arm.com>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
MergeDate: Fri Jul 10 15:40:21 2026
(Merged from https://github.com/openssl/openssl/pull/31875)
diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c
index 017d1a5fb5..c3ef683d75 100644
--- a/ssl/statem/extensions.c
+++ b/ssl/statem/extensions.c
@@ -907,6 +907,11 @@ int tls_collect_extensions(SSL_CONNECTION *s, PACKET *packet,
SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_BAD_EXTENSION);
goto err;
}
+
+ /* The server must tolerate the unknown extension and complete. */
+ if (thisex == NULL)
+ continue;
+
idx = (unsigned int)(thisex - raw_extensions);
/*-
* Check that we requested this extension (if appropriate). Requests can
@@ -937,17 +942,15 @@ int tls_collect_extensions(SSL_CONNECTION *s, PACKET *packet,
SSL_R_UNSOLICITED_EXTENSION);
goto err;
}
- if (thisex != NULL) {
- thisex->data = extension;
- thisex->present = 1;
- thisex->type = type;
- thisex->received_order = i++;
- if (s->ext.debug_cb)
- s->ext.debug_cb(SSL_CONNECTION_GET_USER_SSL(s), !s->server,
- thisex->type, PACKET_data(&thisex->data),
- (int)PACKET_remaining(&thisex->data),
- s->ext.debug_arg);
- }
+ thisex->data = extension;
+ thisex->present = 1;
+ thisex->type = type;
+ thisex->received_order = i++;
+ if (s->ext.debug_cb)
+ s->ext.debug_cb(SSL_CONNECTION_GET_USER_SSL(s), !s->server,
+ thisex->type, PACKET_data(&thisex->data),
+ (int)PACKET_remaining(&thisex->data),
+ s->ext.debug_arg);
}
if (init) {
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 8baf38b93d..d751385fd2 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -12920,6 +12920,51 @@ end:
SSL_CTX_free(cctx);
return testresult;
}
+
+static int un_ext_add_cb(SSL *s, unsigned int ext_type,
+ unsigned int context, const unsigned char **out, size_t *outlen, X509 *x,
+ size_t chainidx, int *al, void *add_arg)
+{
+ static const unsigned char data[] = { 0xaa };
+ *out = data;
+ *outlen = sizeof(data);
+ return 1;
+}
+
+static int un_ext_parse_cb(SSL *s, unsigned int ext_type,
+ unsigned int context, const unsigned char *in, size_t inlen, X509 *x,
+ size_t chainidx, int *al, void *parse_arg)
+{
+ return 1;
+}
+
+/*
+ * Test that a handshake succeeds when the peer sends an extension type we do
+ * not recognise. The client registers a custom extension in its ClientHello
+ * that the server knows nothing about, so on the server tls_collect_extensions()
+ * takes the "unknown extension" branch.
+ */
+static int test_tls13_unknown_extension(void)
+{
+ SSL_CTX *s = NULL, *c = NULL;
+ SSL *s_ssl = NULL, *c_ssl = NULL;
+ int test;
+
+ test = TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
+ TLS_client_method(), TLS1_3_VERSION, TLS1_3_VERSION, &s, &c, cert, privkey))
+ && TEST_true(SSL_CTX_add_custom_ext(c, 0xfefe, SSL_EXT_CLIENT_HELLO,
+ un_ext_add_cb, NULL, NULL, un_ext_parse_cb, NULL))
+ && TEST_true(create_ssl_objects(s, c, &s_ssl, &c_ssl, NULL, NULL))
+ /* The server must tolerate the unknown extension and complete. */
+ && TEST_true(create_ssl_connection(s_ssl, c_ssl, SSL_ERROR_NONE));
+
+ SSL_free(s_ssl);
+ SSL_free(c_ssl);
+ SSL_CTX_free(s);
+ SSL_CTX_free(c);
+ return test;
+}
+
#endif /* OSSL_NO_USABLE_TLS1_3 */
static int check_version_string(SSL *s, int version)
@@ -15380,6 +15425,7 @@ int setup_tests(void)
#ifndef OSSL_NO_USABLE_TLS1_3
ADD_TEST(test_read_ahead_key_change);
ADD_ALL_TESTS(test_tls13_record_padding, 6);
+ ADD_TEST(test_tls13_unknown_extension);
#endif
#if !defined(OPENSSL_NO_TLS1_2) && !defined(OSSL_NO_USABLE_TLS1_3)
ADD_ALL_TESTS(test_serverinfo_custom, 4);