Skip to content

Commit 012b80a

Browse files
committed
Fix exception safety bug in typcache.c.
If an out-of-memory error was thrown at an unfortunate time, ensure_record_cache_typmod_slot_exists() could leak memory and leave behind a global state that produced an infinite loop on the next call. Fix by merging RecordCacheArray and RecordIdentifierArray into a single array. With only one allocation or re-allocation, there is no intermediate state. Back-patch to all supported releases. Reported-by: "James Pang (chaolpan)" <chaolpan@cisco.com> Reviewed-by: Michael Paquier <michael@paquier.xyz> Discussion: https://postgr.es/m/PH0PR11MB519113E738814BDDA702EDADD6EFA%40PH0PR11MB5191.namprd11.prod.outlook.com
1 parent 7e57208 commit 012b80a

File tree

2 files changed

+27
-26
lines changed

2 files changed

+27
-26
lines changed

src/backend/utils/cache/typcache.c

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -263,10 +263,15 @@ static const dshash_parameters srtr_typmod_table_params = {
263263
/* hashtable for recognizing registered record types */
264264
static HTAB *RecordCacheHash = NULL;
265265

266-
/* arrays of info about registered record types, indexed by assigned typmod */
267-
static TupleDesc *RecordCacheArray = NULL;
268-
static uint64 *RecordIdentifierArray = NULL;
269-
static int32 RecordCacheArrayLen = 0; /* allocated length of above arrays */
266+
typedef struct RecordCacheArrayEntry
267+
{
268+
uint64 id;
269+
TupleDesc tupdesc;
270+
} RecordCacheArrayEntry;
271+
272+
/* array of info about registered record types, indexed by assigned typmod */
273+
static RecordCacheArrayEntry *RecordCacheArray = NULL;
274+
static int32 RecordCacheArrayLen = 0; /* allocated length of above array */
270275
static int32 NextRecordTypmod = 0; /* number of entries used */
271276

272277
/*
@@ -1525,10 +1530,8 @@ ensure_record_cache_typmod_slot_exists(int32 typmod)
15251530
{
15261531
if (RecordCacheArray == NULL)
15271532
{
1528-
RecordCacheArray = (TupleDesc *)
1529-
MemoryContextAllocZero(CacheMemoryContext, 64 * sizeof(TupleDesc));
1530-
RecordIdentifierArray = (uint64 *)
1531-
MemoryContextAllocZero(CacheMemoryContext, 64 * sizeof(uint64));
1533+
RecordCacheArray = (RecordCacheArrayEntry *)
1534+
MemoryContextAllocZero(CacheMemoryContext, 64 * sizeof(RecordCacheArrayEntry));
15321535
RecordCacheArrayLen = 64;
15331536
}
15341537

@@ -1539,14 +1542,11 @@ ensure_record_cache_typmod_slot_exists(int32 typmod)
15391542
while (typmod >= newlen)
15401543
newlen *= 2;
15411544

1542-
RecordCacheArray = (TupleDesc *) repalloc(RecordCacheArray,
1543-
newlen * sizeof(TupleDesc));
1545+
RecordCacheArray = (RecordCacheArrayEntry *)
1546+
repalloc(RecordCacheArray,
1547+
newlen * sizeof(RecordCacheArrayEntry));
15441548
memset(RecordCacheArray + RecordCacheArrayLen, 0,
1545-
(newlen - RecordCacheArrayLen) * sizeof(TupleDesc));
1546-
RecordIdentifierArray = (uint64 *) repalloc(RecordIdentifierArray,
1547-
newlen * sizeof(uint64));
1548-
memset(RecordIdentifierArray + RecordCacheArrayLen, 0,
1549-
(newlen - RecordCacheArrayLen) * sizeof(uint64));
1549+
(newlen - RecordCacheArrayLen) * sizeof(RecordCacheArrayEntry));
15501550
RecordCacheArrayLen = newlen;
15511551
}
15521552
}
@@ -1584,8 +1584,8 @@ lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
15841584
{
15851585
/* It is already in our local cache? */
15861586
if (typmod < RecordCacheArrayLen &&
1587-
RecordCacheArray[typmod] != NULL)
1588-
return RecordCacheArray[typmod];
1587+
RecordCacheArray[typmod].tupdesc != NULL)
1588+
return RecordCacheArray[typmod].tupdesc;
15891589

15901590
/* Are we attached to a shared record typmod registry? */
15911591
if (CurrentSession->shared_typmod_registry != NULL)
@@ -1611,19 +1611,19 @@ lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
16111611
* Our local array can now point directly to the TupleDesc
16121612
* in shared memory, which is non-reference-counted.
16131613
*/
1614-
RecordCacheArray[typmod] = tupdesc;
1614+
RecordCacheArray[typmod].tupdesc = tupdesc;
16151615
Assert(tupdesc->tdrefcount == -1);
16161616

16171617
/*
16181618
* We don't share tupdesc identifiers across processes, so
16191619
* assign one locally.
16201620
*/
1621-
RecordIdentifierArray[typmod] = ++tupledesc_id_counter;
1621+
RecordCacheArray[typmod].id = ++tupledesc_id_counter;
16221622

16231623
dshash_release_lock(CurrentSession->shared_typmod_table,
16241624
entry);
16251625

1626-
return RecordCacheArray[typmod];
1626+
return RecordCacheArray[typmod].tupdesc;
16271627
}
16281628
}
16291629
}
@@ -1834,10 +1834,10 @@ assign_record_type_typmod(TupleDesc tupDesc)
18341834
ensure_record_cache_typmod_slot_exists(entDesc->tdtypmod);
18351835
}
18361836

1837-
RecordCacheArray[entDesc->tdtypmod] = entDesc;
1837+
RecordCacheArray[entDesc->tdtypmod].tupdesc = entDesc;
18381838

18391839
/* Assign a unique tupdesc identifier, too. */
1840-
RecordIdentifierArray[entDesc->tdtypmod] = ++tupledesc_id_counter;
1840+
RecordCacheArray[entDesc->tdtypmod].id = ++tupledesc_id_counter;
18411841

18421842
/* Fully initialized; create the hash table entry */
18431843
recentry = (RecordCacheEntry *) hash_search(RecordCacheHash,
@@ -1886,10 +1886,10 @@ assign_record_type_identifier(Oid type_id, int32 typmod)
18861886
* It's a transient record type, so look in our record-type table.
18871887
*/
18881888
if (typmod >= 0 && typmod < RecordCacheArrayLen &&
1889-
RecordCacheArray[typmod] != NULL)
1889+
RecordCacheArray[typmod].tupdesc != NULL)
18901890
{
1891-
Assert(RecordIdentifierArray[typmod] != 0);
1892-
return RecordIdentifierArray[typmod];
1891+
Assert(RecordCacheArray[typmod].id != 0);
1892+
return RecordCacheArray[typmod].id;
18931893
}
18941894

18951895
/* For anonymous or unrecognized record type, generate a new ID */
@@ -1969,7 +1969,7 @@ SharedRecordTypmodRegistryInit(SharedRecordTypmodRegistry *registry,
19691969
TupleDesc tupdesc;
19701970
bool found;
19711971

1972-
tupdesc = RecordCacheArray[typmod];
1972+
tupdesc = RecordCacheArray[typmod].tupdesc;
19731973
if (tupdesc == NULL)
19741974
continue;
19751975

src/tools/pgindent/typedefs.list

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1965,6 +1965,7 @@ ReadExtraTocPtrType
19651965
ReadFunc
19661966
ReassignOwnedStmt
19671967
RecheckForeignScan_function
1968+
RecordCacheArrayEntry
19681969
RecordCacheEntry
19691970
RecordCompareData
19701971
RecordIOData

0 commit comments

Comments
 (0)