-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
Describe the bug
We are using FILE* to initialize fstream on Windows. However, after the same logic is executed approximately 500 times, the function fails with errno == 24.
Background: We discovered that files opened with std::fstream could not be deleted. After deep tracing, we found that fstream lacks the FILE_SHARE_DELETE permission when opening files on Windows. Following a technical article's suggestion(https://stackoverflow.com/questions/38952189/how-to-share-file-delete-privilege-when-i-opening-a-file-by-ifstream), we switched to initializing fstream using FILE* (to specify custom sharing flags), which then led to the aforementioned errno 24 issue.
While all the documentation we found states that fstream automatically releases file descriptor resources upon destruction, our actual execution results suggest otherwise.
We wrote the following code and found that the issue can be consistently reproduced.
for (int i = 0; i < 1024; i++) {
HANDLE handle = CreateFileW(L"D:\\test.txt",
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (handle == INVALID_HANDLE_VALUE) {
std::cerr << " invalid handle err:" << GetLastError() << " i:" << i << std::endl;
return;
}
int fd = _open_osfhandle((intptr_t)handle, _O_RDONLY);
if (0 >= fd) {
std::cerr << " _open_osfhandle err:" << GetLastError() << " errno:" << " i:" << i << errno << std::endl;
return;
}
FILE* fp = _fdopen(fd, "rb");
if (NULL == fp) {
std::cerr << " _fdopen err:" << GetLastError() << " errno:" << errno << " i:" << i << std::endl;
return;
}
std::shared_ptr<std::fstream> streamptr =
std::make_shared<std::fstream>(fp);
}
After adding fclose or streamptr->close() to the last line of the for loop, the issue disappeared.
After reviewing the Windows STL source code, we discovered that when initializing fstream with a FILE*, the _Closef flag in filebuf is set to false. Consequently, the destructor of fstream skips the fclose call, leading to a resource leak.
The screenshot of the function call stack is shown below:
