Commit fc0a52df by Karl Schimpf

Check that address is i32 for indirect calls.

Fixes bug where code did not check that the address of an indirect call must be i32. BUG= https://code.google.com/p/nativeclient/issues/detail?id=4321 R=stichnot@chromium.org Review URL: https://codereview.chromium.org/1363983002 .
parent e0b829f8
...@@ -1598,7 +1598,7 @@ private: ...@@ -1598,7 +1598,7 @@ private:
std::string Buffer; std::string Buffer;
raw_string_ostream StrBuf(Buffer); raw_string_ostream StrBuf(Buffer);
StrBuf << InstructionName << " address not " << PtrType StrBuf << InstructionName << " address not " << PtrType
<< ". Found: " << *Op; << ". Found: " << Op->getType();
Error(StrBuf.str()); Error(StrBuf.str());
return false; return false;
} }
...@@ -2048,6 +2048,24 @@ private: ...@@ -2048,6 +2048,24 @@ private:
return LocalOperands.front(); return LocalOperands.front();
return Context->getGlobalConstantByID(0); return Context->getGlobalConstantByID(0);
} }
void verifyCallArgTypeMatches(Ice::FunctionDeclaration *Fcn,
Ice::SizeT Index, Ice::Type ArgType,
Ice::Type ParamType) {
if (ArgType != ParamType) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Argument " << (Index + 1) << " of " << printName(Fcn)
<< " expects " << ParamType << ". Found: " << ArgType;
Error(StrBuf.str());
}
}
const Ice::IceString printName(Ice::FunctionDeclaration *Fcn) {
if (Fcn)
return Fcn->getName();
return "function";
}
}; };
void FunctionParser::ExitBlock() { void FunctionParser::ExitBlock() {
...@@ -2660,176 +2678,144 @@ void FunctionParser::ProcessRecord() { ...@@ -2660,176 +2678,144 @@ void FunctionParser::ProcessRecord() {
ParamsStartIndex = 3; ParamsStartIndex = 3;
} }
// Extract out the called function and its return type.
uint32_t CalleeIndex = convertRelativeToAbsIndex(Values[1], BaseIndex); uint32_t CalleeIndex = convertRelativeToAbsIndex(Values[1], BaseIndex);
Ice::Operand *Callee = getOperand(CalleeIndex); Ice::Operand *Callee = getOperand(CalleeIndex);
// Pull out signature/return type of call (if possible).
Ice::FunctionDeclaration *Fcn = nullptr;
const Ice::FuncSigType *Signature = nullptr; const Ice::FuncSigType *Signature = nullptr;
Ice::Type ReturnType = Ice::IceType_void; Ice::Type ReturnType = Ice::IceType_void;
const Ice::Intrinsics::FullIntrinsicInfo *IntrinsicInfo = nullptr; const Ice::Intrinsics::FullIntrinsicInfo *IntrinsicInfo = nullptr;
// Name of function if a direct call/intrinsic. Null otherwise.
Ice::FunctionDeclaration *Fcn = nullptr;
if (Record.GetCode() == naclbitc::FUNC_CODE_INST_CALL) { if (Record.GetCode() == naclbitc::FUNC_CODE_INST_CALL) {
Fcn = Context->getFunctionByID(CalleeIndex); Fcn = Context->getFunctionByID(CalleeIndex);
Signature = &Fcn->getSignature(); Signature = &Fcn->getSignature();
ReturnType = Signature->getReturnType(); ReturnType = Signature->getReturnType();
Ice::SizeT NumParams = Values.size() - ParamsStartIndex;
// Check if this direct call is to an Intrinsic (starts with "llvm.") if (NumParams != Signature->getNumArgs()) {
bool BadIntrinsic;
const Ice::IceString &Name = Fcn->getName();
IntrinsicInfo = getTranslator().getContext()->getIntrinsicsInfo().find(
Name, BadIntrinsic);
if (BadIntrinsic) {
std::string Buffer; std::string Buffer;
raw_string_ostream StrBuf(Buffer); raw_string_ostream StrBuf(Buffer);
StrBuf << "Invalid PNaCl intrinsic call to " << Name; StrBuf << "Call to " << printName(Fcn) << " has " << NumParams
<< " parameters. Signature expects: " << Signature->getNumArgs();
Error(StrBuf.str()); Error(StrBuf.str());
if (ReturnType != Ice::IceType_void) if (ReturnType != Ice::IceType_void)
appendErrorInstruction(ReturnType); setNextLocalInstIndex(nullptr);
return; return;
} }
} else {
ReturnType = Context->getSimpleTypeByID(Values[2]);
}
// Check return type. // Check if this direct call is to an Intrinsic (starts with "llvm.")
if (IntrinsicInfo == nullptr && !isCallReturnType(ReturnType)) { bool BadIntrinsic;
std::string Buffer; IntrinsicInfo = getTranslator().getContext()->getIntrinsicsInfo().find(
raw_string_ostream StrBuf(Buffer); Fcn->getName(), BadIntrinsic);
StrBuf << "Return type of called function is invalid: " << ReturnType; if (BadIntrinsic) {
Error(StrBuf.str());
ReturnType = Ice::IceType_i32;
}
// Extract call information.
uint64_t CCInfo = Values[0];
CallingConv::ID CallingConv;
if (!naclbitc::DecodeCallingConv(CCInfo >> 1, CallingConv)) {
std::string Buffer; std::string Buffer;
raw_string_ostream StrBuf(Buffer); raw_string_ostream StrBuf(Buffer);
StrBuf << "Function call calling convention value " << (CCInfo >> 1) StrBuf << "Invalid PNaCl intrinsic call to " << Fcn->getName();
<< " not understood.";
Error(StrBuf.str()); Error(StrBuf.str());
if (ReturnType != Ice::IceType_void) IntrinsicInfo = nullptr;
appendErrorInstruction(ReturnType);
return;
} }
bool IsTailCall = static_cast<bool>(CCInfo & 1); if (IntrinsicInfo && IntrinsicInfo->getNumArgs() != NumParams) {
Ice::SizeT NumParams = Values.size() - ParamsStartIndex;
if (Signature && NumParams != Signature->getNumArgs()) {
std::string Buffer; std::string Buffer;
raw_string_ostream StrBuf(Buffer); raw_string_ostream StrBuf(Buffer);
StrBuf << "Call has " << NumParams StrBuf << "Call to " << printName(Fcn) << " has " << NumParams
<< " parameters. Signature expects: " << Signature->getNumArgs(); << " parameters. Intrinsic expects: " << Signature->getNumArgs();
Error(StrBuf.str()); Error(StrBuf.str());
// Error recover by only checking parameters in both signature and call.
NumParams = std::min(NumParams, Signature->getNumArgs());
}
if (isIRGenerationDisabled()) {
assert(Callee == nullptr);
// Check that parameters are defined.
for (Ice::SizeT ParamIndex = 0; ParamIndex < NumParams; ++ParamIndex) {
assert(getRelativeOperand(Values[ParamsStartIndex + ParamIndex],
BaseIndex) == nullptr);
}
// Define value slot only if value returned.
if (ReturnType != Ice::IceType_void) if (ReturnType != Ice::IceType_void)
setNextLocalInstIndex(nullptr); setNextLocalInstIndex(nullptr);
return; return;
} }
// Create the call instruction. } else { // Record.GetCode() == naclbitc::FUNC_CODE_INST_CALL_INDIRECT
Ice::Variable *Dest = (ReturnType == Ice::IceType_void) // There is no signature. Assume defined by parameter types.
? nullptr ReturnType = Context->getSimpleTypeByID(Values[2]);
: getNextInstVar(ReturnType); if (!isIRGenerationDisabled() && Callee != nullptr)
std::unique_ptr<Ice::InstCall> Inst; isValidPointerType(Callee, "Call indirect");
if (IntrinsicInfo) {
Inst.reset(Ice::InstIntrinsicCall::create(Func.get(), NumParams, Dest,
Callee, IntrinsicInfo->Info));
} else {
Inst.reset(Ice::InstCall::create(Func.get(), NumParams, Dest, Callee,
IsTailCall));
} }
// Add parameters. if (Callee == nullptr && !isIRGenerationDisabled())
for (Ice::SizeT ParamIndex = 0; ParamIndex < NumParams; ++ParamIndex) { return;
Ice::Operand *Op =
getRelativeOperand(Values[ParamsStartIndex + ParamIndex], BaseIndex); // Extract out the the call parameters.
SmallVector<Ice::Operand*, 8> Params;
for (Ice::SizeT Index = ParamsStartIndex; Index < Values.size(); ++Index) {
Ice::Operand *Op = getRelativeOperand(Values[Index], BaseIndex);
if (isIRGenerationDisabled())
continue;
if (Op == nullptr) { if (Op == nullptr) {
std::string Buffer; std::string Buffer;
raw_string_ostream StrBuf(Buffer); raw_string_ostream StrBuf(Buffer);
StrBuf << "Parameter " << ParamIndex << " of call not defined"; StrBuf << "Parameter " << (Index - ParamsStartIndex + 1)
<< " of " << printName(Fcn) << " is not defined";
Error(StrBuf.str()); Error(StrBuf.str());
if (ReturnType != Ice::IceType_void) if (ReturnType != Ice::IceType_void)
appendErrorInstruction(ReturnType); setNextLocalInstIndex(nullptr);
return; return;
} }
Params.push_back(Op);
// Check that parameter type is valid.
if (Signature) {
if (Op->getType() != Signature->getArgType(ParamIndex)) {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Call argument " << *Op << " expects "
<< Signature->getArgType(ParamIndex)
<< ". Found: " << Op->getType();
Error(StrBuf.str());
} else if (IntrinsicInfo == nullptr &&
!isCallParameterType(Op->getType())) {
// TODO(kschimpf): Move this check to the function declaration, so
// that it only needs to be checked once.
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Call argument " << *Op
<< " matches declaration but has invalid type: "
<< Op->getType();
Error(StrBuf.str());
} }
} else if (!isCallParameterType(Op->getType())) {
// Check return type.
if (IntrinsicInfo == nullptr && !isCallReturnType(ReturnType)) {
std::string Buffer; std::string Buffer;
raw_string_ostream StrBuf(Buffer); raw_string_ostream StrBuf(Buffer);
StrBuf << "Call argument " << *Op StrBuf << "Return type of " << printName(Fcn) << " is invalid: "
<< " has invalid type: " << Op->getType(); << ReturnType;
Error(StrBuf.str()); Error(StrBuf.str());
ReturnType = Ice::IceType_i32;
} }
Inst->addArg(Op);
if (isIRGenerationDisabled()) {
if (ReturnType != Ice::IceType_void)
setNextLocalInstIndex(nullptr);
return;
} }
// If intrinsic call, validate call signature. // Type check call parameters.
for (Ice::SizeT Index = 0; Index < Params.size(); ++Index) {
Ice::Operand *Op = Params[Index];
Ice::Type OpType = Op->getType();
if (Signature)
verifyCallArgTypeMatches(
Fcn, Index, OpType, Signature->getArgType(Index));
if (IntrinsicInfo) { if (IntrinsicInfo) {
Ice::SizeT ArgIndex = 0; verifyCallArgTypeMatches(Fcn, Index, OpType,
switch (IntrinsicInfo->validateCall(Inst.get(), ArgIndex)) { IntrinsicInfo->getArgType(Index));
case Ice::Intrinsics::IsValidCall: } else if (!isCallParameterType(OpType)) {
break;
case Ice::Intrinsics::BadReturnType: {
std::string Buffer; std::string Buffer;
raw_string_ostream StrBuf(Buffer); raw_string_ostream StrBuf(Buffer);
StrBuf << "Intrinsic " << Fcn->getName() << " expects return type" StrBuf << "Argument " << *Op << " of " << printName(Fcn)
<< IntrinsicInfo->getReturnType() << " has invalid type: " << Op->getType();
<< ". Found: " << Inst->getReturnType();
Error(StrBuf.str()); Error(StrBuf.str());
break; appendErrorInstruction(ReturnType);
} }
case Ice::Intrinsics::WrongNumOfArgs: {
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Intrinsic " << Fcn->getName() << " expects "
<< IntrinsicInfo->getNumArgs()
<< ". Found: " << Inst->getNumArgs();
Error(StrBuf.str());
break;
} }
case Ice::Intrinsics::WrongCallArgType: {
// Extract call information.
uint64_t CCInfo = Values[0];
CallingConv::ID CallingConv;
if (!naclbitc::DecodeCallingConv(CCInfo >> 1, CallingConv)) {
std::string Buffer; std::string Buffer;
raw_string_ostream StrBuf(Buffer); raw_string_ostream StrBuf(Buffer);
StrBuf << "Intrinsic " << Fcn->getName() << " expects " StrBuf << "Function call calling convention value " << (CCInfo >> 1)
<< IntrinsicInfo->getArgType(ArgIndex) << " for argument " << " not understood.";
<< (ArgIndex + 1)
<< ". Found: " << Inst->getArg(ArgIndex)->getType();
Error(StrBuf.str()); Error(StrBuf.str());
break; appendErrorInstruction(ReturnType);
} return;
}
} }
bool IsTailCall = static_cast<bool>(CCInfo & 1);
// Create the call instruction.
Ice::Variable *Dest = (ReturnType == Ice::IceType_void)
? nullptr
: getNextInstVar(ReturnType);
std::unique_ptr<Ice::InstCall> Inst;
if (IntrinsicInfo) {
Inst.reset(Ice::InstIntrinsicCall::create(Func.get(), Params.size(), Dest,
Callee, IntrinsicInfo->Info));
} else {
Inst.reset(Ice::InstCall::create(Func.get(), Params.size(), Dest, Callee,
IsTailCall));
}
for (Ice::Operand *Param : Params)
Inst->addArg(Param);
CurrentNode->appendInst(Inst.release()); CurrentNode->appendInst(Inst.release());
return; return;
} }
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
11,1,2,1; 11,1,2,1;
10,2; 10,2;
2,3,2,1; 2,3,2,1;
34,0,5,0; 34,0,5,1;
2,1,5,0; 2,1,5,0;
65534; 65534;
5534; 5534;
......
65535,8,2;
1,1;
65535,17,2;
1,5;
2;
21,0,0;
7,32;
3;
21,0,0,2,3;
65534;
8,1,0,1,0;
8,4,0,0,0;
65535,19,2;
5,0;
65534;
65535,14,2;
1,1,102;
1,0,103;
65534;
65535,12,2;
1,1;
44,0,1,0;
10;
65534;
65534;
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
; RUN: -bitcode-format=pnacl -notranslate -build-on-read 2>&1 \ ; RUN: -bitcode-format=pnacl -notranslate -build-on-read 2>&1 \
; RUN: | FileCheck %s ; RUN: | FileCheck %s
; CHECK: Intrinsic llvm.nacl.setjmp expects i32 for argument 1. Found: double ; CHECK: Argument 1 of llvm.nacl.setjmp expects i32. Found: double
; RUN: pnacl-bcfuzz -bitcode-as-text \ ; RUN: pnacl-bcfuzz -bitcode-as-text \
; RUN: %p/Inputs/bad-intrinsic-arg.tbc -output - \ ; RUN: %p/Inputs/bad-intrinsic-arg.tbc -output - \
......
...@@ -10,6 +10,6 @@ declare void @f(i8); ...@@ -10,6 +10,6 @@ declare void @f(i8);
define void @Test() { define void @Test() {
entry: entry:
call void @f(i8 1) call void @f(i8 1)
; CHECK: Call argument 1 matches declaration but has invalid type: i8 ; CHECK: Argument 1 of f has invalid type: i8
ret void ret void
} }
...@@ -10,7 +10,7 @@ declare i1 @f(); ...@@ -10,7 +10,7 @@ declare i1 @f();
define void @Test() { define void @Test() {
entry: entry:
%v = call i1 @f() %v = call i1 @f()
; CHECK: Return type of called function is invalid: i1 ; CHECK: Return type of f is invalid: i1
ret void ret void
} }
...@@ -8,6 +8,6 @@ define void @CallIndirectI32(i32 %f_addr) { ...@@ -8,6 +8,6 @@ define void @CallIndirectI32(i32 %f_addr) {
entry: entry:
%f = inttoptr i32 %f_addr to i32(i8)* %f = inttoptr i32 %f_addr to i32(i8)*
%r = call i32 %f(i8 1) %r = call i32 %f(i8 1)
; CHECK: Call argument 1 has invalid type: i8 ; CHECK: Argument 1 of function has invalid type: i8
ret void ret void
} }
; Tests that we check the call address is a pointer on an indirect call.
; REQUIRES: no_minimal_build
; RUN: not %pnacl_sz -bitcode-as-text \
; RUN: %p/Inputs/indirect-call-on-float.tbc \
; RUN: -bitcode-format=pnacl -notranslate -build-on-read 2>&1 \
; RUN: | FileCheck %s
; CHECK: Call indirect address not i32. Found: float
; RUN: pnacl-bcfuzz -bitcode-as-text \
; RUN: %p/Inputs/indirect-call-on-float.tbc -output - \
; RUN: | not pnacl-bcdis -no-records | FileCheck -check-prefix=ASM %s
; ASM: function void @f1(i32 %p0, float %p1) { // BlockID = 12
; ASM: blocks 1;
; ASM: %b0:
; ASM: call void %p1();
; ASM: ret void;
; ASM: }
...@@ -9,11 +9,13 @@ ...@@ -9,11 +9,13 @@
; CHECK: Module valuesymtab not allowed after function blocks ; CHECK: Module valuesymtab not allowed after function blocks
; RUN: pnacl-bcfuzz -bitcode-as-text %p/Inputs/symtab-after-fcn.tbc \ ; RUN: pnacl-bcfuzz -bitcode-as-text %p/Inputs/symtab-after-fcn.tbc \
; RUN: -output - | pnacl-bcdis -no-records | FileCheck -check-prefix=ASM %s ; RUN: -output - | not pnacl-bcdis -no-records \
; RUN: | FileCheck -check-prefix=ASM %s
; ASM: module { // BlockID = 8 ; ASM: module { // BlockID = 8
; ASM: function void @f0() { // BlockID = 12 ; ASM: function void @f0() { // BlockID = 12
; ASM: } ; ASM: }
; ASM: valuesymtab { // BlockID = 14 ; ASM: valuesymtab { // BlockID = 14
; ASM: Error({{.*}}): Module symbol table must appear before function blocks
; ASM: } ; ASM: }
; ASM: } ; ASM: }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment