[FuzzMutate] Skip EHPad during mutation and avoid replacing callee with pointer when...
authorHenry Yu <hnryu@ucdavis.edu>
Wed, 26 Apr 2023 23:43:48 +0000 (16:43 -0700)
committerPeter Rong <PeterRong96@gmail.com>
Wed, 26 Apr 2023 23:45:49 +0000 (16:45 -0700)
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

llvm/include/llvm/FuzzMutate/IRMutator.h
llvm/lib/FuzzMutate/IRMutator.cpp
llvm/lib/FuzzMutate/RandomIRBuilder.cpp
llvm/unittests/FuzzMutate/RandomIRBuilderTest.cpp
llvm/unittests/FuzzMutate/StrategiesTest.cpp

index 9600246..e01b911 100644 (file)
@@ -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();
index 2d6c83e..58d58de 100644 (file)
@@ -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) {
@@ -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()))) {
index 4359ae6..2ebd0c8 100644 (file)
@@ -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:
index 7afedf7..aed9589 100644 (file)
@@ -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
index 1de027c..850ae16 100644 (file)
@@ -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