FIx: race condition on service reload

This commit is contained in:
Vladimir Dubrovin 2026-10-10 18:08:12 +03:00
parent b4f5ffbf8c
commit 7ffc8f820f

View File

@ -1339,7 +1339,16 @@ void srvfree(struct srvparam * srv){
if(srv->cbsock != INVALID_SOCKET) so._closesocket(srv->so.state, srv->cbsock); if(srv->cbsock != INVALID_SOCKET) so._closesocket(srv->so.state, srv->cbsock);
srv->cbsock = INVALID_SOCKET; srv->cbsock = INVALID_SOCKET;
srv->service = S_ZOMBIE; srv->service = S_ZOMBIE;
while(srv->child) usleep(SLEEPTIME * 100); /* under the mutex, so the last child has let go of it before it is destroyed */
for(;;){
struct clientparam *child;
_3proxy_mutex_lock(&srv->counter_mutex);
child = srv->child;
_3proxy_mutex_unlock(&srv->counter_mutex);
if(!child) break;
usleep(SLEEPTIME * 100);
}
#ifndef STDMAIN #ifndef STDMAIN
if(srv->filter){ if(srv->filter){
while(srv->nfilters){ while(srv->nfilters){
@ -1384,22 +1393,13 @@ void srvfree(struct srvparam * srv){
void freeparam(struct clientparam * param) { void freeparam(struct clientparam * param) {
if(param->srv){ if(param->srv){
if(param->srv->so.freefunc) param->srv->so.freefunc(param->sostate); if(param->srv->so.freefunc) param->srv->so.freefunc(param->sostate);
_3proxy_mutex_lock(&param->srv->counter_mutex);
#ifndef STDMAIN #ifndef STDMAIN
if(param->srv->service == S_UDPPM) hashdelete(&udp_table, param); if(param->srv->service == S_UDPPM){
#endif _3proxy_mutex_lock(&param->srv->counter_mutex);
if(param->prev || param->next || param->srv->child == param){ hashdelete(&udp_table, param);
if(param->prev){ _3proxy_mutex_unlock(&param->srv->counter_mutex);
param->prev->next = param->next;
}
else
param->srv->child = param->next;
if(param->next){
param->next->prev = param->prev;
}
(param->srv->childcount)--;
} }
_3proxy_mutex_unlock(&param->srv->counter_mutex); #endif
} }
if(param->clibuf) free(param->clibuf); if(param->clibuf) free(param->clibuf);
if(param->srvbuf) free(param->srvbuf); if(param->srvbuf) free(param->srvbuf);
@ -1439,6 +1439,24 @@ void freeparam(struct clientparam * param) {
} }
if(param->connlim) stopconnlims(param); if(param->connlim) stopconnlims(param);
#endif #endif
/* Leaving the service comes last. Once its last child is gone, srvfree()
frees the service along with its socket functions, filters and mutex,
and a close above may have lingered for seconds before getting here. */
if(param->srv){
_3proxy_mutex_lock(&param->srv->counter_mutex);
if(param->prev || param->next || param->srv->child == param){
if(param->prev){
param->prev->next = param->next;
}
else
param->srv->child = param->next;
if(param->next){
param->next->prev = param->prev;
}
(param->srv->childcount)--;
}
_3proxy_mutex_unlock(&param->srv->counter_mutex);
}
if(param->hostname) free(param->hostname); if(param->hostname) free(param->hostname);
if(param->username) free(param->username); if(param->username) free(param->username);
if(param->password) free(param->password); if(param->password) free(param->password);