Page MenuHomePhabricator

crash in nonblock-write mode of ssh_handle_key_exchange() when receiving tcp rst after 3WHS (keepalive)
Open, Needs TriagePublic

Description

Hi,
I found libssh will crash in nonblock-write mode of ssh_handle_key_exchange() when receiving tcp rst after 3WHS (keepalive client).
Programming server with libssh like:

/* Accept socket */
ssh_bind_accept(sshbind, session);
/* Set write won't block */
ssh_set_fd_towrite(session);
/* Do the banner and key exchange */
ssh_handle_key_exchange(session);

If receiving tcp rst after 3WHS, it will crash in:
ssh_handle_key_exchange() ---> ssh_send_banner() ---> ssh_socket_write() ---> ssh_socket_nonblocking_flush()

src/socket.c
644     if (s->write_wontblock && len > 0) {                                         
645         ssize_t bwritten;                                                        
646                                                                                  
647         bwritten = ssh_socket_unbuffered_write(s,                                
648                                                ssh_buffer_get(s->out_buffer),    
649                                                len); 
               /* Failed to write because of tcp rst */                            
650         if (bwritten < 0) {                                                      
651             session->alive = 0;                                                  
652             ssh_socket_close(s);                                                 
653             
                  /* Crash here!!!!!!! Segmentfault signal 11: Invalid memory reference */                                                                     
654            ** if (s->callbacks && s->callbacks->exception) {**                       
655                 s->callbacks->exception(SSH_SOCKET_EXCEPTION_ERROR,              
656                                         s->last_errno,                           
657                                         s->callbacks->userdata);                 
658             } else {                                                             
659                 ssh_set_error(session,                                           
660                               SSH_FATAL,                                         
661                                "Writing packet: error on socket (or connection "  
662                                "closed): %s",                                     
663                                strerror(s->last_errno));                          
664             }                                                                    
665                                                                                  
666             return SSH_ERROR;                                                    
667         }

Then I notice that s->callbacks wasn't initialized in ssh_socket_new(). And s->callbacks will be set in ssh_handle_key_exchange() after ssh_send_banner():

src/server.c
462 /* Do the banner and key exchange */                                             
463 int ssh_handle_key_exchange(ssh_session session) {                               
464     int rc;                                                                      
465     if (session->session_state != SSH_SESSION_STATE_NONE)                        
466         goto pending;                                                              
467     rc = ssh_send_banner(session, 1);                                            
468     if (rc < 0) {                                                                
469         return SSH_ERROR;                                                        
470     }                                                                            
471                                                                                  
472     session->alive = 1;                                                          
473                                                                                  
474     session->ssh_connection_callback = ssh_server_connection_callback;           
475     session->session_state = SSH_SESSION_STATE_SOCKET_CONNECTED; 
           /* Set callbacks after ssh_send_banner()!!!!!! */                
476     **ssh_socket_set_callbacks(session->socket,&session->socket_callbacks);**        
477     session->socket_callbacks.data=callback_receive_banner;                      
478     session->socket_callbacks.exception=ssh_socket_exception_callback;           
479     session->socket_callbacks.userdata=session;                                  
480                                                                                  
481     rc = server_set_kex(session);

I think it's necessary to initialize callbacks to NULL in ssh_socket_new(). Please check it out, thanks.

Event Timeline

lzfmars created this task.Sun, Mar 3, 10:39 AM
lzfmars updated the task description. (Show Details)Sun, Mar 3, 10:42 AM
lzfmars added a project: Restricted Project.Mon, Mar 4, 3:55 AM