mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-12-01 01:31:26 +00:00
[FuzzMutate] Handle BB without predecessor, avoid insertion after musttail call
, avoid sinking token type
FuzzMutate didn't consider some corner cases and leads to mutation failure when mutating some modules. This patch fixes 3 bugs: - Add null check when encountering basic blocks without predecessor to avoid segmentation fault - Avoid insertion after `musttail call` instruction - Avoid sinking token type Unit tests are also added. Reviewed By: Peter Differential Revision: https://reviews.llvm.org/D151936
This commit is contained in:
parent
15a719de01
commit
258cd1fc38
@ -114,10 +114,16 @@ InjectorIRStrategy::chooseOperation(Value *Src, RandomIRBuilder &IB) {
|
||||
return *RS;
|
||||
}
|
||||
|
||||
static inline iterator_range<BasicBlock::iterator>
|
||||
getInsertionRange(BasicBlock &BB) {
|
||||
auto End = BB.getTerminatingMustTailCall() ? std::prev(BB.end()) : BB.end();
|
||||
return make_range(BB.getFirstInsertionPt(), End);
|
||||
}
|
||||
|
||||
void InjectorIRStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) {
|
||||
SmallVector<Instruction *, 32> Insts;
|
||||
for (auto I = BB.getFirstInsertionPt(), E = BB.end(); I != E; ++I)
|
||||
Insts.push_back(&*I);
|
||||
for (Instruction &I : getInsertionRange(BB))
|
||||
Insts.push_back(&I);
|
||||
if (Insts.size() < 1)
|
||||
return;
|
||||
|
||||
@ -360,6 +366,10 @@ void InsertFunctionStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) {
|
||||
|
||||
auto RS = makeSampler(IB.Rand, Functions);
|
||||
Function *F = RS.getSelection();
|
||||
// Some functions accept metadata type or token type as arguments.
|
||||
// We don't call those functions for now.
|
||||
// For example, `@llvm.dbg.declare(metadata, metadata, metadata)`
|
||||
// https://llvm.org/docs/SourceLevelDebugging.html#llvm-dbg-declare
|
||||
auto IsUnsupportedTy = [](Type *T) {
|
||||
return T->isMetadataTy() || T->isTokenTy();
|
||||
};
|
||||
@ -385,7 +395,7 @@ void InsertFunctionStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) {
|
||||
};
|
||||
|
||||
SmallVector<Instruction *, 32> Insts;
|
||||
for (Instruction &I : make_range(BB.getFirstInsertionPt(), BB.end()))
|
||||
for (Instruction &I : getInsertionRange(BB))
|
||||
Insts.push_back(&I);
|
||||
if (Insts.size() < 1)
|
||||
return;
|
||||
@ -411,7 +421,7 @@ void InsertFunctionStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) {
|
||||
|
||||
void InsertCFGStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) {
|
||||
SmallVector<Instruction *, 32> Insts;
|
||||
for (Instruction &I : make_range(BB.getFirstInsertionPt(), BB.end()))
|
||||
for (Instruction &I : getInsertionRange(BB))
|
||||
Insts.push_back(&I);
|
||||
if (Insts.size() < 1)
|
||||
return;
|
||||
@ -551,7 +561,7 @@ void InsertPHIStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) {
|
||||
PHI->addIncoming(Src, Pred);
|
||||
}
|
||||
SmallVector<Instruction *, 32> InstsAfter;
|
||||
for (Instruction &I : make_range(BB.getFirstInsertionPt(), BB.end()))
|
||||
for (Instruction &I : getInsertionRange(BB))
|
||||
InstsAfter.push_back(&I);
|
||||
IB.connectToSink(BB, InstsAfter, PHI);
|
||||
}
|
||||
@ -563,7 +573,7 @@ void SinkInstructionStrategy::mutate(Function &F, RandomIRBuilder &IB) {
|
||||
}
|
||||
void SinkInstructionStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) {
|
||||
SmallVector<Instruction *, 32> Insts;
|
||||
for (Instruction &I : make_range(BB.getFirstInsertionPt(), BB.end()))
|
||||
for (Instruction &I : getInsertionRange(BB))
|
||||
Insts.push_back(&I);
|
||||
if (Insts.size() < 1)
|
||||
return;
|
||||
@ -572,9 +582,9 @@ void SinkInstructionStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) {
|
||||
Instruction *Inst = Insts[Idx];
|
||||
// `Idx + 1` so we don't sink to ourselves.
|
||||
auto InstsAfter = ArrayRef(Insts).slice(Idx + 1);
|
||||
LLVMContext &C = BB.getParent()->getParent()->getContext();
|
||||
// Don't sink terminators, void function calls, etc.
|
||||
if (Inst->getType() != Type::getVoidTy(C))
|
||||
Type *Ty = Inst->getType();
|
||||
// Don't sink terminators, void function calls, token, etc.
|
||||
if (!Ty->isVoidTy() && !Ty->isTokenTy())
|
||||
// Find a new sink and wire up the results of the operation.
|
||||
IB.connectToSink(BB, InstsAfter, Inst);
|
||||
}
|
||||
|
@ -27,7 +27,12 @@ using namespace fuzzerop;
|
||||
static std::vector<BasicBlock *> getDominators(BasicBlock *BB) {
|
||||
std::vector<BasicBlock *> ret;
|
||||
DominatorTree DT(*BB->getParent());
|
||||
DomTreeNode *Node = DT[BB]->getIDom();
|
||||
DomTreeNode *Node = DT.getNode(BB);
|
||||
// It's possible that an orphan block is not in the dom tree. In that case we
|
||||
// just return nothing.
|
||||
if (!Node)
|
||||
return ret;
|
||||
Node = Node->getIDom();
|
||||
while (Node && Node->getBlock()) {
|
||||
ret.push_back(Node->getBlock());
|
||||
// Get parent block.
|
||||
@ -41,7 +46,12 @@ static std::vector<BasicBlock *> getDominators(BasicBlock *BB) {
|
||||
static std::vector<BasicBlock *> getDominatees(BasicBlock *BB) {
|
||||
DominatorTree DT(*BB->getParent());
|
||||
std::vector<BasicBlock *> ret;
|
||||
for (DomTreeNode *Child : DT[BB]->children())
|
||||
DomTreeNode *Parent = DT.getNode(BB);
|
||||
// It's possible that an orphan block is not in the dom tree. In that case we
|
||||
// just return nothing.
|
||||
if (!Parent)
|
||||
return ret;
|
||||
for (DomTreeNode *Child : Parent->children())
|
||||
ret.push_back(Child->getBlock());
|
||||
uint64_t Idx = 0;
|
||||
while (Idx < ret.size()) {
|
||||
|
@ -563,4 +563,53 @@ TEST(RandomIRBuilderTest, DoNotCallPointerWhenSink) {
|
||||
}
|
||||
ASSERT_FALSE(Modified);
|
||||
}
|
||||
|
||||
TEST(RandomIRBuilderTest, SrcAndSinkWOrphanBlock) {
|
||||
const char *Source = "\n\
|
||||
define i1 @test(i1 %Bool, i32 %Int, i64 %Long) { \n\
|
||||
Entry: \n\
|
||||
%Eq0 = icmp eq i64 %Long, 0 \n\
|
||||
br i1 %Eq0, label %True, label %False \n\
|
||||
True: \n\
|
||||
%Or = or i1 %Bool, %Eq0 \n\
|
||||
ret i1 %Or \n\
|
||||
False: \n\
|
||||
%And = and i1 %Bool, %Eq0 \n\
|
||||
ret i1 %And \n\
|
||||
Orphan_1: \n\
|
||||
%NotBool = sub i1 1, %Bool \n\
|
||||
ret i1 %NotBool \n\
|
||||
Orphan_2: \n\
|
||||
%Le42 = icmp sle i32 %Int, 42 \n\
|
||||
ret i1 %Le42 \n\
|
||||
}";
|
||||
LLVMContext Ctx;
|
||||
std::mt19937 mt(Seed);
|
||||
std::uniform_int_distribution<int> RandInt(INT_MIN, INT_MAX);
|
||||
std::array<Type *, 3> IntTys(
|
||||
{Type::getInt64Ty(Ctx), Type::getInt32Ty(Ctx), Type::getInt1Ty(Ctx)});
|
||||
std::vector<Value *> Constants;
|
||||
for (Type *IntTy : IntTys) {
|
||||
for (size_t v : {1, 42}) {
|
||||
Constants.push_back(ConstantInt::get(IntTy, v));
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 10; i++) {
|
||||
RandomIRBuilder IB(RandInt(mt), IntTys);
|
||||
std::unique_ptr<Module> M = parseAssembly(Source, Ctx);
|
||||
Function &F = *M->getFunction("test");
|
||||
for (BasicBlock &BB : F) {
|
||||
SmallVector<Instruction *, 4> Insts;
|
||||
for (Instruction &I : BB) {
|
||||
Insts.push_back(&I);
|
||||
}
|
||||
for (int j = 0; j < 10; j++) {
|
||||
IB.findOrCreateSource(BB, Insts);
|
||||
}
|
||||
for (Value *V : Constants) {
|
||||
IB.connectToSink(BB, Insts, V);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
@ -129,6 +129,30 @@ TEST(InjectorIRStrategyTest, LargeInsertion) {
|
||||
mutateAndVerifyModule(Source, Mutator, 100);
|
||||
}
|
||||
|
||||
TEST(InjectorIRStrategyTest, InsertWMustTailCall) {
|
||||
StringRef Source = "\n\
|
||||
define i1 @recursive() { \n\
|
||||
Entry: \n\
|
||||
%Ret = musttail call i1 @recursive() \n\
|
||||
ret i1 %Ret \n\
|
||||
}";
|
||||
auto Mutator = createInjectorMutator();
|
||||
ASSERT_TRUE(Mutator);
|
||||
mutateAndVerifyModule(Source, Mutator, 100);
|
||||
}
|
||||
|
||||
TEST(InjectorIRStrategyTest, InsertWTailCall) {
|
||||
StringRef Source = "\n\
|
||||
define i1 @recursive() { \n\
|
||||
Entry: \n\
|
||||
%Ret = tail call i1 @recursive() \n\
|
||||
ret i1 %Ret \n\
|
||||
}";
|
||||
auto Mutator = createInjectorMutator();
|
||||
ASSERT_TRUE(Mutator);
|
||||
mutateAndVerifyModule(Source, Mutator, 100);
|
||||
}
|
||||
|
||||
TEST(InstDeleterIRStrategyTest, EmptyFunction) {
|
||||
// Test that we don't crash even if we can't remove from one of the functions.
|
||||
|
||||
@ -576,6 +600,19 @@ TEST(SinkInstructionStrategy, Operand) {
|
||||
mutateAndVerifyModule<SinkInstructionStrategy>(Source);
|
||||
}
|
||||
|
||||
TEST(SinkInstructionStrategy, DoNotSinkTokenType) {
|
||||
StringRef Source = "\n\
|
||||
declare ptr @fake_personality_function() \n\
|
||||
declare token @llvm.experimental.gc.statepoint.p0(i64 immarg %0, i32 immarg %1, ptr %2, i32 immarg %3, i32 immarg %4, ...) \n\
|
||||
define void @test() gc \"statepoint-example\" personality ptr @fake_personality_function { \n\
|
||||
Entry: \n\
|
||||
%token1 = call token (i64, i32, ptr, i32, i32, ...) \
|
||||
@llvm.experimental.gc.statepoint.p0(i64 0, i32 0, ptr elementtype(ptr addrspace(1) ()) undef, i32 0, i32 0, i32 0, i32 0) \n\
|
||||
ret void \n\
|
||||
}";
|
||||
mutateAndVerifyModule<SinkInstructionStrategy>(Source);
|
||||
}
|
||||
|
||||
static void VerifyBlockShuffle(StringRef Source) {
|
||||
LLVMContext Ctx;
|
||||
auto Mutator = createMutator<ShuffleBlockStrategy>();
|
||||
|
Loading…
Reference in New Issue
Block a user