Re: [Clamav-devel] clamd: Keep scanning while reloading database

2014-04-28 Thread Julius Plenz
Hi,

* Julius Plenz pl...@cis.fu-berlin.de [2014-04-07 20:43]:
  1) Start reload_db() in a background thread, resume scanning, and call
 back once the new engine is in place; then exchange the pointers
 from old to new engine and free the old one.
 
 FWIW, I have implemented this option, and it seems to work just
 fine.

We have been running ClamAV on all incoming mail for the past week
while periodically reloading a fresh definition database, and it works
very well. Any chance of getting this included into upstream?

Current patch against Git tag clamav-0.98.1 pasted below.

Cheers,

Julius
-- 
 clamd/server-th.c | 145 --
 clamd/server.h|   7 +++
 2 files changed, 94 insertions(+), 58 deletions(-)

diff --git a/clamd/server-th.c b/clamd/server-th.c
index 86a3a48..3df5c9c 100644
--- a/clamd/server-th.c
+++ b/clamd/server-th.c
@@ -64,6 +64,8 @@
 int progexit = 0;
 pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER;
 int reload = 0;
+volatile int reload_in_progress = 0;
+struct cl_engine *reload_engine = NULL;
 time_t reloaded_time = 0;
 pthread_mutex_t reload_mutex = PTHREAD_MUTEX_INITIALIZER;
 int sighup = 0;
@@ -158,40 +160,27 @@ void sighandler_th(int sig)
logg($Failed to write to syncpipe\n);
 }
 
-static struct cl_engine *reload_db(struct cl_engine *engine, unsigned int 
dboptions, const struct optstruct *opts, int do_check, int *ret)
+static void *reload_db(void *argp)
 {
-   const char *dbdir;
-   int retval;
-   unsigned int sigs = 0;
-   struct cl_settings *settings = NULL;
-
-*ret = 0;
-if(do_check) {
-   if(!dbstat.entries) {
-   logg(No stats for Database check - forcing reload\n);
-   return engine;
-   }
-
-   if(cl_statchkdir(dbstat) == 1) {
-   logg(SelfCheck: Database modification detected. Forcing 
reload.\n);
-   return engine;
-   } else {
-   logg(SelfCheck: Database status OK.\n);
-   return NULL;
-   }
-}
-
-/* release old structure */
-if(engine) {
+const char *dbdir;
+int retval;
+unsigned int sigs = 0;
+struct cl_settings *settings = NULL;
+struct cl_engine *engine = NULL;
+
+struct reload_db_args *args = (struct reload_db_args *)argp;
+struct cl_engine *oldengine = args-engine;
+unsigned int dboptions = args-dboptions;
+const struct optstruct *opts = args-opts;
+
+if(oldengine) {
/* copy current settings */
-   settings = cl_engine_settings_copy(engine);
+   settings = cl_engine_settings_copy(oldengine);
if(!settings)
logg(^Can't make a copy of the current engine settings\n);
-
-   thrmgr_setactiveengine(NULL);
-   cl_engine_free(engine);
 }
 
+
 dbdir = optget(opts, DatabaseDirectory)-strarg;
 logg(Reading databases from %s\n, dbdir);
 
@@ -201,18 +190,12 @@ static struct cl_engine *reload_db(struct cl_engine 
*engine, unsigned int dbopti
 memset(dbstat, 0, sizeof(struct cl_stat));
 if((retval = cl_statinidir(dbdir, dbstat))) {
logg(!cl_statinidir() failed: %s\n, cl_strerror(retval));
-   *ret = 1;
-   if(settings)
-   cl_engine_settings_free(settings);
-   return NULL;
+   goto err;
 }
 
 if(!(engine = cl_engine_new())) {
logg(!Can't initialize antivirus engine\n);
-   *ret = 1;
-   if(settings)
-   cl_engine_settings_free(settings);
-   return NULL;
+   goto err;
 }
 
 if(settings) {
@@ -222,25 +205,38 @@ static struct cl_engine *reload_db(struct cl_engine 
*engine, unsigned int dbopti
logg(^Using default engine settings\n);
}
cl_engine_settings_free(settings);
+   settings = NULL;
 }
 
 if((retval = cl_load(dbdir, engine, sigs, dboptions))) {
logg(!reload db failed: %s\n, cl_strerror(retval));
-   cl_engine_free(engine);
-   *ret = 1;
-   return NULL;
+   goto err;
 }
 
 if((retval = cl_engine_compile(engine)) != 0) {
logg(!Database initialization error: can't compile engine: %s\n, 
cl_strerror(retval));
-   cl_engine_free(engine);
-   *ret = 1;
-   return NULL;
+   goto err;
 }
 logg(Database correctly reloaded (%u signatures)\n, sigs);
 
-thrmgr_setactiveengine(engine);
-return engine;
+pthread_mutex_lock(reload_mutex);
+time(reloaded_time);
+reload_engine = engine;
+goto end;
+
+err:
+if(settings)
+   cl_engine_settings_free(settings);
+if(engine)
+   cl_engine_free(engine);
+
+pthread_mutex_lock(reload_mutex);
+
+end:
+reload_in_progress = 0;
+pthread_mutex_unlock(reload_mutex);
+
+free(args);
 }
 
 /*
@@ -713,6 +709,7 @@ int recvloop_th(int *socketds, unsigned nsockets, struct 
cl_engine *engine, unsi
unsigned long long val;
size_t i, j, rr_last = 0;
pthread_t accept_th;
+   pthread_t reload_th;
pthread_mutex_t 

Re: [Clamav-devel] clamd: Keep scanning while reloading database

2014-04-28 Thread Steven Morgan
Thanks for sending a patch, we will look at integrating it with ClamAV.

I have opened bug # 10979 at bugzilla.clamav.net for tracking.


On Mon, Apr 28, 2014 at 10:29 AM, Julius Plenz pl...@cis.fu-berlin.dewrote:

 Hi,

 * Julius Plenz pl...@cis.fu-berlin.de [2014-04-07 20:43]:
   1) Start reload_db() in a background thread, resume scanning, and call
  back once the new engine is in place; then exchange the pointers
  from old to new engine and free the old one.
 
  FWIW, I have implemented this option, and it seems to work just
  fine.

 We have been running ClamAV on all incoming mail for the past week
 while periodically reloading a fresh definition database, and it works
 very well. Any chance of getting this included into upstream?

 Current patch against Git tag clamav-0.98.1 pasted below.

 Cheers,

 Julius
 --
  clamd/server-th.c | 145
 --
  clamd/server.h|   7 +++
  2 files changed, 94 insertions(+), 58 deletions(-)

 diff --git a/clamd/server-th.c b/clamd/server-th.c
 index 86a3a48..3df5c9c 100644
 --- a/clamd/server-th.c
 +++ b/clamd/server-th.c
 @@ -64,6 +64,8 @@
  int progexit = 0;
  pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER;
  int reload = 0;
 +volatile int reload_in_progress = 0;
 +struct cl_engine *reload_engine = NULL;
  time_t reloaded_time = 0;
  pthread_mutex_t reload_mutex = PTHREAD_MUTEX_INITIALIZER;
  int sighup = 0;
 @@ -158,40 +160,27 @@ void sighandler_th(int sig)
 logg($Failed to write to syncpipe\n);
  }

 -static struct cl_engine *reload_db(struct cl_engine *engine, unsigned int
 dboptions, const struct optstruct *opts, int do_check, int *ret)
 +static void *reload_db(void *argp)
  {
 -   const char *dbdir;
 -   int retval;
 -   unsigned int sigs = 0;
 -   struct cl_settings *settings = NULL;
 -
 -*ret = 0;
 -if(do_check) {
 -   if(!dbstat.entries) {
 -   logg(No stats for Database check - forcing reload\n);
 -   return engine;
 -   }
 -
 -   if(cl_statchkdir(dbstat) == 1) {
 -   logg(SelfCheck: Database modification detected. Forcing
 reload.\n);
 -   return engine;
 -   } else {
 -   logg(SelfCheck: Database status OK.\n);
 -   return NULL;
 -   }
 -}
 -
 -/* release old structure */
 -if(engine) {
 +const char *dbdir;
 +int retval;
 +unsigned int sigs = 0;
 +struct cl_settings *settings = NULL;
 +struct cl_engine *engine = NULL;
 +
 +struct reload_db_args *args = (struct reload_db_args *)argp;
 +struct cl_engine *oldengine = args-engine;
 +unsigned int dboptions = args-dboptions;
 +const struct optstruct *opts = args-opts;
 +
 +if(oldengine) {
 /* copy current settings */
 -   settings = cl_engine_settings_copy(engine);
 +   settings = cl_engine_settings_copy(oldengine);
 if(!settings)
 logg(^Can't make a copy of the current engine settings\n);
 -
 -   thrmgr_setactiveengine(NULL);
 -   cl_engine_free(engine);
  }

 +
  dbdir = optget(opts, DatabaseDirectory)-strarg;
  logg(Reading databases from %s\n, dbdir);

 @@ -201,18 +190,12 @@ static struct cl_engine *reload_db(struct cl_engine
 *engine, unsigned int dbopti
  memset(dbstat, 0, sizeof(struct cl_stat));
  if((retval = cl_statinidir(dbdir, dbstat))) {
 logg(!cl_statinidir() failed: %s\n, cl_strerror(retval));
 -   *ret = 1;
 -   if(settings)
 -   cl_engine_settings_free(settings);
 -   return NULL;
 +   goto err;
  }

  if(!(engine = cl_engine_new())) {
 logg(!Can't initialize antivirus engine\n);
 -   *ret = 1;
 -   if(settings)
 -   cl_engine_settings_free(settings);
 -   return NULL;
 +   goto err;
  }

  if(settings) {
 @@ -222,25 +205,38 @@ static struct cl_engine *reload_db(struct cl_engine
 *engine, unsigned int dbopti
 logg(^Using default engine settings\n);
 }
 cl_engine_settings_free(settings);
 +   settings = NULL;
  }

  if((retval = cl_load(dbdir, engine, sigs, dboptions))) {
 logg(!reload db failed: %s\n, cl_strerror(retval));
 -   cl_engine_free(engine);
 -   *ret = 1;
 -   return NULL;
 +   goto err;
  }

  if((retval = cl_engine_compile(engine)) != 0) {
 logg(!Database initialization error: can't compile engine: %s\n,
 cl_strerror(retval));
 -   cl_engine_free(engine);
 -   *ret = 1;
 -   return NULL;
 +   goto err;
  }
  logg(Database correctly reloaded (%u signatures)\n, sigs);

 -thrmgr_setactiveengine(engine);
 -return engine;
 +pthread_mutex_lock(reload_mutex);
 +time(reloaded_time);
 +reload_engine = engine;
 +goto end;
 +
 +err:
 +if(settings)
 +   cl_engine_settings_free(settings);
 +if(engine)
 +   cl_engine_free(engine);
 +
 +pthread_mutex_lock(reload_mutex);
 +
 +end:
 +

Re: [Clamav-devel] clamd: Keep scanning while reloading database

2014-04-14 Thread Julius Plenz
Hi, Mark!

* Mark Pizzolato - ClamAV-devel clamav-de...@subscriptions.pizzolato.net 
[2014-04-10 18:45]:
 One more question (as I originally said, I haven't looked closely at
 either the current code or your suggested patch)...  What happens if
 multiple reload requests come in right after each other while
 scanning thread(s) are still scanning some file(s)?

Then the threads would override each other's work and you'd have
unreferenced engines flying around. Good catch. Pasted below is a
patch to address that problem.

Julius
-- 
From a178a359970d65a0c439c8e3a84e861cbc9fd944 Mon Sep 17 00:00:00 2001
From: Julius Plenz pl...@cis.fu-berlin.de
Date: Mon, 14 Apr 2014 11:05:06 +0200
Subject: [PATCH] Introduce a reload_in_progress flag

---
 clamd/server-th.c | 9 -
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/clamd/server-th.c b/clamd/server-th.c
index 9afa2b7..2167475 100644
--- a/clamd/server-th.c
+++ b/clamd/server-th.c
@@ -64,6 +64,7 @@
 int progexit = 0;
 pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER;
 int reload = 0;
+volatile int reload_in_progress = 0;
 struct cl_engine *reload_engine = NULL;
 time_t reloaded_time = 0;
 pthread_mutex_t reload_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -226,6 +227,7 @@ static void *reload_db(void *argp)
 pthread_mutex_lock(reload_mutex);
 time(reloaded_time);
 reload_engine = engine;
+reload_in_progress = 0;
 pthread_mutex_unlock(reload_mutex);
 }
 
@@ -1355,7 +1357,11 @@ int recvloop_th(int *socketds, unsigned nsockets, struct 
cl_engine *engine, unsi
 
/* DB reload */
pthread_mutex_lock(reload_mutex);
-   if(reload) {
+   if(reload  reload_in_progress) {
+   logg(Database reload already in progress; ignoring reload 
request.\n);
+   reload = 0;
+   pthread_mutex_unlock(reload_mutex);
+   } else if(reload) {
pthread_mutex_unlock(reload_mutex);
struct reload_db_args reload_db_args = {
.engine = engine,
@@ -1364,6 +1370,7 @@ int recvloop_th(int *socketds, unsigned nsockets, struct 
cl_engine *engine, unsi
};
pthread_mutex_lock(reload_mutex);
reload = 0;
+   reload_in_progress = 1;
pthread_create(reload_th, NULL, reload_db, reload_db_args);
pthread_mutex_unlock(reload_mutex);
} else {
-- 
1.9.0-zedat

___
http://lurker.clamav.net/list/clamav-devel.html
Please submit your patches to our Bugzilla: http://bugs.clamav.net


Re: [Clamav-devel] clamd: Keep scanning while reloading database

2014-04-10 Thread Julius Plenz
Hi, Mark!

* Mark Pizzolato - ClamAV-devel clamav-de...@subscriptions.pizzolato.net 
[2014-04-08 00:02]:
  It appears that for every connection that is acceptey by clamd,
  the current engine value is passed in the conn struct. The
  engine struct has a ref count, and a process grabs the engine by
  calling cl_engine_addref(), thus increasing the ref count. Only
  when cl_engine_free() is called and the ref count is zero is the
  object actually freed.

 It would seem that there is a race condition in this paradigm.  The
 reference to the engine object should be added when the engine value
 is set in the conn structure (this determining of the engine value
 AND the addition of the reference count should be done with the
 related mutex held).

True, this would be the better approach. The downside is that one has
to free (i.e. decrease the ref count of) this not-yet-used object
every time an error occurs. So in many error cases, you'd have
useless calls to cl_engine_free().

 The current paradigm seems to be creating an un-accounted reference
 and later on incrementing the reference value.  By the time that
 increment happens the engine value which was passed may have already
 been freed and thus the pointer being deference is no longer
 pointing at a valid object.

This is a valid concern, but with the patch I posted I think this can
not happen. The patch mainly changes the behaviour of recvloop_th(),
the receiving thread. Further we have an accept thread and scanner
threads.

The receiving thread loops over outstanding requests and dispatches
them. It is after the dispatching is done that we check for errors, if
the program should exit, or if the DB should be reloaded. Then the
loop is re-started.

The dispatching happens via the following callpath:

recvloop_th - parse_dispatch_cmd -
- execute_or_dispatch_command - dispatch_command

dispatch_command() duplicates the conn structure, and now calls
cl_engine_addref(dup_conn-engine). But only now the connection is
handed to a scanner thread via thrmgr_group_dispatch(). In case this
works, dispatch_command() will not free the engine, because this is
now the scanner thread's job. The function returns to the receive loop
eventually -- only now we can come to the point where we re-set the
pointer to the newly-loaded engine and free the old one.

So I think there is no race condition here.

Julius
___
http://lurker.clamav.net/list/clamav-devel.html
Please submit your patches to our Bugzilla: http://bugs.clamav.net


Re: [Clamav-devel] clamd: Keep scanning while reloading database

2014-04-10 Thread Mark Pizzolato - ClamAV-devel
Hi Julius,

On Thursday, April 10, 2014 at 4:19 AM, Julius Plenz wrote:
 Hi, Mark!
 
 * Mark Pizzolato - ClamAV-devel clamav-de...@subscriptions.pizzolato.net 
 [2014-04-08 00:02]:
   It appears that for every connection that is acceptey by clamd, the
   current engine value is passed in the conn struct. The engine
   struct has a ref count, and a process grabs the engine by calling
   cl_engine_addref(), thus increasing the ref count. Only when
   cl_engine_free() is called and the ref count is zero is the object
   actually freed.
 
  It would seem that there is a race condition in this paradigm.  The
  reference to the engine object should be added when the engine value
  is set in the conn structure (this determining of the engine value AND
  the addition of the reference count should be done with the related
  mutex held).
 
 True, this would be the better approach. The downside is that one has to
 free (i.e. decrease the ref count of) this not-yet-used object every time an
 error occurs. So in many error cases, you'd have useless calls to
 cl_engine_free().
 
  The current paradigm seems to be creating an un-accounted reference
  and later on incrementing the reference value.  By the time that
  increment happens the engine value which was passed may have already
  been freed and thus the pointer being deference is no longer pointing
  at a valid object.
 
 This is a valid concern, but with the patch I posted I think this can not 
 happen.
 The patch mainly changes the behaviour of recvloop_th(), the receiving
 thread. Further we have an accept thread and scanner threads.
 
 The receiving thread loops over outstanding requests and dispatches them.
 It is after the dispatching is done that we check for errors, if the program
 should exit, or if the DB should be reloaded. Then the loop is re-started.
 
 The dispatching happens via the following callpath:
 
 recvloop_th - parse_dispatch_cmd -
 - execute_or_dispatch_command - dispatch_command
 
 dispatch_command() duplicates the conn structure, and now calls
 cl_engine_addref(dup_conn-engine). But only now the connection is
 handed to a scanner thread via thrmgr_group_dispatch(). In case this works,
 dispatch_command() will not free the engine, because this is now the
 scanner thread's job. The function returns to the receive loop eventually --
 only now we can come to the point where we re-set the pointer to the
 newly-loaded engine and free the old one.
 
 So I think there is no race condition here.

Well, I initially said I haven't looked closely'.  You now have looked closely 
and your analysis looks quite reasonable.

One more question (as I originally said, I haven't looked closely at either the 
current code or your suggested patch)...  What happens if multiple reload 
requests come in right after each other while scanning thread(s) are still 
scanning some file(s)?

- Mark

___
http://lurker.clamav.net/list/clamav-devel.html
Please submit your patches to our Bugzilla: http://bugs.clamav.net


Re: [Clamav-devel] clamd: Keep scanning while reloading database

2014-04-08 Thread Julius Plenz
Hi, Mark!

* Mark Pizzolato - ClamAV-devel clamav-de...@subscriptions.pizzolato.net 
[2014-04-08 00:02]:
  It appears that for every connection that is acceptey by clamd,
  the current engine value is passed in the conn struct. The
  engine struct has a ref count, and a process grabs the engine by
  calling cl_engine_addref(), thus increasing the ref count. Only
  when cl_engine_free() is called and the ref count is zero is the
  object actually freed.

 It would seem that there is a race condition in this paradigm.  The
 reference to the engine object should be added when the engine value
 is set in the conn structure (this determining of the engine value
 AND the addition of the reference count should be done with the
 related mutex held).  The current paradigm seems to be creating an
 un-accounted reference and later on incrementing the reference
 value.  By the time that increment happens the engine value which
 was passed may have already been freed and thus the pointer being
 deference is no longer pointing at a valid object.

Yes, you are certainly right. Thanks for pointing this out! I will try
to work around this issue some time this week.

Julius
___
http://lurker.clamav.net/list/clamav-devel.html
Please submit your patches to our Bugzilla: http://bugs.clamav.net


Re: [Clamav-devel] clamd: Keep scanning while reloading database

2014-04-07 Thread Julius Plenz
Hi,

 1) Start reload_db() in a background thread, resume scanning, and call
back once the new engine is in place; then exchange the pointers
from old to new engine and free the old one.

FWIW, I have implemented this option, and it seems to work just fine.
Patch is pasted below.

Do you see any problems with this approach?

Julius

-- 
From 56e376e55db86571e50737cf20a33b8716cad291 Mon Sep 17 00:00:00 2001
From: Julius Plenz pl...@cis.fu-berlin.de
Date: Mon, 7 Apr 2014 19:21:44 +0200
Subject: [PATCH] Reload virus definition in separate thread, eliminating delay

This implements option #1 of
http://lurker.clamav.net/message/20140407.145009.12f41a68.en.html

We introduce a daemon-global reload_engine pointer, also protected
under the reload_mutex. Instead of calling reload_db() syncronously, we
dispatch a thread that will eventually write to the reload_engine
pointer, signaling that the new engine is fully set up. Then, under
protection of the mutex, the current engine is exchanged and the old
engine freed.
---
 clamd/server-th.c | 120 +-
 clamd/server.h|   7 
 2 files changed, 71 insertions(+), 56 deletions(-)

diff --git a/clamd/server-th.c b/clamd/server-th.c
index 86a3a48..9afa2b7 100644
--- a/clamd/server-th.c
+++ b/clamd/server-th.c
@@ -64,6 +64,7 @@
 int progexit = 0;
 pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER;
 int reload = 0;
+struct cl_engine *reload_engine = NULL;
 time_t reloaded_time = 0;
 pthread_mutex_t reload_mutex = PTHREAD_MUTEX_INITIALIZER;
 int sighup = 0;
@@ -158,40 +159,27 @@ void sighandler_th(int sig)
logg($Failed to write to syncpipe\n);
 }
 
-static struct cl_engine *reload_db(struct cl_engine *engine, unsigned int 
dboptions, const struct optstruct *opts, int do_check, int *ret)
+static void *reload_db(void *argp)
 {
const char *dbdir;
int retval;
unsigned int sigs = 0;
struct cl_settings *settings = NULL;
+   struct cl_engine *engine = NULL;
 
-*ret = 0;
-if(do_check) {
-   if(!dbstat.entries) {
-   logg(No stats for Database check - forcing reload\n);
-   return engine;
-   }
-
-   if(cl_statchkdir(dbstat) == 1) {
-   logg(SelfCheck: Database modification detected. Forcing 
reload.\n);
-   return engine;
-   } else {
-   logg(SelfCheck: Database status OK.\n);
-   return NULL;
-   }
-}
+   struct reload_db_args *args = (struct reload_db_args *)argp;
+   struct cl_engine *oldengine = args-engine;
+   unsigned int dboptions = args-dboptions;
+   const struct optstruct *opts = args-opts;
 
-/* release old structure */
-if(engine) {
+if(oldengine) {
/* copy current settings */
-   settings = cl_engine_settings_copy(engine);
+   settings = cl_engine_settings_copy(oldengine);
if(!settings)
logg(^Can't make a copy of the current engine settings\n);
-
-   thrmgr_setactiveengine(NULL);
-   cl_engine_free(engine);
 }
 
+
 dbdir = optget(opts, DatabaseDirectory)-strarg;
 logg(Reading databases from %s\n, dbdir);
 
@@ -201,18 +189,16 @@ static struct cl_engine *reload_db(struct cl_engine 
*engine, unsigned int dbopti
 memset(dbstat, 0, sizeof(struct cl_stat));
 if((retval = cl_statinidir(dbdir, dbstat))) {
logg(!cl_statinidir() failed: %s\n, cl_strerror(retval));
-   *ret = 1;
if(settings)
cl_engine_settings_free(settings);
-   return NULL;
+   return;
 }
 
 if(!(engine = cl_engine_new())) {
logg(!Can't initialize antivirus engine\n);
-   *ret = 1;
if(settings)
cl_engine_settings_free(settings);
-   return NULL;
+   return;
 }
 
 if(settings) {
@@ -227,20 +213,20 @@ static struct cl_engine *reload_db(struct cl_engine 
*engine, unsigned int dbopti
 if((retval = cl_load(dbdir, engine, sigs, dboptions))) {
logg(!reload db failed: %s\n, cl_strerror(retval));
cl_engine_free(engine);
-   *ret = 1;
-   return NULL;
+   return;
 }
 
 if((retval = cl_engine_compile(engine)) != 0) {
logg(!Database initialization error: can't compile engine: %s\n, 
cl_strerror(retval));
cl_engine_free(engine);
-   *ret = 1;
-   return NULL;
+   return;
 }
 logg(Database correctly reloaded (%u signatures)\n, sigs);
 
-thrmgr_setactiveengine(engine);
-return engine;
+pthread_mutex_lock(reload_mutex);
+time(reloaded_time);
+reload_engine = engine;
+pthread_mutex_unlock(reload_mutex);
 }
 
 /*
@@ -713,6 +699,7 @@ int recvloop_th(int *socketds, unsigned nsockets, struct 
cl_engine *engine, unsi
unsigned long long val;
size_t i, j, rr_last = 0;
pthread_t accept_th;
+   pthread_t reload_th;
pthread_mutex_t fds_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t recvfds_mutex = 

Re: [Clamav-devel] clamd: Keep scanning while reloading database

2014-04-07 Thread Mark Pizzolato - ClamAV-devel
  1) Start reload_db() in a background thread, resume scanning, and call
 back once the new engine is in place; then exchange the pointers
 from old to new engine and free the old one.
 
 FWIW, I have implemented this option, and it seems to work just fine.
 Patch is pasted below.
 
 Do you see any problems with this approach?

Hi,

I haven't looked closely, but how is the fact that each thread (which may 
currently be scanning a different file and may finish at some arbitrary time in 
the future) has a reference to the current engine object handled?  It would 
seem that some sort of a reference count could be used to manage which thread 
actually frees the engine object which is being replaced...  Maybe the settings 
object also needs a local reference in each of the scanning threads...

- Mark Pizzolato


___
http://lurker.clamav.net/list/clamav-devel.html
Please submit your patches to our Bugzilla: http://bugs.clamav.net


Re: [Clamav-devel] clamd: Keep scanning while reloading database

2014-04-07 Thread Julius Plenz
Hi, Mark!

* Mark Pizzolato - ClamAV-devel clamav-de...@subscriptions.pizzolato.net 
[2014-04-07 21:17]:
 I haven't looked closely, but how is the fact that each thread
 (which may currently be scanning a different file and may finish at
 some arbitrary time in the future) has a reference to the current
 engine object handled?

It appears that for every connection that is acceptey by clamd, the
current engine value is passed in the conn struct. The engine
struct has a ref count, and a process grabs the engine by calling
cl_engine_addref(), thus increasing the ref count. Only when
cl_engine_free() is called and the ref count is zero is the object
actually freed.

This is what I got from a cursory reading of the code... (such a
request/command is passed around quite a lot.) I verified that the old
engine was indeed freed after the scan finished by plucking in debug
statements in the relevant functions.

Julius
___
http://lurker.clamav.net/list/clamav-devel.html
Please submit your patches to our Bugzilla: http://bugs.clamav.net


Re: [Clamav-devel] clamd: Keep scanning while reloading database

2014-04-07 Thread Mark Pizzolato - ClamAV-devel
Hi Julius,

On Monday, April 07, 2014 at 1:50 PM, Julius Plenz wrote:
 * Mark Pizzolato - ClamAV-devel clamav-
 de...@subscriptions.pizzolato.net [2014-04-07 21:17]:
  I haven't looked closely, but how is the fact that each thread (which
  may currently be scanning a different file and may finish at some
  arbitrary time in the future) has a reference to the current engine
  object handled?
 
 It appears that for every connection that is acceptey by clamd, the current
 engine value is passed in the conn struct. The engine struct has a ref
 count, and a process grabs the engine by calling cl_engine_addref(), thus
 increasing the ref count. Only when
 cl_engine_free() is called and the ref count is zero is the object actually
 freed.

It would seem that there is a race condition in this paradigm.  The reference 
to the engine object should be added when the engine value is set in the conn 
structure (this determining of the engine value AND the addition of the 
reference count should be done with the related mutex held).  The current 
paradigm seems to be creating an un-accounted reference and later on 
incrementing the reference value.  By the time that increment happens the 
engine value which was passed may have already been freed and thus the pointer 
being deference is no longer pointing at a valid object.

 This is what I got from a cursory reading of the code... (such a
 request/command is passed around quite a lot.) I verified that the old engine
 was indeed freed after the scan finished by plucking in debug statements in
 the relevant functions.

It is good that it usually is freed.  That doesn't prove that there isn't a 
race condition which can cause corruption at arbitrary times (Not your fault).

- Mark


___
http://lurker.clamav.net/list/clamav-devel.html
Please submit your patches to our Bugzilla: http://bugs.clamav.net