From e893d2e53a9bde6ddf4462d9f8b206c51ebd40cb Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Mon, 24 Jun 2024 18:15:53 +0300 Subject: [PATCH 3/4] WIP: Add injection points and test for early backend startup errors --- src/backend/tcop/backend_startup.c | 9 ++++ src/backend/utils/misc/injection_point.c | 33 +++++++++++- src/include/utils/injection_point.h | 1 + src/interfaces/libpq/meson.build | 1 + .../libpq/t/005_negotiate_encryption.pl | 50 +++++++++++++++++++ 5 files changed, 92 insertions(+), 2 deletions(-) diff --git a/src/backend/tcop/backend_startup.c b/src/backend/tcop/backend_startup.c index cfa27551964..7b4a3e23458 100644 --- a/src/backend/tcop/backend_startup.c +++ b/src/backend/tcop/backend_startup.c @@ -33,6 +33,7 @@ #include "tcop/backend_startup.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" +#include "utils/injection_point.h" #include "utils/memutils.h" #include "utils/ps_status.h" #include "utils/timeout.h" @@ -160,6 +161,14 @@ BackendInitialize(ClientSocket *client_sock, CAC_state cac) whereToSendOutput = DestRemote; /* now safe to ereport to client */ + /* For testing client error handling */ + INJECTION_POINT("backend-initialize"); + if (IsInjectionPointAttached("backend-initialize-v2-error")) + { + FrontendProtocol = PG_PROTOCOL(2,0); + elog(FATAL, "protocol version 2 error triggered"); + } + /* set these to empty in case they are needed before we set them up */ port->remote_host = ""; port->remote_port = ""; diff --git a/src/backend/utils/misc/injection_point.c b/src/backend/utils/misc/injection_point.c index 5c2a0d2297e..d3995dfd6fc 100644 --- a/src/backend/utils/misc/injection_point.c +++ b/src/backend/utils/misc/injection_point.c @@ -24,6 +24,7 @@ #include "port/pg_bitutils.h" #include "storage/fd.h" #include "storage/lwlock.h" +#include "storage/proc.h" #include "storage/shmem.h" #include "utils/hsearch.h" #include "utils/injection_point.h" @@ -281,6 +282,31 @@ InjectionPointDetach(const char *name) #endif } +/* + * Test if an injection point is defined. + */ +bool +IsInjectionPointAttached(const char *name) +{ +#ifdef USE_INJECTION_POINTS + bool found; + + /* FIXME: just skip locking if we don't have a PGPROC entry yet. Obviously unsafe.. */ + if (MyProc != NULL && IsUnderPostmaster) + LWLockAcquire(InjectionPointLock, LW_SHARED); + + (void) hash_search(InjectionPointHash, name, HASH_FIND, &found); + + if (MyProc != NULL && IsUnderPostmaster) + LWLockRelease(InjectionPointLock); + + return found; +#else + elog(ERROR, "Injection points are not supported by this build"); + return false; /* silence compiler */ +#endif +} + /* * Execute an injection point, if defined. * @@ -296,11 +322,14 @@ InjectionPointRun(const char *name) InjectionPointCallback injection_callback; const void *private_data; - LWLockAcquire(InjectionPointLock, LW_SHARED); + /* FIXME: just skip locking if we don't have a PGPROC entry yet. Obviously unsafe.. */ + if (MyProc != NULL && IsUnderPostmaster) + LWLockAcquire(InjectionPointLock, LW_SHARED); entry_by_name = (InjectionPointEntry *) hash_search(InjectionPointHash, name, HASH_FIND, &found); - LWLockRelease(InjectionPointLock); + if (MyProc != NULL && IsUnderPostmaster) + LWLockRelease(InjectionPointLock); /* * If not found, do nothing and remove it from the local cache if it diff --git a/src/include/utils/injection_point.h b/src/include/utils/injection_point.h index a61d5d44391..5715eefb31e 100644 --- a/src/include/utils/injection_point.h +++ b/src/include/utils/injection_point.h @@ -35,6 +35,7 @@ extern void InjectionPointAttach(const char *name, const void *private_data, int private_data_size); extern void InjectionPointRun(const char *name); +extern bool IsInjectionPointAttached(const char *name); extern bool InjectionPointDetach(const char *name); #endif /* INJECTION_POINT_H */ diff --git a/src/interfaces/libpq/meson.build b/src/interfaces/libpq/meson.build index ed2a4048d18..7623aeadab7 100644 --- a/src/interfaces/libpq/meson.build +++ b/src/interfaces/libpq/meson.build @@ -121,6 +121,7 @@ tests += { 't/005_negotiate_encryption.pl', ], 'env': { + 'enable_injection_points': get_option('injection_points') ? 'yes' : 'no', 'with_ssl': ssl_library, 'with_gssapi': gssapi.found() ? 'yes' : 'no', 'with_krb_srvnam': 'postgres', diff --git a/src/interfaces/libpq/t/005_negotiate_encryption.pl b/src/interfaces/libpq/t/005_negotiate_encryption.pl index c3f70d31bc8..e6bea0f04f8 100644 --- a/src/interfaces/libpq/t/005_negotiate_encryption.pl +++ b/src/interfaces/libpq/t/005_negotiate_encryption.pl @@ -90,6 +90,8 @@ my $kerberos_enabled = $ENV{PG_TEST_EXTRA} && $ENV{PG_TEST_EXTRA} =~ /\bkerberos\b/; my $ssl_supported = $ENV{with_ssl} eq 'openssl'; +my $injection_points_supported = $ENV{enable_injection_points} eq 'yes'; + ### ### Prepare test server for GSSAPI and SSL authentication, with a few ### different test users and helper functions. We don't actually @@ -155,6 +157,10 @@ $node->safe_psql('postgres', 'CREATE USER ssluser;'); $node->safe_psql('postgres', 'CREATE USER nossluser;'); $node->safe_psql('postgres', 'CREATE USER gssuser;'); $node->safe_psql('postgres', 'CREATE USER nogssuser;'); +if ($injection_points_supported != 0) +{ + $node->safe_psql('postgres', 'CREATE EXTENSION injection_points;') +} my $unixdir = $node->safe_psql('postgres', 'SHOW unix_socket_directories;'); chomp($unixdir); @@ -320,6 +326,27 @@ nossluser . disable postgres connect, authok \@all_sslmodes, \@all_sslnegotiations, parse_table($test_table)); + if ($injection_points_supported != 0) + { + $node->safe_psql('postgres', + "SELECT injection_points_attach('backend-initialize', 'error');", + connstr => "user=localuser host=$unixdir"); + connect_test( + $node, + "user=testuser sslmode=prefer", + 'backenderror -> fail'); + $node->restart; + + $node->safe_psql('postgres', + "SELECT injection_points_attach('backend-initialize-v2-error', 'error');", + connstr => "user=localuser host=$unixdir"); + connect_test( + $node, + "user=testuser sslmode=prefer", + 'v2error -> fail'); + $node->restart; + } + # Disable SSL again $node->adjust_conf('postgresql.conf', 'ssl', 'off'); $node->reload; @@ -403,6 +430,27 @@ nogssuser disable disable postgres connect, authok test_matrix($node, $server_config, [ 'testuser', 'gssuser', 'nogssuser' ], \@all_gssencmodes, $sslmodes, $sslnegotiations, parse_table($test_table)); + + if ($injection_points_supported != 0) + { + $node->safe_psql('postgres', + "SELECT injection_points_attach('backend-initialize', 'error');", + connstr => "user=localuser host=$unixdir"); + connect_test( + $node, + "user=testuser gssencmode=prefer sslmode=disable", + 'backenderror, backenderror -> fail'); + $node->restart; + + $node->safe_psql('postgres', + "SELECT injection_points_attach('backend-initialize-v2-error', 'error');", + connstr => "user=localuser host=$unixdir"); + connect_test( + $node, + "user=testuser gssencmode=prefer sslmode=disable", + 'v2error -> fail'); + $node->restart; + } } ### @@ -750,6 +798,8 @@ sub parse_log_events push @events, "gssreject" if $line =~ /GSSENCRequest rejected/; push @events, "authfail" if $line =~ /no pg_hba.conf entry/; push @events, "authok" if $line =~ /connection authenticated/; + push @events, "backenderror" if $line =~ /error triggered for injection point backend-/; + push @events, "v2error" if $line =~ /protocol version 2 error triggered/; } # No events at all is represented by "-" -- 2.39.2