public inbox for [email protected]
help / color / mirror / Atom feedFrom: surya poondla <[email protected]>
To: [email protected]
To: [email protected]
Subject: Re: BUG #19382: Server crash at __nss_database_lookup
Date: Wed, 18 Mar 2026 22:00:41 -0700
Message-ID: <CAOVWO5oSeBouPv0ueVByh+_6EgRCjWh0spSmnF6Cv-TF1twqKg@mail.gmail.com> (raw)
In-Reply-To: <CAOVWO5r19cctAFKbW24jfMKsD-pkyV21w+z7L3pCPxM1CArtjQ@mail.gmail.com>
References: <[email protected]>
<CAOVWO5rVBKsjG4YwO_PJQu2OBGp8qUdF1jineYY6Lm3zc6-KWQ@mail.gmail.com>
<CAOVWO5rbwKgHWLYJMvKuvGxW9eFSk7LADk=ZxDEvwA1uTefvAg@mail.gmail.com>
<CAOVWO5qN8UXdwMnDa+a7aVq=irGSfm2aeYvEc-uV6j4QHZiyrA@mail.gmail.com>
<CAOVWO5py4zLPYsnGK1EEzHnC4feTSmbsEZn-BiDgY_=J1P6Wmw@mail.gmail.com>
<CAOVWO5pbgCVx0zgTr1mxZug2hoGwxZOk+-Owvwg0jaQv9JE3Fw@mail.gmail.com>
<CAOVWO5r19cctAFKbW24jfMKsD-pkyV21w+z7L3pCPxM1CArtjQ@mail.gmail.com>
Hi All,
I was able to reproduce the crash on laster master (19), the above patch
applies cleanly on postgres 19 and doesn't crash the server.
psql (19devel)
Type "help" for help.
postgres=# DROP FUNCTION IF EXISTS bar();
NOTICE: function bar() does not exist, skipping
DROP FUNCTION
postgres=# DROP TYPE IF EXISTS foo CASCADE;
NOTICE: type "foo" does not exist, skipping
DROP TYPE
postgres=# CREATE TYPE foo AS (a INT, b INT);
CREATE TYPE
postgres=# CREATE FUNCTION bar() RETURNS RECORD AS $$
postgres$# DECLARE
postgres$# r foo := ROW(123, power(2, 30));
postgres$# BEGIN
postgres$# ALTER TYPE foo ALTER ATTRIBUTE b TYPE TEXT;
postgres$# RETURN r;
postgres$# END;
postgres$# $$ LANGUAGE plpgsql;
CREATE FUNCTION
postgres=# SELECT bar();
bar
------------------
(123,1073741824)
(1 row)
postgres=# DROP FUNCTION IF EXISTS bar1();
NOTICE: function bar1() does not exist, skipping
DROP FUNCTION
postgres=# DROP TYPE IF EXISTS foo1 CASCADE;
NOTICE: type "foo1" does not exist, skipping
DROP TYPE
postgres=# CREATE TYPE foo1 AS (a INT, b INT);
CREATE TYPE
postgres=# CREATE FUNCTION bar1(OUT r1 foo1) AS $$
postgres$# BEGIN
postgres$# r1 := ROW(1, 2);
postgres$# ALTER TYPE foo1 ALTER ATTRIBUTE b TYPE TEXT;
postgres$# RETURN;
postgres$# END;
postgres$# $$ LANGUAGE plpgsql;
CREATE FUNCTION
postgres=# SELECT bar1();
bar1
-------
(1,2)
(1 row)
postgres=# DROP FUNCTION IF EXISTS bar2();
NOTICE: function bar2() does not exist, skipping
DROP FUNCTION
postgres=# DROP TYPE IF EXISTS foo2 CASCADE;
NOTICE: type "foo2" does not exist, skipping
DROP TYPE
postgres=# CREATE TYPE foo2 AS (a INT, b TEXT);
CREATE TYPE
postgres=# CREATE FUNCTION bar2() RETURNS foo2 AS $$
postgres$# DECLARE
postgres$# r foo2 := ROW(1, 'hello');
postgres$# BEGIN
postgres$# ALTER TYPE foo2 ALTER ATTRIBUTE b TYPE INT;
postgres$# RETURN r;
postgres$# END;
postgres$# $$ LANGUAGE plpgsql;
CREATE FUNCTION
postgres=# SELECT bar2();
ERROR: invalid input syntax for type integer: "hello"
CONTEXT: PL/pgSQL function bar2() line 6 at RETURN
postgres=# DROP FUNCTION bar();
DROP FUNCTION
postgres=# DROP FUNCTION bar1();
DROP FUNCTION
postgres=# DROP FUNCTION bar2();
DROP FUNCTION
postgres=# DROP TYPE IF EXISTS foo CASCADE;
DROP TYPE
postgres=# DROP TYPE IF EXISTS foo1 CASCADE;
DROP TYPE
postgres=# DROP TYPE IF EXISTS foo2 CASCADE;
DROP TYPE
postgres=# quit
Regards,
Surya Poondla
Attachments:
[application/octet-stream] 0003-Fix-bug-19382-server-crash-when-ALTER-TYPE-is-used-m_PG19.patch (7.0K, 3-0003-Fix-bug-19382-server-crash-when-ALTER-TYPE-is-used-m_PG19.patch)
download | inline diff:
From 3289746d88fbca10712d43cc4f50fa2ce99e62c1 Mon Sep 17 00:00:00 2001
From: spoondla <[email protected]>
Date: Fri, 23 Jan 2026 17:28:54 -0800
Subject: [PATCH v3] Fix (bug #19382) server crash when ALTER TYPE is used
mid-transaction in PL/pgSQL
When ALTER TYPE changes a composite type's column types within a
transaction, PL/pgSQL record variables that were populated before
the ALTER still hold data in the old format. Returning such records
causes a crash because the output functions expect data matching the
new type definition, not the old one.
The crash manifested as a segmentation fault in record_out() when it
attempted to interpret integer data as a text pointer, due to the
mismatch between the stored data and the current type definition.
---
src/pl/plpgsql/src/pl_exec.c | 173 ++++++++++++++++++++++++++++++++++-
1 file changed, 172 insertions(+), 1 deletion(-)
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 6b077febdc8..0d85a95d795 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -458,6 +458,8 @@ static char *format_preparedparamsdata(PLpgSQL_execstate *estate,
static PLpgSQL_variable *make_callstmt_target(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr);
+static void convert_record_for_altered_type(PLpgSQL_execstate *estate,
+ PLpgSQL_rec *rec);
/* ----------
* plpgsql_exec_function Called by the call handler for
@@ -3244,8 +3246,30 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
}
break;
- case PLPGSQL_DTYPE_ROW:
case PLPGSQL_DTYPE_REC:
+ {
+ PLpgSQL_rec *rec = (PLpgSQL_rec *) retvar;
+ int32 rettypmod;
+
+ /*
+ * Check if the record's composite type was altered since
+ * the record was populated. If so, convert the data to
+ * prevent crashes when outputting the record.
+ */
+ if (rec->rectypeid != RECORDOID && rec->erh != NULL &&
+ !ExpandedRecordIsEmpty(rec->erh))
+ convert_record_for_altered_type(estate, rec);
+
+ exec_eval_datum(estate,
+ retvar,
+ &estate->rettype,
+ &rettypmod,
+ &estate->retval,
+ &estate->retisnull);
+ }
+ break;
+
+ case PLPGSQL_DTYPE_ROW:
{
/* exec_eval_datum can handle these cases */
int32 rettypmod;
@@ -3390,6 +3414,14 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
TupleDesc rec_tupdesc;
TupleConversionMap *tupmap;
+ /*
+ * Check if the record's composite type was altered since
+ * the record was populated. If so, convert the data to
+ * prevent crashes when storing to the tuplestore.
+ */
+ if (rec->rectypeid != RECORDOID && rec->erh != NULL)
+ convert_record_for_altered_type(estate, rec);
+
/* If rec is null, try to convert it to a row of nulls */
if (rec->erh == NULL)
instantiate_empty_record_variable(estate, rec);
@@ -8883,3 +8915,142 @@ format_preparedparamsdata(PLpgSQL_execstate *estate,
return paramstr.data;
}
+
+/*
+ * convert_record_for_altered_type
+ *
+ * Check if a record's composite type has been altered since the record
+ * was populated, and if so, convert the record data to match the new
+ * type definition. This prevents crashes that can occur when the stored
+ * data doesn't match the current type definition.
+ *
+ * If conversion is needed, assigns the new record to rec via
+ * assign_record_var(), which transfers it to datum_context and frees
+ * the old record.
+ */
+static void
+convert_record_for_altered_type(PLpgSQL_execstate *estate,
+ PLpgSQL_rec *rec)
+{
+ ExpandedRecordHeader *erh = rec->erh;
+ Oid rectypeid = rec->rectypeid;
+ TupleDesc old_tupdesc;
+ TupleDesc new_tupdesc;
+ TypeCacheEntry *typentry;
+ uint64 current_tupdesc_id;
+ ExpandedRecordHeader *new_erh;
+ Datum *old_values;
+ bool *old_nulls;
+ Datum *new_values;
+ bool *new_nulls;
+ int natts;
+ int i;
+ MemoryContext oldcxt;
+ bool need_conversion = false;
+
+ /* Nothing to do for anonymous RECORD type */
+ if (rectypeid == RECORDOID)
+ return;
+
+ /* Get current type definition from typcache */
+ typentry = lookup_type_cache(rectypeid,
+ TYPECACHE_TUPDESC |
+ TYPECACHE_DOMAIN_BASE_INFO);
+ if (typentry->typtype == TYPTYPE_DOMAIN)
+ typentry = lookup_type_cache(typentry->domainBaseType,
+ TYPECACHE_TUPDESC);
+
+ current_tupdesc_id = typentry->tupDesc_identifier;
+
+ /* If type hasn't changed, nothing to do (fast path) */
+ if (erh->er_tupdesc_id == current_tupdesc_id)
+ return;
+
+ /*
+ * Type version has changed. Need to check if field types actually differ
+ * and convert if necessary.
+ */
+ old_tupdesc = erh->er_tupdesc;
+ new_tupdesc = typentry->tupDesc;
+
+ /* Sanity check: must have same number of attributes */
+ if (old_tupdesc->natts != new_tupdesc->natts)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("record type \"%s\" structure has changed",
+ format_type_be(rectypeid)),
+ errdetail("Number of columns changed from %d to %d.",
+ old_tupdesc->natts, new_tupdesc->natts)));
+
+ natts = old_tupdesc->natts;
+
+ /* Deconstruct the old record to access field values */
+ deconstruct_expanded_record(erh);
+ old_values = erh->dvalues;
+ old_nulls = erh->dnulls;
+
+ /* Allocate arrays for new values */
+ oldcxt = MemoryContextSwitchTo(get_eval_mcontext(estate));
+ new_values = (Datum *) palloc(natts * sizeof(Datum));
+ new_nulls = (bool *) palloc(natts * sizeof(bool));
+ MemoryContextSwitchTo(oldcxt);
+
+ /* Convert each field */
+ for (i = 0; i < natts; i++)
+ {
+ Form_pg_attribute old_att = TupleDescAttr(old_tupdesc, i);
+ Form_pg_attribute new_att = TupleDescAttr(new_tupdesc, i);
+
+ /* Skip dropped columns */
+ if (old_att->attisdropped || new_att->attisdropped)
+ {
+ new_values[i] = (Datum) 0;
+ new_nulls[i] = true;
+ continue;
+ }
+
+ /* If null, stays null */
+ if (old_nulls[i])
+ {
+ new_values[i] = (Datum) 0;
+ new_nulls[i] = true;
+ continue;
+ }
+
+ /* If same type, no conversion needed */
+ if (old_att->atttypid == new_att->atttypid &&
+ (old_att->atttypmod == new_att->atttypmod ||
+ new_att->atttypmod == -1))
+ {
+ new_values[i] = old_values[i];
+ new_nulls[i] = false;
+ continue;
+ }
+
+ /* Different type: convert using exec_cast_value */
+ need_conversion = true;
+ new_nulls[i] = false;
+ new_values[i] = exec_cast_value(estate,
+ old_values[i],
+ &new_nulls[i],
+ old_att->atttypid,
+ old_att->atttypmod,
+ new_att->atttypid,
+ new_att->atttypmod);
+ }
+
+ /* If no actual conversion was needed, return without modifying rec */
+ if (!need_conversion)
+ return;
+
+ /* Build new expanded record with converted values */
+ new_erh = make_expanded_record_from_typeid(rectypeid, -1,
+ get_eval_mcontext(estate));
+ expanded_record_set_fields(new_erh, new_values, new_nulls, true);
+
+ /*
+ * Assign the new record to rec, transferring it to datum_context
+ * and freeing the old record.
+ */
+ assign_record_var(estate, rec, new_erh);
+}
--
2.39.5 (Apple Git-154)
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected]
Subject: Re: BUG #19382: Server crash at __nss_database_lookup
In-Reply-To: <CAOVWO5oSeBouPv0ueVByh+_6EgRCjWh0spSmnF6Cv-TF1twqKg@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox