Commit d7cdf6ee authored by Noah Misch's avatar Noah Misch

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).
parent 24e786f0
......@@ -9475,6 +9475,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
......@@ -9491,6 +9502,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 :
......
......@@ -1097,10 +1097,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])],
......
......@@ -10,7 +10,9 @@ EXTENSION = dblink
DATA = dblink--1.1.sql dblink--1.0--1.1.sql dblink--unpackaged--1.0.sql
PGFILEDESC = "dblink - connect to other PostgreSQL databases"
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 =
......
......@@ -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
......
-- 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)$$;
-- 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)$$;
# 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
......@@ -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');
......
......@@ -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();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment