FileLister: ensure enough space for resulting dirent (#2850)

On some platforms, the 'd_name' field of struct dirent is not a static
fixed-sized array but a "flexarray" (i.e. a single character); in this
situation, 'd_name' points to a buffer allocated somewhere, usually
at the end of the buffer used for dirent (which is then allocated in a
bigger memory). Because of this, creating a struct dirent on stack as
buffer for readdir_r is not enough to store all the memory needed for
a dirent on those platforms.

As result, create an helper union with all the needed space, calculated
statically at build time. NAME_MAX+1 is still not a perfect option, but
it will do the job in the vast majority of cases.
This commit is contained in:
Pino Toscano 2020-10-18 20:43:33 +02:00 committed by GitHub
parent 2624d791e6
commit 0a50d8e8f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 16 additions and 2 deletions

View File

@ -189,12 +189,26 @@ static void addFiles2(std::map<std::string, std::size_t> &files,
if (!dir)
return;
dirent entry;
dirent * dir_result;
// make sure we reserve enough space for the readdir_r() buffer;
// according to POSIX:
// The storage pointed to by entry shall be large enough for a
// dirent with an array of char d_name members containing at
// least {NAME_MAX}+1 elements.
// on some platforms, d_name is not a static sized-array but
// a pointer to space usually reserved right after the dirent
// struct; the union here allows to reserve the space and to
// provide a pointer to the right type that can be passed where
// needed without casts
union {
dirent entry;
char buf[sizeof(*dir_result) + (sizeof(dir_result->d_name) > 1 ? 0 : NAME_MAX + 1)];
} dir_result_buffer;
UNUSED(dir_result_buffer.buf); // do not trigger cppcheck itself on the "unused buf"
std::string new_path;
new_path.reserve(path.length() + 100);// prealloc some memory to avoid constant new/deletes in loop
while ((readdir_r(dir, &entry, &dir_result) == 0) && (dir_result != nullptr)) {
while ((readdir_r(dir, &dir_result_buffer.entry, &dir_result) == 0) && (dir_result != nullptr)) {
if ((std::strcmp(dir_result->d_name, ".") == 0) ||
(std::strcmp(dir_result->d_name, "..") == 0))