#250: libssh2_channel_send_eof() sometimes fails when used in non-blocking mode
-------------------------------------------+--------------------
Reporter: jfriesne | Owner:
Type: defect | Status: new
Priority: normal | Milestone: 1.4.3
Component: API | Version: 1.4.2
Keywords: non-blocking channel_send_eof | Blocked By:
Blocks: |
-------------------------------------------+--------------------
Hi there libssh2 developers,
I think I've found a bug in the libssh2_channel_send_eof() function, when
it is used in conjunction with libssh's non-blocking mode.
The symptom for me is this: I have a program that uses libssh2 1.4.2 to
upload multiple files at once. Each upload is handled by a different
thread with its own (non-shared) libssh2 session object. Under Windows 7,
about 80% of the time one or more of the uploads (at random) would fail
with libssh2_channel_send_eof() returning error -7 (aka
LIBSSH2_ERROR_SOCKET_SEND).
I did some investigation and found found that the call to
_libssh2_transport_write() inside channel_send_eof() was failing, with
error code -39 (aka LIBSSH2_ERROR_BAD_USE), which AFAIK should never
occur.
A little more investigation revealed what is happening: sometimes
channel_send_eof()'s first call to _libssh2_transport_write() would result
in LIBSSH2_ERROR_EAGAIN, because the socket's output buffer has no more
space. This is fine, but the problem is that the pointer passed in to
_libssh2_transport_write() gets recorded into libssh2's transportpacket
data structure, and then the next time _libssh2_transport_write() is
called, it calls send_existing() and send_existing() checks to make sure
that the pointer passed in on the second attempt is the same as the
pointer that was passed in on the first attempt.
That would all be fine, except that the 5-byte char array being sent by
channel_send_eof() is located on the stack:
static int channel_send_eof(LIBSSH2_CHANNEL *channel)
{
LIBSSH2_SESSION *session = channel->session;
unsigned char packet[5]; /* packet_type(1) + channelno(4) */
int rc;
[...]
rc = _libssh2_transport_write(session, packet, 5);
[...]
.... which means that the memory location pointed to by (packet) may be
different each time channel_send_eof() is called, even if the data pointed
to by (packet) is the same. This is what triggers the
LIBSSH2_ERROR_BAD_USE error inside send_existing(), which in turn causes
libssh2_channel_send_eof() to fail.
In my local copy of libssh2 I was able to resolve the problem by moving
unsigned char packet[5]; /* packet_type(1) + channelno(4) */
from the stack of the channel_send_eof() function into the LIBSSH2_CHANNEL
object instead (i.e. as a member variable). That way the memory location
of (packet) will always be the same for a given channel object, and thus
the false-positive error detection is avoided.
I hope that all made sense -- if not, feel free to email me
(jaf_at_meyersound.com).
-Jeremy
-- Ticket URL: <https://trac.libssh2.org/ticket/250> libssh2 <https://trac.libssh2.org/> C library for writing portable SSH2 clients _______________________________________________ libssh2-devel http://cool.haxx.se/cgi-bin/mailman/listinfo/libssh2-develReceived on 2012-10-23