This is an archive of the discontinued LLVM Phabricator instance.

Fix sanitizers' FastUnwindStack() to work in the unwinding state
Needs ReviewPublic

Authored by kutuzov.viktor.84 on Jul 10 2014, 6:15 AM.

Details

Reviewers
kcc
samsonov
Summary

According to GetStackTraceWithPcBpAndContext() implementation:

void GetStackTraceWithPcBpAndContext(StackTrace *stack, uptr max_depth, uptr pc,
                                     uptr bp, void *context, bool fast) {
#if SANITIZER_WINDOWS
  stack->Unwind(max_depth, pc, bp, context, 0, 0, fast);
#else
  ...
    if ((t = GetCurrentThread()) && !t->isUnwinding()) {
      ...
    } else if (t == 0 && !fast) {
        /* If GetCurrentThread() has failed, try to do slow unwind anyways. */
      stack->Unwind(max_depth, pc, bp, context, 0, 0, false);
    }
  }
#endif  // SANITIZER_WINDOWS
}

if the current thread is in the unwinding state, the 'stack_top' and 'stack_bottom' parameters of the StackTrace::FastUnwindStack() function both set to 0. That, in turn, results in the sanity condition for 'stack_top' within that function always met and thus no call stack is provided.

Diff Detail

Event Timeline

kutuzov.viktor.84 retitled this revision from to Fix sanitizers' FastUnwindStack() to work in the unwinding state.
kutuzov.viktor.84 updated this object.
kutuzov.viktor.84 edited the test plan for this revision. (Show Details)
kutuzov.viktor.84 added reviewers: kcc, samsonov.
kutuzov.viktor.84 added a subscriber: Unknown Object (MLST).
kcc edited edge metadata.Jul 10 2014, 6:22 AM

Oh my... I wonder if a test case is possible here...

On FreeBSD we only rely on the fast unwinding so without that fix some tests like null_deref.cc fail.

I will try to prepare a Linux test. I suspect the 'fast_unwind_on_fatal' option may help...

samsonov edited edge metadata.Jul 10 2014, 11:20 AM

Wait, comment in GetStackTraceWithPcBpAndContext explicitly says that we're "trying to do slow unwind anyways", and here you're running a fast unwind. FastUnwindStack just should not be called with "0/0" as stack top/bottom, it's way too confusing.

What is the case when you need to get a stack trace while you're already unwinding?

If you absolutely need this change, I'd prefer to either fire a call to WillUseFastUnwind() in GetStackTraceWithPcBpAndContext(), or provide better fake values for stack_top/stack_bottom in the call to StackTrace::Unwind().

Wait, comment in GetStackTraceWithPcBpAndContext explicitly says that we're "trying to do slow unwind anyways", and here you're running a fast unwind.

Yes, I didn't notice the 't == 0' condition. Thanks.

What is the case when you need to get a stack trace while you're already unwinding?

In my specific case with null_deref.cc failure on FreeBSD GetCurrentThread() return 0 when called from within a signal handler (ReportSIGSEGV, namely). This is why it falls to the slow unwinding branch.

I'm not sure what this line:

} else if (t == 0 && !fast) {

is supposed to do. Is the 'fast' parameter a hint/recommendation or a requirement? Note is that the 'fast' parameter is set to whatever 'common_flags()->fast_unwind_on_fatal' is, which is false by default--even on platforms where fast unwinding is the only option (Mac, FreeBSD). On the other hand, the condition above means no stack trace will be provided if 'fast' is raised.

So, it seems like we should either raise 'fast_unwind_on_fatal' (and keep it so) on platforms like Mac and FreeBSD or remove the '!fast' part of the condition.

If you absolutely need this change, I'd prefer to either fire a call to WillUseFastUnwind() in GetStackTraceWithPcBpAndContext(), or provide better fake values for stack_top/stack_bottom in the call to StackTrace::Unwind().

Since FastUnwindStack() takes the bound values into account, I believe we have to choose some realistic numbers anyway?