Browse code

Make cl_load thread safe (bb #2333).

Parallel cl_load() crash (bb #2333).
Reason is twofold:
- cache.c had 2 'static' global variables, thus trying to initialize same cache
from multiple threads
- bytecode2llvm.cpp: something in LLVM 2.7 is crashing when loading in
parallel

Fix is to drop the 'static' on the variable (cache is per engine already).
This also fixes a potential memory leak in clamd!

The other part of the fix is to turn on the mutex around bytecode compilation
always. We don't call cl_load in parallel, so this doesn't affect clamd, but
some may need to call cl_load in parallel.

Török Edvin authored on 2010/11/05 04:14:14
Showing 4 changed files
... ...
@@ -1,3 +1,7 @@
1
+Thu Nov  4 21:12:53 EET 2010 (edwin)
2
+------------------------------------
3
+ * libclamav/cache.c,c++/bytecode2llvm.cpp}: make cl_load thread safe (bb #2333).
4
+
1 5
 Thu Nov  4 19:47:17 EET 2010 (edwin)
2 6
 ------------------------------------
3 7
  * freshclam: load database in subprocess (bb #2147).
... ...
@@ -1611,11 +1611,14 @@ class LLVMApiScopedLock {
1611 1611
 	// we need to wrap all LLVM API calls with a giant mutex lock, but
1612 1612
 	// only then.
1613 1613
 	LLVMApiScopedLock() {
1614
-	    if (!llvm_is_multithreaded())
1614
+	    // It is safer to just run all codegen under the mutex,
1615
+	    // it is not like we are going to codegen from multiple threads
1616
+	    // at a time anyway.
1617
+//	    if (!llvm_is_multithreaded())
1615 1618
 		llvm_api_lock.acquire();
1616 1619
 	}
1617 1620
 	~LLVMApiScopedLock() {
1618
-	    if (!llvm_is_multithreaded())
1621
+//	    if (!llvm_is_multithreaded())
1619 1622
 		llvm_api_lock.release();
1620 1623
 	}
1621 1624
 };
... ...
@@ -589,7 +589,7 @@ struct CACHE {
589 589
 
590 590
 /* Allocates the trees for the engine cache */
591 591
 int cli_cache_init(struct cl_engine *engine) {
592
-    static struct CACHE *cache;
592
+    struct CACHE *cache;
593 593
     unsigned int i, j;
594 594
 
595 595
     if(!engine) {
... ...
@@ -623,7 +623,7 @@ int cli_cache_init(struct cl_engine *engine) {
623 623
 
624 624
 /* Frees the engine cache */
625 625
 void cli_cache_destroy(struct cl_engine *engine) {
626
-    static struct CACHE *cache;
626
+    struct CACHE *cache;
627 627
     unsigned int i;
628 628
 
629 629
     if(!engine || !(cache = engine->cache))
... ...
@@ -519,6 +519,43 @@ START_TEST (test_load_bytecode_int)
519 519
 }
520 520
 END_TEST
521 521
 
522
+#ifdef CL_THREAD_SAFE
523
+static pthread_barrier_t barrier;
524
+static void* thread(void *arg)
525
+{
526
+    struct cl_engine *engine;
527
+    engine = cl_engine_new();
528
+    fail_unless(!!engine, "failed to create engine\n");
529
+    /* run all cl_load at once, to maximize chance of a crash
530
+     * in case of a race condition */
531
+    pthread_barrier_wait(&barrier);
532
+    runload("input/bytecode.cvd", engine, 5);
533
+    cl_engine_free(engine);
534
+    return NULL;
535
+}
536
+
537
+START_TEST (test_parallel_load)
538
+{
539
+#define N 5
540
+    pthread_t threads[N];
541
+    unsigned i;
542
+
543
+    cl_init(CL_INIT_DEFAULT);
544
+    pthread_barrier_init(&barrier, NULL, N);
545
+    for (i=0;i<N;i++) {
546
+	pthread_create(&threads[i], NULL, thread, NULL);
547
+    }
548
+    for (i=0;i<N;i++) {
549
+	pthread_join(threads[i], NULL);
550
+    }
551
+    /* DB load used to crash due to 'static' variable in cache.c,
552
+     * and also due to something wrong in LLVM 2.7.
553
+     * Enabled the mutex around codegen in bytecode2llvm.cpp, and this test is
554
+     * here to make sure it doesn't crash */
555
+}
556
+END_TEST
557
+#endif
558
+
522 559
 Suite *test_bytecode_suite(void)
523 560
 {
524 561
     Suite *s = suite_create("bytecode");
... ...
@@ -575,6 +612,9 @@ Suite *test_bytecode_suite(void)
575 575
 
576 576
     tcase_add_test(tc_cli_arith, test_load_bytecode_jit);
577 577
     tcase_add_test(tc_cli_arith, test_load_bytecode_int);
578
+#ifdef CL_THREAD_SAFE
579
+    tcase_add_test(tc_cli_arith, test_parallel_load);
580
+#endif
578 581
 
579 582
     return s;
580 583
 }