Hello,

I am using libssh2 version 0.18 along with cURL 7.17.1 and 7.18.0 on several 
platforms (SuSE Linux 10, Solaris 10 x86, AIX 5.3).

When trying to upload large files via SCP using cURL, I repeatedly got the 
error message "curl: (55) Failed sending data to the peer". After some 
debugging, I found that this is caused by a bug in libssh2 - to be precise, in 
the function libssh2_channel_write_ex.

The issue can also be reproduced with the sample programs included in the 
libssh2 package. "scp", "scp_nonblock" and "scp_write" seem to work properly. 
However, "scp_write_nonblock" runs occasionally into a memory allocation error. 
With tracing enabled (LIBSSH2_TRACE_ERRORS | LIBSSH2_TRACE_CONN) I got the 
following output:
...
[libssh2] Connection: Writing 1024 bytes on channel 0/0, stream #0
[libssh2] Connection: Splitting write block due to 1000 byte window_size on 
0/0/0
[libssh2] Connection: Sending 1000 bytes on channel 0/0, stream_id=0
[libssh2] Connection: Window adjust received for channel 0/0, adding 74776 
bytes, new window_size=74776
[libssh2] Connection: Sending 1024 bytes on channel 0/0, stream_id=0
[libssh2] Connection: Writing -1000 bytes on channel 0/0, stream #0
[libssh2] Failure Event: -6 - Unable to allocte space for data transmission 
packet
ERROR -1
...
As you can see, a message had to be split due to window size limitations. In 
the first iteration, only 1000 out of 1024 bytes were transmitted. But in the 
next iteration, the function transmitted the full 1024 byte packet again, 
rather than only the 24 bytes left from the previous iteration. So the return 
value of libssh2_channel_write_ex() was 2024, though the calling program did 
only pass 1024 bytes - and this wrong return value messed up the caller's 
transfer byte counter. Due to this, the sample program passed a negative buffer 
size to the write function in the next iteraction, causing the memory 
allocation to fail.

With some more debugging code added to channel.c, I found, that this issue only 
occurs, if an idle loop (returning LIBSSH2_EAGAIN) occurs between two 
consecutive parts of a split message. If the whole message is transferred 
within a single invocation of libssh2_channel_write_ex, buflen is adjusted 
appropriately. But if the functions returns to the caller inbetween, the 
original buflen value is passed in again. That's why scp_write (blocking) 
worked properly, while scp_write_nonblock ran into the failure.

The reason for this is, that the value of channel->write_bufwrite is set to the 
full buffer length at channel.c[1695] rather than the difference between the 
buffer length and the already transferred byte count (buflen - 
channel->write_bufwrote). The patch appended blow will fix this issue and 
adjusts some other variables accordingly.

With this patch applied, arbitrary test files could be uploaded properly with 
both, the sample programs and cURL.


Cheers,
Erik Brossler



diff -Naur libssh2-0.18-orig/src/channel.c libssh2-0.18/src/channel.c
--- libssh2-0.18-orig/src/channel.c     2007-11-08 16:11:33.000000000 +0100
+++ libssh2-0.18/src/channel.c  2008-02-04 20:48:30.000000000 +0100
@@ -1690,9 +1690,9 @@
         channel->write_state = libssh2_NB_state_allocated;
     }
 
-    while (buflen > 0) {
+    while (buflen > channel->write_bufwrote) {
         if (channel->write_state == libssh2_NB_state_allocated) {
-            channel->write_bufwrite = buflen;
+            channel->write_bufwrite = buflen - channel->write_bufwrote;
             channel->write_s = channel->write_packet;
 
             *(channel->write_s++) =
@@ -1714,6 +1714,7 @@
                 if (rc < 0) {
                     /* Error or EAGAIN occurred, disconnect? */
                     if (rc != PACKET_EAGAIN) {
+                       LIBSSH2_FREE(session, channel->write_packet);
                         channel->write_state = libssh2_NB_state_idle;
                     }
                     return rc;
@@ -1747,7 +1748,7 @@
             }
             libssh2_htonu32(channel->write_s, channel->write_bufwrite);
             channel->write_s += 4;
-            memcpy(channel->write_s, buf, channel->write_bufwrite);
+            memcpy(channel->write_s, buf + channel->write_bufwrote, 
channel->write_bufwrite);
             channel->write_s += channel->write_bufwrite;
 
             _libssh2_debug(session, LIBSSH2_DBG_CONN,
@@ -1774,10 +1775,6 @@
             }
             /* Shrink local window size */
             channel->local.window_size -= channel->write_bufwrite;
-
-            /* Adjust buf for next iteration */
-            buflen -= channel->write_bufwrite;
-            buf += channel->write_bufwrite;
             channel->write_bufwrote += channel->write_bufwrite;
 
             channel->write_state = libssh2_NB_state_allocated;

_____________________________________________________________________
Der WEB.DE SmartSurfer hilft bis zu 70% Ihrer Onlinekosten zu sparen!
http://smartsurfer.web.de/?mc=100071&distributionid=000000000066


-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
libssh2-devel mailing list
libssh2-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/libssh2-devel

Reply via email to