From 21dcf57b4e8c8c308be4acb992c803ec8509fd25 Mon Sep 17 00:00:00 2001 From: rswindell <> Date: Wed, 22 Aug 2012 08:05:22 +0000 Subject: [PATCH] Fix race-condition during shutdown or recycle that could cause crash: active_clients (protected integer) could be 0 at the time of recycle because there can be delay at the beginning of the FTP ctrl thread (e.g. looking up filtered IPs/hostnames) before the active_clients is incremented which could be during/after it was destroyed by the cleanup() function. Now tracking number of threads using protected integer which increments immediately upon child thread creation and is not destroyed until the main thread terminates. There is currently *not* timeout while waiting for child threads to terminate upon shutdown/recycle, so this could result in a hung server thread if a child thread is indefinitely hung - consider using a timeout. --- src/sbbs3/ftpsrvr.c | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/sbbs3/ftpsrvr.c b/src/sbbs3/ftpsrvr.c index 9476df4979..7562982b24 100644 --- a/src/sbbs3/ftpsrvr.c +++ b/src/sbbs3/ftpsrvr.c @@ -8,7 +8,7 @@ * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * * - * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html * + * Copyright 2012 Rob Swindell - http://www.synchro.net/copyright.html * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * @@ -80,6 +80,7 @@ static ftp_startup_t* startup=NULL; static scfg_t scfg; static SOCKET server_socket=INVALID_SOCKET; static protected_int32_t active_clients; +static protected_int32_t thread_count; static volatile time_t uptime=0; static volatile ulong served=0; static volatile BOOL terminate_server=FALSE; @@ -205,16 +206,20 @@ static void client_off(SOCKET sock) startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE); } -static void thread_up(BOOL setuid) +static int32_t thread_up(BOOL setuid) { + int32_t count = protected_int32_adjust(&thread_count,1); if(startup!=NULL && startup->thread_up!=NULL) startup->thread_up(startup->cbdata,TRUE, setuid); + return count; } -static void thread_down(void) +static int32_t thread_down(void) { + int32_t count = protected_int32_adjust(&thread_count,-1); if(startup!=NULL && startup->thread_up!=NULL) startup->thread_up(startup->cbdata,FALSE, FALSE); + return count; } static SOCKET ftp_open_socket(int type) @@ -4473,12 +4478,12 @@ static void ctrl_thread(void* arg) ftp_close_socket(&tmp_sock,__LINE__); { - int32_t remain = protected_int32_adjust(&active_clients, -1); + int32_t clients = protected_int32_adjust(&active_clients, -1); + int32_t threads = thread_down(); update_clients(); - thread_down(); - lprintf(LOG_INFO,"%04d CTRL thread terminated (%ld clients remain, %lu served)" - ,sock, remain, served); + lprintf(LOG_INFO,"%04d CTRL thread terminated (%ld clients and %ld threads remain, %lu served)" + ,sock, clients, threads, served); } } @@ -4497,6 +4502,13 @@ static void cleanup(int code, int line) if(server_socket!=INVALID_SOCKET) ftp_close_socket(&server_socket,__LINE__); + if(thread_count.value > 1) { + lprintf(LOG_DEBUG,"#### Waiting for %d child threads to terminate", thread_count.value-1); + while(thread_count.value > 1) { + mswait(100); + } + } + if(active_clients.value) lprintf(LOG_WARNING,"#### !FTP Server terminating with %ld active clients", active_clients.value); else @@ -4615,6 +4627,7 @@ void DLLCALL ftp_server(void* arg) startup->recycle_now=FALSE; startup->shutdown_now=FALSE; terminate_server=FALSE; + protected_int32_init(&thread_count, 0); do { @@ -4641,7 +4654,7 @@ void DLLCALL ftp_server(void* arg) if(!winsock_startup()) { cleanup(1,__LINE__); - return; + break; } t=time(NULL); @@ -4660,7 +4673,7 @@ void DLLCALL ftp_server(void* arg) lprintf(LOG_CRIT,"!ERROR %s",error); lprintf(LOG_CRIT,"!Failed to load configuration files"); cleanup(1,__LINE__); - return; + break; } if(startup->host_name[0]==0) @@ -4683,7 +4696,7 @@ void DLLCALL ftp_server(void* arg) if(!isdir(scfg.temp_dir)) { lprintf(LOG_CRIT,"!Invalid temp directory: %s", scfg.temp_dir); cleanup(1,__LINE__); - return; + break; } if(!startup->max_clients) { @@ -4724,7 +4737,7 @@ void DLLCALL ftp_server(void* arg) if((server_socket=ftp_open_socket(SOCK_STREAM))==INVALID_SOCKET) { lprintf(LOG_CRIT,"!ERROR %d opening socket", ERROR_VALUE); cleanup(1,__LINE__); - return; + break; } lprintf(LOG_DEBUG,"%04d FTP Server socket opened",server_socket); @@ -4751,14 +4764,14 @@ void DLLCALL ftp_server(void* arg) if(result!=0) { lprintf(LOG_CRIT,"%04d %s", server_socket, BIND_FAILURE_HELP); cleanup(1,__LINE__); - return; + break; } if((result=listen(server_socket, 1))!= 0) { lprintf(LOG_CRIT,"%04d !ERROR %d (%d) listening on socket" ,server_socket, result, ERROR_VALUE); cleanup(1,__LINE__); - return; + break; } lprintf(LOG_INFO,"%04d FTP Server listening on port %u",server_socket,startup->port); @@ -4782,7 +4795,7 @@ void DLLCALL ftp_server(void* arg) while(server_socket!=INVALID_SOCKET && !terminate_server) { - if(active_clients.value==0) { + if(thread_count.value <= 1) { if(!(startup->options&FTP_OPT_NO_RECYCLE)) { if((p=semfile_list_check(&initialized,recycle_semfiles))!=NULL) { lprintf(LOG_INFO,"0000 Recycle semaphore file (%s) detected",p); @@ -4907,4 +4920,6 @@ void DLLCALL ftp_server(void* arg) } } while(!terminate_server); + + protected_int32_destroy(thread_count); } -- GitLab