Bounds-check access to TupleDescAttr with an Assert.

The second argument to TupleDescAttr should always be at least zero
and less than natts; otherwise, we index outside of the attribute
array. Assert that this is the case.

Various violations, or possible violations, of this rule that are
currently in the tree are actually harmless, because while
we do call TupleDescAttr() before verifying that the argument is
within range, we don't actually dereference it unless the argument
was within range all along. Nonetheless, the Assert means we
should be more careful, so tidy up accordingly.

Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: http://postgr.es/m/CA+TgmoacixUZVvi00hOjk_d9B4iYKswWP1gNqQ8Vfray-AcOCA@mail.gmail.com
This commit is contained in:
Robert Haas 2026-03-24 08:58:50 -04:00
parent e2f289e5b9
commit c98ad086ad
4 changed files with 26 additions and 16 deletions

View file

@ -246,10 +246,11 @@ CreateTupleDescCopy(TupleDesc tupdesc)
desc = CreateTemplateTupleDesc(tupdesc->natts);
/* Flat-copy the attribute array */
memcpy(TupleDescAttr(desc, 0),
TupleDescAttr(tupdesc, 0),
desc->natts * sizeof(FormData_pg_attribute));
/* Flat-copy the attribute array (unless there are no attributes) */
if (desc->natts > 0)
memcpy(TupleDescAttr(desc, 0),
TupleDescAttr(tupdesc, 0),
desc->natts * sizeof(FormData_pg_attribute));
/*
* Since we're not copying constraints and defaults, clear fields
@ -294,10 +295,11 @@ CreateTupleDescTruncatedCopy(TupleDesc tupdesc, int natts)
desc = CreateTemplateTupleDesc(natts);
/* Flat-copy the attribute array */
memcpy(TupleDescAttr(desc, 0),
TupleDescAttr(tupdesc, 0),
desc->natts * sizeof(FormData_pg_attribute));
/* Flat-copy the attribute array (unless there are no attributes) */
if (desc->natts > 0)
memcpy(TupleDescAttr(desc, 0),
TupleDescAttr(tupdesc, 0),
desc->natts * sizeof(FormData_pg_attribute));
/*
* Since we're not copying constraints and defaults, clear fields
@ -339,10 +341,11 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
desc = CreateTemplateTupleDesc(tupdesc->natts);
/* Flat-copy the attribute array */
memcpy(TupleDescAttr(desc, 0),
TupleDescAttr(tupdesc, 0),
desc->natts * sizeof(FormData_pg_attribute));
/* Flat-copy the attribute array (unless there are no attributes) */
if (desc->natts > 0)
memcpy(TupleDescAttr(desc, 0),
TupleDescAttr(tupdesc, 0),
desc->natts * sizeof(FormData_pg_attribute));
for (i = 0; i < desc->natts; i++)
{

View file

@ -179,6 +179,8 @@ TupleDescAttr(TupleDesc tupdesc, int i)
{
FormData_pg_attribute *attrs = TupleDescAttrAddress(tupdesc);
Assert(i >= 0 && i < tupdesc->natts);
return &attrs[i];
}

View file

@ -1093,7 +1093,7 @@ plperl_build_tuple_result(HV *perlhash, TupleDesc td)
SV *val = HeVAL(he);
char *key = hek2cstr(he);
int attn = SPI_fnumber(td, key);
Form_pg_attribute attr = TupleDescAttr(td, attn - 1);
Form_pg_attribute attr;
if (attn == SPI_ERROR_NOATTRIBUTE)
ereport(ERROR,
@ -1106,6 +1106,7 @@ plperl_build_tuple_result(HV *perlhash, TupleDesc td)
errmsg("cannot set system attribute \"%s\"",
key)));
attr = TupleDescAttr(td, attn - 1);
values[attn - 1] = plperl_sv_to_datum(val,
attr->atttypid,
attr->atttypmod,
@ -1799,7 +1800,7 @@ plperl_modify_tuple(HV *hvTD, TriggerData *tdata, HeapTuple otup)
char *key = hek2cstr(he);
SV *val = HeVAL(he);
int attn = SPI_fnumber(tupdesc, key);
Form_pg_attribute attr = TupleDescAttr(tupdesc, attn - 1);
Form_pg_attribute attr;
if (attn == SPI_ERROR_NOATTRIBUTE)
ereport(ERROR,
@ -1811,6 +1812,8 @@ plperl_modify_tuple(HV *hvTD, TriggerData *tdata, HeapTuple otup)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot set system attribute \"%s\"",
key)));
attr = TupleDescAttr(tupdesc, attn - 1);
if (attr->attgenerated)
ereport(ERROR,
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),

View file

@ -3401,7 +3401,7 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
PLpgSQL_var *var = (PLpgSQL_var *) retvar;
Datum retval = var->value;
bool isNull = var->isnull;
Form_pg_attribute attr = TupleDescAttr(tupdesc, 0);
Form_pg_attribute attr;
if (natts != 1)
ereport(ERROR,
@ -3414,6 +3414,7 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
var->datatype->typlen);
/* coerce type if needed */
attr = TupleDescAttr(tupdesc, 0);
retval = exec_cast_value(estate,
retval,
&isNull,
@ -3532,7 +3533,7 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
}
else
{
Form_pg_attribute attr = TupleDescAttr(tupdesc, 0);
Form_pg_attribute attr;
/* Simple scalar result */
if (natts != 1)
@ -3541,6 +3542,7 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
errmsg("wrong result type supplied in RETURN NEXT")));
/* coerce type if needed */
attr = TupleDescAttr(tupdesc, 0);
retval = exec_cast_value(estate,
retval,
&isNull,