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