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).
This commit is contained in:
Noah Misch 2014-07-22 11:01:03 -04:00
parent 24e786f056
commit d7cdf6ee36
11 changed files with 216 additions and 1 deletions

52
configure vendored
View File

@ -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 :

View File

@ -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])],

View File

@ -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 =

1
contrib/dblink/expected/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/paths.out

View File

@ -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

View File

@ -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)$$;

View File

@ -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)$$;

View File

@ -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

1
contrib/dblink/sql/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/paths.sql

View File

@ -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');

View File

@ -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();
}