sea-dsa icon indicating copy to clipboard operation
sea-dsa copied to clipboard

Question about "function never returning"

Open adrianherrera opened this issue 2 years ago • 6 comments

Hi folks,

I am trying to apply sea-dsa to some codebases where some functions contain only calls to exit or abort (e.g., error-handling functions). I am trying to construct the MemorySSA, which reports a warning about these functions "never returning". This then results in the assert here failing.

What is it about functions not returning that would cause this to assertion to fail? If you have any pointers to help me get around this (and hopefully contribute a patch!) that would be greatly appreciated.

Thanks!

adrianherrera avatar Jan 27 '22 10:01 adrianherrera

The pointer you have is to mem-ssa that computes memory SSA form of the program. That code might silently assume that a function has a designated exit basic block. Functions that do not return might not have it.

Can you create a small repro for us to debug? Most of our own clients of sea-dsa ensure that functions always terminate by cutting out non-terminating paths. So we have probably missed some case here.

agurfinkel avatar Jan 27 '22 14:01 agurfinkel

Hey Arie,

Thanks for the reply!

Most of our own clients of sea-dsa ensure that functions always terminate by cutting out non-terminating paths.

Do you mean like what CLAM does (e.g., the RemoveUnreachableBlocksPass)? I guess these blocks are not actually non-terminating, because they explicitly call exit/abort/etc.

Currently, my client code is a relatively straight forward pass:

void MyPass::getAnalysisUsage(AnalysisUsage &AU) const {
  AU.setPreservesCFG();
  AU.addRequired<seadsa::ShadowMemPass>();
}

bool MyPass::runOnModule(Module &M) {
  auto &ShadowMem = getAnalysis<seadsa::ShadowMemPass>().getShadowMem();

  for (auto &F : M) {
    const auto *MemSSA = ShadowMem.getMemorySSA(F);
    outs() << "```";
    MemSSA->print(outs());
    outs() << "```\n";
  }

  // Remove the shadow memory annotations
  auto StripShadowMem = std::make_unique<seadsa::StripShadowMemPass>();
  StripShadowMem->runOnModule(M);

  return false;
}

And my first test program was the AFL fuzzer test program:

int main(int argc, char** argv) {

  char buf[8];

  if (read(0, buf, 8) < 1) {
    printf("Hum?\n");
    exit(1);
  }

  if (buf[0] == '0')
    printf("Looks like a zero to me!\n");
  else
    printf("A non-zero value? How quaint!\n");

  exit(0);

}

I'll have a deeper look at the CLAM source code to get some ideas (this is all pretty new to me!), but any pointers (hah!) would be greatly appreciated :)

~Adrian

adrianherrera avatar Jan 28 '22 09:01 adrianherrera

@adrianherrera : thanks for the test. I'll look at it. By non-terminating paths, @arie meant that the execution path does not return. You are probably thinking about non-terminating loops. For our clients (e.g., Clam and SeaHorn), we typically do not care if an execution terminates with the LLVM instruction unreachable. If you look at the bitcode generated by your test, you will see an unreachable LLVM instruction after each call to exit. The pass RemoveUnreachableBlockPass removes any block and predecessors that always terminate with an unreachable instruction.

caballa avatar Jan 28 '22 10:01 caballa

Thanks @caballa!

Given that, does it suffice to generalize HasReturn (as used here) to also allow for UnreachableInsts and then only mark a cell with a mem.out marker here if the terminator isa<ReturnInst> (and just leave it untouched if it's an UnreachableInst, as this indicates program termination anyway)?

adrianherrera avatar Feb 02 '22 23:02 adrianherrera

Hey @agurfinkel and @caballa,

Just wondering if you had any thoughts on https://github.com/seahorn/sea-dsa/pull/139?

adrianherrera avatar Feb 22 '22 02:02 adrianherrera

Hi @adrianherrera sorry for being so late. I still need more time to think about it. Sorry about that

caballa avatar Mar 03 '22 02:03 caballa