099_stack_overflow.pl
application/x-perl
Filename: 099_stack_overflow.pl
Type: application/x-perl
Part: 0
# Copyright (c) 2026, PostgreSQL Global Development Group
# Test if stack overflow with SIGSEGV happens under memory pressure
use strict;
use warnings FATAL => 'all';
use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;
use Config;
if ($windows_os)
{
plan skip_all => 'preallocate_stack is not supported on Windows';
}
if ($Config{osname} eq 'darwin')
{
plan skip_all => 'ulimit fails on macOS with "virtual memory: cannot modify limit: Invalid argument"';
}
# Initialize a test cluster
my $node = PostgreSQL::Test::Cluster->new('primary');
$node->init();
sub test_server
{
my $node = shift;
my $mem = shift;
diag("Testing ulimit -Sv $mem");
my $data_dir = $node->data_dir;
my $logfile = $node->logfile;
local %ENV = $node->_get_env(PGAPPNAME => undef);
my $res = system_log("ulimit -Sv $mem; pg_ctl -w -D $data_dir -l $logfile start");
if ($res != 0)
{
print "# pg_ctl start failed; see logfile for details: $logfile\n";
BAIL_OUT("pg_ctl start failed");
}
$node->_update_pid(1);
$node->safe_psql('postgres', 'DROP DATABASE IF EXISTS testdb');
$node->safe_psql('postgres', 'CREATE DATABASE testdb');
my ($ret, $out, $err) = $node->psql(
'testdb',
q[
create function explainer(text) returns setof text
language plpgsql as
$$
declare
ln text;
begin
for ln in execute format('explain analyze %s', $1)
loop
return next ln;
end loop;
end;
$$;
prepare stmt as select explainer('execute stmt');
select explainer('execute stmt');]
);
$node->stop;
return $err;
}
my $lb = 280000;
my $ub = 2000000;
my $v;
for ($v = $lb; $lb < $ub - 1;)
{
my $err = test_server($node, $v);
if ($err =~ /ERROR: (stack depth limit exceeded)/)
{
diag("$1");
$ub = $v;
$v = $v - int(($v - $lb)/ 2);
}
elsif ($err =~ /ERROR: (out of memory)/)
{
diag("$1");
$lb = $v;
$v = $v + int(($ub - $v) / 2);
}
else
{
diag($err);
BAIL_OUT("unexpected error occurred");
}
}
diag("Boundary between 'out of memory' and 'stack depth limit exceeded' found: $v");
# More precise check below the boundary found
for (my $i = 1; $i < 32; $i++)
{
my $err = test_server($node, $v - $i * 1024);
if ($err =~ /ERROR: (stack depth limit exceeded|out of memory)/)
{
diag($1);
}
else
{
diag($err);
BAIL_OUT("unexpected error occurred");
}
}
ok(1, 'OK');
done_testing();