Menu

#606 Crash in curl_multi_cleanup on upload to ftp errors

closed-fixed
ftp (93)
5
2014-11-13
2006-09-19
Anonymous
No

danil at mtsnet.ru

Platform: Win2000, mingw-2.95.2
libcurl: libcurl-7.15.4 and libcurl-7.15.6-20060911
+ssl builds downloaded from the official site

This is the similar bug to the described in https://
sourceforge.net/tracker/index.php?func=detail&aid=
1560773&group_id=976&atid=100976 but it happens during
cleanup of the multi handle after unsuccesful ftp
upload:

In the example below I'm running guildFTPd localy:

* About to connect() to 127.0.0.1 port 21
* Expire at 888825 / 165000 (300000ms)
* Trying 127.0.0.1... * connected
* Connected to 127.0.0.1 (127.0.0.1) port 21
* Expire cleared
* Remembering we are in dir Upload/test
< 220-GuildFTPd FTP Server (c) 1997-2002
< 220-Version 0.999.13
< 220 Please enter your name:
* server did not report OK, got 220
* Connection #0 to host 127.0.0.1 left intact
^^^^^
1) First we see that libcurl misinterprets ftpd result
of the ftpd (I don't see it in the easy-mode nor in
the application i've hit originally this bug).
2) When trying to release multi handle after this (or
other errors) - libcurl crashes

#include <stdio.h>
#include <curl/curl.h>

const char* theData = "this is a test\n";
int hasNotBeenSent = 1;

int fRead(void *dataPtr, unsigned items, unsigned
size, void* whatever);

int main()
{
CURLSH *curlShare;
CURLM* multiHandle;
CURL* easyHandle;
CURLcode curlCode;
int isMultiRunning;

curl\_global\_init\(CURL\_GLOBAL\_ALL \);

easyHandle = curl\_easy\_init\(\);
curl\_easy\_setopt\( easyHandle, CURLOPT\_VERBOSE,

1);
curl_easy_setopt( easyHandle, CURLOPT_
READFUNCTION, fRead);
curl_easy_setopt( easyHandle, CURLOPT_READDATA,
0);
curl_easy_setopt( easyHandle, CURLOPT_URL, "ftp:/
/127.0.0.1/Upload/test");
curl_easy_setopt( easyHandle, CURLOPT_
INFILESIZE, strlen(theData) );
curl_easy_setopt( easyHandle, CURLOPT_UPLOAD, 1);

multiHandle = curl\_multi\_init\(\);
curl\_multi\_add\_handle\( multiHandle, easyHandle

);

while\(curlCode = curl\_multi\_perform\(

multiHandle, &isMultiRunning ),
curlCode == CURLM_
CALL_MULTI_PERFORM) ;;;

curl\_multi\_remove\_handle\(multiHandle,

easyHandle);

curl\_multi\_cleanup\(multiHandle\);
printf\("I can not get here\n"\);
curl\_easy\_cleanup\(easyHandle\);
curl\_global\_cleanup\(\);

return 0;

}

int fRead(void *dataPtr, unsigned items, unsigned
size, void* whatever)
{
/* send the string one time */
return hasNotBeenSent ?
memcpy( dataPtr, theData, strlen(theData
)), hasNotBeenSent = 0, strlen (theData)
: 0 ;
}

Discussion

  • Daniel Stenberg

    Daniel Stenberg - 2006-09-19

    Logged In: YES
    user_id=1110

    That trace isn't complete, is it?

    It says "Remembering we are in..." very early, which is what
    it normally says after an FTP transfer.

     
  • Daniel Stenberg

    Daniel Stenberg - 2006-09-19
    • labels: --> ftp
    • assigned_to: nobody --> bagder
     
  • Nobody/Anonymous

    Logged In: NO

    danil at mtsnet.ru

    The trace looks strange for me too - but this is exactly
    what i see when I run the code in the example. Another
    strange thing is that the example code doesn't do an
    upload, but the similar code from program I'm writing
    currently (it is a libcurl binding for squeak smalltalk ) -
    does (a can not instantly provide trace from it, because I
    need to rebuild squeak vm as the console application or to
    make hooks to the debug functions).

    So, the first upload from squeak goes ok, the second upload
    is not permitted by server because file is already there -
    crash when releasing the multi handle.
    I didn't manage to exactly reproduce this behaviour in C -
    it fails from the start, but the example reflects my main
    concern anyway - crash in multi_handle_cleanup when ftp
    upload failed.

    If you need any additional info or things to try - please
    ask

     
  • Daniel Stenberg

    Daniel Stenberg - 2006-09-20

    Logged In: YES
    user_id=1110

    I agree this shouldn't crash. Can you provide any details on
    the actual crash, like file and line number and possibly the
    contents of some local variables at the time of the crash?

    Your example code is however not something that is likely to
    ever succeed in an upload since you seem to have forgot to
    read the man page for curl_multi_perform() and especially
    its return codes.

    And as a side-note your "unorthodox" (ab)use of the comma
    operator makes your program hard to follow.

    Test case 525 makes an FTP upload fine using the multi
    interface.

     
  • Nobody/Anonymous

    Logged In: NO

    Thank you, now I see where the difference is coming from (I
    forgot to put the part checking if there are active
    transfers in place).
    Sorry for the hurry and the dirt in code.
    So the next code actually works the first time and crashes
    on the second on windows (it works like it should on
    freebsd) and behaves exactly like squeak plugin:

    #include <stdio.h>
    #include <curl/curl.h>

    const char* theData = "this is a test\n";
    int hasNotBeenSent = 1;

    int fRead(void *dataPtr, unsigned items, unsigned
    size, void* whatever);

    int main()
    {
    CURLSH *curlShare;
    CURLM* multiHandle;
    CURL* easyHandle;
    CURLcode curlCode;
    int isMultiRunning=0;

    curl_global_init(CURL_GLOBAL_ALL );

    easyHandle = curl_easy_init();
    curl_easy_setopt( easyHandle, CURLOPT_VERBOSE, 1);
    curl_easy_setopt( easyHandle, CURLOPT_READFUNCTION, fRead);
    curl_easy_setopt( easyHandle, CURLOPT_READDATA, 0);
    curl_easy_setopt( easyHandle, CURLOPT_URL,
    "ftp://127.0.0.1/Upload/test");
    curl_easy_setopt( easyHandle, CURLOPT_INFILESIZE,
    strlen(theData) );
    curl_easy_setopt( easyHandle, CURLOPT_UPLOAD, 1);

    multiHandle = curl_multi_init();
    curl_multi_add_handle( multiHandle, easyHandle);

    curlCode = curl_multi_perform( multiHandle, &isMultiRunning );

    while (isMultiRunning) {
    if ( curlCode == CURLM_CALL_MULTI_PERFORM ||
    curlCode == 0)
    curlCode = curl_multi_perform( multiHandle,
    &isMultiRunning );
    else
    break
    ;;
    }

    curl_multi_remove_handle(multiHandle, easyHandle);

    curl_multi_cleanup(multiHandle);
    printf("I can not get here\n");
    curl_easy_cleanup(easyHandle);
    curl_global_cleanup();

    return 0;
    }

    int fRead(void *dataPtr, unsigned items, unsigned
    size, void* whatever)
    {
    /* send the string one time */
    if (hasNotBeenSent){
    memcpy( dataPtr, theData, strlen(theData));
    hasNotBeenSent = 0;
    return strlen (theData);
    }
    else
    return 0;
    }

    And transcript:
    $ ./a
    * About to connect() to 127.0.0.1 port 21
    * Expire at 1306 / 108000 (300000ms)
    * Trying 127.0.0.1... * connected
    * Connected to 127.0.0.1 (127.0.0.1) port 21
    * Expire cleared
    < 220-GuildFTPd FTP Server (c) 1997-2002
    < 220-Version 0.999.13
    < 220 Please enter your name:
    > USER anonymous
    < 331 User name okay, Need password.
    > PASS curl_by_daniel@haxx.se
    < 230 User logged in.
    > PWD
    < 257 "/" is current directory.
    * Entry path is '/'
    * Connection #0: send pipe size = 1
    > CWD Upload
    < 250 "/Upload" is current directory.
    > EPSV
    * Connect data stream passively
    < 500 'EPSV': command not understood.
    * disabling EPSV usage
    > PASV
    < 227 Entering Passive Mode (127,0,0,1,7,225)
    * Expire at 1307 / 983000 (300000ms)
    * Trying 127.0.0.1... * connected
    * Connecting to 127.0.0.1 (127.0.0.1) port 2017
    * Expire cleared
    > TYPE I
    < 200 Type set to I.
    > STOR test
    < 150 Opening binary mode data connection for /Upload/test.
    * Connection #0: recv pipe size = 1
    * Remembering we are in dir Upload/
    < 226 Transfer complete. 15 bytes in 0 sec. (0.00 Kb/s).
    * Connection #0 to host 127.0.0.1 left intact
    > QUIT
    < 221 Goodbye. Control connection closed.
    * Closing connection #0
    I can not get here

    $ ./a
    * About to connect() to 127.0.0.1 port 21
    * Expire at 1310 / 499000 (300000ms)
    * Trying 127.0.0.1... * connected
    * Connected to 127.0.0.1 (127.0.0.1) port 21
    * Expire cleared
    < 220-GuildFTPd FTP Server (c) 1997-2002
    < 220-Version 0.999.13
    < 220 Please enter your name:
    > USER anonymous
    < 331 User name okay, Need password.
    > PASS curl_by_daniel@haxx.se
    < 230 User logged in.
    > PWD
    < 257 "/" is current directory.
    * Entry path is '/'
    * Connection #0: send pipe size = 1
    > CWD Upload
    < 250 "/Upload" is current directory.
    > EPSV
    * Connect data stream passively
    < 500 'EPSV': command not understood.
    * disabling EPSV usage
    > PASV
    < 227 Entering Passive Mode (127,0,0,1,7,235)
    * Expire at 1312 / 280000 (300000ms)
    * Trying 127.0.0.1... * connected
    * Connecting to 127.0.0.1 (127.0.0.1) port 2027
    * Expire cleared
    > TYPE I
    < 200 Type set to I.
    > STOR test
    < 425 Permission Denied. File Exists.
    * Failed FTP upload: 425
    * Remembering we are in dir Upload/
    * Uploaded unaligned file size (0 out of 15 bytes)
    * Connection #0 to host 127.0.0.1 left intact
    <^^^^^crash here>

    I have no debugger on the windows box yet and I'm leaving
    for business trip now, so i will provide stack dump later on
    the first opportunity

    thank you

     
  • Daniel Stenberg

    Daniel Stenberg - 2006-09-21

    Logged In: YES
    user_id=1110

    I can't repeat the crash here when I try the lib525 source
    code with a 425 response to STOR.

    The CVS version does however not work perfectly so I'm
    investigating a missing QUIT and a small memory leak.

     
  • Daniel Stenberg

    Daniel Stenberg - 2006-09-23

    Logged In: YES
    user_id=1110

    Ok, the new test case 531 tests alsmost exactly this and it
    works fine for me.

    Do note that your updated test code is also a very bad use
    of the libcurl API.

    Can you still get this problem with the latest CVS version?

     
  • Nobody/Anonymous

    Logged In: NO

    Thank you, I'm testing it with nightly build (libcurl-7.15.6
    -20060925)- both the test code and the squeak plugin are
    working without crashes. However when running it under gdb,
    it shows sigsegv (as it runs ok without it - I don't care
    much):

    (gdb) run
    Starting program: E:\Squeak\vm-source/a.exe

    Program received signal SIGSEGV, Segmentation fault.
    0x61e19bda in TXT_DB_free ()
    (gdb) bt
    #0 0x61e19bda in TXT_DB_free ()
    #1 0x00c1f560 in ?? ()
    #2 0x61e0d022 in sha_block_host_order ()
    #3 0x004013cb in main () at test.c:49
    #4 0x004011b1 in __mingw_CRTStartup ()
    #5 0x004011cf in mainCRTStartup ()
    #6 0x7c5989a5 in _libmsvcrt_a_iname ()
    (gdb) list 49
    44
    45 curl_multi_remove_handle(multiHandle, easyHandle);
    46
    47 curl_multi_cleanup(multiHandle);
    48 printf("I can not get here\n");
    49 curl_easy_cleanup(easyHandle);
    50 curl_global_cleanup();
    51
    52 return 0;
    53 }

    As for the bad use of the lib api, I would say that the
    test code was intended to higlight the issue, not to show
    exactly how it actually works in plugin. If you see any
    problems with it other than cpu-eating loop and the
    absense of error handling - i would like to now.

    Generally it works fine now, thank you for this great
    library.

     
  • Daniel Stenberg

    Daniel Stenberg - 2006-09-26

    Logged In: YES
    user_id=1110

    I think the error here is that you call curl_multi_cleanup()
    *before* you call curl_easy_cleanup() on the handle, which
    is against how this is documented to get used.

    But still, crashing due to this is badness and I'm looking
    into this right now.

    Thanks for your patience and details.

     
  • Nobody/Anonymous

    Logged In: NO

    Argh, you are talking about inproper cleanup of handles?
    Actually that was an ugly workaround :), because when I did
    it in the right order - it crashed, but when I did it in
    this way, for some reason it worked. Basically, squeak
    plugin instantiates a pair of multihandle and easyhandle
    and joins them togather when it needs to perform a
    transfer. I had to get new multi handle right before
    transfer and release after it in this cruel way (and to use
    additional ugly tricks).
    Now all this mess completely ceased.
    Thank you

     
  • Daniel Stenberg

    Daniel Stenberg - 2006-09-28

    Logged In: YES
    user_id=1110

    Fix and test case to verify was just committed.

    Thanks! Case closed.

     
  • Daniel Stenberg

    Daniel Stenberg - 2006-09-28
    • status: open --> closed-fixed