pingpong: on disconnect, check for unflushed pingpong state

When a pingpong based protocol tries to perform a connection disconnect,
it sends a sort of "logout" command to the server, unless the connection
is deemed dead.

But the disconnect might happen before pingpong data has been completely
sent, in which case sending the "logout" will not work. Check the
pingpong state and do not "logout" when data is pending.

This was detected as a condition in fuzzing that triggered a debug
assert in the pingpong sending.

Closes #17555
This commit is contained in:
Stefan Eissing 2025-06-08 12:00:00 +02:00 committed by Daniel Stenberg
parent b42776b4f4
commit c314759c4c
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
4 changed files with 9 additions and 8 deletions

View file

@ -4117,7 +4117,7 @@ static CURLcode ftp_disconnect(struct Curl_easy *data,
will try to send the QUIT command, otherwise it will just return.
*/
ftpc->shutdown = TRUE;
if(dead_connection)
if(dead_connection || Curl_pp_needs_flush(data, &ftpc->pp))
ftpc->ctl_valid = FALSE;
/* The FTP session may or may not have been allocated/setup at this point! */

View file

@ -1780,12 +1780,11 @@ static CURLcode imap_disconnect(struct Curl_easy *data,
(void)data;
if(imapc) {
/* We cannot send quit unconditionally. If this connection is stale or
bad in any way, sending quit and waiting around here will make the
bad in any way (pingpong has pending data to send),
sending quit and waiting around here will make the
disconnect wait in vain and cause more problems than we need to. */
/* The IMAP session may or may not have been allocated/setup at this
point! */
if(!dead_connection && conn->bits.protoconnstart) {
if(!dead_connection && conn->bits.protoconnstart &&
!Curl_pp_needs_flush(data, &imapc->pp)) {
if(!imap_perform_logout(data, imapc))
(void)imap_block_statemach(data, imapc, TRUE); /* ignore errors */
}

View file

@ -1450,7 +1450,8 @@ static CURLcode pop3_disconnect(struct Curl_easy *data,
bad in any way, sending quit and waiting around here will make the
disconnect wait in vain and cause more problems than we need to. */
if(!dead_connection && conn->bits.protoconnstart) {
if(!dead_connection && conn->bits.protoconnstart &&
!Curl_pp_needs_flush(data, &pop3c->pp)) {
if(!pop3_perform_quit(data, conn))
(void)pop3_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */
}

View file

@ -1625,7 +1625,8 @@ static CURLcode smtp_disconnect(struct Curl_easy *data,
bad in any way, sending quit and waiting around here will make the
disconnect wait in vain and cause more problems than we need to. */
if(!dead_connection && conn->bits.protoconnstart) {
if(!dead_connection && conn->bits.protoconnstart &&
!Curl_pp_needs_flush(data, &smtpc->pp)) {
if(!smtp_perform_quit(data, smtpc))
(void)smtp_block_statemach(data, smtpc, TRUE); /* ignore on QUIT */
}