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.
... | ... |
@@ -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 |
} |