Skip to content

Commit d01a744

Browse files
committed
Fix hash_search to avoid corruption of the hash table on out-of-memory.
An out-of-memory error during expand_table() on a palloc-based hash table would leave a partially-initialized entry in the table. This would not be harmful for transient hash tables, since they'd get thrown away anyway at transaction abort. But for long-lived hash tables, such as the relcache hash, this would effectively corrupt the table, leading to crash or other misbehavior later. To fix, rearrange the order of operations so that table enlargement is attempted before we insert a new entry, rather than after adding it to the hash table. Problem discovered by Hitoshi Harada, though this is a bit different from his proposed patch.
1 parent 823f83d commit d01a744

File tree

1 file changed

+25
-16
lines changed

1 file changed

+25
-16
lines changed

src/backend/utils/hash/dynahash.c

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,27 @@ hash_search_with_hash_value(HTAB *hashp,
820820
hctl->accesses++;
821821
#endif
822822

823+
/*
824+
* If inserting, check if it is time to split a bucket.
825+
*
826+
* NOTE: failure to expand table is not a fatal error, it just means we
827+
* have to run at higher fill factor than we wanted. However, if we're
828+
* using the palloc allocator then it will throw error anyway on
829+
* out-of-memory, so we must do this before modifying the table.
830+
*/
831+
if (action == HASH_ENTER || action == HASH_ENTER_NULL)
832+
{
833+
/*
834+
* Can't split if running in partitioned mode, nor if frozen, nor if
835+
* table is the subject of any active hash_seq_search scans. Strange
836+
* order of these tests is to try to check cheaper conditions first.
837+
*/
838+
if (!IS_PARTITIONED(hctl) && !hashp->frozen &&
839+
hctl->nentries / (long) (hctl->max_bucket + 1) >= hctl->ffactor &&
840+
!has_seq_scans(hashp))
841+
(void) expand_table(hashp);
842+
}
843+
823844
/*
824845
* Do the initial lookup
825846
*/
@@ -940,24 +961,12 @@ hash_search_with_hash_value(HTAB *hashp,
940961
currBucket->hashvalue = hashvalue;
941962
hashp->keycopy(ELEMENTKEY(currBucket), keyPtr, keysize);
942963

943-
/* caller is expected to fill the data field on return */
944-
945964
/*
946-
* Check if it is time to split a bucket. Can't split if running
947-
* in partitioned mode, nor if table is the subject of any active
948-
* hash_seq_search scans. Strange order of these tests is to try
949-
* to check cheaper conditions first.
965+
* Caller is expected to fill the data field on return. DO NOT
966+
* insert any code that could possibly throw error here, as doing
967+
* so would leave the table entry incomplete and hence corrupt the
968+
* caller's data structure.
950969
*/
951-
if (!IS_PARTITIONED(hctl) &&
952-
hctl->nentries / (long) (hctl->max_bucket + 1) >= hctl->ffactor &&
953-
!has_seq_scans(hashp))
954-
{
955-
/*
956-
* NOTE: failure to expand table is not a fatal error, it just
957-
* means we have to run at higher fill factor than we wanted.
958-
*/
959-
expand_table(hashp);
960-
}
961970

962971
return (void *) ELEMENTKEY(currBucket);
963972
}

0 commit comments

Comments
 (0)