From d7ba2117a5f5cc6ea89c553f604fd4432c657861 Mon Sep 17 00:00:00 2001
From: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
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 <signal.h>
+#ifndef WIN32
+#include <unistd.h>
+#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

