[Gpg4win-devel] kdpipeio close buglet

Marcus Brinkmann marcus.brinkmann at ruhr-uni-bochum.de
Tue Oct 2 02:05:13 CEST 2007


Hi,

I am currently looking through w32-qt-io.c to make sure it gets the
same bugfixes (if necessary) that we applied to the w32-glib-io.c
backend last week.

The short story is that I found a bug in that kdpipeiodevice does not
DuplicateHandle the _get_osfhandle (fd) at initialization time.
_get_osfhandle does not acquire a reference for the handle for you, so
you call CloseHandle before _close(), which will call CloseHandle
again, and you end up closing the same handle twice with only one
reference.

Here is the long story, with additional notes and some suggestions how
the double close can be fixed.

* MAX_SLAFD 50000

  This seems excessive.  libc handles are guaranteed to be small A
  smaller constant can catch libc vs osfhandle confusion (together
  with the bound checks), and saves a bit of memory.

  Suggestion: Lower to eg 1024.

* DeviceEntry: I can't see how actual_fd can ever be something else
  than the index of the pointer to this entry in the table.  So, this
  member seems redundant.  Suggestion: let's remove it.

* _gpgme_io_close: The loop over all entries in the iodevice_table
  seems unnecessary, as an entry should never be copied without
  creating a new reference.  Should probably be removed or replaced by
  an assert (but the loop for an assert is somewhat costly).

  You call iodev->close() and _close (fd).  In kdpipeiodevice close
  you do a CloseHandle on handle, which is initialized to
  _get_osfhandle (fd).  But _get_osfhandle does not acquire a
  reference for the handle, so the CloseHandle actually releases the
  libc file descriptor's internal reference.

  Three possible solutions: 1) kdpipeiodevice could acquire its own
  reference by calling DuplicateHandle, or it could 2) swallow the FD,
  then it (rather than _gpgme_io_close) should call _close, or it
  could 3) rely on the caller to keep the reference and not close the
  handle.  glib does take approach 2).  It seems to me that approach
  1) fits best in the current code.

* For _gpgme_io_dup, it can be assumed here that FD was previously
  created with _gpgme_io_pipe, so the iodevice should already exist.
  I suggest we replace this by an assert.

  It is correct to share the iodevice for dup'ed handles.  This is not
  strictly following the Unix semantics of dup, so the function name
  is a misnomer.  My bad (see priv-io.h for some details).

  Note that GPGME guarantees that the dup'ed FD is closed before the
  FD from which it was dup'ed.  This is important, because the
  channels are shared, and for glib closing the FD implies destroying
  the channel (due to the swallowing approach taken at initialization
  time).  If you take approach 1), this would not be an issue.

  In any case, the FD dup you create here must be closed in
  _gpgme_io_close.  If you choose to use DuplicateHandle in
  kdpipeiodevice (the first approach above), then it is correct to
  call _close in _gpgme_io_close for all FDs.  In the glib approach
  (swallow FD) we remember the channel fd and skip the _close for that
  FD only.

  (Note to self and Werner: We could _dup the FD before creating the
  channel, and then simply use glib's close_on_unref mode and glib's
  reference counting to keep track of users, thereby taking approach
  1) rather than approach 2).

Thanks,
Marcus




More information about the Gpg4win-devel mailing list