From d7ba2117a5f5cc6ea89c553f604fd4432c657861 Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Fri, 2 Oct 2026 09:06:30 +0000 Subject: [PATCH v4 3/3] Test PANIC core-dump suppression Use one SQL helper to raise an ERROR in a critical section and intercept SIGABRT with a distinctive exit code, avoiding actual core files. Check disk-full suppression, the default behavior for another errno, and the developer override. Use worker I/O for the crash-recovery cycles. --- .../injection_points--1.0.sql | 6 +++ .../injection_points/injection_points.c | 45 ++++++++++++++++ src/test/modules/test_misc/meson.build | 1 + .../modules/test_misc/t/016_panic_no_core.pl | 53 +++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 src/test/modules/test_misc/t/016_panic_no_core.pl diff --git a/src/test/modules/injection_points/injection_points--1.0.sql b/src/test/modules/injection_points/injection_points--1.0.sql index 2efb307f5bf..94493294c43 100644 --- a/src/test/modules/injection_points/injection_points--1.0.sql +++ b/src/test/modules/injection_points/injection_points--1.0.sql @@ -48,6 +48,12 @@ RETURNS void AS 'MODULE_PATHNAME', 'injection_points_run' LANGUAGE C PARALLEL UNSAFE; +-- Raise an ERROR in a critical section, intercepting abort(). +CREATE FUNCTION injection_points_panic(IN disk_full boolean) +RETURNS void +AS 'MODULE_PATHNAME', 'injection_points_panic' +LANGUAGE C STRICT PARALLEL UNSAFE; + -- -- injection_points_cached() -- diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c index 5e1bbc2f5c9..d9f6b4dd64c 100644 --- a/src/test/modules/injection_points/injection_points.c +++ b/src/test/modules/injection_points/injection_points.c @@ -17,6 +17,11 @@ #include "postgres.h" +#include +#ifndef WIN32 +#include +#endif + #include "fmgr.h" #include "funcapi.h" #include "injection_points.h" @@ -87,12 +92,27 @@ static bool injection_point_local = false; static void injection_shmem_request(void *arg); static void injection_shmem_init(void *arg); +#ifdef WIN32 +static void injection_abort_handler(int signal); +#else +static void injection_abort_handler(SIGNAL_ARGS); +#endif static const ShmemCallbacks injection_shmem_callbacks = { .request_fn = injection_shmem_request, .init_fn = injection_shmem_init, }; +static void +#ifdef WIN32 +injection_abort_handler(int signal) +#else +injection_abort_handler(SIGNAL_ARGS) +#endif +{ + _exit(42); +} + /* * Routine for shared memory area initialization, used as a callback * when initializing dynamically with a DSM or when loading the module. @@ -455,6 +475,31 @@ injection_points_run(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } +/* Make abort() observable without producing a core file. */ +PG_FUNCTION_INFO_V1(injection_points_panic); +Datum +injection_points_panic(PG_FUNCTION_ARGS) +{ + int errnum = PG_GETARG_BOOL(0) ? ENOSPC : EIO; + +#ifdef WIN32 + if (signal(SIGABRT, injection_abort_handler) == SIG_ERR) + elog(ERROR, "could not install SIGABRT handler"); +#else + pqsignal(SIGABRT, injection_abort_handler); +#endif + + START_CRIT_SECTION(); + errno = errnum; + ereport(ERROR, + (errcode_for_file_access(), + errnocoredump_on_errno(ENOSPC), + errmsg("PANIC requested by test"))); + END_CRIT_SECTION(); + + PG_RETURN_VOID(); +} + /* * SQL function for triggering an injection point from cache. */ diff --git a/src/test/modules/test_misc/meson.build b/src/test/modules/test_misc/meson.build index 5d81f5b13be..4c520a339f8 100644 --- a/src/test/modules/test_misc/meson.build +++ b/src/test/modules/test_misc/meson.build @@ -24,6 +24,7 @@ tests += { 't/013_temp_obj_multisession.pl', 't/014_log_statement_max_length.pl', 't/015_temp_schema_exit_deferrable.pl', + 't/016_panic_no_core.pl', ], # The injection points are cluster-wide, so disable installcheck 'runningcheck': false, diff --git a/src/test/modules/test_misc/t/016_panic_no_core.pl b/src/test/modules/test_misc/t/016_panic_no_core.pl new file mode 100644 index 00000000000..8250782f186 --- /dev/null +++ b/src/test/modules/test_misc/t/016_panic_no_core.pl @@ -0,0 +1,53 @@ +# Copyright (c) 2026, PostgreSQL Global Development Group + +use strict; +use warnings FATAL => 'all'; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +plan skip_all => 'Injection points not supported by this build' + unless $ENV{enable_injection_points} eq 'yes'; + +my $node = PostgreSQL::Test::Cluster->new('panic_no_core'); +$node->init; +# Avoid exhausting io_uring resources across repeated crash recovery. +$node->append_conf( + 'postgresql.conf', qq{ +restart_after_crash = on +log_min_messages = warning +io_method = worker +}); +$node->start; + +$node->safe_psql('postgres', 'CREATE EXTENSION injection_points'); + +sub check_panic +{ + my ($disk_full, $exit_code, $description) = @_; + + my $log_offset = -s $node->logfile; + $node->psql( + 'postgres', "SELECT injection_points_panic($disk_full)"); + + $node->wait_for_log(qr/PANIC:.*PANIC requested by test/, $log_offset); + $node->wait_for_log(qr/\(PID \d+\) exited with exit code $exit_code/, + $log_offset); + ok($node->poll_query_until('postgres', undef, ''), $description); +} + +check_panic('true', 2, 'disk-full PANIC exits without abort'); +check_panic('false', 42, 'other PANIC errors still abort'); + +$node->safe_psql('postgres', + 'ALTER SYSTEM SET force_core_dump_on_panic = on'); +$node->safe_psql('postgres', 'SELECT pg_reload_conf()'); +ok( $node->poll_query_until( + 'postgres', + q{SELECT current_setting('force_core_dump_on_panic') = 'on'}), + 'core dump override is active'); + +check_panic('true', 42, 'override forces abort for disk-full PANIC'); + +$node->stop; +done_testing(); -- 2.43.0