Skip to content

Commit 3acd059

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)" <[email protected]> Reviewed-by: Michael Paquier <[email protected]> Discussion: https://postgr.es/m/PH0PR11MB519113E738814BDDA702EDADD6EFA%40PH0PR11MB5191.namprd11.prod.outlook.com
1 parent 522a31a commit 3acd059

File tree

2 files changed

+28
-21
lines changed

2 files changed

+28
-21
lines changed

src/backend/utils/cache/typcache.c

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -273,10 +273,15 @@ static const dshash_parameters srtr_typmod_table_params = {
273273
/* hashtable for recognizing registered record types */
274274
static HTAB *RecordCacheHash = NULL;
275275

276-
/* arrays of info about registered record types, indexed by assigned typmod */
277-
static TupleDesc *RecordCacheArray = NULL;
278-
static uint64 *RecordIdentifierArray = NULL;
279-
static int32 RecordCacheArrayLen = 0; /* allocated length of above arrays */
276+
typedef struct RecordCacheArrayEntry
277+
{
278+
uint64 id;
279+
TupleDesc tupdesc;
280+
} RecordCacheArrayEntry;
281+
282+
/* array of info about registered record types, indexed by assigned typmod */
283+
static RecordCacheArrayEntry *RecordCacheArray = NULL;
284+
static int32 RecordCacheArrayLen = 0; /* allocated length of above array */
280285
static int32 NextRecordTypmod = 0; /* number of entries used */
281286

282287
/*
@@ -1703,19 +1708,20 @@ ensure_record_cache_typmod_slot_exists(int32 typmod)
17031708
{
17041709
if (RecordCacheArray == NULL)
17051710
{
1706-
RecordCacheArray = (TupleDesc *)
1707-
MemoryContextAllocZero(CacheMemoryContext, 64 * sizeof(TupleDesc));
1708-
RecordIdentifierArray = (uint64 *)
1709-
MemoryContextAllocZero(CacheMemoryContext, 64 * sizeof(uint64));
1711+
RecordCacheArray = (RecordCacheArrayEntry *)
1712+
MemoryContextAllocZero(CacheMemoryContext,
1713+
64 * sizeof(RecordCacheArrayEntry));
17101714
RecordCacheArrayLen = 64;
17111715
}
17121716

17131717
if (typmod >= RecordCacheArrayLen)
17141718
{
17151719
int32 newlen = pg_nextpower2_32(typmod + 1);
17161720

1717-
RecordCacheArray = repalloc0_array(RecordCacheArray, TupleDesc, RecordCacheArrayLen, newlen);
1718-
RecordIdentifierArray = repalloc0_array(RecordIdentifierArray, uint64, RecordCacheArrayLen, newlen);
1721+
RecordCacheArray = repalloc0_array(RecordCacheArray,
1722+
RecordCacheArrayEntry,
1723+
RecordCacheArrayLen,
1724+
newlen);
17191725
RecordCacheArrayLen = newlen;
17201726
}
17211727
}
@@ -1753,8 +1759,8 @@ lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
17531759
{
17541760
/* It is already in our local cache? */
17551761
if (typmod < RecordCacheArrayLen &&
1756-
RecordCacheArray[typmod] != NULL)
1757-
return RecordCacheArray[typmod];
1762+
RecordCacheArray[typmod].tupdesc != NULL)
1763+
return RecordCacheArray[typmod].tupdesc;
17581764

17591765
/* Are we attached to a shared record typmod registry? */
17601766
if (CurrentSession->shared_typmod_registry != NULL)
@@ -1780,19 +1786,19 @@ lookup_rowtype_tupdesc_internal(Oid type_id, int32 typmod, bool noError)
17801786
* Our local array can now point directly to the TupleDesc
17811787
* in shared memory, which is non-reference-counted.
17821788
*/
1783-
RecordCacheArray[typmod] = tupdesc;
1789+
RecordCacheArray[typmod].tupdesc = tupdesc;
17841790
Assert(tupdesc->tdrefcount == -1);
17851791

17861792
/*
17871793
* We don't share tupdesc identifiers across processes, so
17881794
* assign one locally.
17891795
*/
1790-
RecordIdentifierArray[typmod] = ++tupledesc_id_counter;
1796+
RecordCacheArray[typmod].id = ++tupledesc_id_counter;
17911797

17921798
dshash_release_lock(CurrentSession->shared_typmod_table,
17931799
entry);
17941800

1795-
return RecordCacheArray[typmod];
1801+
return RecordCacheArray[typmod].tupdesc;
17961802
}
17971803
}
17981804
}
@@ -2005,10 +2011,10 @@ assign_record_type_typmod(TupleDesc tupDesc)
20052011
ensure_record_cache_typmod_slot_exists(entDesc->tdtypmod);
20062012
}
20072013

2008-
RecordCacheArray[entDesc->tdtypmod] = entDesc;
2014+
RecordCacheArray[entDesc->tdtypmod].tupdesc = entDesc;
20092015

20102016
/* Assign a unique tupdesc identifier, too. */
2011-
RecordIdentifierArray[entDesc->tdtypmod] = ++tupledesc_id_counter;
2017+
RecordCacheArray[entDesc->tdtypmod].id = ++tupledesc_id_counter;
20122018

20132019
/* Fully initialized; create the hash table entry */
20142020
recentry = (RecordCacheEntry *) hash_search(RecordCacheHash,
@@ -2057,10 +2063,10 @@ assign_record_type_identifier(Oid type_id, int32 typmod)
20572063
* It's a transient record type, so look in our record-type table.
20582064
*/
20592065
if (typmod >= 0 && typmod < RecordCacheArrayLen &&
2060-
RecordCacheArray[typmod] != NULL)
2066+
RecordCacheArray[typmod].tupdesc != NULL)
20612067
{
2062-
Assert(RecordIdentifierArray[typmod] != 0);
2063-
return RecordIdentifierArray[typmod];
2068+
Assert(RecordCacheArray[typmod].id != 0);
2069+
return RecordCacheArray[typmod].id;
20642070
}
20652071

20662072
/* For anonymous or unrecognized record type, generate a new ID */
@@ -2140,7 +2146,7 @@ SharedRecordTypmodRegistryInit(SharedRecordTypmodRegistry *registry,
21402146
TupleDesc tupdesc;
21412147
bool found;
21422148

2143-
tupdesc = RecordCacheArray[typmod];
2149+
tupdesc = RecordCacheArray[typmod].tupdesc;
21442150
if (tupdesc == NULL)
21452151
continue;
21462152

src/tools/pgindent/typedefs.list

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2251,6 +2251,7 @@ ReadLocalXLogPageNoWaitPrivate
22512251
ReadReplicationSlotCmd
22522252
ReassignOwnedStmt
22532253
RecheckForeignScan_function
2254+
RecordCacheArrayEntry
22542255
RecordCacheEntry
22552256
RecordCompareData
22562257
RecordIOData

0 commit comments

Comments
 (0)