Skip to content

Commit 263091e

Browse files
committed
find_key(): This routine wasn't thread-correct, and accounts for the
release-build failures noted in bug 1041645. This is a critical bugfix. I'm not going to backport it, though (no time).
1 parent 5c14e64 commit 263091e

File tree

2 files changed

+22
-5
lines changed

2 files changed

+22
-5
lines changed

Misc/NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ What's New in Python 2.4 beta 1?
1212
Core and builtins
1313
-----------------
1414

15+
- The internal portable implementation of thread-local storage (TLS), used
16+
by the ``PyGILState_Ensure()``/``PyGILState_Release()`` API, was not
17+
thread-correct. This could lead to a variety of problems, up to and
18+
including segfaults. See bug 1041645 for an example.
19+
1520
- Added a command line option, -m module, which searches sys.path for the
1621
module and then runs it. (Contributed by Nick Coghlan.)
1722

Python/thread.c

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -209,29 +209,41 @@ static int nkeys = 0; /* PyThread_create_key() hands out nkeys+1 next */
209209
* So when value==NULL, this acts like a pure lookup routine, and when
210210
* value!=NULL, this acts like dict.setdefault(), returning an existing
211211
* mapping if one exists, else creating a new mapping.
212+
*
213+
* Caution: this used to be too clever, trying to hold keymutex only
214+
* around the "p->next = keyhead; keyhead = p" pair. That allowed
215+
* another thread to mutate the list, via key deletion, concurrent with
216+
* find_key() crawling over the list. Hilarity ensued. For example, when
217+
* the for-loop here does "p = p->next", p could end up pointing at a
218+
* record that PyThread_delete_key_value() was concurrently free()'ing.
219+
* That could lead to anything, from failing to find a key that exists, to
220+
* segfaults. Now we lock the whole routine.
212221
*/
213222
static struct key *
214223
find_key(int key, void *value)
215224
{
216225
struct key *p;
217226
long id = PyThread_get_thread_ident();
218227

228+
PyThread_acquire_lock(keymutex, 1);
219229
for (p = keyhead; p != NULL; p = p->next) {
220230
if (p->id == id && p->key == key)
221-
return p;
231+
goto Done;
232+
}
233+
if (value == NULL) {
234+
assert(p == NULL);
235+
goto Done;
222236
}
223-
if (value == NULL)
224-
return NULL;
225237
p = (struct key *)malloc(sizeof(struct key));
226238
if (p != NULL) {
227239
p->id = id;
228240
p->key = key;
229241
p->value = value;
230-
PyThread_acquire_lock(keymutex, 1);
231242
p->next = keyhead;
232243
keyhead = p;
233-
PyThread_release_lock(keymutex);
234244
}
245+
Done:
246+
PyThread_release_lock(keymutex);
235247
return p;
236248
}
237249

0 commit comments

Comments
 (0)