Commit 62570ca6e9 for openssl.org
commit 62570ca6e93af540907330d50ee581fe82cd18fd
Author: Bob Beck <beck@openssl.org>
Date: Wed Aug 5 11:39:56 2026 -0600
Don't strncpy() counted ASN1_STRING data in ossl_sk_ASN1_UTF8STRING2text()
The element data is counted, not a C string, so strncpy() was wrong two
ways: an empty element yields strncpy(p, NULL, 0), which is undefined, and a
value containing an embedded NUL was truncated and zero padded instead of
copied verbatim.
Use memcpy() bounded by the element length, skipping empty elements. The
separator copy becomes a plain memcpy() as well, dropping the "+ 1 to
silence gcc" strncpy() hack. The computed buffer length and terminator are
unchanged.
Note in the documentation that using utf8 values in C strings is a
footgun if they contain NUL bytes internally
Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Andrew Dinh <andrewd@openssl.org>
Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
Merge-date: Thu Aug 27 13:52:44 2026
Merged-from: https://github.com/openssl/openssl/pull/32178
diff --git a/crypto/asn1/asn1_lib.c b/crypto/asn1/asn1_lib.c
index a4d80eaa29..7209e470b3 100644
--- a/crypto/asn1/asn1_lib.c
+++ b/crypto/asn1/asn1_lib.c
@@ -534,7 +534,6 @@ const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x)
return x->data;
}
-/* |max_len| excludes NUL terminator and may be 0 to indicate no restriction */
char *ossl_sk_ASN1_UTF8STRING2text(STACK_OF(ASN1_UTF8STRING) *text,
const char *sep, size_t max_len)
{
@@ -564,11 +563,13 @@ char *ossl_sk_ASN1_UTF8STRING2text(STACK_OF(ASN1_UTF8STRING) *text,
current = sk_ASN1_UTF8STRING_value(text, i);
length = ASN1_STRING_get_length(current);
if (i > 0 && sep_len > 0) {
- strncpy(p, sep, sep_len + 1); /* using + 1 to silence gcc warning */
+ memcpy(p, sep, sep_len);
p += sep_len;
}
- strncpy(p, (const char *)ASN1_STRING_get0_data(current), length);
- p += length;
+ if (length > 0) {
+ memcpy(p, ASN1_STRING_get0_data(current), length);
+ p += length;
+ }
}
*p = '\0';
diff --git a/include/internal/cryptlib.h b/include/internal/cryptlib.h
index 8bc22e39ce..1a367b3238 100644
--- a/include/internal/cryptlib.h
+++ b/include/internal/cryptlib.h
@@ -154,6 +154,21 @@ const void *ossl_bsearch(const void *key, const void *base, int num,
int (*cmp_thunk)(int (*real_cmp_fn)(const void *, const void *), const void *, const void *),
int flags);
+/**
+ * @brief Join a stack of ASN1_UTF8STRINGs into one allocated string.
+ *
+ * Concatenates the elements of text, separated by sep, into a newly allocated
+ * NUL terminated string. The element data is copied in full and may itself
+ * contain embedded NUL bytes, so a caller that treats the result as a C string
+ * will see it truncated at the first such byte.
+ *
+ * @param text the stack of ASN1_UTF8STRINGs to join
+ * @param sep separator placed between elements, or NULL for none
+ * @param max_len maximum length of the result, excluding the NUL terminator,
+ * or 0 for no restriction
+ * @returns a newly allocated string to be freed with OPENSSL_free(), or NULL on
+ * error or if the result would exceed max_len
+ */
char *ossl_sk_ASN1_UTF8STRING2text(STACK_OF(ASN1_UTF8STRING) *text,
const char *sep, size_t max_len);
char *ossl_ipaddr_to_asc(const unsigned char *p, int len);