Browse code

Handle runtime errors with setjmp/longjmp, using a thread-local jmpbuf to make it threadsafe.

Török Edvin authored on 2009/08/28 19:18:17
Showing 3 changed files
... ...
@@ -19,7 +19,7 @@
19 19
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 20
  *  MA 02110-1301, USA.
21 21
  */
22
-
22
+#define DEBUG_TYPE "clamavjit"
23 23
 #include "llvm/ADT/DenseMap.h"
24 24
 #include "llvm/DerivedTypes.h"
25 25
 #include "llvm/Function.h"
... ...
@@ -31,6 +31,7 @@
31 31
 #include "llvm/PassManager.h"
32 32
 #include "llvm/ModuleProvider.h"
33 33
 #include "llvm/Support/Compiler.h"
34
+#include "llvm/Support/Debug.h"
34 35
 #include "llvm/Support/CommandLine.h"
35 36
 #include "llvm/Support/DataTypes.h"
36 37
 #include "llvm/Support/ErrorHandling.h"
... ...
@@ -43,9 +44,11 @@
43 43
 #include "llvm/Target/TargetSelect.h"
44 44
 #include "llvm/Target/TargetData.h"
45 45
 #include "llvm/Support/TargetFolder.h"
46
-#include "llvm/Transforms/Scalar.h"
47 46
 #include "llvm/Analysis/Verifier.h"
47
+#include "llvm/Transforms/Scalar.h"
48
+#include "llvm/System/ThreadLocal.h"
48 49
 #include <cstdlib>
50
+#include <csetjmp>
49 51
 #include <new>
50 52
 
51 53
 #include "clamav.h"
... ...
@@ -65,15 +68,21 @@ struct cli_bcengine {
65 65
 
66 66
 namespace {
67 67
 
68
+static sys::ThreadLocal<const jmp_buf> ExceptionReturn;
69
+
68 70
 void do_shutdown() {
69 71
     llvm_shutdown();
70 72
 }
71 73
 
74
+static void NORETURN jit_exception_handler(void)
75
+{
76
+    longjmp(*const_cast<jmp_buf*>(ExceptionReturn.get()), 1);
77
+}
78
+
72 79
 void llvm_error_handler(void *user_data, const std::string &reason)
73 80
 {
74 81
     errs() << reason;
75
-    //TODO: better error handling, don't exit here
76
-    exit(1);
82
+    jit_exception_handler();
77 83
 }
78 84
 
79 85
 class VISIBILITY_HIDDEN LLVMCodegen {
... ...
@@ -147,7 +156,7 @@ private:
147 147
 	// Constant
148 148
 	operand -= func->numValues;
149 149
 	// This was already validated by libclamav.
150
-       	assert(operand < func->numConstants && "Constant out of range");
150
+	assert(operand < func->numConstants && "Constant out of range");
151 151
 	uint64_t *c = &func->constants[operand];
152 152
 	uint64_t v;
153 153
 	const Type *Ty;
... ...
@@ -179,6 +188,19 @@ private:
179 179
 	assert(dest >= numArgs && dest < numLocals+numArgs && "Instruction destination out of range");
180 180
 	Builder.CreateStore(V, Values[dest]);
181 181
     }
182
+
183
+    // Insert code that calls \arg FHandler if \arg FailCond is true.
184
+    void InsertVerify(Value *FailCond, BasicBlock *&Fail, Function *FHandler, 
185
+		      Function *F) {
186
+	if (!Fail) {
187
+	    Fail = BasicBlock::Create(Context, "fail", F);
188
+	    CallInst::Create(FHandler,"",Fail);
189
+	    new UnreachableInst(Context, Fail);
190
+	}
191
+	BasicBlock *OkBB = BasicBlock::Create(Context, "", F);
192
+	Builder.CreateCondBr(FailCond, Fail, OkBB);
193
+	Builder.SetInsertPoint(OkBB);
194
+    }
182 195
 public:
183 196
     LLVMCodegen(const struct cli_bc *bc, Module *M, FunctionMapTy &cFuncs,
184 197
 		ExecutionEngine *EE, FunctionPassManager &PM)
... ...
@@ -191,6 +213,16 @@ public:
191 191
     bool generate() {
192 192
 	PrettyStackTraceString Trace(BytecodeID.str().c_str());
193 193
 	convertTypes();
194
+
195
+	llvm::FunctionType *FTy = FunctionType::get(Type::getVoidTy(Context),
196
+						    false);
197
+	Function *FHandler = Function::Create(FTy, Function::InternalLinkage,
198
+					      "clamjit.fail", M);
199
+	FHandler->setDoesNotReturn();
200
+	FHandler->setDoesNotThrow();
201
+	FHandler->addFnAttr(Attribute::NoInline);
202
+	EE->addGlobalMapping(FHandler, (void*)jit_exception_handler); 
203
+
194 204
 	Function **Functions = new Function*[bc->num_func];
195 205
 	for (unsigned j=0;j<bc->num_func;j++) {
196 206
 	    PrettyStackTraceString CrashInfo("Generate LLVM IR functions");
... ...
@@ -205,6 +237,7 @@ public:
205 205
 							 false);
206 206
 	    Functions[j] = Function::Create(FTy, Function::InternalLinkage, 
207 207
 					   BytecodeID+"f"+Twine(j), M);
208
+	    Functions[j]->setDoesNotThrow();
208 209
 	}
209 210
 	for (unsigned j=0;j<bc->num_func;j++) {
210 211
 	    PrettyStackTraceString CrashInfo("Generate LLVM IR");
... ...
@@ -216,6 +249,7 @@ public:
216 216
 		BB[i] = BasicBlock::Create(Context, "", F);
217 217
 	    }
218 218
 
219
+	    BasicBlock *Fail = 0;
219 220
 	    Values = new Value*[func->numValues];
220 221
 	    Builder.SetInsertPoint(BB[0]);
221 222
 	    Function::arg_iterator I = F->arg_begin();
... ...
@@ -276,17 +310,35 @@ public:
276 276
 			    Store(inst->dest, Builder.CreateMul(Op0, Op1));
277 277
 			    break;
278 278
 			case OP_UDIV:
279
+			{
280
+			    Value *Bad = Builder.CreateICmpEQ(Op1, ConstantInt::get(Op1->getType(), 0));
281
+			    InsertVerify(Bad, Fail, FHandler, F);
279 282
 			    Store(inst->dest, Builder.CreateUDiv(Op0, Op1));
280 283
 			    break;
284
+			}
281 285
 			case OP_SDIV:
286
+			{
287
+			    //TODO: also verify Op0 == -1 && Op1 = INT_MIN
288
+			    Value *Bad = Builder.CreateICmpEQ(Op1, ConstantInt::get(Op1->getType(), 0));
289
+			    InsertVerify(Bad, Fail, FHandler, F);
282 290
 			    Store(inst->dest, Builder.CreateSDiv(Op0, Op1));
283 291
 			    break;
292
+			}
284 293
 			case OP_UREM:
294
+			{
295
+			    Value *Bad = Builder.CreateICmpEQ(Op1, ConstantInt::get(Op1->getType(), 0));
296
+			    InsertVerify(Bad, Fail, FHandler, F);
285 297
 			    Store(inst->dest, Builder.CreateURem(Op0, Op1));
286 298
 			    break;
299
+			}
287 300
 			case OP_SREM:
301
+			{
302
+			    //TODO: also verify Op0 == -1 && Op1 = INT_MIN
303
+			    Value *Bad = Builder.CreateICmpEQ(Op1, ConstantInt::get(Op1->getType(), 0));
304
+			    InsertVerify(Bad, Fail, FHandler, F);
288 305
 			    Store(inst->dest, Builder.CreateSRem(Op0, Op1));
289 306
 			    break;
307
+			}
290 308
 			case OP_SHL:
291 309
 			    Store(inst->dest, Builder.CreateShl(Op0, Op1));
292 310
 			    break;
... ...
@@ -354,25 +406,25 @@ public:
354 354
 			    Store(inst->dest, Builder.CreateICmpNE(Op0, Op1));
355 355
 			    break;
356 356
 			case OP_ICMP_UGT:
357
-			    Store(inst->dest, Builder.CreateICmpNE(Op0, Op1));
357
+			    Store(inst->dest, Builder.CreateICmpUGT(Op0, Op1));
358 358
 			    break;
359 359
 			case OP_ICMP_UGE:
360
-			    Store(inst->dest, Builder.CreateICmpNE(Op0, Op1));
360
+			    Store(inst->dest, Builder.CreateICmpUGE(Op0, Op1));
361 361
 			    break;
362 362
 			case OP_ICMP_ULT:
363
-			    Store(inst->dest, Builder.CreateICmpNE(Op0, Op1));
363
+			    Store(inst->dest, Builder.CreateICmpULT(Op0, Op1));
364 364
 			    break;
365 365
 			case OP_ICMP_ULE:
366
-			    Store(inst->dest, Builder.CreateICmpNE(Op0, Op1));
366
+			    Store(inst->dest, Builder.CreateICmpULE(Op0, Op1));
367 367
 			    break;
368 368
 			case OP_ICMP_SGT:
369
-			    Store(inst->dest, Builder.CreateICmpNE(Op0, Op1));
369
+			    Store(inst->dest, Builder.CreateICmpSGT(Op0, Op1));
370 370
 			    break;
371 371
 			case OP_ICMP_SGE:
372
-			    Store(inst->dest, Builder.CreateICmpNE(Op0, Op1));
372
+			    Store(inst->dest, Builder.CreateICmpSGE(Op0, Op1));
373 373
 			    break;
374 374
 			case OP_ICMP_SLT:
375
-			    Store(inst->dest, Builder.CreateICmpNE(Op0, Op1));
375
+			    Store(inst->dest, Builder.CreateICmpSLT(Op0, Op1));
376 376
 			    break;
377 377
 			case OP_SELECT:
378 378
 			    Store(inst->dest, Builder.CreateSelect(Op0, Op1, Op2));
... ...
@@ -404,15 +456,23 @@ public:
404 404
 	    }
405 405
 	    PM.run(*F);
406 406
 	    delete [] Values;
407
+	    delete [] BB;
407 408
 	}
408 409
 
410
+	DEBUG(M->dump());
411
+	delete [] TypeMap;
412
+	llvm::FunctionType *Callable = FunctionType::get(Type::getInt32Ty(Context),false);
409 413
 	for (unsigned j=0;j<bc->num_func;j++) {
410 414
 	    const struct cli_bc_func *func = &bc->funcs[j];
411 415
 	    PrettyStackTraceString CrashInfo2("Native machine codegen");
412 416
 	    // Codegen current function as executable machine code.
413
-	    compiledFunctions[func] = EE->getPointerToFunction(Functions[j]);
417
+	    void *code = EE->getPointerToFunction(Functions[j]);
418
+
419
+	    // If prototype matches, add to callable functions
420
+	    if (Functions[j]->getFunctionType() == Callable)
421
+		compiledFunctions[func] = code;
414 422
 	}
415
-	delete [] TypeMap;
423
+	delete [] Functions;
416 424
 	return true;
417 425
     }
418 426
 };
... ...
@@ -421,17 +481,40 @@ public:
421 421
 int cli_vm_execute_jit(const struct cli_all_bc *bcs, struct cli_bc_ctx *ctx,
422 422
 		       const struct cli_bc_func *func)
423 423
 {
424
+    jmp_buf env;
424 425
     void *code = bcs->engine->compiledFunctions[func];
425
-    assert(code);
426
+    if (!code) {
427
+	errs() << MODULE << "Unable to find compiled function\n";
428
+	return CL_EBYTECODE;
429
+    }
426 430
     // execute;
427
-    uint32_t result = ((uint32_t (*)(void))code)();
428
-    *(uint32_t*)ctx->values = result;
429
-    return 0;
431
+    if (setjmp(env) == 0) {
432
+	// setup exception handler to longjmp back here
433
+	ExceptionReturn.set(&env);
434
+	uint32_t result = ((uint32_t (*)(void))code)();
435
+	*(uint32_t*)ctx->values = result;
436
+	return 0;
437
+    }
438
+    errs() << "\n";
439
+    errs().changeColor(raw_ostream::RED, true) << MODULE 
440
+	<< "*** JITed code intercepted runtime error!\n";
441
+    errs().resetColor();
442
+    return CL_EBYTECODE;
430 443
 }
431 444
 
432 445
 
433 446
 int cli_bytecode_prepare_jit(struct cli_all_bc *bcs)
434 447
 {
448
+  jmp_buf env;
449
+  // setup exception handler to longjmp back here
450
+  ExceptionReturn.set(&env);  
451
+  if (setjmp(env) != 0) {
452
+      errs() << "\n";
453
+      errs().changeColor(raw_ostream::RED, true) << MODULE 
454
+      << "*** FATAL error encountered during bytecode generation\n";
455
+      errs().resetColor();
456
+      return CL_EBYTECODE;
457
+  }
435 458
   // LLVM itself never throws exceptions, but operator new may throw bad_alloc
436 459
   try {
437 460
     Module *M = new Module("ClamAV jit module", bcs->engine->Context);
... ...
@@ -454,6 +537,7 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs)
454 454
 
455 455
 	EE->RegisterJITEventListener(createOProfileJITEventListener());
456 456
 	EE->DisableLazyCompilation();
457
+	EE->DisableSymbolSearching();
457 458
 
458 459
 	FunctionPassManager OurFPM(MP);
459 460
 	// Set up the optimizer pipeline.  Start with registering info about how
... ...
@@ -461,8 +545,6 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs)
461 461
 	OurFPM.add(new TargetData(*EE->getTargetData()));
462 462
 	// Promote allocas to registers.
463 463
 	OurFPM.add(createPromoteMemoryToRegisterPass());
464
-	// Do simple "peephole" optimizations and bit-twiddling optzns.
465
-	OurFPM.add(createInstructionCombiningPass());
466 464
 	OurFPM.doInitialization();
467 465
 	for (unsigned i=0;i<bcs->count;i++) {
468 466
 	    const struct cli_bc *bc = &bcs->all_bcs[i];
... ...
@@ -34,7 +34,7 @@
34 34
 #include "../libclamav/bytecode.h"
35 35
 #include "checks.h"
36 36
 
37
-static void runtest(const char *file, uint64_t expected)
37
+static void runtest(const char *file, uint64_t expected, int fail)
38 38
 {
39 39
     int rc;
40 40
     int fd = open_testfile(file);
... ...
@@ -68,11 +68,13 @@ static void runtest(const char *file, uint64_t expected)
68 68
 
69 69
     cli_bytecode_context_setfuncid(ctx, &bc, 0);
70 70
     rc = cli_bytecode_run(&bcs, &bc, ctx);
71
-    fail_unless(rc == CL_SUCCESS, "cli_bytecode_run failed");
71
+    fail_unless(rc == fail, "cli_bytecode_run failed");
72 72
 
73
-    v = cli_bytecode_context_getresult_int(ctx);
74
-    fail_unless_fmt(v == expected, "Invalid return value from bytecode run, expected: %llx, have: %llx\n",
75
-		    expected, v);
73
+    if (rc == CL_SUCCESS) {
74
+	v = cli_bytecode_context_getresult_int(ctx);
75
+	fail_unless_fmt(v == expected, "Invalid return value from bytecode run, expected: %llx, have: %llx\n",
76
+			expected, v);
77
+    }
76 78
     cli_bytecode_context_destroy(ctx);
77 79
     cli_bytecode_destroy(&bc);
78 80
     cli_bytecode_done(&bcs);
... ...
@@ -80,22 +82,28 @@ static void runtest(const char *file, uint64_t expected)
80 80
 
81 81
 START_TEST (test_retmagic)
82 82
 {
83
-    runtest("input/retmagic.cbc", 0x1234f00d);
83
+    runtest("input/retmagic.cbc", 0x1234f00d, CL_SUCCESS);
84 84
 }
85 85
 END_TEST
86 86
 
87 87
 START_TEST (test_arith)
88 88
 {
89
-    runtest("input/arith.cbc", 0xd5555555);
89
+    runtest("input/arith.cbc", 0xd5555555, CL_SUCCESS);
90 90
 }
91 91
 END_TEST
92 92
 
93 93
 START_TEST (test_apicalls)
94 94
 {
95
-    runtest("input/apicalls.cbc", 0xf00d);
95
+    runtest("input/apicalls.cbc", 0xf00d, CL_SUCCESS);
96 96
 }
97 97
 END_TEST
98 98
 
99
+START_TEST (test_div0)
100
+{
101
+    /* must not crash on div#0 but catch it */
102
+    runtest("input/div0.cbc", 0, CL_EBYTECODE);
103
+}
104
+END_TEST
99 105
 
100 106
 Suite *test_bytecode_suite(void)
101 107
 {
... ...
@@ -106,5 +114,6 @@ Suite *test_bytecode_suite(void)
106 106
     tcase_add_test(tc_cli_arith, test_retmagic);
107 107
     tcase_add_test(tc_cli_arith, test_arith);
108 108
     tcase_add_test(tc_cli_arith, test_apicalls);
109
+    tcase_add_test(tc_cli_arith, test_div0);
109 110
     return s;
110 111
 }
111 112
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+ClamBCaa`|`````|`aeabp`clamcoincidencejb
1
+Ted
2
+E``
3
+A`b`bLaab`bFabaa
4
+Bb`b`oaaab@dTcab`b`E
5
+Aab`bLaab`bb`bFabaa
6
+Bb`baae`Aad`Tcab`baaE