`file_descriptor` device leaks fd on Windows when constructed from int fd
julianbrost opened this issue · 0 comments
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:
iostreams/src/file_descriptor.cpp
Lines 111 to 112 in 8003232
Which is then later used with CloseHandle
:
iostreams/src/file_descriptor.cpp
Line 266 in 8003232
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).