version: make curl_version* thread-safe without using global context

Closes #5010
This commit is contained in:
Daniel Stenberg 2020-03-01 16:16:19 +01:00
parent 310dc709ff
commit e364546fb3
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
6 changed files with 101 additions and 104 deletions

View File

@ -83,8 +83,6 @@
#include "curl_memory.h" #include "curl_memory.h"
#include "memdebug.h" #include "memdebug.h"
void Curl_version_init(void);
/* true globals -- for curl_global_init() and curl_global_cleanup() */ /* true globals -- for curl_global_init() and curl_global_cleanup() */
static unsigned int initialized; static unsigned int initialized;
static long init_flags; static long init_flags;
@ -201,8 +199,6 @@ static CURLcode global_init(long flags, bool memoryfuncs)
init_flags = flags; init_flags = flags;
Curl_version_init();
return CURLE_OK; return CURLE_OK;
fail: fail:

View File

@ -333,7 +333,7 @@ static const struct Curl_handler Curl_handler_http2_ssl = {
int Curl_http2_ver(char *p, size_t len) int Curl_http2_ver(char *p, size_t len)
{ {
nghttp2_info *h2 = nghttp2_version(0); nghttp2_info *h2 = nghttp2_version(0);
return msnprintf(p, len, " nghttp2/%s", h2->version_str); return msnprintf(p, len, "nghttp2/%s", h2->version_str);
} }
/* /*

View File

@ -66,16 +66,6 @@
#include <brotli/decode.h> #include <brotli/decode.h>
#endif #endif
void Curl_version_init(void);
/* For thread safety purposes this function is called by global_init so that
the static data in both version functions is initialized. */
void Curl_version_init(void)
{
curl_version();
curl_version_info(CURLVERSION_NOW);
}
#ifdef HAVE_BROTLI #ifdef HAVE_BROTLI
static size_t brotli_version(char *buf, size_t bufsz) static size_t brotli_version(char *buf, size_t bufsz)
{ {
@ -88,95 +78,108 @@ static size_t brotli_version(char *buf, size_t bufsz)
} }
#endif #endif
/*
* curl_version() returns a pointer to a static buffer.
*
* It is implemented to work multi-threaded by making sure repeated invokes
* generate the exact same string and never write any temporary data like
* zeros in the data.
*/
char *curl_version(void) char *curl_version(void)
{ {
static bool initialized; static char out[250];
static char version[250]; char *outp;
char *ptr = version; size_t outlen;
size_t len; const char *src[14];
size_t left = sizeof(version); #ifdef USE_SSL
char ssl_version[40];
if(initialized) #endif
return version;
strcpy(ptr, LIBCURL_NAME "/" LIBCURL_VERSION);
len = strlen(ptr);
left -= len;
ptr += len;
len = Curl_ssl_version(ptr + 1, left - 1);
if(len > 0) {
*ptr = ' ';
left -= ++len;
ptr += len;
}
#ifdef HAVE_LIBZ #ifdef HAVE_LIBZ
len = msnprintf(ptr, left, " zlib/%s", zlibVersion()); char z_version[40];
left -= len;
ptr += len;
#endif #endif
#ifdef HAVE_BROTLI #ifdef HAVE_BROTLI
len = msnprintf(ptr, left, "%s", " brotli/"); char br_version[40] = "brotli/";
left -= len;
ptr += len;
len = brotli_version(ptr, left);
left -= len;
ptr += len;
#endif #endif
#ifdef USE_ARES #ifdef USE_ARES
/* this function is only present in c-ares, not in the original ares */ char cares_version[40];
len = msnprintf(ptr, left, " c-ares/%s", ares_version(NULL)); #endif
left -= len; #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN)
ptr += len; char idn_version[40];
#endif
#ifdef USE_LIBPSL
char psl_version[40];
#endif
#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS)
char iconv_version[40]="iconv";
#endif
#ifdef USE_SSH
char ssh_version[40];
#endif
#ifdef USE_NGHTTP2
char h2_version[40];
#endif
#ifdef ENABLE_QUIC
char h3_version[40];
#endif
#ifdef USE_LIBRTMP
char rtmp_version[40];
#endif
int i = 0;
int j;
src[i++] = LIBCURL_NAME "/" LIBCURL_VERSION;
#ifdef USE_SSL
Curl_ssl_version(ssl_version, sizeof(ssl_version));
src[i++] = ssl_version;
#endif
#ifdef HAVE_LIBZ
msnprintf(z_version, sizeof(z_version), "zlib/%s", zlibVersion());
src[i++] = z_version;
#endif
#ifdef HAVE_BROTLI
brotli_version(&br_version[7], sizeof(br_version) - 7);
src[i++] = br_version;
#endif
#ifdef USE_ARES
msnprintf(cares_version, sizeof(cares_version),
"c-ares/%s", ares_version(NULL));
src[i++] = cares_version;
#endif #endif
#ifdef USE_LIBIDN2 #ifdef USE_LIBIDN2
if(idn2_check_version(IDN2_VERSION)) { if(idn2_check_version(IDN2_VERSION)) {
len = msnprintf(ptr, left, " libidn2/%s", idn2_check_version(NULL)); msnprintf(idn_version, sizeof(idn_version),
left -= len; "libidn2/%s", idn2_check_version(NULL));
ptr += len; src[i++] = idn_version;
} }
#elif defined(USE_WIN32_IDN)
msnprintf(idn_version, sizeof(idn_version), "WinIDN");
src[i++] = idn_version;
#endif #endif
#ifdef USE_LIBPSL #ifdef USE_LIBPSL
len = msnprintf(ptr, left, " libpsl/%s", psl_get_version()); msnprintf(psl_version, sizeof(psl_version), "libpsl/%s", psl_get_version());
left -= len; src[i++] = psl_version;
ptr += len;
#endif
#ifdef USE_WIN32_IDN
len = msnprintf(ptr, left, " WinIDN");
left -= len;
ptr += len;
#endif #endif
#if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS) #if defined(HAVE_ICONV) && defined(CURL_DOES_CONVERSIONS)
#ifdef _LIBICONV_VERSION #ifdef _LIBICONV_VERSION
len = msnprintf(ptr, left, " iconv/%d.%d", msnprintf(iconv_version, sizeof(iconv_version), "iconv/%d.%d",
_LIBICONV_VERSION >> 8, _LIBICONV_VERSION & 255); _LIBICONV_VERSION >> 8, _LIBICONV_VERSION & 255);
#else #else
/* version unknown */ /* version unknown, let the default stand */
len = msnprintf(ptr, left, " iconv");
#endif /* _LIBICONV_VERSION */ #endif /* _LIBICONV_VERSION */
left -= len; src[i++] = iconv_version;
ptr += len;
#endif #endif
#ifdef USE_SSH #ifdef USE_SSH
if(left) { Curl_ssh_version(ssh_version, sizeof(ssh_version));
*ptr++=' '; src[i++] = ssh_version;
left--;
}
len = Curl_ssh_version(ptr, left);
left -= len;
ptr += len;
#endif #endif
#ifdef USE_NGHTTP2 #ifdef USE_NGHTTP2
len = Curl_http2_ver(ptr, left); Curl_http2_ver(h2_version, sizeof(h2_version));
left -= len; src[i++] = h2_version;
ptr += len;
#endif #endif
#ifdef ENABLE_QUIC #ifdef ENABLE_QUIC
len = Curl_quic_ver(ptr, left); Curl_quic_ver(h3_version, sizeof(h3_version));
left -= len; src[i++] = h3_version;
ptr += len;
#endif #endif
#ifdef USE_LIBRTMP #ifdef USE_LIBRTMP
{ {
@ -188,27 +191,32 @@ char *curl_version(void)
else else
suff[0] = '\0'; suff[0] = '\0';
msnprintf(ptr, left, " librtmp/%d.%d%s", msnprintf(rtmp_version, sizeof(rtmp_version), "librtmp/%d.%d%s",
RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff, RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff,
suff); suff);
/* src[i++] = rtmp_version;
If another lib version is added below this one, this code would
also have to do:
len = what msnprintf() returned
left -= len;
ptr += len;
*/
} }
#endif #endif
/* Silent scan-build even if librtmp is not enabled. */ outp = &out[0];
(void) left; outlen = sizeof(out);
(void) ptr; for(j = 0; j < i; j++) {
size_t n = strlen(src[j]);
/* we need room for a space, the string and the final zero */
if(outlen <= (n + 2))
break;
if(j) {
/* prepend a space if not the first */
*outp++ = ' ';
outlen--;
}
memcpy(outp, src[j], n);
outp += n;
outlen -= n;
}
*outp = 0;
initialized = true; return out;
return version;
} }
/* data for curl_version_info /* data for curl_version_info
@ -391,7 +399,6 @@ static curl_version_info_data version_info = {
curl_version_info_data *curl_version_info(CURLversion stamp) curl_version_info_data *curl_version_info(CURLversion stamp)
{ {
static bool initialized;
#if defined(USE_SSH) #if defined(USE_SSH)
static char ssh_buffer[80]; static char ssh_buffer[80];
#endif #endif
@ -406,9 +413,6 @@ curl_version_info_data *curl_version_info(CURLversion stamp)
static char brotli_buffer[80]; static char brotli_buffer[80];
#endif #endif
if(initialized)
return &version_info;
#ifdef USE_SSL #ifdef USE_SSL
Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer)); Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer));
version_info.ssl_version = ssl_buffer; version_info.ssl_version = ssl_buffer;
@ -476,7 +480,5 @@ curl_version_info_data *curl_version_info(CURLversion stamp)
#endif #endif
(void)stamp; /* avoid compiler warnings, we don't use this */ (void)stamp; /* avoid compiler warnings, we don't use this */
initialized = true;
return &version_info; return &version_info;
} }

View File

@ -641,7 +641,7 @@ int Curl_quic_ver(char *p, size_t len)
{ {
ngtcp2_info *ng2 = ngtcp2_version(0); ngtcp2_info *ng2 = ngtcp2_version(0);
nghttp3_info *ht3 = nghttp3_version(0); nghttp3_info *ht3 = nghttp3_version(0);
return msnprintf(p, len, " ngtcp2/%s nghttp3/%s", return msnprintf(p, len, "ngtcp2/%s nghttp3/%s",
ng2->version_str, ht3->version_str); ng2->version_str, ht3->version_str);
} }

View File

@ -532,7 +532,7 @@ static ssize_t h3_stream_send(struct connectdata *conn,
*/ */
int Curl_quic_ver(char *p, size_t len) int Curl_quic_ver(char *p, size_t len)
{ {
return msnprintf(p, len, " quiche/%s", quiche_version()); return msnprintf(p, len, "quiche/%s", quiche_version());
} }
/* Index where :authority header field will appear in request header /* Index where :authority header field will appear in request header

View File

@ -262,7 +262,6 @@ bool Curl_ssl_false_start(void);
#define Curl_ssl_send(a,b,c,d,e) -1 #define Curl_ssl_send(a,b,c,d,e) -1
#define Curl_ssl_recv(a,b,c,d,e) -1 #define Curl_ssl_recv(a,b,c,d,e) -1
#define Curl_ssl_initsessions(x,y) CURLE_OK #define Curl_ssl_initsessions(x,y) CURLE_OK
#define Curl_ssl_version(x,y) 0
#define Curl_ssl_data_pending(x,y) 0 #define Curl_ssl_data_pending(x,y) 0
#define Curl_ssl_check_cxn(x) 0 #define Curl_ssl_check_cxn(x) 0
#define Curl_ssl_free_certinfo(x) Curl_nop_stmt #define Curl_ssl_free_certinfo(x) Curl_nop_stmt