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

Commit f305e84

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 7fbdefd commit f305e84

File tree

7 files changed

+132
-10
lines changed

7 files changed

+132
-10
lines changed

configure

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

14873-
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 utime utimes wcstombs_l
14873+
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 uselocale utime utimes wcstombs_l
1487414874
do :
1487514875
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
1487614876
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
@@ -15647,6 +15647,17 @@ fi
1564715647

1564815648
# Win32 (really MinGW) support
1564915649
if test "$PORTNAME" = "win32"; then
15650+
for ac_func in _configthreadlocale
15651+
do :
15652+
ac_fn_c_check_func "$LINENO" "_configthreadlocale" "ac_cv_func__configthreadlocale"
15653+
if test "x$ac_cv_func__configthreadlocale" = xyes; then :
15654+
cat >>confdefs.h <<_ACEOF
15655+
#define HAVE__CONFIGTHREADLOCALE 1
15656+
_ACEOF
15657+
15658+
fi
15659+
done
15660+
1565015661
ac_fn_c_check_func "$LINENO" "gettimeofday" "ac_cv_func_gettimeofday"
1565115662
if test "x$ac_cv_func_gettimeofday" = xyes; then :
1565215663
$as_echo "#define HAVE_GETTIMEOFDAY 1" >>confdefs.h

configure.in

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1568,7 +1568,31 @@ PGAC_FUNC_WCSTOMBS_L
15681568
LIBS_including_readline="$LIBS"
15691569
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
15701570

1571-
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 utime utimes wcstombs_l])
1571+
AC_CHECK_FUNCS(m4_normalize([
1572+
cbrt
1573+
clock_gettime
1574+
dlopen
1575+
fdatasync
1576+
getifaddrs
1577+
getpeerucred
1578+
getrlimit
1579+
mbstowcs_l
1580+
memmove
1581+
poll
1582+
posix_fallocate
1583+
pstat
1584+
pthread_is_threaded_np
1585+
readlink
1586+
setproctitle
1587+
setsid
1588+
shm_open
1589+
symlink
1590+
sync_file_range
1591+
uselocale
1592+
utime
1593+
utimes
1594+
wcstombs_l
1595+
]))
15721596

15731597
AC_REPLACE_FUNCS(fseeko)
15741598
case $host_os in
@@ -1732,6 +1756,7 @@ fi
17321756

17331757
# Win32 (really MinGW) support
17341758
if test "$PORTNAME" = "win32"; then
1759+
AC_CHECK_FUNCS(_configthreadlocale)
17351760
AC_REPLACE_FUNCS(gettimeofday)
17361761
AC_LIBOBJ(dirmod)
17371762
AC_LIBOBJ(kill)

src/include/pg_config.h.in

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

680+
/* Define to 1 if you have the `uselocale' function. */
681+
#undef HAVE_USELOCALE
682+
680683
/* Define to 1 if you have the `utime' function. */
681684
#undef HAVE_UTIME
682685

@@ -743,6 +746,9 @@
743746
/* Define to 1 if your compiler understands __builtin_unreachable. */
744747
#undef HAVE__BUILTIN_UNREACHABLE
745748

749+
/* Define to 1 if you have the `_configthreadlocale' function. */
750+
#undef HAVE__CONFIGTHREADLOCALE
751+
746752
/* Define to 1 if you have __cpuid. */
747753
#undef HAVE__CPUID
748754

src/include/pg_config.h.win32

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

544+
/* Define to 1 if you have the `uselocale' function. */
545+
/* #undef HAVE_USELOCALE */
546+
544547
/* Define to 1 if you have the `utime' function. */
545548
#define HAVE_UTIME 1
546549

@@ -592,6 +595,9 @@
592595
/* Define to 1 if your compiler understands __builtin_unreachable. */
593596
/* #undef HAVE__BUILTIN_UNREACHABLE */
594597

598+
/* Define to 1 if you have the `_configthreadlocale' function. */
599+
#define HAVE__CONFIGTHREADLOCALE 1
600+
595601
/* Define to 1 if you have __cpuid. */
596602
#define HAVE__CPUID 1
597603

src/interfaces/ecpg/ecpglib/descriptor.c

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

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

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

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

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
{
@@ -61,7 +64,15 @@ struct statement
6164
bool questionmarks;
6265
struct variable *inlist;
6366
struct variable *outlist;
67+
#ifdef HAVE_USELOCALE
68+
locale_t clocale;
69+
locale_t oldlocale;
70+
#else
6471
char *oldlocale;
72+
#ifdef HAVE__CONFIGTHREADLOCALE
73+
int oldthreadlocale;
74+
#endif
75+
#endif
6576
int nparams;
6677
char **paramvalues;
6778
PGresult *results;

0 commit comments

Comments
 (0)