@ -148,6 +148,26 @@ int stcp_try_send(struct stcp_conn *c, uint8_t *data, size_t len) {
return 0 ;
return 0 ;
}
}
// Write-error больше не закрывает conn синхронно: do_close → on_close →
// tcp_link_close_cb → etcp_link_close освобождает ETCP_LINK, поэтому при
// синхронном send-пути (stcp_link_send → flush → try_send) каскад уходил бы
// в стек вызывающего (keepalive_timer_cb/etcp_tcp_send) — use-after-free.
static void stcp_close_deferred ( void * arg ) {
struct stcp_conn * c = ( struct stcp_conn * ) arg ;
if ( ! c ) return ;
c - > close_call_soon = NULL ;
DEBUG_DEBUG ( DEBUG_CATEGORY_ETCP , " stcp write-error close deferred: err=%d state=%d sock=%d " ,
c - > close_err , ( int ) c - > state , ( int ) c - > sock ) ;
stcp_conn_do_close ( c , c - > close_err ) ;
}
static void stcp_schedule_close ( struct stcp_conn * c , int err ) {
if ( ! c ) return ;
if ( c - > close_call_soon ) return ; // уже запланирован — первый err остаётся
c - > close_err = err ;
c - > close_call_soon = uasync_call_soon ( c - > ua , c , stcp_close_deferred ) ;
}
void stcp_write_cb ( socket_t sock , void * arg ) {
void stcp_write_cb ( socket_t sock , void * arg ) {
struct stcp_conn * c = ( struct stcp_conn * ) arg ;
struct stcp_conn * c = ( struct stcp_conn * ) arg ;
if ( ! c - > send_buf ) { uasync_set_socket_write ( c - > ua , c - > socket_id , 0 ) ; return ; }
if ( ! c - > send_buf ) { uasync_set_socket_write ( c - > ua , c - > socket_id , 0 ) ; return ; }
@ -156,7 +176,7 @@ void stcp_write_cb(socket_t sock, void *arg) {
int err = socket_get_error ( ) ;
int err = socket_get_error ( ) ;
if ( err = = ERR_AGAIN | | err = = ERR_WOULDBLOCK ) return ;
if ( err = = ERR_AGAIN | | err = = ERR_WOULDBLOCK ) return ;
DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " stcp_write_cb send failed err=%d " , err ) ;
DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " stcp_write_cb send failed err=%d " , err ) ;
if ( c - > on_write_error ) c - > on_write_error ( c , err ) ;
stcp_schedule_close ( c , err ) ;
return ;
return ;
}
}
c - > send_offset + = ( size_t ) sent ;
c - > send_offset + = ( size_t ) sent ;
@ -169,7 +189,7 @@ void stcp_write_cb(socket_t sock, void *arg) {
}
}
void stcp_flush_pending ( struct stcp_conn * c ) {
void stcp_flush_pending ( struct stcp_conn * c ) {
if ( ! c ) return ;
if ( ! c | | c - > state ! = STCP_STATE_DATA ) return ; // conn закрыт/не DATA — слать нечего
while ( ! c - > send_buf & & c - > pending_head ) {
while ( ! c - > send_buf & & c - > pending_head ) {
struct pending_entry * pe = c - > pending_head ;
struct pending_entry * pe = c - > pending_head ;
c - > pending_head = pe - > next ;
c - > pending_head = pe - > next ;
@ -180,14 +200,14 @@ void stcp_flush_pending(struct stcp_conn *c) {
if ( ! enc ) { DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " stcp_flush_pending malloc enc failed len=%zu " , pe - > len ) ; if ( pe - > data ) u_free ( pe - > data ) ; u_free ( pe ) ; continue ; }
if ( ! enc ) { DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " stcp_flush_pending malloc enc failed len=%zu " , pe - > len ) ; if ( pe - > data ) u_free ( pe - > data ) ; u_free ( pe ) ; continue ; }
if ( stcp_frame_encrypt ( c , pe - > data , pe - > len , enc , & enc_len ) ) {
if ( stcp_frame_encrypt ( c , pe - > data , pe - > len , enc , & enc_len ) ) {
DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " stcp_flush_pending encrypt failed " ) ; u_free ( enc ) ; if ( pe - > data ) u_free ( pe - > data ) ; u_free ( pe ) ;
DEBUG_ERROR ( DEBUG_CATEGORY_ETCP , " stcp_flush_pending encrypt failed " ) ; u_free ( enc ) ; if ( pe - > data ) u_free ( pe - > data ) ; u_free ( pe ) ;
if ( c - > on_write_error ) c - > on_write_error ( c , ECANCELED ) ;
stcp_schedule_close ( c , ECANCELED ) ;
return ;
return ;
}
}
if ( pe - > data ) u_free ( pe - > data ) ;
if ( pe - > data ) u_free ( pe - > data ) ;
u_free ( pe ) ;
u_free ( pe ) ;
int r = stcp_try_send ( c , enc , enc_len ) ;
int r = stcp_try_send ( c , enc , enc_len ) ;
if ( r < 0 ) { u_free ( enc ) ; if ( c - > on_write_error ) c - > on_write_error ( c , ECANCELED ) ; return ; }
if ( r < 0 ) { u_free ( enc ) ; stcp_schedule_close ( c , ECANCELED ) ; return ; }
if ( r > 0 ) return ;
if ( r > 0 ) return ;
}
}
}
}
@ -321,6 +341,7 @@ void stcp_conn_do_close(struct stcp_conn *c, int err) {
stcp_server_remove_conn ( c ) ;
stcp_server_remove_conn ( c ) ;
if ( c - > hs_timer ) { uasync_cancel_timeout ( c - > ua , c - > hs_timer ) ; c - > hs_timer = NULL ; }
if ( c - > hs_timer ) { uasync_cancel_timeout ( c - > ua , c - > hs_timer ) ; c - > hs_timer = NULL ; }
if ( c - > close_call_soon ) { uasync_call_soon_cancel ( c - > ua , c - > close_call_soon ) ; c - > close_call_soon = NULL ; }
DEBUG_INFO ( DEBUG_CATEGORY_ETCP , " stcp_conn close is_server=%d prev_state=%d err=%d sock=%d " , c - > is_server , prev , err , ( int ) c - > sock ) ;
DEBUG_INFO ( DEBUG_CATEGORY_ETCP , " stcp_conn close is_server=%d prev_state=%d err=%d sock=%d " , c - > is_server , prev , err , ( int ) c - > sock ) ;