Skip to content

Commit 6106f9d

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 5aa03f9 commit 6106f9d

File tree

7 files changed

+134
-10
lines changed

7 files changed

+134
-10
lines changed

configure

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

13160-
for ac_func in cbrt clock_gettime 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
13160+
for ac_func in cbrt clock_gettime 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
1316113161
do :
1316213162
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
1316313163
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
@@ -13911,6 +13911,17 @@ fi
1391113911

1391213912
# Win32 (really MinGW) support
1391313913
if test "$PORTNAME" = "win32"; then
13914+
for ac_func in _configthreadlocale
13915+
do :
13916+
ac_fn_c_check_func "$LINENO" "_configthreadlocale" "ac_cv_func__configthreadlocale"
13917+
if test "x$ac_cv_func__configthreadlocale" = xyes; then :
13918+
cat >>confdefs.h <<_ACEOF
13919+
#define HAVE__CONFIGTHREADLOCALE 1
13920+
_ACEOF
13921+
13922+
fi
13923+
done
13924+
1391413925
ac_fn_c_check_func "$LINENO" "gettimeofday" "ac_cv_func_gettimeofday"
1391513926
if test "x$ac_cv_func_gettimeofday" = xyes; then :
1391613927
$as_echo "#define HAVE_GETTIMEOFDAY 1" >>confdefs.h

configure.in

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1476,7 +1476,33 @@ PGAC_FUNC_WCSTOMBS_L
14761476
LIBS_including_readline="$LIBS"
14771477
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
14781478

1479-
AC_CHECK_FUNCS([cbrt clock_gettime 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])
1479+
AC_CHECK_FUNCS(m4_normalize([
1480+
cbrt
1481+
clock_gettime
1482+
dlopen
1483+
fdatasync
1484+
getifaddrs
1485+
getpeerucred
1486+
getrlimit
1487+
mbstowcs_l
1488+
memmove
1489+
poll
1490+
posix_fallocate
1491+
pstat
1492+
pthread_is_threaded_np
1493+
readlink
1494+
setproctitle
1495+
setsid
1496+
shm_open
1497+
symlink
1498+
sync_file_range
1499+
towlower
1500+
uselocale
1501+
utime
1502+
utimes
1503+
wcstombs
1504+
wcstombs_l
1505+
]))
14801506

14811507
AC_REPLACE_FUNCS(fseeko)
14821508
case $host_os in
@@ -1640,6 +1666,7 @@ fi
16401666

16411667
# Win32 (really MinGW) support
16421668
if test "$PORTNAME" = "win32"; then
1669+
AC_CHECK_FUNCS(_configthreadlocale)
16431670
AC_REPLACE_FUNCS(gettimeofday)
16441671
AC_LIBOBJ(dirmod)
16451672
AC_LIBOBJ(kill)

src/include/pg_config.h.in

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

647+
/* Define to 1 if you have the `uselocale' function. */
648+
#undef HAVE_USELOCALE
649+
647650
/* Define to 1 if you have the `utime' function. */
648651
#undef HAVE_UTIME
649652

@@ -701,6 +704,9 @@
701704
/* Define to 1 if your compiler understands __builtin_unreachable. */
702705
#undef HAVE__BUILTIN_UNREACHABLE
703706

707+
/* Define to 1 if you have the `_configthreadlocale' function. */
708+
#undef HAVE__CONFIGTHREADLOCALE
709+
704710
/* Define to 1 if you have __cpuid. */
705711
#undef HAVE__CPUID
706712

src/include/pg_config.h.win32

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

497+
/* Define to 1 if you have the `uselocale' function. */
498+
/* #undef HAVE_USELOCALE */
499+
497500
/* Define to 1 if you have the `utime' function. */
498501
#define HAVE_UTIME 1
499502

@@ -536,6 +539,9 @@
536539
/* Define to 1 if your compiler understands __builtin_unreachable. */
537540
/* #undef HAVE__BUILTIN_UNREACHABLE */
538541

542+
/* Define to 1 if you have the `_configthreadlocale' function. */
543+
#define HAVE__CONFIGTHREADLOCALE 1
544+
539545
/* Define to 1 if you have __cpuid. */
540546
#define HAVE__CPUID 1
541547

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
@@ -103,7 +103,12 @@ free_statement(struct statement *stmt)
103103
free_variable(stmt->outlist);
104104
ecpg_free(stmt->command);
105105
ecpg_free(stmt->name);
106+
#ifdef HAVE_USELOCALE
107+
if (stmt->clocale)
108+
freelocale(stmt->clocale);
109+
#else
106110
ecpg_free(stmt->oldlocale);
111+
#endif
107112
ecpg_free(stmt);
108113
}
109114

@@ -1778,15 +1783,40 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
17781783

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

17911821
#ifdef ENABLE_THREAD_SAFETY
17921822
ecpg_pthreads_init();
@@ -1989,8 +2019,18 @@ ecpg_do_epilogue(struct statement *stmt)
19892019
if (stmt == NULL)
19902020
return;
19912021

2022+
#ifdef HAVE_USELOCALE
2023+
if (stmt->oldlocale != (locale_t) 0)
2024+
uselocale(stmt->oldlocale);
2025+
#else
19922026
if (stmt->oldlocale)
2027+
{
19932028
setlocale(LC_NUMERIC, stmt->oldlocale);
2029+
#ifdef HAVE__CONFIGTHREADLOCALE
2030+
_configthreadlocale(stmt->oldthreadlocale);
2031+
#endif
2032+
}
2033+
#endif
19942034

19952035
free_statement(stmt);
19962036
}

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)