chopper icon indicating copy to clipboard operation
chopper copied to clipboard

"Bad signature" LLVM assert raised when replacing calls with wrappers

Open jordr opened this issue 5 years ago • 2 comments

I have had this LLVM assert raised on the bc benchmark on several functions, on several functions, such as prog_char and some floating point functions. I get different errors, sometimes the arguments count is bad, sometimes it's the argument type.

Possibly this is related to structs (my best guess).

Minimal steps to reproduce it on a smaller program:

  1. Write main.c:
int main(int argc, char *argv[]) {
    _fp_out_narrow(0, 0, 0, 0);
    return 0;
}

(I was not able to isolate the part of __fp_out_narrow that causes this, so this minimal example is not very well self-contained)

  1. Run clang (+opt -mem2reg file.bc -o file.bc)
$ clang -c -g -emit-llvm main.c -o main.bc -I~/code/chopper/include/
main.c:3:5: warning: implicit declaration of function '_fp_out_narrow' is invalid in C99 [-Wimplicit-function-declaration]
    _fp_out_narrow(0, 0, 0, 0);
    ^
1 warning generated.
  1. Run legacy Chopper, skip _fp_out_narrow1:
$ kleegacy -libc=uclibc -simplify-sym-indices -search=nurs:covnew -split-search -output-module -skip-functions=_fp_out_narrow1 main.bc
KLEE: NOTE: Using klee-uclibc : /home/ubuntu/code/chopper/legacy_build/Release+Asserts/lib/klee-uclibc.bca
kleegacy: /home/ubuntu/code/llvm-project/llvm/lib/IR/Instructions.cpp:281: void llvm::CallInst::init(llvm::Value*, llvm::ArrayRef<llvm::Value*>, const llvm::Twine&): Assertion `(i >= FTy->getNumParams() || FTy->getParamType(i) == Args[i]->getType()) && "Calling a function with a bad signature!"' failed.
0  kleegacy        0x0000555731055992 llvm::sys::PrintStackTrace(_IO_FILE*) + 50
1  kleegacy        0x000055573105528c
2  libpthread.so.0 0x00007f9e9c453890
3  libc.so.6       0x00007f9e9b321e97 gsignal + 199
4  libc.so.6       0x00007f9e9b323801 abort + 321
5  libc.so.6       0x00007f9e9b31339a
6  libc.so.6       0x00007f9e9b313412
7  kleegacy        0x0000555730fc5a3f llvm::CallInst::init(llvm::Value*, llvm::ArrayRef<llvm::Value*>, llvm::Twine const&) + 335
8  kleegacy        0x000055573059c999
9  kleegacy        0x000055573059c688
10 kleegacy        0x000055573068457a
11 kleegacy        0x0000555730683afa klee::ReturnToVoidFunctionPass::replaceCall(llvm::CallInst*, llvm::Function*, llvm::Function*) + 812
12 kleegacy        0x0000555730683712 klee::ReturnToVoidFunctionPass::replaceCalls(llvm::Function*, llvm::Function*, std::vector<unsigned int, std::allocator<unsigned int> > const&) + 480
13 kleegacy        0x0000555730682fcd klee::ReturnToVoidFunctionPass::runOnFunction(llvm::Function&, llvm::Module&) + 469
14 kleegacy        0x0000555730683d18 klee::ReturnToVoidFunctionPass::runOnModule(llvm::Module&) + 138
15 kleegacy        0x0000555730fdcef1 llvm::legacy::PassManagerImpl::run(llvm::Module&) + 849
16 kleegacy        0x000055573066b3c9 klee::KModule::prepare(klee::Interpreter::ModuleOptions const&, std::vector<klee::Interpreter::SkippedFunctionOption, std::allocator<klee::Interpreter::SkippedFunctionOption> > const&, klee::InterpreterHandler*, ReachabilityAnalysis*, Inliner*, AAPass*, ModRefAnalysis*, Cloner*, SliceGenerator*) + 1659
17 kleegacy        0x00005557305b3640 klee::Executor::setModule(llvm::Module*, klee::Interpreter::ModuleOptions const&) + 1656
18 kleegacy        0x0000555730596b35 main + 4654
19 libc.so.6       0x00007f9e9b304b97 __libc_start_main + 231
20 kleegacy        0x000055573059071a _start + 42
Aborted (core dumped)

3bis. You can also directly reproduce it on bc by skipping prog_char:

$ kleegacy -libc=uclibc -simplify-sym-indices -search=nurs:covnew -split-search -output-module -skip-functions=prog_char bc.bc
KLEE: NOTE: Using klee-uclibc : /home/ubuntu/code/chopper/legacy_build/Release+Asserts/lib/klee-uclibc.bca
KLEE: output directory is "/home/ubuntu/code/chopper-experiments/bc/klee-out-24"
Using STP solver backend
kleegacy: /home/ubuntu/code/llvm-project/llvm/lib/IR/Instructions.cpp:276: void llvm::CallInst::init(llvm::Value*, llvm::ArrayRef<llvm::Value*>, const llvm::Twine&): Assertion `(Args.size() == FTy->getNumParams() || (FTy->isVarArg() && Args.size() > FTy->getNumParams())) && "Calling a function with bad signature!"' failed.

Dodging the issue

I was not able to fix the issue, and instead I dodged it by aborting the replaceCalls. The ugly workaround is https://github.com/jordr/chopper/commit/6feb19dd89fded83ad39cd14423353c9a2858c02 and yields:

[4941c8] KLEE: WARNING: Wrapper has bad signature: 'prog_char'! LLVM refuses to create call. Args.size()=3, FTy->getNumParams()=1, FTY->isVarArg()=0
[4941c8] KLEE: WARNING: Wrapper has bad signature: 'input_char'! LLVM refuses to create call. Args.size()=3, FTy->getNumParams()=1, FTY->isVarArg()=0
[eab1d] KLEE: WARNING: Wrapper has bad signature: '_fp_out_narrow'! bad type of Args[2].type=4 =/= wrapper.type=10 LLVM refuses to create call.

Some debugging

Sometimes the mismatch happens on the type of the arguments, sometimes on the count of arguments.

prog_char

Using a modified Chopper, I get:

$ klee -libc=uclibc -simplify-sym-indices -search=nurs:covnew -split-search -skip-functions-not=__errno_location,__h_errno_location,__user_main bc.bc
[988dcb] KLEE: 	 createWrapperFunction(f=prog_char)
[a17b2a] KLEE: WARNING: 		[createWrapperFunction] Set the arguments
[596ea7] KLEE: WARNING: !!
[6b00f9] KLEE: WARNING: 		[createWrapperFunction] Create BB entry
[e1f7eb] KLEE: WARNING: 	replaceCall to call, arg[0]= (2 args) by f=prog_char
[69a30f] KLEE: WARNING: 	- originalCall.argoperand[0] = 
; Function Attrs: nounwind uwtable
define zeroext i8 @prog_char() #0 {
entry:
  tail call void @llvm.dbg.value(metadata !3258, i64 0, metadata !3259), !dbg !3261
  %0 = load i32* getelementptr inbounds (%struct.program_counter* @pc, i64 0, i32 1), align 4, !dbg !3262, !tbaa !3263
  %inc.i = add nsw i32 %0, 1, !dbg !3262
  store i32 %inc.i, i32* getelementptr inbounds (%struct.program_counter* @pc, i64 0, i32 1), align 4, !dbg !3262, !tbaa !3263
  %idxprom.i = sext i32 %0 to i64, !dbg !3262
  %1 = load i32* getelementptr inbounds (%struct.program_counter* @pc, i64 0, i32 0), align 4, !dbg !3262, !tbaa !3268
  %idxprom1.i = sext i32 %1 to i64, !dbg !3262
  %2 = load %struct.bc_function** @functions, align 8, !dbg !3262, !tbaa !3269
  %f_body.i = getelementptr inbounds %struct.bc_function* %2, i64 %idxprom1.i, i32 1, !dbg !3262
  %3 = load i8** %f_body.i, align 8, !dbg !3262, !tbaa !3271
  %arrayidx2.i = getelementptr inbounds i8* %3, i64 %idxprom.i, !dbg !3262
  %4 = load i8* %arrayidx2.i, align 1, !dbg !3262, !tbaa !3273
  ret i8 %4, !dbg !3260
}
(TYPE=i8 ()*)(TID=14)
[69a30f] KLEE: WARNING: 	- originalCall.argoperand[1] =   %const_base.0 = load i32* %const_base.0.in, align 4, !dbg !3362(TYPE=i32)(TID=10)
[21c4f4] KLEE: WARNING: CreateCall! wrapper=__wrap_prog_char, origCallInst->getNumArgOperands()=2, 
[1780fd] KLEE: WARNING: getNumParams of wrapper= 1, type = 14
[510366] KLEE: WARNING: f.CONV:0, wrapper.CONV:0
[f81146] KLEE: WARNING: Wrapper has bad signature! LLVM refuses to create call.
[6c15e4] KLEE: WARNING: Failed to replace call to f=prog_char

_fp_out_narrow

With the minimal example from above, I get:

$ klee -libc=uclibc -simplify-sym-indices -search=nurs:covnew -split-search -skip-functions-not=__errno_location,__h_errno_location,__user_main,prog_char,input_char bc.bc
[988dcb] KLEE: 	 createWrapperFunction(f=_fp_out_narrow)
[a17b2a] KLEE: WARNING: 		[createWrapperFunction] Set the arguments
[6b00f9] KLEE: WARNING: 		[createWrapperFunction] Create BB entry
[e1f7eb] KLEE: WARNING: 	replaceCall to call, arg[0]= (4 args) by f=_fp_out_narrow
[69a30f] KLEE: WARNING: 	- originalCall.argoperand[0] = %struct.__STDIO_FILE_STRUCT.215* %stream(TYPE=%struct.__STDIO_FILE_STRUCT.215*)(TID=14)
[69a30f] KLEE: WARNING: 	- originalCall.argoperand[1] =   %cond194 = phi x86_fp80 [ %42, %cond.true188 ], [ %conv192, %cond.false190 ], !dbg !3435(TYPE=x86_fp80)(TID=4)
[69a30f] KLEE: WARNING: 	- originalCall.argoperand[2] =   %info195 = getelementptr inbounds %struct.ppfs_t* %ppfs, i32 0, i32 1, !dbg !3435(TYPE=%struct.printf_info.315*)(TID=14)
[69a30f] KLEE: WARNING: 	- originalCall.argoperand[3] = 
; Function Attrs: nounwind uwtable
define internal i64 @_fp_out_narrow(%struct.__STDIO_FILE_STRUCT.215* %fp, i64 %type, i64 %len, i64 %buf) #10 {
entry:
  call void @llvm.dbg.value(metadata !{%struct.__STDIO_FILE_STRUCT.215* %fp}, i64 0, metadata !3258), !dbg !3259
  call void @llvm.dbg.value(metadata !{i64 %type}, i64 0, metadata !3260), !dbg !3259
  call void @llvm.dbg.value(metadata !{i64 %len}, i64 0, metadata !3261), !dbg !3259
  call void @llvm.dbg.value(metadata !{i64 %buf}, i64 0, metadata !3262), !dbg !3259
  call void @llvm.dbg.value(metadata !3263, i64 0, metadata !3264), !dbg !3265
  %and = and i64 %type, 128, !dbg !3266
  %tobool = icmp ne i64 %and, 0, !dbg !3266
  br i1 %tobool, label %if.then, label %if.end12, !dbg !3266

if.then:                                          ; preds = %entry
  %0 = inttoptr i64 %buf to i8*, !dbg !3268
  %call = call i64 @strlen(i8* %0) #17, !dbg !3268
  %conv = trunc i64 %call to i32, !dbg !3268
  call void @llvm.dbg.value(metadata !{i32 %conv}, i64 0, metadata !3270), !dbg !3268
  %conv1 = sext i32 %conv to i64, !dbg !3271
  %sub = sub nsw i64 %len, %conv1, !dbg !3271
  call void @llvm.dbg.value(metadata !{i64 %sub}, i64 0, metadata !3261), !dbg !3271
  %cmp = icmp sgt i64 %sub, 0, !dbg !3271
  br i1 %cmp, label %if.then3, label %if.end10, !dbg !3271

if.then3:                                         ; preds = %if.then
  %and4 = and i64 %type, 127, !dbg !3273
  %conv5 = trunc i64 %and4 to i32, !dbg !3273
  %1 = alloca i64, !dbg !3273
  call void @__wrap__charpad(i64* %1, %struct.__STDIO_FILE_STRUCT.215* %fp, i32 %conv5, i64 %sub), !dbg !3273
  %2 = load i64* %1, !dbg !3273
  call void @llvm.dbg.value(metadata !{i64 %2}, i64 0, metadata !3264), !dbg !3273
  %cmp7 = icmp ne i64 %2, %sub, !dbg !3273
  br i1 %cmp7, label %if.then9, label %if.end, !dbg !3273

if.then9:                                         ; preds = %if.then3
  br label %return, !dbg !3276

if.end:                                           ; preds = %if.then3
  br label %if.end10, !dbg !3278

if.end10:                                         ; preds = %if.end, %if.then
  %r.0 = phi i64 [ %2, %if.end ], [ 0, %if.then ]
  %conv11 = sext i32 %conv to i64, !dbg !3279
  call void @llvm.dbg.value(metadata !{i64 %conv11}, i64 0, metadata !3261), !dbg !3279
  br label %if.end12, !dbg !3280

if.end12:                                         ; preds = %if.end10, %entry
  %len.addr.0 = phi i64 [ %conv11, %if.end10 ], [ %len, %entry ]
  %r.1 = phi i64 [ %r.0, %if.end10 ], [ 0, %entry ]
  %cmp13 = icmp sgt i64 %len.addr.0, 0, !dbg !3281
  br i1 %cmp13, label %cond.true, label %cond.false, !dbg !3281

cond.true:                                        ; preds = %if.end12
  %3 = inttoptr i64 %buf to i8*, !dbg !3281
  %call15 = call i64 @__stdio_fwrite(i8* %3, i64 %len.addr.0, %struct.__STDIO_FILE_STRUCT.215* %fp) #18, !dbg !3281
  br label %cond.end, !dbg !3281

cond.false:                                       ; preds = %if.end12
  br label %cond.end, !dbg !3281

cond.end:                                         ; preds = %cond.false, %cond.true
  %cond = phi i64 [ %call15, %cond.true ], [ 0, %cond.false ], !dbg !3281
  %add = add i64 %r.1, %cond, !dbg !3281
  br label %return, !dbg !3281

return:                                           ; preds = %cond.end, %if.then9
  %retval.0 = phi i64 [ %2, %if.then9 ], [ %add, %cond.end ]
  ret i64 %retval.0, !dbg !3282
}
(TYPE=i64 (%struct.__STDIO_FILE_STRUCT.215*, i64, i64, i64)*)(TID=14)
[21c4f4] KLEE: WARNING: CreateCall! wrapper=__wrap__fp_out_narrow, origCallInst->getNumArgOperands()=4, 
[1780fd] KLEE: WARNING: getNumParams of wrapper= 5, type = 14
[510366] KLEE: WARNING: f.CONV:0, wrapper.CONV:0
[4322c4] KLEE: WARNING: Wrapper has bad signature! bad type of Args[2].type=4 =/= wrapper.type=10        LLVM refuses to create call.
[6c15e4] KLEE: WARNING: Failed to replace call to f=_fp_out_narrow

The mismatch happens on the type:

[4322c4] KLEE: WARNING: Wrapper has bad signature! bad type of Args[2].type=4 =/= wrapper.type=10        LLVM refuses to create call.

According to llvm/IR/type.h, 4 and 10 are:

    X86_FP80TyID,    ///<  4: 80-bit floating point type (X87)
/* ... */
    IntegerTyID,     ///< 10: Arbitrary bit width integers

Do you have any clue about this?

jordr avatar Dec 04 '19 16:12 jordr

Which klee-uclibc commit do you use in the _fp_out_narrow example?

davidtr1037 avatar Feb 29 '20 14:02 davidtr1037

I use klee-uclibc 07c4d2d1aff3858506c81ab1e7e4f4780dfe2a72

I figured this is related to variadic functions, I'll submit a patch when I get everything worked out. In any case, here is some useful assembly.ll from the example above:

; Function Attrs: nounwind uwtable
define internal i32 @__user_main(i32 %argc, i8** %argv) #0 {
entry:
  %call = call i32 (i32, i32, i32, i32, ...)* bitcast (i32 (...)* @_fp_out_narrow to i32 (i32, i32, i32, i32, ...)*)(i32 0, i32 0, i32 0, i32 0), !dbg !1271
  ret i32 0, !dbg !1272
}

jordr avatar Mar 03 '20 12:03 jordr