Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit eb3e90c

Browse files
committed
Avoid thread-safety problem in ecpglib.
ecpglib attempts to force the LC_NUMERIC locale to "C" while reading server output, to avoid problems with strtod() and related functions. Historically it's just issued setlocale() calls to do that, but that has major problems if we're in a threaded application. setlocale() itself is not required by POSIX to be thread-safe (and indeed is not, on recent OpenBSD). Moreover, its effects are process-wide, so that we could cause unexpected results in other threads, or another thread could change our setting. On platforms having uselocale(), which is required by POSIX:2008, we can avoid these problems by using uselocale() instead. Windows goes its own way as usual, but we can make it safe by using _configthreadlocale(). Platforms having neither continue to use the old code, but that should be pretty much nobody among current systems. (Subsequent buildfarm results show that recent NetBSD versions still lack uselocale(), but it's not a big problem because they also do not support non-"C" settings for LC_NUMERIC.) Back-patch of commits 8eb4a93 and ee27584. Michael Meskes and Tom Lane; thanks also to Takayuki Tsunakawa. Discussion: https://postgr.es/m/31420.1547783697@sss.pgh.pa.us
1 parent 4aead13 commit eb3e90c

File tree

7 files changed

+133
-10
lines changed

7 files changed

+133
-10
lines changed

configure

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12650,7 +12650,7 @@ fi
1265012650
LIBS_including_readline="$LIBS"
1265112651
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
1265212652

12653-
for ac_func in cbrt dlopen fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate pstat pthread_is_threaded_np readlink setproctitle setsid shm_open symlink sync_file_range towlower utime utimes wcstombs wcstombs_l
12653+
for ac_func in cbrt dlopen fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate pstat pthread_is_threaded_np readlink setproctitle setsid shm_open symlink sync_file_range towlower uselocale utime utimes wcstombs wcstombs_l
1265412654
do :
1265512655
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
1265612656
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
@@ -13404,6 +13404,17 @@ fi
1340413404

1340513405
# Win32 (really MinGW) support
1340613406
if test "$PORTNAME" = "win32"; then
13407+
for ac_func in _configthreadlocale
13408+
do :
13409+
ac_fn_c_check_func "$LINENO" "_configthreadlocale" "ac_cv_func__configthreadlocale"
13410+
if test "x$ac_cv_func__configthreadlocale" = xyes; then :
13411+
cat >>confdefs.h <<_ACEOF
13412+
#define HAVE__CONFIGTHREADLOCALE 1
13413+
_ACEOF
13414+
13415+
fi
13416+
done
13417+
1340713418
ac_fn_c_check_func "$LINENO" "gettimeofday" "ac_cv_func_gettimeofday"
1340813419
if test "x$ac_cv_func_gettimeofday" = xyes; then :
1340913420
$as_echo "#define HAVE_GETTIMEOFDAY 1" >>confdefs.h

configure.in

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1503,7 +1503,32 @@ PGAC_FUNC_WCSTOMBS_L
15031503
LIBS_including_readline="$LIBS"
15041504
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
15051505

1506-
AC_CHECK_FUNCS([cbrt dlopen fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate pstat pthread_is_threaded_np readlink setproctitle setsid shm_open symlink sync_file_range towlower utime utimes wcstombs wcstombs_l])
1506+
AC_CHECK_FUNCS(m4_normalize([
1507+
cbrt
1508+
dlopen
1509+
fdatasync
1510+
getifaddrs
1511+
getpeerucred
1512+
getrlimit
1513+
mbstowcs_l
1514+
memmove
1515+
poll
1516+
posix_fallocate
1517+
pstat
1518+
pthread_is_threaded_np
1519+
readlink
1520+
setproctitle
1521+
setsid
1522+
shm_open
1523+
symlink
1524+
sync_file_range
1525+
towlower
1526+
uselocale
1527+
utime
1528+
utimes
1529+
wcstombs
1530+
wcstombs_l
1531+
]))
15071532

15081533
AC_REPLACE_FUNCS(fseeko)
15091534
case $host_os in
@@ -1667,6 +1692,7 @@ fi
16671692

16681693
# Win32 (really MinGW) support
16691694
if test "$PORTNAME" = "win32"; then
1695+
AC_CHECK_FUNCS(_configthreadlocale)
16701696
AC_REPLACE_FUNCS(gettimeofday)
16711697
AC_LIBOBJ(dirmod)
16721698
AC_LIBOBJ(kill)

src/include/pg_config.h.in

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,9 @@
650650
/* Define to 1 if the system has the type `unsigned long long int'. */
651651
#undef HAVE_UNSIGNED_LONG_LONG_INT
652652

653+
/* Define to 1 if you have the `uselocale' function. */
654+
#undef HAVE_USELOCALE
655+
653656
/* Define to 1 if you have the `utime' function. */
654657
#undef HAVE_UTIME
655658

@@ -707,6 +710,9 @@
707710
/* Define to 1 if your compiler understands __builtin_unreachable. */
708711
#undef HAVE__BUILTIN_UNREACHABLE
709712

713+
/* Define to 1 if you have the `_configthreadlocale' function. */
714+
#undef HAVE__CONFIGTHREADLOCALE
715+
710716
/* Define to 1 if you have __cpuid. */
711717
#undef HAVE__CPUID
712718

src/include/pg_config.h.win32

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,9 @@
497497
/* Define to 1 if you have the `unsetenv' function. */
498498
/* #undef HAVE_UNSETENV */
499499

500+
/* Define to 1 if you have the `uselocale' function. */
501+
/* #undef HAVE_USELOCALE */
502+
500503
/* Define to 1 if you have the `utime' function. */
501504
#define HAVE_UTIME 1
502505

@@ -539,6 +542,9 @@
539542
/* Define to 1 if your compiler understands __builtin_unreachable. */
540543
/* #undef HAVE__BUILTIN_UNREACHABLE */
541544

545+
/* Define to 1 if you have the `_configthreadlocale' function. */
546+
#define HAVE__CONFIGTHREADLOCALE 1
547+
542548
/* Define to 1 if you have __cpuid. */
543549
#define HAVE__CPUID 1
544550

src/interfaces/ecpg/ecpglib/descriptor.c

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -482,22 +482,45 @@ ECPGget_desc(int lineno, const char *desc_name, int index,...)
482482
if (data_var.type != ECPGt_EORT)
483483
{
484484
struct statement stmt;
485-
char *oldlocale;
485+
486+
memset(&stmt, 0, sizeof stmt);
487+
stmt.lineno = lineno;
486488

487489
/* Make sure we do NOT honor the locale for numeric input */
488490
/* since the database gives the standard decimal point */
489-
oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), lineno);
491+
/* (see comments in execute.c) */
492+
#ifdef HAVE_USELOCALE
493+
stmt.clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
494+
if (stmt.clocale != (locale_t) 0)
495+
stmt.oldlocale = uselocale(stmt.clocale);
496+
#else
497+
#ifdef HAVE__CONFIGTHREADLOCALE
498+
stmt.oldthreadlocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
499+
#endif
500+
stmt.oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), lineno);
490501
setlocale(LC_NUMERIC, "C");
491-
492-
memset(&stmt, 0, sizeof stmt);
493-
stmt.lineno = lineno;
502+
#endif
494503

495504
/* desperate try to guess something sensible */
496505
stmt.connection = ecpg_get_connection(NULL);
497506
ecpg_store_result(ECPGresult, index, &stmt, &data_var);
498507

499-
setlocale(LC_NUMERIC, oldlocale);
500-
ecpg_free(oldlocale);
508+
#ifdef HAVE_USELOCALE
509+
if (stmt.oldlocale != (locale_t) 0)
510+
uselocale(stmt.oldlocale);
511+
if (stmt.clocale)
512+
freelocale(stmt.clocale);
513+
#else
514+
if (stmt.oldlocale)
515+
{
516+
setlocale(LC_NUMERIC, stmt.oldlocale);
517+
ecpg_free(stmt.oldlocale);
518+
}
519+
#ifdef HAVE__CONFIGTHREADLOCALE
520+
if (stmt.oldthreadlocale != -1)
521+
_configthreadlocale(stmt.oldthreadlocale);
522+
#endif
523+
#endif
501524
}
502525
else if (data_var.ind_type != ECPGt_NO_INDICATOR && data_var.ind_pointer != NULL)
503526

src/interfaces/ecpg/ecpglib/execute.c

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,12 @@ free_statement(struct statement * stmt)
104104
free_variable(stmt->outlist);
105105
ecpg_free(stmt->command);
106106
ecpg_free(stmt->name);
107+
#ifdef HAVE_USELOCALE
108+
if (stmt->clocale)
109+
freelocale(stmt->clocale);
110+
#else
107111
ecpg_free(stmt->oldlocale);
112+
#endif
108113
ecpg_free(stmt);
109114
}
110115

@@ -1779,15 +1784,40 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
17791784

17801785
/*
17811786
* Make sure we do NOT honor the locale for numeric input/output since the
1782-
* database wants the standard decimal point
1787+
* database wants the standard decimal point. If available, use
1788+
* uselocale() for this because it's thread-safe. Windows doesn't have
1789+
* that, but it usually does have _configthreadlocale().
17831790
*/
1791+
#ifdef HAVE_USELOCALE
1792+
stmt->clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
1793+
if (stmt->clocale == (locale_t) 0)
1794+
{
1795+
ecpg_do_epilogue(stmt);
1796+
return false;
1797+
}
1798+
stmt->oldlocale = uselocale(stmt->clocale);
1799+
if (stmt->oldlocale == (locale_t) 0)
1800+
{
1801+
ecpg_do_epilogue(stmt);
1802+
return false;
1803+
}
1804+
#else
1805+
#ifdef HAVE__CONFIGTHREADLOCALE
1806+
stmt->oldthreadlocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
1807+
if (stmt->oldthreadlocale == -1)
1808+
{
1809+
ecpg_do_epilogue(stmt);
1810+
return false;
1811+
}
1812+
#endif
17841813
stmt->oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), lineno);
17851814
if (stmt->oldlocale == NULL)
17861815
{
17871816
ecpg_do_epilogue(stmt);
17881817
return false;
17891818
}
17901819
setlocale(LC_NUMERIC, "C");
1820+
#endif
17911821

17921822
#ifdef ENABLE_THREAD_SAFETY
17931823
ecpg_pthreads_init();
@@ -1990,8 +2020,18 @@ ecpg_do_epilogue(struct statement * stmt)
19902020
if (stmt == NULL)
19912021
return;
19922022

2023+
#ifdef HAVE_USELOCALE
2024+
if (stmt->oldlocale != (locale_t) 0)
2025+
uselocale(stmt->oldlocale);
2026+
#else
19932027
if (stmt->oldlocale)
2028+
{
19942029
setlocale(LC_NUMERIC, stmt->oldlocale);
2030+
#ifdef HAVE__CONFIGTHREADLOCALE
2031+
_configthreadlocale(stmt->oldthreadlocale);
2032+
#endif
2033+
}
2034+
#endif
19952035

19962036
free_statement(stmt);
19972037
}

src/interfaces/ecpg/ecpglib/extern.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
#ifndef CHAR_BIT
1313
#include <limits.h>
1414
#endif
15+
#ifdef LOCALE_T_IN_XLOCALE
16+
#include <xlocale.h>
17+
#endif
1518

1619
enum COMPAT_MODE
1720
{
@@ -60,7 +63,15 @@ struct statement
6063
bool questionmarks;
6164
struct variable *inlist;
6265
struct variable *outlist;
66+
#ifdef HAVE_USELOCALE
67+
locale_t clocale;
68+
locale_t oldlocale;
69+
#else
6370
char *oldlocale;
71+
#ifdef HAVE__CONFIGTHREADLOCALE
72+
int oldthreadlocale;
73+
#endif
74+
#endif
6475
int nparams;
6576
char **paramvalues;
6677
PGresult *results;

0 commit comments

Comments
 (0)