I have nailed down that the following code, when compiled with g++14 and adress sanitizers (See details below), triggers a runtime ‘AddressSanitizer: heap-buffer-overflow’ for the for-statement looping over std::filesystem::directory_iterator{sandbox}.
#include <iostream>
#include <fstream>
#include <filesystem>
void test_directory_iterator() {
// Code from https://en.cppreference.com/w/cpp/filesystem/directory_iterator
const std::filesystem::path sandbox{"sandbox"};
std::filesystem::create_directories(sandbox/"dir1"/"dir2");
std::ofstream{sandbox/"file1.txt"};
std::ofstream{sandbox/"file2.txt"};
std::cout << "directory_iterator:n";
// directory_iterator can be iterated using a range-for loop
for (auto const& dir_entry : std::filesystem::directory_iterator{sandbox}) {}
}
int main(int argc, char *argv[])
{
test_directory_iterator();
exit(0);
}
Compiler version:
kjell-olovhogdal@Kjell-Olovs-Mac-Pro src % /usr/local/Cellar/gcc/14.1.0_1/bin/g++-14 -v
Using built-in specs.
COLLECT_GCC=/usr/local/Cellar/gcc/14.1.0_1/bin/g++-14
COLLECT_LTO_WRAPPER=/usr/local/Cellar/gcc/14.1.0_1/bin/../libexec/gcc/x86_64-apple-darwin21/14/lto-wrapper
Target: x86_64-apple-darwin21
Configured with: ../configure –prefix=/usr/local/opt/gcc –libdir=/usr/local/opt/gcc/lib/gcc/current –disable-nls –enable-checking=release –with-gcc-major-version-only –enable-languages=c,c++,objc,obj-c++,fortran –program-suffix=-14 –with-gmp=/usr/local/opt/gmp –with-mpfr=/usr/local/opt/mpfr –with-mpc=/usr/local/opt/libmpc –with-isl=/usr/local/opt/isl –with-zstd=/usr/local/opt/zstd –with-pkgversion=’Homebrew GCC 14.1.0_1′ –with-bugurl=https://github.com/Homebrew/homebrew-core/issues –with-system-zlib –build=x86_64-apple-darwin21 –with-sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX12.sdk
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 14.1.0 (Homebrew GCC 14.1.0_1)
kjell-olovhogdal@Kjell-Olovs-Mac-Pro src %
Build command:
‘/usr/local/Cellar/gcc/14.1.0_1/bin/g++-14 –sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -fdiagnostics-color=always -std=c++23 -g -fsanitize=address,undefined -fno-omit-frame-pointer /Users/kjell-olovhogdal/Documents/Github/cratchit/src/directory_iterator_error.cpp’
kjell-olovhogdal@Kjell-Olovs-Mac-Pro src % ./a.out
a.out(53925,0x1086ea600) malloc: nano zone abandoned due to inability to preallocate reserved vm space.
directory_iterator:
/usr/local/Cellar/gcc/14.1.0_1/include/c++/14/bits/shared_ptr_base.h:1076:26: runtime error: member call on address 0x60b0000003b0 which does not point to an object of type ‘_Sp_counted_base’
0x60b0000003b0: note: object is of type ‘std::_Sp_counted_ptr_inplace<std::filesystem::__cxx11::_Dir, std::allocatorstd::filesystem::__cxx11::_Dir, (__gnu_cxx::_Lock_policy)2>’
==53925==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60b0000003ac at pc 0x000107f49fa0 bp 0x7ff7b88cf870 sp 0x7ff7b88cf040
READ of size 32 at 0x60b0000003ac thread T0
#0 0x107f49f9f in write.part.0+0x1af (libasan.8.dylib:x86_64+0x29f9f)
#1 0x10873a1de in __sanitizer::IsAccessibleMemoryRange(unsigned long, unsigned long)+0x5e (libubsan.1.dylib:x86_64+0x181de)
0x60b0000003ac is located 4 bytes before 112-byte region [0x60b0000003b0,0x60b000000420)
allocated by thread T0 here:
#0 0x107f85838 in _Znwm+0xa8 (libasan.8.dylib:x86_64+0x65838)
#1 0x107c4335e in std::filesystem::__cxx11::directory_iterator::directory_iterator(std::filesystem::__cxx11::path const&, std::filesystem::directory_options, std::error_code*)+0x11e (libstdc++.6.dylib:x86_64+0x10335e)
SUMMARY: AddressSanitizer: heap-buffer-overflow (libasan.8.dylib:x86_64+0x29f9f) in write.part.0+0x1af
Shadow bytes around the buggy address:
0x60b000000100: fd fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa
0x60b000000180: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd
0x60b000000200: fd fa fa fa fa fa fa fa fa fa fd fd fd fd fd fd
0x60b000000280: fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa fa
0x60b000000300: fd fd fd fd fd fd fd fd fd fd fd fd fd fa fa fa
=>0x60b000000380: fa fa fa fa fa[fa]00 00 00 00 00 00 00 00 00 00
0x60b000000400: 00 00 00 00 fa fa fa fa fa fa fa fa fd fd fd fd
0x60b000000480: fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa
0x60b000000500: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 fa
0x60b000000580: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x60b000000600: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==53925==ABORTING
zsh: abort ./a.out
kjell-olovhogdal@Kjell-Olovs-Mac-Pro src %
I have tried the loop with an empty iterator and that works fine.
- Is there a known bug in g++14 for ‘std::filesystem::directory_iterator’?
- Is this problem local to my machine or can you reproduce it?
Steps to reproduce: Compile a cpp-file with the code provided above with the compiler and arguments shown above.
Many thanks in advance!
/Kjell-Olov Högdal