Skip to content

<fstream>: initialize fstream with FILE* lead to resource leak #6041

@coderall2

Description

@coderall2

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:

Image Image Image Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationRelated to documentation or commentsfixedSomething works now, yay!

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions