summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNoah Misch <noah@leadboat.com>2014-07-22 11:01:03 -0400
committerNoah Misch <noah@leadboat.com>2014-07-22 11:01:35 -0400
commitfd18965e33da9dc7326803e61cf28d82237e3bba (patch)
tree34618b2267224ef66ee35118d8ac90926a9b6adf
parent87c4232fd35fc3d1ff06189e886bffb382f6ca87 (diff)
downloadpostgresql-fd18965e33da9dc7326803e61cf28d82237e3bba.tar.gz
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).
-rwxr-xr-xconfigure52
-rw-r--r--configure.in29
-rw-r--r--contrib/dblink/Makefile4
-rw-r--r--contrib/dblink/expected/.gitignore1
-rw-r--r--contrib/dblink/expected/dblink.out27
-rw-r--r--contrib/dblink/input/paths.source14
-rw-r--r--contrib/dblink/output/paths.source11
-rw-r--r--contrib/dblink/pg_service.conf7
-rw-r--r--contrib/dblink/sql/.gitignore1
-rw-r--r--contrib/dblink/sql/dblink.sql28
-rw-r--r--src/test/regress/regress.c43
11 files changed, 216 insertions, 1 deletions
diff --git a/configure b/configure
index b3cd7063d9..6b3cd1711e 100755
--- a/configure
+++ b/configure
@@ -9477,6 +9477,17 @@ fi
fi
+# PGAC_LDAP_SAFE
+# --------------
+# PostgreSQL sometimes loads libldap_r and plain libldap into the same
+# process. Check for OpenLDAP versions known not to tolerate doing so; assume
+# non-OpenLDAP implementations are safe. The dblink test suite exercises the
+# hazardous interaction directly.
+
+
+
+
+
if test "$with_ldap" = yes ; then
if test "$PORTNAME" != "win32"; then
for ac_header in ldap.h
@@ -9493,6 +9504,47 @@ fi
done
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for compatible LDAP implementation" >&5
+$as_echo_n "checking for compatible LDAP implementation... " >&6; }
+if ${pgac_cv_ldap_safe+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ldap.h>
+#if !defined(LDAP_VENDOR_VERSION) || \
+ (defined(LDAP_API_FEATURE_X_OPENLDAP) && \
+ LDAP_VENDOR_VERSION >= 20424 && LDAP_VENDOR_VERSION <= 20431)
+choke me
+#endif
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ pgac_cv_ldap_safe=yes
+else
+ pgac_cv_ldap_safe=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_ldap_safe" >&5
+$as_echo "$pgac_cv_ldap_safe" >&6; }
+
+if test "$pgac_cv_ldap_safe" != yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
+*** With OpenLDAP versions 2.4.24 through 2.4.31, inclusive, each backend
+*** process that loads libpq (via WAL receiver, dblink, or postgres_fdw) and
+*** also uses LDAP will crash on exit." >&5
+$as_echo "$as_me: WARNING:
+*** With OpenLDAP versions 2.4.24 through 2.4.31, inclusive, each backend
+*** process that loads libpq (via WAL receiver, dblink, or postgres_fdw) and
+*** also uses LDAP will crash on exit." >&2;}
+fi
else
for ac_header in winldap.h
do :
diff --git a/configure.in b/configure.in
index b17faa9b94..27e2032e1e 100644
--- a/configure.in
+++ b/configure.in
@@ -1098,10 +1098,39 @@ if test "$with_libxslt" = yes ; then
AC_CHECK_HEADER(libxslt/xslt.h, [], [AC_MSG_ERROR([header file <libxslt/xslt.h> is required for XSLT support])])
fi
+# PGAC_LDAP_SAFE
+# --------------
+# PostgreSQL sometimes loads libldap_r and plain libldap into the same
+# process. Check for OpenLDAP versions known not to tolerate doing so; assume
+# non-OpenLDAP implementations are safe. The dblink test suite exercises the
+# hazardous interaction directly.
+
+AC_DEFUN([PGAC_LDAP_SAFE],
+[AC_CACHE_CHECK([for compatible LDAP implementation], [pgac_cv_ldap_safe],
+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[#include <ldap.h>
+#if !defined(LDAP_VENDOR_VERSION) || \
+ (defined(LDAP_API_FEATURE_X_OPENLDAP) && \
+ LDAP_VENDOR_VERSION >= 20424 && LDAP_VENDOR_VERSION <= 20431)
+choke me
+#endif], [])],
+[pgac_cv_ldap_safe=yes],
+[pgac_cv_ldap_safe=no])])
+
+if test "$pgac_cv_ldap_safe" != yes; then
+ AC_MSG_WARN([
+*** With OpenLDAP versions 2.4.24 through 2.4.31, inclusive, each backend
+*** process that loads libpq (via WAL receiver, dblink, or postgres_fdw) and
+*** also uses LDAP will crash on exit.])
+fi])
+
+
+
if test "$with_ldap" = yes ; then
if test "$PORTNAME" != "win32"; then
AC_CHECK_HEADERS(ldap.h, [],
[AC_MSG_ERROR([header file <ldap.h> is required for LDAP])])
+ PGAC_LDAP_SAFE
else
AC_CHECK_HEADERS(winldap.h, [],
[AC_MSG_ERROR([header file <winldap.h> is required for LDAP])],
diff --git a/contrib/dblink/Makefile b/contrib/dblink/Makefile
index 32314a0abb..09032f8970 100644
--- a/contrib/dblink/Makefile
+++ b/contrib/dblink/Makefile
@@ -9,7 +9,9 @@ SHLIB_PREREQS = submake-libpq
EXTENSION = dblink
DATA = dblink--1.1.sql dblink--1.0--1.1.sql dblink--unpackaged--1.0.sql
-REGRESS = dblink
+REGRESS = paths dblink
+REGRESS_OPTS = --dlpath=$(top_builddir)/src/test/regress
+EXTRA_CLEAN = sql/paths.sql expected/paths.out
# the db name is hard-coded in the tests
override USE_MODULE_DB =
diff --git a/contrib/dblink/expected/.gitignore b/contrib/dblink/expected/.gitignore
new file mode 100644
index 0000000000..d9c7942c64
--- /dev/null
+++ b/contrib/dblink/expected/.gitignore
@@ -0,0 +1 @@
+/paths.out
diff --git a/contrib/dblink/expected/dblink.out b/contrib/dblink/expected/dblink.out
index f237c43d3d..f503691bf2 100644
--- a/contrib/dblink/expected/dblink.out
+++ b/contrib/dblink/expected/dblink.out
@@ -103,6 +103,33 @@ SELECT *
FROM dblink('SELECT * FROM foo') AS t(a int, b text, c text[])
WHERE t.a > 7;
ERROR: connection not available
+-- The first-level connection's backend will crash on exit given OpenLDAP
+-- [2.4.24, 2.4.31]. We won't see evidence of any crash until the victim
+-- process terminates and the postmaster responds. If process termination
+-- entails writing a core dump, that can take awhile. Wait for the process to
+-- vanish. At that point, the postmaster has called waitpid() on the crashed
+-- process, and it will accept no new connections until it has reinitialized
+-- the cluster. (We can't exploit pg_stat_activity, because the crash happens
+-- after the backend updates shared memory to reflect its impending exit.)
+DO $pl$
+DECLARE
+ detail text;
+BEGIN
+ PERFORM wait_pid(crash_pid)
+ FROM dblink('dbname=contrib_regression', $$
+ SELECT pg_backend_pid() FROM dblink(
+ 'service=test_ldap dbname=contrib_regression',
+ -- This string concatenation is a hack to shoehorn a
+ -- set_pgservicefile call into the SQL statement.
+ 'SELECT 1' || set_pgservicefile('pg_service.conf')
+ ) t(c int)
+ $$) AS t(crash_pid int);
+EXCEPTION WHEN OTHERS THEN
+ GET STACKED DIAGNOSTICS detail = PG_EXCEPTION_DETAIL;
+ -- Expected error in a non-LDAP build.
+ IF NOT detail LIKE 'syntax error in service file%' THEN RAISE; END IF;
+END
+$pl$;
-- create a persistent connection
SELECT dblink_connect('dbname=contrib_regression');
dblink_connect
diff --git a/contrib/dblink/input/paths.source b/contrib/dblink/input/paths.source
new file mode 100644
index 0000000000..aab3a3b2bf
--- /dev/null
+++ b/contrib/dblink/input/paths.source
@@ -0,0 +1,14 @@
+-- Initialization that requires path substitution.
+
+CREATE FUNCTION putenv(text)
+ RETURNS void
+ AS '@libdir@/regress@DLSUFFIX@', 'regress_putenv'
+ LANGUAGE C STRICT;
+
+CREATE FUNCTION wait_pid(int)
+ RETURNS void
+ AS '@libdir@/regress@DLSUFFIX@'
+ LANGUAGE C STRICT;
+
+CREATE FUNCTION set_pgservicefile(text) RETURNS void LANGUAGE SQL
+ AS $$SELECT putenv('PGSERVICEFILE=@abs_srcdir@/' || $1)$$;
diff --git a/contrib/dblink/output/paths.source b/contrib/dblink/output/paths.source
new file mode 100644
index 0000000000..e1097f0996
--- /dev/null
+++ b/contrib/dblink/output/paths.source
@@ -0,0 +1,11 @@
+-- Initialization that requires path substitution.
+CREATE FUNCTION putenv(text)
+ RETURNS void
+ AS '@libdir@/regress@DLSUFFIX@', 'regress_putenv'
+ LANGUAGE C STRICT;
+CREATE FUNCTION wait_pid(int)
+ RETURNS void
+ AS '@libdir@/regress@DLSUFFIX@'
+ LANGUAGE C STRICT;
+CREATE FUNCTION set_pgservicefile(text) RETURNS void LANGUAGE SQL
+ AS $$SELECT putenv('PGSERVICEFILE=@abs_srcdir@/' || $1)$$;
diff --git a/contrib/dblink/pg_service.conf b/contrib/dblink/pg_service.conf
new file mode 100644
index 0000000000..92201f0ad4
--- /dev/null
+++ b/contrib/dblink/pg_service.conf
@@ -0,0 +1,7 @@
+# pg_service.conf for minimally exercising libpq use of LDAP.
+
+# Having failed to reach an LDAP server, libpq essentially ignores the
+# "service=test_ldap" in its connection string. Contact the "discard"
+# service; the test works whether or not it answers.
+[test_ldap]
+ldap://127.0.0.1:9/base?attribute?one?filter
diff --git a/contrib/dblink/sql/.gitignore b/contrib/dblink/sql/.gitignore
new file mode 100644
index 0000000000..d17507846d
--- /dev/null
+++ b/contrib/dblink/sql/.gitignore
@@ -0,0 +1 @@
+/paths.sql
diff --git a/contrib/dblink/sql/dblink.sql b/contrib/dblink/sql/dblink.sql
index 2a107601c5..d8d248260c 100644
--- a/contrib/dblink/sql/dblink.sql
+++ b/contrib/dblink/sql/dblink.sql
@@ -65,6 +65,34 @@ SELECT *
FROM dblink('SELECT * FROM foo') AS t(a int, b text, c text[])
WHERE t.a > 7;
+-- The first-level connection's backend will crash on exit given OpenLDAP
+-- [2.4.24, 2.4.31]. We won't see evidence of any crash until the victim
+-- process terminates and the postmaster responds. If process termination
+-- entails writing a core dump, that can take awhile. Wait for the process to
+-- vanish. At that point, the postmaster has called waitpid() on the crashed
+-- process, and it will accept no new connections until it has reinitialized
+-- the cluster. (We can't exploit pg_stat_activity, because the crash happens
+-- after the backend updates shared memory to reflect its impending exit.)
+DO $pl$
+DECLARE
+ detail text;
+BEGIN
+ PERFORM wait_pid(crash_pid)
+ FROM dblink('dbname=contrib_regression', $$
+ SELECT pg_backend_pid() FROM dblink(
+ 'service=test_ldap dbname=contrib_regression',
+ -- This string concatenation is a hack to shoehorn a
+ -- set_pgservicefile call into the SQL statement.
+ 'SELECT 1' || set_pgservicefile('pg_service.conf')
+ ) t(c int)
+ $$) AS t(crash_pid int);
+EXCEPTION WHEN OTHERS THEN
+ GET STACKED DIAGNOSTICS detail = PG_EXCEPTION_DETAIL;
+ -- Expected error in a non-LDAP build.
+ IF NOT detail LIKE 'syntax error in service file%' THEN RAISE; END IF;
+END
+$pl$;
+
-- create a persistent connection
SELECT dblink_connect('dbname=contrib_regression');
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index c1461092fb..09e027c1e5 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -6,6 +6,7 @@
#include <float.h>
#include <math.h>
+#include <signal.h>
#include "access/htup_details.h"
#include "access/transam.h"
@@ -16,6 +17,7 @@
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/spi.h"
+#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/geo_decls.h"
#include "utils/rel.h"
@@ -822,3 +824,44 @@ make_tuple_indirect(PG_FUNCTION_ARGS)
*/
PG_RETURN_POINTER(newtup->t_data);
}
+
+PG_FUNCTION_INFO_V1(regress_putenv);
+
+Datum
+regress_putenv(PG_FUNCTION_ARGS)
+{
+ MemoryContext oldcontext;
+ char *envbuf;
+
+ if (!superuser())
+ elog(ERROR, "must be superuser to change environment variables");
+
+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+ envbuf = text_to_cstring((text *) PG_GETARG_POINTER(0));
+ MemoryContextSwitchTo(oldcontext);
+
+ if (putenv(envbuf) != 0)
+ elog(ERROR, "could not set environment variable: %m");
+
+ PG_RETURN_VOID();
+}
+
+/* Sleep until no process has a given PID. */
+PG_FUNCTION_INFO_V1(wait_pid);
+
+Datum
+wait_pid(PG_FUNCTION_ARGS)
+{
+ int pid = PG_GETARG_INT32(0);
+
+ if (!superuser())
+ elog(ERROR, "must be superuser to check PID liveness");
+
+ while (kill(pid, 0) == 0)
+ pg_usleep(50000);
+
+ if (errno != ESRCH)
+ elog(ERROR, "could not check PID %d liveness: %m", pid);
+
+ PG_RETURN_VOID();
+}