Skip to content

PostgreSQL backend SIGSEGV in plpgsql_check_function_tb #216

Description

@hoangvinh14

Summary

plpgsql_check_function_tb crashes its PostgreSQL backend when it analyzes a
PL/pgSQL function that reads a record-typed OUT column into a local record
and subsequently assigns fields from that local record. The connection is
lost; PostgreSQL recovers and accepts new connections.

This report contains only a standalone synthetic fixture. It has no application
schema, business names, data, credentials, or project-specific SQL.

Environment

  • PostgreSQL 18.4 on Debian arm64.
  • plpgsql_check built from official release v2.10.1
    (7e1d6fcc6d3d5d5fd83337ea6a07003ddcaa3bf3).
  • The extension was invoked with fatal_errors := false and all other
    analyzer options explicitly named below.

The same crash was also observed while evaluating v2.9.3 and the PGDG 2.9.2
package. The reproducer below is the smallest public-safe case currently
available; please use it as the primary diagnosis input.

Reproducer

Run this in a new database with plpgsql_check installed:

CREATE EXTENSION plpgsql_check;

CREATE TYPE demo_payload AS (value integer);

CREATE FUNCTION demo_produce_row(
  OUT status_code integer,
  OUT payload record
) LANGUAGE plpgsql AS $$
BEGIN
  status_code := 0;
  payload := ROW(1)::demo_payload;
END
$$;

CREATE FUNCTION demo_consume_row()
RETURNS void LANGUAGE plpgsql AS $$
DECLARE
  fetched_row record;
  typed_payload demo_payload;
  observed_status integer;
BEGIN
  SELECT * INTO STRICT fetched_row FROM demo_produce_row() AS result_row;
  typed_payload := fetched_row.payload;
  observed_status := fetched_row.status_code;
END
$$;

SELECT count(*)
FROM plpgsql_check_function_tb(
  'demo_consume_row()'::regprocedure,
  fatal_errors := false,
  other_warnings := true,
  performance_warnings := false,
  extra_warnings := true,
  security_warnings := false,
  compatibility_warnings := false,
  without_warnings := false,
  all_warnings := false,
  use_incomment_options := true,
  incomment_options_usage_warning := false,
  constant_tracing := true
);

Actual result

The client connection is closed. PostgreSQL logs a message equivalent to:

server process was terminated by signal 11: Segmentation fault

After the server finishes recovery, a new session can execute SELECT 1.

Expected result

plpgsql_check_function_tb should return diagnostics (or no rows) without
terminating the backend.

Narrowing controls

The crash does not occur in these variants:

  1. Declare payload as demo_payload rather than record in
    demo_produce_row.
  2. Keep payload record and SELECT * INTO STRICT fetched_row, but remove
    both assignments from fetched_row.
  3. Keep payload record, but select only status_code into an integer target.

The suspected triggering shape is therefore:

record-typed OUT field
  + SELECT * INTO STRICT local_record
  + read one or more fields from local_record

Request

Could you please check it?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions