Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d2dc827

Browse filesBrowse files
authored
bpo-40602: _Py_hashtable_set() reports rehash failure (GH-20077)
If _Py_hashtable_set() fails to grow the hash table (rehash), it now fails rather than ignoring the error.
1 parent ce21cfc commit d2dc827
Copy full SHA for d2dc827

File tree

2 files changed

+26
-14
lines changed
Filter options

2 files changed

+26
-14
lines changed

‎Modules/_testinternalcapi.c

Copy file name to clipboardExpand all lines: Modules/_testinternalcapi.c
+9-5Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ test_hashtable(PyObject *self, PyObject *Py_UNUSED(args))
9898
return PyErr_NoMemory();
9999
}
100100

101+
// Using an newly allocated table must not crash
102+
assert(table->nentries == 0);
103+
assert(table->nbuckets > 0);
104+
assert(_Py_hashtable_get(table, TO_PTR('x')) == NULL);
105+
101106
// Test _Py_hashtable_set()
102107
char key;
103108
for (key='a'; key <= 'z'; key++) {
@@ -121,17 +126,15 @@ test_hashtable(PyObject *self, PyObject *Py_UNUSED(args))
121126
// Test _Py_hashtable_get()
122127
for (key='a'; key <= 'z'; key++) {
123128
void *value_ptr = _Py_hashtable_get(table, TO_PTR(key));
124-
int value = (int)FROM_PTR(value_ptr);
125-
assert(value == VALUE(key));
129+
assert((int)FROM_PTR(value_ptr) == VALUE(key));
126130
}
127131

128132
// Test _Py_hashtable_steal()
129133
key = 'p';
130134
void *value_ptr = _Py_hashtable_steal(table, TO_PTR(key));
131-
int value = (int)FROM_PTR(value_ptr);
132-
assert(value == VALUE(key));
133-
135+
assert((int)FROM_PTR(value_ptr) == VALUE(key));
134136
assert(table->nentries == 25);
137+
assert(_Py_hashtable_get_entry(table, TO_PTR(key)) == NULL);
135138

136139
// Test _Py_hashtable_foreach()
137140
int count = 0;
@@ -142,6 +145,7 @@ test_hashtable(PyObject *self, PyObject *Py_UNUSED(args))
142145
// Test _Py_hashtable_clear()
143146
_Py_hashtable_clear(table);
144147
assert(table->nentries == 0);
148+
assert(table->nbuckets > 0);
145149
assert(_Py_hashtable_get(table, TO_PTR('x')) == NULL);
146150

147151
_Py_hashtable_destroy(table);

‎Python/hashtable.c

Copy file name to clipboardExpand all lines: Python/hashtable.c
+17-9Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
((_Py_hashtable_entry_t *)_Py_SLIST_ITEM_NEXT(ENTRY))
6161

6262
/* Forward declaration */
63-
static void hashtable_rehash(_Py_hashtable_t *ht);
63+
static int hashtable_rehash(_Py_hashtable_t *ht);
6464

6565
static void
6666
_Py_slist_init(_Py_slist_t *list)
@@ -198,6 +198,7 @@ _Py_hashtable_steal(_Py_hashtable_t *ht, const void *key)
198198
ht->alloc.free(entry);
199199

200200
if ((float)ht->nentries / (float)ht->nbuckets < HASHTABLE_LOW) {
201+
// Ignore failure: error cannot be reported to the caller
201202
hashtable_rehash(ht);
202203
}
203204
return value;
@@ -228,13 +229,17 @@ _Py_hashtable_set(_Py_hashtable_t *ht, const void *key, void *value)
228229
entry->key = (void *)key;
229230
entry->value = value;
230231

231-
size_t index = entry->key_hash & (ht->nbuckets - 1);
232-
_Py_slist_prepend(&ht->buckets[index], (_Py_slist_item_t*)entry);
233232
ht->nentries++;
234-
235233
if ((float)ht->nentries / (float)ht->nbuckets > HASHTABLE_HIGH) {
236-
hashtable_rehash(ht);
234+
if (hashtable_rehash(ht) < 0) {
235+
ht->nentries--;
236+
ht->alloc.free(entry);
237+
return -1;
238+
}
237239
}
240+
241+
size_t index = entry->key_hash & (ht->nbuckets - 1);
242+
_Py_slist_prepend(&ht->buckets[index], (_Py_slist_item_t*)entry);
238243
return 0;
239244
}
240245

@@ -271,19 +276,19 @@ _Py_hashtable_foreach(_Py_hashtable_t *ht,
271276
}
272277

273278

274-
static void
279+
static int
275280
hashtable_rehash(_Py_hashtable_t *ht)
276281
{
277282
size_t new_size = round_size((size_t)(ht->nentries * HASHTABLE_REHASH_FACTOR));
278283
if (new_size == ht->nbuckets) {
279-
return;
284+
return 0;
280285
}
281286

282287
size_t buckets_size = new_size * sizeof(ht->buckets[0]);
283288
_Py_slist_t *new_buckets = ht->alloc.malloc(buckets_size);
284289
if (new_buckets == NULL) {
285290
/* memory allocation failed */
286-
return;
291+
return -1;
287292
}
288293
memset(new_buckets, 0, buckets_size);
289294

@@ -303,6 +308,7 @@ hashtable_rehash(_Py_hashtable_t *ht)
303308
ht->alloc.free(ht->buckets);
304309
ht->nbuckets = new_size;
305310
ht->buckets = new_buckets;
311+
return 0;
306312
}
307313

308314

@@ -388,7 +394,9 @@ _Py_hashtable_clear(_Py_hashtable_t *ht)
388394
_Py_slist_init(&ht->buckets[i]);
389395
}
390396
ht->nentries = 0;
391-
hashtable_rehash(ht);
397+
// Ignore failure: clear function is not expected to fail
398+
// because of a memory allocation failure.
399+
(void)hashtable_rehash(ht);
392400
}
393401

394402

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.