From 0a50d8e8f4109105855253aaeff55d53e43e57ab Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Sun, 18 Oct 2020 20:43:33 +0200 Subject: [PATCH] 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. --- cli/filelister.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/cli/filelister.cpp b/cli/filelister.cpp index e16bbce42..e55d47f1e 100644 --- a/cli/filelister.cpp +++ b/cli/filelister.cpp @@ -189,12 +189,26 @@ static void addFiles2(std::map &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))