• Rabin Vincent's avatar
    cifs: fix race between call_async() and reconnect() · ece016f8
    Rabin Vincent authored
    commit 820962dc upstream.
    
    cifs_call_async() queues the MID to the pending list and calls
    smb_send_rqst().  If smb_send_rqst() performs a partial send, it sets
    the tcpStatus to CifsNeedReconnect and returns an error code to
    cifs_call_async().  In this case, cifs_call_async() removes the MID
    from the list and returns to the caller.
    
    However, cifs_call_async() releases the server mutex _before_ removing
    the MID.  This means that a cifs_reconnect() can race with this function
    and manage to remove the MID from the list and delete the entry before
    cifs_call_async() calls cifs_delete_mid().  This leads to various
    crashes due to the use after free in cifs_delete_mid().
    
    Task1				Task2
    
    cifs_call_async():
     - rc = -EAGAIN
     - mutex_unlock(srv_mutex)
    
    				cifs_reconnect():
    				 - mutex_lock(srv_mutex)
    				 - mutex_unlock(srv_mutex)
    				 - list_delete(mid)
    				 - mid->callback()
    				 	cifs_writev_callback():
    				 		- mutex_lock(srv_mutex)
    						- delete(mid)
    				 		- mutex_unlock(srv_mutex)
    
     - cifs_delete_mid(mid) <---- use after free
    
    Fix this by removing the MID in cifs_call_async() before releasing the
    srv_mutex.  Also hold the srv_mutex in cifs_reconnect() until the MIDs
    are moved out of the pending list.
    Signed-off-by: default avatarRabin Vincent <rabin.vincent@axis.com>
    Acked-by: default avatarShirish Pargaonkar <shirishpargaonkar@gmail.com>
    Signed-off-by: default avatarSteve French <sfrench@localhost.localdomain>
    [bwh: Backported to 3.2:
     - In cifs_call_async() there are two error paths jumping to 'out_err';
       fix both of them
     - s/cifs_delete_mid/delete_mid/
     - Adjust context]
    Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
    ece016f8
connect.c 109 KB