Skip to content

Commit 7cc4a85

Browse files
committed
pythongh-73069 c1: fix race conditions with os.scandir iterator
1 parent c3b8d73 commit 7cc4a85

File tree

1 file changed

+24
-18
lines changed

1 file changed

+24
-18
lines changed

Modules/posixmodule.c

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16181,6 +16181,7 @@ typedef struct {
1618116181
#ifdef HAVE_FDOPENDIR
1618216182
int fd;
1618316183
#endif
16184+
PyMutex mutex;
1618416185
} ScandirIterator;
1618516186

1618616187
#define ScandirIterator_CAST(op) ((ScandirIterator *)(op))
@@ -16190,18 +16191,21 @@ typedef struct {
1619016191
static int
1619116192
ScandirIterator_is_closed(ScandirIterator *iterator)
1619216193
{
16193-
return iterator->handle == INVALID_HANDLE_VALUE;
16194+
return _Py_atomic_load_ptr_relaxed(&iterator->handle) == INVALID_HANDLE_VALUE;
1619416195
}
1619516196

1619616197
static void
1619716198
ScandirIterator_closedir(ScandirIterator *iterator)
1619816199
{
16200+
PyMutex_Lock(&iterator->mutex);
1619916201
HANDLE handle = iterator->handle;
16202+
iterator->handle = INVALID_HANDLE_VALUE;
16203+
PyMutex_Unlock(&iterator->mutex);
1620016204

16201-
if (handle == INVALID_HANDLE_VALUE)
16205+
if (handle == INVALID_HANDLE_VALUE) {
1620216206
return;
16207+
}
1620316208

16204-
iterator->handle = INVALID_HANDLE_VALUE;
1620516209
Py_BEGIN_ALLOW_THREADS
1620616210
FindClose(handle);
1620716211
Py_END_ALLOW_THREADS
@@ -16215,11 +16219,8 @@ ScandirIterator_iternext(PyObject *op)
1621516219
BOOL success;
1621616220
PyObject *entry;
1621716221

16218-
/* Happens if the iterator is iterated twice, or closed explicitly */
16219-
if (iterator->handle == INVALID_HANDLE_VALUE)
16220-
return NULL;
16221-
16222-
while (1) {
16222+
PyMutex_Lock(&iterator->mutex);
16223+
while (iterator->handle != INVALID_HANDLE_VALUE) {
1622316224
if (!iterator->first_time) {
1622416225
Py_BEGIN_ALLOW_THREADS
1622516226
success = FindNextFileW(iterator->handle, file_data);
@@ -16241,13 +16242,15 @@ ScandirIterator_iternext(PyObject *op)
1624116242
entry = DirEntry_from_find_data(module, &iterator->path, file_data);
1624216243
if (!entry)
1624316244
break;
16245+
PyMutex_Unlock(&iterator->mutex);
1624416246
return entry;
1624516247
}
1624616248

1624716249
/* Loop till we get a non-dot directory or finish iterating */
1624816250
}
1624916251

16250-
/* Error or no more files */
16252+
/* Already closed, error, or no more files */
16253+
PyMutex_Unlock(&iterator->mutex);
1625116254
ScandirIterator_closedir(iterator);
1625216255
return NULL;
1625316256
}
@@ -16257,18 +16260,21 @@ ScandirIterator_iternext(PyObject *op)
1625716260
static int
1625816261
ScandirIterator_is_closed(ScandirIterator *iterator)
1625916262
{
16260-
return !iterator->dirp;
16263+
return !_Py_atomic_load_ptr_relaxed(&iterator->dirp);
1626116264
}
1626216265

1626316266
static void
1626416267
ScandirIterator_closedir(ScandirIterator *iterator)
1626516268
{
16269+
PyMutex_Lock(&iterator->mutex);
1626616270
DIR *dirp = iterator->dirp;
16271+
iterator->dirp = NULL;
16272+
PyMutex_Unlock(&iterator->mutex);
1626716273

16268-
if (!dirp)
16274+
if (!dirp) {
1626916275
return;
16276+
}
1627016277

16271-
iterator->dirp = NULL;
1627216278
Py_BEGIN_ALLOW_THREADS
1627316279
#ifdef HAVE_FDOPENDIR
1627416280
if (iterator->path.fd != -1)
@@ -16288,11 +16294,8 @@ ScandirIterator_iternext(PyObject *op)
1628816294
int is_dot;
1628916295
PyObject *entry;
1629016296

16291-
/* Happens if the iterator is iterated twice, or closed explicitly */
16292-
if (!iterator->dirp)
16293-
return NULL;
16294-
16295-
while (1) {
16297+
PyMutex_Lock(&iterator->mutex);
16298+
while (iterator->dirp) {
1629616299
errno = 0;
1629716300
Py_BEGIN_ALLOW_THREADS
1629816301
direntp = readdir(iterator->dirp);
@@ -16320,13 +16323,15 @@ ScandirIterator_iternext(PyObject *op)
1632016323
);
1632116324
if (!entry)
1632216325
break;
16326+
PyMutex_Unlock(&iterator->mutex);
1632316327
return entry;
1632416328
}
1632516329

1632616330
/* Loop till we get a non-dot directory or finish iterating */
1632716331
}
1632816332

16329-
/* Error or no more files */
16333+
/* Already closed, error, or no more files */
16334+
PyMutex_Unlock(&iterator->mutex);
1633016335
ScandirIterator_closedir(iterator);
1633116336
return NULL;
1633216337
}
@@ -16459,6 +16464,7 @@ os_scandir_impl(PyObject *module, path_t *path)
1645916464
if (!iterator)
1646016465
return NULL;
1646116466

16467+
iterator->mutex = (PyMutex){0};
1646216468
#ifdef MS_WINDOWS
1646316469
iterator->handle = INVALID_HANDLE_VALUE;
1646416470
#else

0 commit comments

Comments
 (0)