mirror of
https://github.com/transmission/transmission
synced 2025-03-03 10:15:45 +00:00
#5851: Use per-thread locale setup if possible
This prevents crashes on concurrent tr_variantFromBuf, tr_variantToBuf and tr_variantGetReal use.
This commit is contained in:
parent
2e6d5c8bc9
commit
ae5755dc1e
3 changed files with 86 additions and 15 deletions
|
@ -381,7 +381,8 @@ include(LargeFileSupport)
|
||||||
set(NEEDED_HEADERS
|
set(NEEDED_HEADERS
|
||||||
stdbool.h
|
stdbool.h
|
||||||
sys/statvfs.h
|
sys/statvfs.h
|
||||||
xfs/xfs.h)
|
xfs/xfs.h
|
||||||
|
xlocale.h)
|
||||||
|
|
||||||
if(ENABLE_NLS)
|
if(ENABLE_NLS)
|
||||||
list(APPEND NEEDED_HEADERS libintl.h)
|
list(APPEND NEEDED_HEADERS libintl.h)
|
||||||
|
@ -416,6 +417,7 @@ set(NEEDED_FUNCTIONS
|
||||||
strlcpy
|
strlcpy
|
||||||
strsep
|
strsep
|
||||||
syslog
|
syslog
|
||||||
|
uselocale
|
||||||
valloc)
|
valloc)
|
||||||
|
|
||||||
foreach(F ${NEEDED_FUNCTIONS})
|
foreach(F ${NEEDED_FUNCTIONS})
|
||||||
|
|
|
@ -106,8 +106,8 @@ fi
|
||||||
AC_HEADER_STDC
|
AC_HEADER_STDC
|
||||||
AC_HEADER_TIME
|
AC_HEADER_TIME
|
||||||
|
|
||||||
AC_CHECK_HEADERS([stdbool.h])
|
AC_CHECK_HEADERS([stdbool.h xlocale.h])
|
||||||
AC_CHECK_FUNCS([iconv_open pread pwrite lrintf strlcpy daemon dirname basename canonicalize_file_name strcasecmp localtime_r fallocate64 posix_fallocate memmem strsep strtold syslog valloc getpagesize posix_memalign statvfs htonll ntohll mkdtemp])
|
AC_CHECK_FUNCS([iconv_open pread pwrite lrintf strlcpy daemon dirname basename canonicalize_file_name strcasecmp localtime_r fallocate64 posix_fallocate memmem strsep strtold syslog valloc getpagesize posix_memalign statvfs htonll ntohll mkdtemp uselocale])
|
||||||
AC_PROG_INSTALL
|
AC_PROG_INSTALL
|
||||||
AC_PROG_MAKE_SET
|
AC_PROG_MAKE_SET
|
||||||
ACX_PTHREAD
|
ACX_PTHREAD
|
||||||
|
|
|
@ -7,6 +7,15 @@
|
||||||
* $Id$
|
* $Id$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if defined (HAVE_USELOCALE) && (!defined (_XOPEN_SOURCE) || _XOPEN_SOURCE < 700)
|
||||||
|
#undef _XOPEN_SOURCE
|
||||||
|
#define _XOPEN_SOURCE 700
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined (HAVE_USELOCALE) && !defined (_GNU_SOURCE)
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdlib.h> /* strtod(), realloc(), qsort() */
|
#include <stdlib.h> /* strtod(), realloc(), qsort() */
|
||||||
|
@ -18,6 +27,10 @@
|
||||||
|
|
||||||
#include <locale.h> /* setlocale() */
|
#include <locale.h> /* setlocale() */
|
||||||
|
|
||||||
|
#if defined (HAVE_USELOCALE) && defined (HAVE_XLOCALE_H)
|
||||||
|
#include <xlocale.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <event2/buffer.h>
|
#include <event2/buffer.h>
|
||||||
|
|
||||||
#define __LIBTRANSMISSION_VARIANT_MODULE__
|
#define __LIBTRANSMISSION_VARIANT_MODULE__
|
||||||
|
@ -34,6 +47,65 @@
|
||||||
***
|
***
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
struct locale_context
|
||||||
|
{
|
||||||
|
#ifdef HAVE_USELOCALE
|
||||||
|
locale_t new_locale;
|
||||||
|
locale_t old_locale;
|
||||||
|
#else
|
||||||
|
#ifdef _WIN32
|
||||||
|
int old_thread_config;
|
||||||
|
#endif
|
||||||
|
int category;
|
||||||
|
char old_locale[128];
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
use_numeric_locale (struct locale_context * context,
|
||||||
|
const char * locale_name)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_USELOCALE
|
||||||
|
|
||||||
|
context->new_locale = newlocale (LC_NUMERIC_MASK, locale_name, NULL);
|
||||||
|
context->old_locale = uselocale (context->new_locale);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
context->old_thread_config = _configthreadlocale (_ENABLE_PER_THREAD_LOCALE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
context->category = LC_NUMERIC;
|
||||||
|
tr_strlcpy (context->old_locale, setlocale (context->category, NULL), sizeof (context->old_locale));
|
||||||
|
setlocale (context->category, locale_name);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
restore_locale (struct locale_context * context)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_USELOCALE
|
||||||
|
|
||||||
|
uselocale (context->old_locale);
|
||||||
|
freelocale (context->new_locale);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
setlocale (context->category, context->old_locale);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
_configthreadlocale (context->old_thread_config);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
****
|
||||||
|
***/
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
tr_variantIsContainer (const tr_variant * v)
|
tr_variantIsContainer (const tr_variant * v)
|
||||||
{
|
{
|
||||||
|
@ -301,14 +373,13 @@ tr_variantGetReal (const tr_variant * v, double * setme)
|
||||||
if (!success && tr_variantIsString (v))
|
if (!success && tr_variantIsString (v))
|
||||||
{
|
{
|
||||||
char * endptr;
|
char * endptr;
|
||||||
char locale[128];
|
struct locale_context locale_ctx;
|
||||||
double d;
|
double d;
|
||||||
|
|
||||||
/* the json spec requires a '.' decimal point regardless of locale */
|
/* the json spec requires a '.' decimal point regardless of locale */
|
||||||
tr_strlcpy (locale, setlocale (LC_NUMERIC, NULL), sizeof (locale));
|
use_numeric_locale (&locale_ctx, "C");
|
||||||
setlocale (LC_NUMERIC, "POSIX");
|
|
||||||
d = strtod (getStr (v), &endptr);
|
d = strtod (getStr (v), &endptr);
|
||||||
setlocale (LC_NUMERIC, locale);
|
restore_locale (&locale_ctx);
|
||||||
|
|
||||||
if ((success = (getStr (v) != endptr) && !*endptr))
|
if ((success = (getStr (v) != endptr) && !*endptr))
|
||||||
*setme = d;
|
*setme = d;
|
||||||
|
@ -1093,12 +1164,11 @@ tr_variantMergeDicts (tr_variant * target, const tr_variant * source)
|
||||||
struct evbuffer *
|
struct evbuffer *
|
||||||
tr_variantToBuf (const tr_variant * v, tr_variant_fmt fmt)
|
tr_variantToBuf (const tr_variant * v, tr_variant_fmt fmt)
|
||||||
{
|
{
|
||||||
char lc_numeric[128];
|
struct locale_context locale_ctx;
|
||||||
struct evbuffer * buf = evbuffer_new();
|
struct evbuffer * buf = evbuffer_new();
|
||||||
|
|
||||||
/* parse with LC_NUMERIC="C" to ensure a "." decimal separator */
|
/* parse with LC_NUMERIC="C" to ensure a "." decimal separator */
|
||||||
tr_strlcpy (lc_numeric, setlocale (LC_NUMERIC, NULL), sizeof (lc_numeric));
|
use_numeric_locale (&locale_ctx, "C");
|
||||||
setlocale (LC_NUMERIC, "C");
|
|
||||||
|
|
||||||
evbuffer_expand (buf, 4096); /* alloc a little memory to start off with */
|
evbuffer_expand (buf, 4096); /* alloc a little memory to start off with */
|
||||||
|
|
||||||
|
@ -1118,7 +1188,7 @@ tr_variantToBuf (const tr_variant * v, tr_variant_fmt fmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* restore the previous locale */
|
/* restore the previous locale */
|
||||||
setlocale (LC_NUMERIC, lc_numeric);
|
restore_locale (&locale_ctx);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1251,11 +1321,10 @@ tr_variantFromBuf (tr_variant * setme,
|
||||||
const char ** setme_end)
|
const char ** setme_end)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
char lc_numeric[128];
|
struct locale_context locale_ctx;
|
||||||
|
|
||||||
/* parse with LC_NUMERIC="C" to ensure a "." decimal separator */
|
/* parse with LC_NUMERIC="C" to ensure a "." decimal separator */
|
||||||
tr_strlcpy (lc_numeric, setlocale (LC_NUMERIC, NULL), sizeof (lc_numeric));
|
use_numeric_locale (&locale_ctx, "C");
|
||||||
setlocale (LC_NUMERIC, "C");
|
|
||||||
|
|
||||||
switch (fmt)
|
switch (fmt)
|
||||||
{
|
{
|
||||||
|
@ -1270,6 +1339,6 @@ tr_variantFromBuf (tr_variant * setme,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* restore the previous locale */
|
/* restore the previous locale */
|
||||||
setlocale (LC_NUMERIC, lc_numeric);
|
restore_locale (&locale_ctx);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue