Skip to content

Commit cec0c21

Browse files
committed
Diagnose incompatible OpenLDAP versions during build and test.
With OpenLDAP versions 2.4.24 through 2.4.31, inclusive, PostgreSQL backends can crash at exit. Raise a warning during "configure" based on the compile-time OpenLDAP version number, and test the crash scenario in the dblink test suite. Back-patch to 9.0 (all supported versions).
1 parent f54d97c commit cec0c21

File tree

11 files changed

+244
-1
lines changed

11 files changed

+244
-1
lines changed

configure

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13663,6 +13663,17 @@ fi
1366313663

1366413664
fi
1366513665

13666+
# PGAC_LDAP_SAFE
13667+
# --------------
13668+
# PostgreSQL sometimes loads libldap_r and plain libldap into the same
13669+
# process. Check for OpenLDAP versions known not to tolerate doing so; assume
13670+
# non-OpenLDAP implementations are safe. The dblink test suite exercises the
13671+
# hazardous interaction directly.
13672+
13673+
13674+
13675+
13676+
1366613677
if test "$with_ldap" = yes ; then
1366713678
if test "$PORTNAME" != "win32"; then
1366813679

@@ -13820,6 +13831,72 @@ fi
1382013831

1382113832
done
1382213833

13834+
{ $as_echo "$as_me:$LINENO: checking for compatible LDAP implementation" >&5
13835+
$as_echo_n "checking for compatible LDAP implementation... " >&6; }
13836+
if test "${pgac_cv_ldap_safe+set}" = set; then
13837+
$as_echo_n "(cached) " >&6
13838+
else
13839+
cat >conftest.$ac_ext <<_ACEOF
13840+
/* confdefs.h. */
13841+
_ACEOF
13842+
cat confdefs.h >>conftest.$ac_ext
13843+
cat >>conftest.$ac_ext <<_ACEOF
13844+
/* end confdefs.h. */
13845+
#include <ldap.h>
13846+
#if !defined(LDAP_VENDOR_VERSION) || \
13847+
(defined(LDAP_API_FEATURE_X_OPENLDAP) && \
13848+
LDAP_VENDOR_VERSION >= 20424 && LDAP_VENDOR_VERSION <= 20431)
13849+
choke me
13850+
#endif
13851+
int
13852+
main ()
13853+
{
13854+
13855+
;
13856+
return 0;
13857+
}
13858+
_ACEOF
13859+
rm -f conftest.$ac_objext
13860+
if { (ac_try="$ac_compile"
13861+
case "(($ac_try" in
13862+
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
13863+
*) ac_try_echo=$ac_try;;
13864+
esac
13865+
eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
13866+
$as_echo "$ac_try_echo") >&5
13867+
(eval "$ac_compile") 2>conftest.er1
13868+
ac_status=$?
13869+
grep -v '^ *+' conftest.er1 >conftest.err
13870+
rm -f conftest.er1
13871+
cat conftest.err >&5
13872+
$as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
13873+
(exit $ac_status); } && {
13874+
test -z "$ac_c_werror_flag" ||
13875+
test ! -s conftest.err
13876+
} && test -s conftest.$ac_objext; then
13877+
pgac_cv_ldap_safe=yes
13878+
else
13879+
$as_echo "$as_me: failed program was:" >&5
13880+
sed 's/^/| /' conftest.$ac_ext >&5
13881+
13882+
pgac_cv_ldap_safe=no
13883+
fi
13884+
13885+
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
13886+
fi
13887+
{ $as_echo "$as_me:$LINENO: result: $pgac_cv_ldap_safe" >&5
13888+
$as_echo "$pgac_cv_ldap_safe" >&6; }
13889+
13890+
if test "$pgac_cv_ldap_safe" != yes; then
13891+
{ $as_echo "$as_me:$LINENO: WARNING:
13892+
*** With OpenLDAP versions 2.4.24 through 2.4.31, inclusive, each backend
13893+
*** process that loads libpq (via WAL receiver, dblink, or postgres_fdw) and
13894+
*** also uses LDAP will crash on exit." >&5
13895+
$as_echo "$as_me: WARNING:
13896+
*** With OpenLDAP versions 2.4.24 through 2.4.31, inclusive, each backend
13897+
*** process that loads libpq (via WAL receiver, dblink, or postgres_fdw) and
13898+
*** also uses LDAP will crash on exit." >&2;}
13899+
fi
1382313900
else
1382413901

1382513902
for ac_header in winldap.h

configure.in

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,10 +1067,39 @@ if test "$with_libxslt" = yes ; then
10671067
AC_CHECK_HEADER(libxslt/xslt.h, [], [AC_MSG_ERROR([header file <libxslt/xslt.h> is required for XSLT support])])
10681068
fi
10691069

1070+
# PGAC_LDAP_SAFE
1071+
# --------------
1072+
# PostgreSQL sometimes loads libldap_r and plain libldap into the same
1073+
# process. Check for OpenLDAP versions known not to tolerate doing so; assume
1074+
# non-OpenLDAP implementations are safe. The dblink test suite exercises the
1075+
# hazardous interaction directly.
1076+
1077+
AC_DEFUN([PGAC_LDAP_SAFE],
1078+
[AC_CACHE_CHECK([for compatible LDAP implementation], [pgac_cv_ldap_safe],
1079+
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
1080+
[#include <ldap.h>
1081+
#if !defined(LDAP_VENDOR_VERSION) || \
1082+
(defined(LDAP_API_FEATURE_X_OPENLDAP) && \
1083+
LDAP_VENDOR_VERSION >= 20424 && LDAP_VENDOR_VERSION <= 20431)
1084+
choke me
1085+
#endif], [])],
1086+
[pgac_cv_ldap_safe=yes],
1087+
[pgac_cv_ldap_safe=no])])
1088+
1089+
if test "$pgac_cv_ldap_safe" != yes; then
1090+
AC_MSG_WARN([
1091+
*** With OpenLDAP versions 2.4.24 through 2.4.31, inclusive, each backend
1092+
*** process that loads libpq (via WAL receiver, dblink, or postgres_fdw) and
1093+
*** also uses LDAP will crash on exit.])
1094+
fi])
1095+
1096+
1097+
10701098
if test "$with_ldap" = yes ; then
10711099
if test "$PORTNAME" != "win32"; then
10721100
AC_CHECK_HEADERS(ldap.h, [],
10731101
[AC_MSG_ERROR([header file <ldap.h> is required for LDAP])])
1102+
PGAC_LDAP_SAFE
10741103
else
10751104
AC_CHECK_HEADERS(winldap.h, [],
10761105
[AC_MSG_ERROR([header file <winldap.h> is required for LDAP])],

contrib/dblink/Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ SHLIB_PREREQS = submake-libpq
99
EXTENSION = dblink
1010
DATA = dblink--1.0.sql dblink--unpackaged--1.0.sql
1111

12-
REGRESS = dblink
12+
REGRESS = paths dblink
13+
REGRESS_OPTS = --dlpath=$(top_builddir)/src/test/regress
14+
EXTRA_CLEAN = sql/paths.sql expected/paths.out
1315

1416
# the db name is hard-coded in the tests
1517
override USE_MODULE_DB =

contrib/dblink/expected/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/paths.out

contrib/dblink/expected/dblink.out

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,33 @@ SELECT *
105105
FROM dblink('SELECT * FROM foo') AS t(a int, b text, c text[])
106106
WHERE t.a > 7;
107107
ERROR: connection not available
108+
-- The first-level connection's backend will crash on exit given OpenLDAP
109+
-- [2.4.24, 2.4.31]. We won't see evidence of any crash until the victim
110+
-- process terminates and the postmaster responds. If process termination
111+
-- entails writing a core dump, that can take awhile. Wait for the process to
112+
-- vanish. At that point, the postmaster has called waitpid() on the crashed
113+
-- process, and it will accept no new connections until it has reinitialized
114+
-- the cluster. (We can't exploit pg_stat_activity, because the crash happens
115+
-- after the backend updates shared memory to reflect its impending exit.)
116+
DO $pl$
117+
DECLARE
118+
detail text;
119+
BEGIN
120+
PERFORM wait_pid(crash_pid)
121+
FROM dblink('dbname=contrib_regression', $$
122+
SELECT pg_backend_pid() FROM dblink(
123+
'service=test_ldap dbname=contrib_regression',
124+
-- This string concatenation is a hack to shoehorn a
125+
-- set_pgservicefile call into the SQL statement.
126+
'SELECT 1' || set_pgservicefile('pg_service.conf')
127+
) t(c int)
128+
$$) AS t(crash_pid int);
129+
EXCEPTION WHEN OTHERS THEN
130+
GET STACKED DIAGNOSTICS detail = PG_EXCEPTION_DETAIL;
131+
-- Expected error in a non-LDAP build.
132+
IF NOT detail LIKE 'syntax error in service file%' THEN RAISE; END IF;
133+
END
134+
$pl$;
108135
-- create a persistent connection
109136
SELECT dblink_connect('dbname=contrib_regression');
110137
dblink_connect

contrib/dblink/input/paths.source

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
-- Initialization that requires path substitution.
2+
3+
CREATE FUNCTION putenv(text)
4+
RETURNS void
5+
AS '@libdir@/regress@DLSUFFIX@', 'regress_putenv'
6+
LANGUAGE C STRICT;
7+
8+
CREATE FUNCTION wait_pid(int)
9+
RETURNS void
10+
AS '@libdir@/regress@DLSUFFIX@'
11+
LANGUAGE C STRICT;
12+
13+
CREATE FUNCTION set_pgservicefile(text) RETURNS void LANGUAGE SQL
14+
AS $$SELECT putenv('PGSERVICEFILE=@abs_srcdir@/' || $1)$$;

contrib/dblink/output/paths.source

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-- Initialization that requires path substitution.
2+
CREATE FUNCTION putenv(text)
3+
RETURNS void
4+
AS '@libdir@/regress@DLSUFFIX@', 'regress_putenv'
5+
LANGUAGE C STRICT;
6+
CREATE FUNCTION wait_pid(int)
7+
RETURNS void
8+
AS '@libdir@/regress@DLSUFFIX@'
9+
LANGUAGE C STRICT;
10+
CREATE FUNCTION set_pgservicefile(text) RETURNS void LANGUAGE SQL
11+
AS $$SELECT putenv('PGSERVICEFILE=@abs_srcdir@/' || $1)$$;

contrib/dblink/pg_service.conf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# pg_service.conf for minimally exercising libpq use of LDAP.
2+
3+
# Having failed to reach an LDAP server, libpq essentially ignores the
4+
# "service=test_ldap" in its connection string. Contact the "discard"
5+
# service; the test works whether or not it answers.
6+
[test_ldap]
7+
ldap://127.0.0.1:9/base?attribute?one?filter

contrib/dblink/sql/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/paths.sql

contrib/dblink/sql/dblink.sql

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,34 @@ SELECT *
6565
FROM dblink('SELECT * FROM foo') AS t(a int, b text, c text[])
6666
WHERE t.a > 7;
6767

68+
-- The first-level connection's backend will crash on exit given OpenLDAP
69+
-- [2.4.24, 2.4.31]. We won't see evidence of any crash until the victim
70+
-- process terminates and the postmaster responds. If process termination
71+
-- entails writing a core dump, that can take awhile. Wait for the process to
72+
-- vanish. At that point, the postmaster has called waitpid() on the crashed
73+
-- process, and it will accept no new connections until it has reinitialized
74+
-- the cluster. (We can't exploit pg_stat_activity, because the crash happens
75+
-- after the backend updates shared memory to reflect its impending exit.)
76+
DO $pl$
77+
DECLARE
78+
detail text;
79+
BEGIN
80+
PERFORM wait_pid(crash_pid)
81+
FROM dblink('dbname=contrib_regression', $$
82+
SELECT pg_backend_pid() FROM dblink(
83+
'service=test_ldap dbname=contrib_regression',
84+
-- This string concatenation is a hack to shoehorn a
85+
-- set_pgservicefile call into the SQL statement.
86+
'SELECT 1' || set_pgservicefile('pg_service.conf')
87+
) t(c int)
88+
$$) AS t(crash_pid int);
89+
EXCEPTION WHEN OTHERS THEN
90+
GET STACKED DIAGNOSTICS detail = PG_EXCEPTION_DETAIL;
91+
-- Expected error in a non-LDAP build.
92+
IF NOT detail LIKE 'syntax error in service file%' THEN RAISE; END IF;
93+
END
94+
$pl$;
95+
6896
-- create a persistent connection
6997
SELECT dblink_connect('dbname=contrib_regression');
7098

src/test/regress/regress.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <float.h>
88
#include <math.h>
9+
#include <signal.h>
910

1011
#include "access/transam.h"
1112
#include "access/xact.h"
@@ -14,8 +15,10 @@
1415
#include "commands/trigger.h"
1516
#include "executor/executor.h"
1617
#include "executor/spi.h"
18+
#include "miscadmin.h"
1719
#include "utils/builtins.h"
1820
#include "utils/geo_decls.h"
21+
#include "utils/memutils.h"
1922
#include "utils/rel.h"
2023

2124

@@ -35,6 +38,8 @@ extern char *reverse_name(char *string);
3538
extern int oldstyle_length(int n, text *t);
3639
extern Datum int44in(PG_FUNCTION_ARGS);
3740
extern Datum int44out(PG_FUNCTION_ARGS);
41+
extern Datum regress_putenv(PG_FUNCTION_ARGS);
42+
extern Datum wait_pid(PG_FUNCTION_ARGS);
3843

3944
#ifdef PG_MODULE_MAGIC
4045
PG_MODULE_MAGIC;
@@ -737,3 +742,44 @@ int44out(PG_FUNCTION_ARGS)
737742
*--walk = '\0';
738743
PG_RETURN_CSTRING(result);
739744
}
745+
746+
PG_FUNCTION_INFO_V1(regress_putenv);
747+
748+
Datum
749+
regress_putenv(PG_FUNCTION_ARGS)
750+
{
751+
MemoryContext oldcontext;
752+
char *envbuf;
753+
754+
if (!superuser())
755+
elog(ERROR, "must be superuser to change environment variables");
756+
757+
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
758+
envbuf = text_to_cstring((text *) PG_GETARG_POINTER(0));
759+
MemoryContextSwitchTo(oldcontext);
760+
761+
if (putenv(envbuf) != 0)
762+
elog(ERROR, "could not set environment variable: %m");
763+
764+
PG_RETURN_VOID();
765+
}
766+
767+
/* Sleep until no process has a given PID. */
768+
PG_FUNCTION_INFO_V1(wait_pid);
769+
770+
Datum
771+
wait_pid(PG_FUNCTION_ARGS)
772+
{
773+
int pid = PG_GETARG_INT32(0);
774+
775+
if (!superuser())
776+
elog(ERROR, "must be superuser to check PID liveness");
777+
778+
while (kill(pid, 0) == 0)
779+
pg_usleep(50000);
780+
781+
if (errno != ESRCH)
782+
elog(ERROR, "could not check PID %d liveness: %m", pid);
783+
784+
PG_RETURN_VOID();
785+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy