Skip to content

Commit f899c7f

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 3f7b7d9 commit f899c7f

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy