Browse code

Use a watchdog thread. Also make timeout be ms instead of us.

Török Edvin authored on 2010/03/23 23:33:41
Showing 5 changed files
... ...
@@ -465,7 +465,7 @@ Example
465 465
 # Default: TrustSigned
466 466
 #BytecodeSecurity TrustSigned
467 467
 
468
-# Set bytecode timeout in microseconds.
468
+# Set bytecode timeout in miliseconds.
469 469
 # 
470
-# Default: 5000000
471
-#BytecodeTimeout 5000000
470
+# Default: 5000
471
+# BytecodeTimeout 5000
... ...
@@ -42,7 +42,7 @@ static const uint32_t nomatch[64];
42 42
 struct cli_bc_ctx *cli_bytecode_context_alloc(void)
43 43
 {
44 44
     struct cli_bc_ctx *ctx = cli_calloc(1, sizeof(*ctx));
45
-    ctx->bytecode_timeout = 5000000;
45
+    ctx->bytecode_timeout = 5000;
46 46
     return ctx;
47 47
 }
48 48
 
... ...
@@ -379,7 +379,6 @@ public:
379 379
 
380 380
 
381 381
     virtual bool runOnFunction(Function &F) {
382
-	bool Changed = false;
383 382
 	BBSetTy BackedgeTargets;
384 383
 	if (!F.isDeclaration()) {
385 384
 	    // Get the common backedge targets.
... ...
@@ -491,7 +490,6 @@ public:
491 491
 	    TerminatorInst *TI = BB->getTerminator();
492 492
 	    BranchInst::Create(AbrtBB, newBB, Cond, TI);
493 493
 	    TI->eraseFromParent();
494
-	    BB->dump();
495 494
 	    // Update dominator info
496 495
 	    DomTreeNode *N = DT.getNode(AbrtBB);
497 496
 	    if (!N) {
... ...
@@ -503,8 +501,7 @@ public:
503 503
 	    }
504 504
 	    DEBUG(errs() << *I << "\n");
505 505
 	}
506
-	F.dump();
507
-	verifyFunction(F);
506
+	//verifyFunction(F);
508 507
 	return true;
509 508
     }
510 509
 
... ...
@@ -1497,15 +1494,31 @@ static void addFunctionProtos(struct CommonFunctions *CF, ExecutionEngine *EE, M
1497 1497
 
1498 1498
 }
1499 1499
 
1500
-struct bc_thread {
1501
-    void *code;
1502
-    struct cli_bc_ctx *ctx;
1500
+struct bc_watchdog {
1501
+    volatile uint8_t* timeout;
1502
+    struct timespec * abstimeout;
1503
+    pthread_mutex_t   mutex;
1504
+    pthread_cond_t    cond;
1503 1505
     int finished;
1504
-    pthread_mutex_t  mutex;
1505
-    pthread_cond_t cond;
1506 1506
 };
1507 1507
 
1508
-static int bytecode_thread_execute(intptr_t code, struct cli_bc_ctx *ctx)
1508
+static void *bytecode_watchdog(void *arg)
1509
+{
1510
+    int ret = 0;
1511
+    struct bc_watchdog *w = (struct bc_watchdog*)arg;
1512
+    pthread_mutex_lock(&w->mutex);
1513
+    while (!w->finished && ret != ETIMEDOUT) {
1514
+	ret = pthread_cond_timedwait(&w->cond, &w->mutex, w->abstimeout);
1515
+    }
1516
+    pthread_mutex_unlock(&w->mutex);
1517
+    if (ret == ETIMEDOUT) {
1518
+	*w->timeout = 1;
1519
+	errs() << "Bytecode run timed out, timeout flag set\n";
1520
+    }
1521
+    return NULL;
1522
+}
1523
+
1524
+static int bytecode_execute(intptr_t code, struct cli_bc_ctx *ctx)
1509 1525
 {
1510 1526
     jmp_buf env;
1511 1527
     // execute;
... ...
@@ -1520,33 +1533,17 @@ static int bytecode_thread_execute(intptr_t code, struct cli_bc_ctx *ctx)
1520 1520
     errs().changeColor(raw_ostream::RED, true) << MODULE
1521 1521
 	<< "*** JITed code intercepted runtime error!\n";
1522 1522
     errs().resetColor();
1523
-    return 1;
1523
+    return CL_EBYTECODE;
1524 1524
 }
1525 1525
 
1526
-static void* bytecode_thread(void* dummy)
1527
-{
1528
-    int ret;
1529
-    struct bc_thread *thr = (struct bc_thread*)dummy;
1530
-    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
1531
-    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
1532
-    ret = bytecode_thread_execute((intptr_t)thr->code, thr->ctx);
1533
-    pthread_mutex_lock(&thr->mutex);
1534
-    thr->finished = 1;
1535
-    pthread_cond_signal(&thr->cond);
1536
-    pthread_mutex_unlock(&thr->mutex);
1537
-    return ret ? dummy : NULL;
1538
-}
1539 1526
 extern "C" const char *cli_strerror(int errnum, char* buf, size_t len);
1540 1527
 int cli_vm_execute_jit(const struct cli_all_bc *bcs, struct cli_bc_ctx *ctx,
1541 1528
 		       const struct cli_bc_func *func)
1542 1529
 {
1543 1530
     char buf[1024];
1544 1531
     int ret;
1545
-    void *threadret;
1546 1532
     pthread_t thread;
1547 1533
     struct timeval tv0, tv1;
1548
-    struct timespec abstimeout;
1549
-    int timedout = 0;
1550 1534
     uint32_t timeoutus;
1551 1535
     // no locks needed here, since LLVM automatically acquires a JIT lock
1552 1536
     // if needed.
... ...
@@ -1558,54 +1555,43 @@ int cli_vm_execute_jit(const struct cli_all_bc *bcs, struct cli_bc_ctx *ctx,
1558 1558
 		<< (unsigned)func->numArgs << " arguments, it must have 0 to be called as entrypoint\n";
1559 1559
 	return CL_EBYTECODE;
1560 1560
     }
1561
-    struct bc_thread bcthr = {
1562
-	code, ctx, 0,
1563
-	PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER
1564
-    };
1565
-    ctx->timeout = 0;
1566 1561
     gettimeofday(&tv0, NULL);
1567
-    pthread_mutex_lock(&bcthr.mutex);
1568
-    ret = pthread_create(&thread, NULL, bytecode_thread, &bcthr);
1569
-    if (ret) {
1562
+    struct timespec abstime;
1563
+
1564
+    timeoutus = (ctx->bytecode_timeout%1000)*1000 + tv0.tv_usec;
1565
+    abstime.tv_sec = tv0.tv_sec + ctx->bytecode_timeout/1000 + timeoutus/1000000;
1566
+    abstime.tv_nsec = 1000*(timeoutus%1000000);
1567
+    ctx->timeout = 0;
1568
+
1569
+    struct bc_watchdog w = {
1570
+	&ctx->timeout,
1571
+	&abstime,
1572
+	PTHREAD_MUTEX_INITIALIZER,
1573
+	PTHREAD_COND_INITIALIZER,
1574
+	0
1575
+    };
1576
+
1577
+    if ((ret = pthread_create(&thread, NULL, bytecode_watchdog, &w))) {
1570 1578
 	errs() << "Bytecode: failed to create new thread!";
1571 1579
 	errs() << cli_strerror(ret, buf, sizeof(buf));
1572 1580
 	errs() << "\n";
1573 1581
 	return CL_EBYTECODE;
1574 1582
     }
1575 1583
 
1576
-    timeoutus = ctx->bytecode_timeout + tv0.tv_usec;
1577
-    abstimeout.tv_nsec = 1000*(timeoutus%1000000);
1578
-    abstimeout.tv_sec = tv0.tv_sec + timeoutus/1000000;
1579
-    do {
1580
-	ret = pthread_cond_timedwait(&bcthr.cond, &bcthr.mutex, &abstimeout);
1581
-    } while (!bcthr.finished && ret != ETIMEDOUT);
1582
-    pthread_mutex_unlock(&bcthr.mutex);
1583
-    if (ret == ETIMEDOUT) {
1584
-	errs() << "Bytecode run timed out, canceling thread\n";
1585
-	timedout = 1;
1586
-	ctx->timeout = 1;
1587
-#if 0
1588
-	ret = pthread_cancel(thread);
1589
-	if (ret) {
1590
-	    errs() << "Bytecode: failed to create new thread!";
1591
-	    errs() << cli_strerror(ret, buf, sizeof(buf));
1592
-	    errs() << "\n";
1593
-	}
1594
-#endif
1595
-    }
1596
-    ret = pthread_join(thread, &threadret);
1597
-    if (ret) {
1598
-	errs() << "Bytecode: failed to create new thread!";
1599
-	errs() << cli_strerror(ret, buf, sizeof(buf));
1600
-	errs() << "\n";
1601
-    }
1584
+    ret = bytecode_execute((intptr_t)code, ctx);
1585
+    pthread_mutex_lock(&w.mutex);
1586
+    w.finished = 1;
1587
+    pthread_cond_signal(&w.cond);
1588
+    pthread_mutex_unlock(&w.mutex);
1589
+    pthread_join(thread, NULL);
1590
+
1602 1591
     if (cli_debug_flag) {
1603 1592
 	gettimeofday(&tv1, NULL);
1604 1593
 	tv1.tv_sec -= tv0.tv_sec;
1605 1594
 	tv1.tv_usec -= tv0.tv_usec;
1606 1595
 	errs() << "bytecode finished in " << (tv1.tv_sec*1000000 + tv1.tv_usec) << "us\n";
1607 1596
     }
1608
-    return timedout ? CL_ETIMEOUT : (threadret ? CL_EBYTECODE : CL_SUCCESS);
1597
+    return ctx->timeout ? CL_ETIMEOUT : ret;
1609 1598
 }
1610 1599
 
1611 1600
 static unsigned char name_salt[16] = { 16, 38, 97, 12, 8, 4, 72, 196, 217, 144, 33, 124, 18, 11, 17, 253 };
... ...
@@ -250,8 +250,8 @@ const struct clam_option __clam_options[] = {
250 250
     { "Bytecode", "bytecode", 0, TYPE_BOOL, MATCH_BOOL, 1, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "With this option enabled ClamAV will load bytecode from the database. It is highly recommended you keep this option on, otherwise you'll miss detections for many new viruses.", "yes" },
251 251
     { "BytecodeSecurity", NULL, 0, TYPE_STRING, "^(None|TrustSigned|Paranoid)$", -1, "TrustSigned", 0, OPT_CLAMD, 
252 252
 	"Set bytecode security level.\nPossible values:\n\tNone - no security at all, meant for debugging. DO NOT USE THIS ON PRODUCTION SYSTEMS\n\tTrustSigned - trust bytecode loaded from signed .c[lv]d files,\n\t\t insert runtime safety checks for bytecode loaded from other sources\n\tParanoid - don't trust any bytecode, insert runtime checks for all\nRecommended: TrustSigned, because bytecode in .cvd files already has these checks\n","TrustSigned"},
253
-    { "BytecodeTimeout", "bytecode-timeout", 0, TYPE_NUMBER, MATCH_NUMBER, 5000000, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, 
254
-	"Set bytecode timeout in microseconds.\n","5000000"},
253
+    { "BytecodeTimeout", "bytecode-timeout", 0, TYPE_NUMBER, MATCH_NUMBER, 5000, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, 
254
+	"Set bytecode timeout in miliseconds.\n","5000"},
255 255
     { "DetectPUA", "detect-pua", 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "Detect Potentially Unwanted Applications.", "yes" },
256 256
 
257 257
     { "ExcludePUA", "exclude-pua", 0, TYPE_STRING, NULL, -1, NULL, FLAG_MULTIPLE, OPT_CLAMD | OPT_CLAMSCAN, "Exclude a specific PUA category. This directive can be used multiple times.\nSee http://www.clamav.net/support/pua for the complete list of PUA\ncategories.", "NetTool\nPWTool" },
... ...
@@ -75,7 +75,7 @@ static void runtest(const char *file, uint64_t expected, int fail, int nojit)
75 75
 
76 76
     ctx = cli_bytecode_context_alloc();
77 77
     /* small timeout, these bytecodes are fast! */
78
-    ctx->bytecode_timeout = 100000;
78
+    ctx->bytecode_timeout = 10;
79 79
     fail_unless(!!ctx, "cli_bytecode_context_alloc failed");
80 80
 
81 81
     cli_bytecode_context_setfuncid(ctx, &bc, 0);