Subject: [libssh2] #250: libssh2_channel_send_eof() sometimes fails when used in non-blocking mode

[libssh2] #250: libssh2_channel_send_eof() sometimes fails when used in non-blocking mode

From: libssh2 Trac <trac_at_libssh2.stuge.se>
Date: Tue, 23 Oct 2012 01:46:29 -0000

#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-devel
Received on 2012-10-23