Skip to content

Commit

Permalink
[FuzzMutate] Skip EHPad during mutation and avoid replacing callee wi…
Browse files Browse the repository at this point in the history
…th pointer when sinking

This patch addresses 2 problems:

- In `ShuffleBlockStrategy`, when `BB` is an EHPad, `BB.getFirstInsertionPt()` will return `BB.end()`, which cannot be dereferenced and will cause crash in following loop.
- In `isCompatibleReplacement`, a call instruction's callee might be replaced by a pointer, causing 2 subproblems:
  - we cannot guarantee that the pointer is a function pointer (even if it is, we cannot guarantee it matches the signature).
  - after such a replacement, `getCalledFunction` will from then on return `nullptr` (since it's indirect call) which causes Segmentation Fault in the lines below.

This patch fixes the first problem by checking if a block to be mutated is an EHPad in base class `IRMutationStrategy` and skipping mutating it if so.

This patch fixes the second problem by avoiding replacing callee with pointer and adding a null check for indirect calls.

Reviewed By: Peter

Differential Revision: https://reviews.llvm.org/D148853
  • Loading branch information
HazyFish authored and DataCorrupted committed Apr 26, 2023
1 parent 5b7fa4a commit 66892f2
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 4 deletions.
1 change: 1 addition & 0 deletions llvm/include/llvm/FuzzMutate/IRMutator.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class InjectorIRStrategy : public IRMutationStrategy {
RandomIRBuilder &IB);

public:
InjectorIRStrategy() : Operations(getDefaultOps()) {}
InjectorIRStrategy(std::vector<fuzzerop::OpDescriptor> &&Operations)
: Operations(std::move(Operations)) {}
static std::vector<fuzzerop::OpDescriptor> getDefaultOps();
Expand Down
6 changes: 4 additions & 2 deletions llvm/lib/FuzzMutate/IRMutator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ void IRMutationStrategy::mutate(Module &M, RandomIRBuilder &IB) {
}

void IRMutationStrategy::mutate(Function &F, RandomIRBuilder &IB) {
mutate(*makeSampler(IB.Rand, make_pointer_range(F)).getSelection(), IB);
auto Range = make_filter_range(make_pointer_range(F),
[](BasicBlock *BB) { return !BB->isEHPad(); });

mutate(*makeSampler(IB.Rand, Range).getSelection(), IB);
}

void IRMutationStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) {
Expand Down Expand Up @@ -566,7 +569,6 @@ void SinkInstructionStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) {
}

void ShuffleBlockStrategy::mutate(BasicBlock &BB, RandomIRBuilder &IB) {

SmallPtrSet<Instruction *, 8> AliveInsts;
for (auto &I : make_early_inc_range(make_range(
BB.getFirstInsertionPt(), BB.getTerminator()->getIterator()))) {
Expand Down
11 changes: 9 additions & 2 deletions llvm/lib/FuzzMutate/RandomIRBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,15 @@ static bool isCompatibleReplacement(const Instruction *I, const Use &Operand,
case Instruction::Call:
case Instruction::Invoke:
case Instruction::CallBr: {
const CallBase *II = cast<CallBase>(I);
const Function *Callee = II->getCalledFunction();
const Function *Callee = cast<CallBase>(I)->getCalledFunction();
// If it's an indirect call, give up.
if (!Callee)
return false;
// If callee is not an intrinsic, operand 0 is the function to be called.
// Since we cannot assume that the replacement is a function pointer,
// we give up.
if (!Callee->getIntrinsicID() && OperandNo == 0)
return false;
return !Callee->hasParamAttribute(OperandNo, Attribute::ImmArg);
}
default:
Expand Down
30 changes: 30 additions & 0 deletions llvm/unittests/FuzzMutate/RandomIRBuilderTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -533,4 +533,34 @@ TEST(RandomIRBuilderTest, sinkToInstrinsic) {
}
ASSERT_FALSE(Modified);
}

TEST(RandomIRBuilderTest, DoNotCallPointerWhenSink) {
const char *Source = "\n\
declare void @g() \n\
define void @f(ptr %ptr) { \n\
Entry: \n\
call void @g() \n\
ret void \n\
}";
LLVMContext Ctx;
std::mt19937 mt(Seed);
std::uniform_int_distribution<int> RandInt(INT_MIN, INT_MAX);

RandomIRBuilder IB(RandInt(mt), {});
std::unique_ptr<Module> M = parseAssembly(Source, Ctx);
Function &F = *M->getFunction("f");
BasicBlock &BB = F.getEntryBlock();
bool Modified = false;

Instruction *I = &*BB.begin();
for (int i = 0; i < 20; i++) {
Value *OldOperand = I->getOperand(0);
Value *Src = F.getArg(0);
IB.connectToSink(BB, {I}, Src);
Value *NewOperand = I->getOperand(0);
Modified |= (OldOperand != NewOperand);
ASSERT_FALSE(verifyModule(*M, &errs()));
}
ASSERT_FALSE(Modified);
}
} // namespace
26 changes: 26 additions & 0 deletions llvm/unittests/FuzzMutate/StrategiesTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -665,4 +665,30 @@ TEST(ShuffleBlockStrategy, ShuffleLoop) {
}";
VerifyBlockShuffle(Source);
}

TEST(AllStrategies, SkipEHPad) {
StringRef Source = "\n\
define void @f(i32 %x) personality ptr @__CxxFrameHandler3 { \n\
entry: \n\
invoke void @g() to label %try.cont unwind label %catch.dispatch \n\
catch.dispatch: \n\
%0 = catchswitch within none [label %catch] unwind to caller \n\
catch: \n\
%1 = catchpad within %0 [ptr null, i32 64, ptr null] \n\
catchret from %1 to label %try.cont \n\
try.cont: \n\
ret void \n\
} \n\
declare void @g() \n\
declare i32 @__CxxFrameHandler3(...) \n\
";

mutateAndVerifyModule<ShuffleBlockStrategy>(Source);
mutateAndVerifyModule<InsertPHIStrategy>(Source);
mutateAndVerifyModule<InsertFunctionStrategy>(Source);
mutateAndVerifyModule<InsertCFGStrategy>(Source);
mutateAndVerifyModule<SinkInstructionStrategy>(Source);
mutateAndVerifyModule<InjectorIRStrategy>(Source);
mutateAndVerifyModule<InstModificationIRStrategy>(Source);
}
} // namespace

0 comments on commit 66892f2

Please sign in to comment.