Skip to content

SIGSEGV on permission-denied SECURITY DEFINER call under SET LOCAL ROLE (17.6.1.106) #2367

Description

@hhtransportationtx

Summary

On ghcr.io/supabase/postgres:17.6.1.106, a DO block that does SET LOCAL ROLE
to an unprivileged role and then EXECUTEs a call to a SECURITY DEFINER
PL/pgSQL function that role cannot execute, with all-NULL arguments,
terminates the backend with SIGSEGV instead of raising insufficient_privilege
(42501).

The postmaster then terminates every other backend and enters recovery, so all
concurrent work on the server is lost.

This does not reproduce on upstream PostgreSQL 17.10, nor on Supabase's
managed PostgreSQL 17.6.

Reproduction

create role lowpriv nologin;
create table t_events(a text, b text, c text, d uuid, e text, f timestamptz, g text);
create table t_rows(id text, status text);

create or replace function public.f_definer(
  p1 text, p2 text, p3 text, p4 uuid, p5 text, p6 timestamptz, p7 text)
returns void language plpgsql security definer set search_path = public as $$
declare v text;
begin
  if exists (select 1 from public.t_events where b = p2 and c = p3) then return; end if;
  select status into v from public.t_rows where id = p2;
  insert into public.t_events(a,b,c,d,e,f,g)
  values (p1,p2,p3,p4,coalesce(p5,'x'),coalesce(p6,now()),coalesce(p7,'y'));
end $$;

revoke all on function public.f_definer(text,text,text,uuid,text,timestamptz,text)
  from public, lowpriv;

begin;
do $$
begin
  set local role lowpriv;
  begin
    execute 'select public.f_definer(null,null,null,null,null,null,null)';
    reset role;
  exception
    when insufficient_privilege then reset role;
    when others then reset role;
  end;
end $$;
rollback;

Expected: insufficient_privilege is caught; transaction rolls back.
Actual: backend segfaults.

Server log

LOG:  server process (PID 283) was terminated by signal 11: Segmentation fault
LOG:  terminating any other active server processes
LOG:  all server processes terminated; reinitializing
LOG:  database system was not properly shut down; automatic recovery in progress
FATAL:  the database system is in recovery mode

Environment matrix

Environment Version Build / arch Transport Result
ghcr.io/supabase/postgres:17.6.1.106 17.6 GCC 15.2.0 · x86_64 psql SEGFAULT
Supabase managed 17.6 GCC 15.2.0 · aarch64 Management API survives (42501)
Upstream Homebrew 17.10 Apple clang 21 · aarch64 psql survives (42501)

Ruled out

  • Extensions. pg_net 0.20.0 and pg_graphql 1.5.11 were dropped so the set
    matched the managed instance exactly (btree_gist, pg_stat_statements,
    pgcrypto, plpgsql, supabase_vault, uuid-ossp). Still crashes.
  • Test framework. The script above runs standalone under psql.
  • Transport. Upstream 17.10 survives the identical sequence over the same
    psql connection.

Not isolated

Architecture (x86_64 vs aarch64), patch version (17.6 vs 17.10), and the
Supabase build vs upstream remain confounded — separating them needs an aarch64
build of the same image, or an x86_64 upstream 17.6.

Impact

A permission check that segfaults is a denial-of-service vector: any role able to
attempt an unauthorised call can restart the database. Found by a security test
that asserts revoked functions are refused.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions