Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

file_descriptor device leaks fd on Windows when constructed from int fd #152

Open
julianbrost opened this issue Jul 27, 2022 · 0 comments
Open

Comments

@julianbrost
Copy link

boost::iostreams::file_descriptor has the following two constructors on Windows (among others):

file_descriptor( int fd, file_descriptor_flags );
file_descriptor( HANDLE hFile, file_descriptor_flags );

When using the first one in combination with close_handle, the file descriptor is leaked. Take the following code:

#ifdef _WIN32
#include <io.h>
#endif
#include <fcntl.h>
#include <sys/stat.h>
#include <iostream>
#include <boost/iostreams/device/file_descriptor.hpp>

int main() {
	std::cout << "open + stream:" << std::endl;
	for (int i = 0; i < 3; i++) {
		int fd = open("tmp", O_CREAT|O_RDWR, S_IREAD|S_IWRITE);
		std::cout << "open(): " << fd << std::endl;
		boost::iostreams::file_descriptor iostreamsFd(fd, boost::iostreams::close_handle);
	}

	std::cout << "open + close:" << std::endl;
	for (int i = 0; i < 3; i++) {
		int fd = open("tmp", O_CREAT|O_RDWR, S_IREAD|S_IWRITE);
		std::cout << "open(): " << fd << std::endl;
		close(fd);
	}
}

It repeatedly uses open to create int file descriptors, and then constructs boost::iostreams::file_descriptor with boost::iostreams::close_handle from it, which then go out of scope immediately, which should close the fd. For comparison, a second loop just calls close to verify that fd numbers are actually reused immediately.

Running this on Windows (Windows 10, MSVC 19.29.30146.0, Boost 1.79.0) gives the following output, i.e. it leaks the fds from the first loop but not from the second:

open + stream:
open(): 3
open(): 4
open(): 5
open + close:
open(): 6
open(): 6
open(): 6

In comparison, running the same code on Linux (Arch Linux, g++ 12.1.0, Boost 1.79.0), the output looks like this, no fds are leaked:

open + stream:
open(): 3
open(): 3
open(): 3
open + close:
open(): 3
open(): 3
open(): 3

It looks like _get_osfhandle is used to obtain a HANDLE from the int fd:

void file_descriptor_impl::open(int fd, flags f)
{ open(reinterpret_cast<file_handle>(_get_osfhandle(fd)), f); }

Which is then later used with CloseHandle:

success = ::CloseHandle(handle_) == 1;

According to the documentation for _get_osfhandle, this is something you explicitly shouldn't do:

To close a file whose operating system (OS) file handle is obtained by _get_osfhandle, call _close on the file descriptor fd. Never call CloseHandle on the return value of this function. The underlying OS file handle is owned by the fd file descriptor, and is closed when _close is called on fd.

This probably explains the leak: due to not using close/_close, the int fd is kept open (but now refers to an invalid handle).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant