8000 gh-73069: fix race conditions in os.scandir and associated direntry by duaneg · Pull Request #131642 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-73069: fix race conditions in os.scandir and associated direntry #131642

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Prev Previous commit
Next Next commit
struct dirent is variable length in the original POSIX specification …
…(the final

filename field being an unsized array) and in actual practice on the WASI
platform. Thus just copying one into a stack-allocated structure is not good
enough.

The obvious way to address this is to allocate memory on the heap and return
that. However, it seems wasteful to dynamically allocate a temporary chunk of
data just to copy some data in and then out again.

Instead, if the name field is potentially not long enough, allocate some extra
space *on the stack* following the structure such that it must be sufficient to
hold the the full record length (and copy all of that, not just the potentially
smaller fixed structure size).
  • Loading branch information
duaneg committed Mar 26, 2025
commit 072a07cec3e0400d69e1ab7c4725ab12c01cb2dc
30 changes: 27 additions & 3 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@
# include <sanitizer/msan_interface.h> // __msan_unpoison()
#endif

#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif

// --- More complex system includes -----------------------------------------

Expand Down Expand Up @@ -16303,9 +16306,19 @@
Py_END_ALLOW_THREADS
has_error = !result && errno != 0;
}

/* We need to make a copy of the result before releasing the lock */
if (result)
memcpy(direntp, result, sizeof(struct dirent));
if (result) {

/* A note on calculating the size: the dirent structure may be variable
* length with the d_name field being a flexible array member, so we
* cannot just use sizeof(struct dirent). On Linux there is a d_reclen
* field, however that is not guaranteed to exist (and doesn't on WASI,
* which happens to be the platform where this is an issue). Hence we
* calculate the relevant record size thus. */
size_t reclen = offsetof(struct dirent, d_name) + strlen(result->d_name) + 1;
memcpy(direntp, result, reclen);
}
PyMutex_Unlock(&iterator->mutex);

/* Error or no more files */
Expand All @@ -16320,12 +16333,22 @@
static PyObject *
ScandirIterator_iternext(PyObject *op)
{
ScandirIterator *iterator = ScandirIterator_CAST(op);
#ifdef MS_WINDOWS
WIN32_FIND_DATAW dirent;
#else
/*
* This may be a variable length structure, with the final "d_name" field
* possibly being a flexible array member. In that case allocate enough
* extra space on the stack, immediately following the structure,
* sufficient to handle the longest possible filename. */
struct dirent dirent;
if (sizeof(dirent) - offsetof(struct dirent, d_name) < (NAME_MAX + 1)
&& !alloca(NAME_MAX + 1 - (sizeof(dirent) - offsetof(struct dirent, d_name)))) {
PyErr_NoMemory();
goto error;
}
#endif
ScandirIterator *iterator = ScandirIterator_CAST(op);

while ((ScandirIterator_nextdirentry(iterator, &dirent))) {

Expand All @@ -16341,6 +16364,7 @@
}

/* Already closed, error, or no more files */
error:

Check warning on line 16367 in Modules/posixmodule.c

View workflow job for this annotation

GitHub Actions / Windows / build (arm64)

'error': unreferenced label [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 16367 in Modules/posixmodule.c

View workflow job for this annotation

GitHub Actions / Windows / build (arm64)

'error': unreferenced label [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 16367 in Modules/posixmodule.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / build (arm64)

'error': unreferenced label [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 16367 in Modules/posixmodule.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / build (arm64)

'error': unreferenced label [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 16367 in Modules/posixmodule.c

View workflow job for this annotation

GitHub Actions / Windows / build and test (x64)

'error': unreferenced label [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 16367 in Modules/posixmodule.c

View workflow job for this annotation

GitHub Actions / Windows / build and test (x64)

'error': unreferenced label [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 16367 in Modules/posixmodule.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / build and test (x64)

'error': unreferenced label [D:\a\cpython\cpython\PCbuild\_freeze_module.vcxproj]

Check warning on line 16367 in Modules/posixmodule.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / build and test (x64)

'error': unreferenced label [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]
ScandirIterator_closedir(iterator);
return NULL;
}
Expand Down
Loading
0